FineCMS任意头像上传漏洞复现:文件的四次上传
目录
- 文件上传
文件上传
1、文件上传完整代码
PHP代码
<?phpheader("Content-Type:text/html; charset=utf-8");require_once('pclzip.lib.php');// echo "";
// var_dump($_POST['username']);exit;// // var_dump(file_get_contents("php://input"));exit;// // var_dump($GLOBALS['HTTP_RAW_POST_DATA']);exit;// if (!isset($GLOBALS['HTTP_RAW_POST_DATA'])) {// exit('环境不支持');// }// // 创建图片存储文件夹// $dir = 'upload/';// if (!file_exists($dir)) {// mkdir($dir);// }function check_dir($dir){ $handle = opendir($dir); while(($f = readdir($handle)) !== false){ if(!in_array($f, array('.', '..'))){ if(is_dir($dir.$f)){ check_dir($dir.$f.'/'); }else{ $ext = strtolower(substr(strrchr($f, '.'), 1)); if(!in_array($ext, array('jpg', 'gif', 'png'))){ unlink($dir.$f); } } } }}// // 创建图片存储的临时文件夹// $temp = $dir.'member/1/';// if (!file_exists($temp)) {// mkdir($temp);// }// $filename = $temp.'avatar.zip'; // 存储flashpost图片// file_put_contents($filename, $GLOBALS['HTTP_RAW_POST_DATA']);// //第三次绕过// // $zip=new ZipArchive;//新建一个ZipArchive的对象 // // if(!$zip->open($filename)){// // //check_dir($dir);// // exit("fail to open zip file");// // }// // if(!$zip->extractTo($temp)) {// // exit("fail to extract zip file");// // }// $archive = new PclZip($filename);// if ($archive->extract(PCLZIP_OPT_PATH, $temp, PCLZIP_OPT_REPLACE_NEWER) == 0) {// check_dir($dir);// exit("解压失败");// }// check_dir($dir);// exit('success');$file = $_FILES['file'];if (!$file) { exit("请勿上传空文件");}$name = $file['name'];$dir = 'upload/';$ext = strtolower(substr(strrchr($name, '.'), 1));//递归删除 zip 1 web.php// function check_dir($dir)// {// $handle = opendir($dir);// while (($f = readdir($handle)) !== false) {// if (!in_array($f, array('.', '..'))) {// $ext = strtolower(substr(strrchr($f, '.'), 1));// if (!in_array($ext, array('jpg', 'gif', 'png'))) {// unlink($dir . $f);// }// }// }// }// mkdir($dir);if (!is_dir($dir)) { mkdir($dir);}$temp_dir = $dir.md5(time(). rand(1000,9999)).'/';// $temp_dir = $dir . 'member/1/';if (!is_dir($temp_dir)) { mkdir($temp_dir);}if (in_array($ext, array('zip', 'jpg', 'gif', 'png'))) { if ($ext == 'zip') { // $zip = new ZipArchive; // if(!$zip->open($file['tmp_name'])) { // echo "fail"; // return false; // } // if(!$zip->extractTo($temp_dir)) { // check_dir($temp_dir); // exit("fail to extract"); // } $archive = new PclZip($file['tmp_name']); // foreach($archive->listContent() as $value){ // $filename = $value["filename"]; // if(preg_match('/\.php$/', $filename)){ // exit("压缩包内不允许含有php文件!"); // } // } if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) { check_dir($dir); exit("解压失败"); } check_dir($temp_dir); exit('上传成功!'); } else { move_uploaded_file($file['tmp_name'], $temp_dir . '/' . $file['name']); check_dir($temp_dir); exit('上传成功!'); }} else { exit('仅允许上传zip、jpg、gif、png文件!');}
HTML代码
DOCTYPE html><html lang="zh"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>文件上传章节练习题title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style type="text/css"> .login-box{ margin-top: 100px; height: 500px; border: 1px solid #000; } body{ background: white; } .btn1{ width: 200px; } .d1{ display: block; height: 400px; } style>head><body> <form method="post" action="upload.php" enctype="multipart/form-data"> <input type="file" name="file" value=""/> <input type="submit" name="submit" value="upload"/> form>body>html>
2、第一次上传 【无递归删除】
1、PHP代码【含代码解析】
header("Content-Type:text/html; charset=utf-8");require_once('pclzip.lib.php');$file = $_FILES['file'];if (!$file) { exit("请勿上传空文件");}$name = $file['name'];$dir = 'upload/';$ext = strtolower(substr(strrchr($name, '.'), 1)); //拿出最后的后缀为.的后面内容function check_dir($dir){ $handle = opendir($dir); while (($f = readdir($handle)) !== false) { if (!in_array($f, array('.', '..'))) { $ext = strtolower(substr(strrchr($f, '.'), 1)); if (!in_array($ext, array('jpg', 'gif', 'png'))) { unlink($dir . $f); //直接删除 } } }}$temp_dir = $dir . 'member/1/'; //临时文件,这里1其实应该代表有用户IDif (!is_dir($dir)) { //判断文件有无文件 mkdir($dir);}if (in_array($ext, array('zip', 'jpg', 'gif', 'png'))) { //判断文件后缀 if ($ext == 'zip') { $archive = new PclZip($file['tmp_name']);//打开压缩包,后面进行解压 if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) { check_dir($dir); exit("解压失败"); } check_dir($temp_dir); //运用check_dir函数,判断文件把不符合规则的文件类型删除 exit('上传成功!'); } else { move_uploaded_file($file['tmp_name'], $temp_dir . '/' . $file['name']); // check_dir($temp_dir); exit('上传成功!'); }} else { exit('仅允许上传zip、jpg、gif、png文件!');}
2、简述代码整体运行过程与破解
上述过程就是上传压缩包,然后打开压缩包看里面文件类型进行删除与保留。
此过程想要上传恶意代码很简单,因为它打开压缩包打开的内容包括各种文件。但是它只扫描它打开的这层文件中的内容。即不会扫描文件夹里的内容。
3、测试代码
1、压缩包包含php与图片文件上传 【被删除PHP文件】
上传压缩包,并且尝试在内容里面加入PHP文件
上传文件结果
查看结果:创建了upload文件,并且发现在文件夹1中只有图片,成功过滤了PHP文件
2、压缩包包含文件夹与图片文件上传 【没有删除PHP文件】
由代码查看发现他编码进行递归查询,如此就不会第二次打开文件夹的。为此我们才有包含文件夹进行绕过删除文件。
创建文件夹web1
将web1文件压缩同图片文件一起压缩
上传压缩包
弹出的第二行错显示了
Permission denied
,表示它没有权限删除我们创建的文件夹web1。
查看上传内容:文件夹没有被删除,但是php文件被删除了。
继续查看web1文件夹的内容
我们发现web1文件没有删除为此就绕过删除了。
这不用时间竞争型漏洞,因为要试很多次。【也可以用,只是麻烦了】
有更好的把上传的压缩包包含文件夹
unlink:直接删除 【只能删除文件不能删除文件夹,因为没有递归删除】
4、防御第一次绕过
既然我们知道了它没有进行递归查询,没有检测文件夹内容。我们就修改函数使用递归
function check_dir($dir){ $handle = opendir($dir); while(($f = readdir($handle)) !== false){ if(!in_array($f, array('.', '..'))){ if(is_dir($dir.$f)){//判断是否还有文件夹 check_dir($dir.$f.'/'); //再次调用函数 }else{ $ext = strtolower(substr(strrchr($f, '.'), 1)); if(!in_array($ext, array('jpg', 'gif', 'png'))){ unlink($dir.$f); } } } }}
函数内再次检测了文件,对文件夹进行检测。
3、第二次上传 【有递归查询】
1、PHP 代码 【修改第一次上传的函数内容】
function check_dir($dir){ $handle = opendir($dir); while(($f = readdir($handle)) !== false){ if(!in_array($f, array('.', '..'))){ if(is_dir($dir.$f)){//判断是否还有文件夹 check_dir($dir.$f.'/'); //再次调用函数 }else{ $ext = strtolower(substr(strrchr($f, '.'), 1)); if(!in_array($ext, array('jpg', 'gif', 'png'))){ unlink($dir.$f); } } } }}
2、简述代码整体运行过程与破解
上述代码进行解压压缩包,对文件再次检索查看是否有文件夹。当有文件夹再次运行函数打开文件夹,再在文件夹中再次检索。直到没有文件夹检索为至。
这个方法虽然解决了第一次上传文件夹不背扫描问题,但是并没有解决第一个上传仍然存在的时间竞争漏洞。为此我们可以采用时间竞争漏洞绕过
3、测试代码
1、压缩包包含文件夹与图片文件上传 【PHP文件被删除】
选择新建文件夹.zip
成功上传我们查看文件夹web1 是否内部php被过滤
我们发现它成功过滤了文件夹内的PHP文件
2、时间竞争型漏洞绕过
1、编写竞争代码
在删除之前访问上传的php文件,从而执行上传文件中的php代码。
竞争型文件上传过程介绍
文件上传过程:
服务器获取文件>>>保存上传临时文件>>>重命名移动临时文件
编写PHP代码,将要删除前重命名实现移动文件【跳三层到xss_location目录】
fputs(fopen('../../../payload.php','w'),'');?>
上传web1.zip 【其中1.php代码修改为了上述代码】
2、Burp Suite抓取上传包
1、准备工作
开启抓包
将手工代理开启【代理端口与Burp Suite设置的一样】
将本地地址改为本机IP一致
2、开始抓包
上传文件抓包
上图成功被拦截,并且拦截的是压缩包 【拦截中的PK代表压缩包】
将其抓取内容放到爆破中
先clear掉符号
随后找个数字进行添加字符
让其运行2000次
如果想手动访问也可以就要访问
169.254.2.70/xss_location/mermber/1/1/web1.php
,手动刷新页面。【要关拦截】
开始访问
结果查看
4、防御第二次上传
因为时间竞争型漏洞是猜到了存储文件的,为此我们将其创建文件名方式进行修改。使用对文件随机数进行命名。
$temp_dir = $dir.md5(time(). rand(1000,9999)).'/';// $temp_dir = $dir . 'member/1/';
3、第三次上传 【随机命名防止时间竞争漏洞】
1、PHP代码改进 【文件随机命名】
$temp_dir = $dir.md5(time(). rand(1000,9999)).'/';// $temp_dir = $dir . 'member/1/';
2、简述代码整体运行过程与破解
不同之前是将上传文件创建的 文件夹随机命令。导致无法猜测到文件名进行恶意代码上传。
从结果上来看我们确实是无法在解压后运用条件绕过了删除,那我们就可以考虑在解压过程中下手。即实现当解压一半出错,它直接跳转解压失败不进行条件判断删除PHP文件了。
3、解压一个出错的zip【知识点补充】
这个问题其实需要看具体情况,看解压的那个程序的容忍程度,我这里就以两个解压的程序作为例子:
- Windows下的7zip
- PHP自带的ZipArchive库
- 先说7zip。7zip的容忍度很低,只要压缩包中某一个文件的CRC校验码出错,就会报错退出。 如何修改压缩包里文件的CRC校验码呢?可以使用010editor。
4.我们先准备两个文件。【简单点我就用现在的文件夹】我们用010editor打开zip,可以看到右下角有这个文件的格式信息,它被分成几部分,我们打开第6部分【png那个】,其中有个deCrc,我们随便把值改成其他的值,然后保存.
修改内容并且保存
进行解压
如此我们发现出错了。但是PHP依旧解压了
此时用7zip解压就会出错,解压出的PHP是完好的,另一个文件是图片出现错所以无法显示了,【如果是txt,会显示文件但是因为报错内容无法显示就只有txt文件】
接下来我们说一下PHP的解压。
采用一个PHP的解压代码测试【为了方便我把压缩包移动到同级目录下】
function unzip($zipname, $path) { $zip = new ZipArchive; if(!$zip->open($zipname)) { echo "fail"; return false; } if(!$zip->extractTo($path)) { echo "fail to extract"; return false; } $zip->close(); return true; } if(unzip('./web1.zip', './')) { echo "success zip"; }else { echo "failt to unzip"; }
采用在Windows中使用PHP来解压
他成功解压了并且中目录里
如果压缩入txt文件【文件内容为zzzzzz,压缩包为hhhh.zip,修改php内容文件为hhhh.zip】 同样先修改deCrc
依旧成功
并且不同Windows会是txt文件没有内容
综上我们发现在PHP中是好像无采用压缩包报错绕过,但是如果文件内容无法修改就文件
这也说明ZipArchive的容忍度比较高。 那么我们又如何让ZipArchive出错呢?最简单的方法,我们可以在文件名上下功夫。
比如,Windows下不允许文件名中包含冒号(:),我们就可以在010editor中将2.txt的deFileName属性的值改成“2.tx:”,
再次解压
发现对文件解压时出错了。【目录下也解压出来php,没有txt】
Linux下也有类似的方法,我们可以将文件名改成5个斜杠(/)
总结;
1.Windows系统下7zip的容忍度很低,只要压缩包中某一个文件的CRC校验码出错,就会报错退出
2.PHP代码中 .。
使用zip解压时deCrc,我们随便把值改成其他的值,然后保存就可以出错。
使用7z情况下修改文件名Windows下不允许文件名中包含冒号(:),我们就可以在010editor中将2.txt的deFileName属性的值改成“2.tx:”
3.linux系统将文件名改成5个斜杠(/)【前提这个压缩至少有五个字符可以修改】
4、测试代码
1、上传错误压缩包绕过删除
采用上述的压缩包上传。
因为使用PHP类要修改一下
// //第三次绕过$zip=new ZipArchive;//新建一个ZipArchive的对象 if(!$zip->open($filename)){ //check_dir($dir); exit("fail to open zip file");}if(!$zip->extractTo($temp)) { exit("fail to extract zip file");}// $archive = new PclZip($filename);// if ($archive->extract(PCLZIP_OPT_PATH, $temp, PCLZIP_OPT_REPLACE_NEWER) == 0) {// check_dir($dir);// exit("解压失败");// }
结果表示未能提取【上传了hhhh.zip,包含php文件,与txt文件】
查看上传文件是否成功有php
发现成功上传php,但是因为txt出错没有被上传。并且因为出错绕过了判断而没有删除
5、防御第三次绕过
既然我们知道他是在是解压过程在避开递归删除
如此对应防御:如果在解压到php后就先递归删除再解压下一个文件,就算退出但是第一个已经递归删除过了就解决问题了
if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) { check_dir($dir); //解压成功一个后进行递归删除 exit("解压失败"); }
4、第四次绕过 【在之前基础上对解码后的文件进行递归删除】
1、PHP代码 【在解码后进行递归删除】
if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) { check_dir($dir); //解压成功一个后进行递归删除 exit("解压失败"); }
2、简述代码整体运行过程与破解
代码的之前代码基础上当解码失败后,不会直接保存而是再进行删除递归后才真正的结束、
相较于第三次的防御解决方法 可以尝试命名php为的value为…/…/aaa.php
即解压完后修改名称,如此是解压到上级目录 使用工具修改aaaaaaaaa.php改为…/…/aaa.php
3、测试代码
1、上传修改了value的php文件
同之前一样修改【改为txt:,与…/…/aaa.php】
php的修改只要是有aaaaaaaaaaaaaa.php原来名字的文件都修改
查看上上级目录是否有对应php文件
总结
第一次绕过代码没有文件再次检测,采用传包含文件夹的压缩包绕过或使用时间竞争漏洞绕过
2、第二个绕过代码添加了递归对文件夹再次检测,但由于没有间竞争漏洞绕过问题。使用间竞争漏洞绕过
3、第三次绕过代码进行随机命名文件无法猜文件名间竞争漏洞绕过失效,但是可以采用解压一半报错保存php文件
4、第四次绕过代码对报错保留的文件进行了递归删除,但是我们可以欺骗系统将解码的文件判定为上级目录文件规避了递归在当前目录下是所有检测。
其实这种漏洞补一处出现处,要从根本解决。
根本原因:它是在网站的根目录下解压文件,用户可以访问。
解决:为此在临时文件夹中解压,再把需要的文件提取到网站目录下。
来源地址:https://blog.csdn.net/Miracle_ze/article/details/126347577
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341