直接给源码吧:
- <?PHP
- //error_reporting(0);
- //$dir=md5("icq" . $_SERVER['REMOTE_ADDR']);
- $dir=md5("icq");
- $sandbox = '/var/sandbox/' . $dir;
- @mkdir($sandbox);
- @chdir($sandbox);
- if($_FILES['file']['name']){
- $filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];
- if (!is_array($filename)) {
- $filename = explode('.', $filename);
- }
- $ext = end($filename);
- if($ext==$filename[count($filename) - 1]){
- die("emmmm...");
- }
- $new_name = (string)rand(100,999).".".$ext;
- move_uploaded_file($_FILES['file']['tmp_name'],$new_name);
- $_ = $_POST['hehe'];
- if(@substr(file($_)[0],0,6)==='@<?php' && strpos($_,$new_name)===false){
- include($_);
- }
- unlink($new_name);
- }
- else{
- highlight_file(__FILE__);
先看后缀名绕过吧. 看着有点熟悉, 总感觉在哪见过, 后来才想起是 pwnhub 的公开赛里见过, 考察的是 end() 函数. 给个例子输出就清楚了.
- <?PHP
- $a[2] = '222';
- $a[1] = '111';
- $a[0] = '000';
- $end = end($a);
- var_dump($end);
- $arr = $a[count($a)-1]
- var_dump($arr);
- ?>
输出结果:
end 输出输入顺序最后一个
所以这里绕过是文件名取 $_POST'file'绕过
至于下面 unlink 的绕过, 有两种解法, 一种是加 /., 一种是../ 绕过.
我本地搭建更换了目录, 可以看到已经成功写入
加 /. unlink 无法删除是 Linux 下的特性, Windows 并不适用.
接下来传入 hehe 参数, 爆破包含即可.
至于加../, 产生的随机数会生成会被当成路径.
然后直接包含我们的文件即可.
看下当前目录
生成了 shell.PHP, 也成功写入了.
这里加 /. 和 ../ unlink 都没删除, p 牛小密圈给过解释
查看 PHP 源码, 其实我们能发现, PHP 读取, 写入文件, 都会调用 php_stream_open_wrapper_ex 来打开流, 而判断文件存在, 重命名, 删除文件等操作则无需打开文件流.
我们跟一跟 php_stream_open_wrapper_ex 就会发现, 其实最后会使用 tsrm_realpath 函数来将 filename 给标准化成一个绝对路径. 而文件删除等操作则不会, 这就是二者的区别.
所以, 如果我们传入的是文件名中包含一个不存在的路径, 写入的时候因为会处理掉 "../" 等相对路径, 所以不会出错; 判断, 删除的时候因为不会处理, 所以就会出现 "No such file or directory" 的错误.
来源: https://www.qcloud.com/developer/article/1360551