【CTF题解】BUUCTF - cat flag 命令注入与字符剥离绕过
[BUUCTF] cat flag 题解
1. 题目源码
题目给出的 PHP 源代码如下:
<?php
if (isset($_GET['cmd'])) {
$cmd = $_GET['cmd'];
if (!preg_match('/flag/i', $cmd))
{
$cmd = escapeshellarg($cmd);
system('cat ' . $cmd);
}
} else {
highlight_file(__FILE__);
}
?>
逻辑分析:
- 程序通过
GET方式接收cmd参数。 - 使用正则
preg_match('/flag/i', $cmd)对字符串中的flag关键字(不区分大小写)进行过滤。 - 若未触发正则拦截,则对
$cmd调用escapeshellarg()函数进行转义,最后拼接至system('cat ' . $cmd)并执行命令。
2. 漏洞原理与延伸
1. escapeshellarg() 剥离非法 UTF-8 字符绕过
escapeshellarg() 的设计初衷是为字符串两端包裹单引号,并转义已有的单引号,使得整个字符串作为单一安全参数传递给 Shell,防止命令注入。
但是在 PHP 7.3(某些具体版本)中,escapeshellarg() 在处理包含非法 UTF-8 字符字节(例如 %80 / \x80)的字符串时,会将该非法字节直接从结果中剥离删除。
- 绕过正则:传入
fl%80ag时,原始输入字符串为fl\x80ag,不匹配正则表达式"flag",从而顺利绕过preg_match('/flag/i', $cmd)黑名单检测。 - 执行命令:经过
escapeshellarg()处理后,0x80字节被系统剔除,处理后的字符串恢复为'flag',最终拼接为cat 'flag'并成功执行。
2. 知识点拓展:escapeshellarg() + escapeshellcmd() 经典组合漏洞
在 CTF 题目中,经常会见到将这两个转义函数组合使用的场景,例如:
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if(!isset($_GET['host'])) {
highlight_file(__FILE__);
} else {
$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox);
chdir($sandbox);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}
- 产生机制:
escapeshellarg()率先把$host转义并用单引号包裹成单一参数。- 紧接着对处理结果调用
escapeshellcmd(),后者会再次转义字符串中的 Shell 元字符(如& | ; < > \及未配对的单引号)。这破坏了原本建立的单引号边界。
- 危害后果:攻击者可利用单引号配对错位拆分参数,形成参数注入(Argument Injection)(例如向
nmap注入追加参数以实现任意文件写入/命令执行),而非直接通过;进行简单的指令拼接。
3. 解题过程
Step 1. 尝试绕过限制并读取 flag
构造包含非法 UTF-8 字节的 Payload 绕过黑名单限制:
?cmd=/fl%80ag

页面返回提示我们并没有完整拿到 flag。
Step 2. 抓包分析 HTTP 响应头
抓包查看完整的 HTTP 响应包数据:

从响应头中可注意到 Server: openresty。这表明服务器使用了 OpenResty/Nginx 架构,提示我们可以尝试读取 Nginx 的日志文件来获取更多线索。
Step 3. 读取日志定位真实 Flag 文件
利用 cmd 参数读取默认的 Nginx 访问日志文件 access.log:
?cmd=/var/log/nginx/access.log

在日志记录中成功找到真实的 flag 文件名:this_is_final_flag_e2a457126032b42d.php。
Step 4. 读取真实 Flag 文件源码
再次结合 %80 字符绕过黑名单,读取目标 PHP 文件:
?cmd=this_is_final_fl%80ag_e2a457126032b42d.php
由于目标文件是 .php 文件,内容会被 PHP 解析执行或被浏览器当作 HTML 隐藏,按下 Ctrl + U 查看网页源代码:
CTF2{f02ea6b9-aa0b-442e-907b-213b82b99107}

成功获取最终 Flag!