白袍的小行星

escapeshellarg()与escapeshellcmd()的奇妙组合

字数统计: 712阅读时长: 2 min
2020/03/12 Share

最近做一道题,源码是这样的:

<?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);
}
?>

一看这两个函数名估计就是过滤,再看后面的system,思路大致就是绕过过滤执行命令拿到flag,具体是啥慢慢往下看。

函数介绍

先来介绍一下这两个函数。

escapeshellarg — 将给字符串增加一对单引号并且能引用或者转码任何已经存在的单引号

演示一下:

<?php
error_reporting(0);

$cmd = $_GET['cmd'];
if(isset($cmd)){
echo escapeshellarg($cmd);
}
?>

结果是这样的:

如果加一个单引号就会:

下面来看另一个。

escapeshellcmd() 对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。

其使用反斜杠转义的字符包括:

& # ; ` | * ? ~ < > ^ ( ) [ ] { } $\ \x0A \xFF

'"仅在不配对的时候被转义。

回到题目

这道题目先使用escapeshellarg(),再使用了escapeshellcmd(),看似严谨,实则暴露出了弱点。

我们还是先写一个demo出来进行测试:

<?php
error_reporting(0);

$cmd = $_GET['cmd'];
if(isset($cmd)){
$cmd = escapeshellarg($cmd);
$cmd = escapeshellcmd($cmd);
echo $cmd;
}
?>

输入数据为1'时,返回变成了这样:

分析一下:

在经过第一个函数后,结果我们测试过了,即'1'\''',第二个函数处理时,给反斜杠和落单的引号都加上了反斜杠,所以就变成了'1'\\''\'

也许你有点晕,那么来看看一个清晰点的对比:

'1' \'' '
'1' \\'' \'

这样是不是清楚多了?

现在来放到题目中看看:

"nmap -T5 -sT -Pn --host-timeout 2 -F" . '1' \\'' \'

假设1就是我们注入的恶意代码,现在看这个语句能正常使用吗?

\\将会被认为就是\,而两个单引号连接在一起会被认为是一个空白符,而最后的单引号就是单引号。

如果我们输入的是1'ls'呢,最后的命令会变成什么样子?

"nmap -T5 -sT -Pn --host-timeout 2 -F" . '127.0.0.1'\\'' ls '\\'''

这里的ls只是举例,实际在这里应该注入一个nmap的参数进去,写shell最后得到flag.

总结

这个题目本质上考察的就是两个函数组合起来互相制肘结果变成了漏洞,在现实中是有实际案例的,PHPMailer < 5.2.18的RCE漏洞补丁就使用了这种方法,结果又被绕过了。

这个漏洞基本只能用来进行参数注入,因为类似|&都被过滤掉了。

CATALOG
  1. 1. 函数介绍
  2. 2. 回到题目
  3. 3. 总结