menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right 007-papers chevron_right 0155-第三方接口 黑客怎么爱你都不嫌多.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    0155-第三方接口 黑客怎么爱你都不嫌多.md
    8.4 KB / 2021-07-17 00:01:36
        # 第三方接口 黑客怎么爱你都不嫌多
    
    0x01 写在前面
    ---------
    
    * * *
    
    本文总结了乌云主站最近提交的 由于第三方接口程序导致的高危漏洞 本文实例都是使用率高,且在近期爆出漏洞的API,具有一定现实意义
    
    在程序中嵌入第三方程序,可以追溯到discuz!。后来的各种SNS程序,CMS,BBS都纷纷效仿,他们或由官方 或由站长自己添加了各种插件和api,一方面 这些脚本增加了用户体验,然而在黑客眼中,实则是增大了入侵几率。
    
    各种接口的添加,一般是在整站程序开发周期之后,那么开发全局观的不同步是显而易见的后果,简而言之,前期种种过滤和安全防护,可能由于后期开发对第三方插件盲目的信任,被彻底绕过,导致漏洞形成。
    
    0x02  实例分析
    ----------
    
    * * *
    
    ### 看看那些厂商都是怎么被出卖的
    
    **第一弹 Alipay支付插件那碎一地的节操**
    
    ```
    影响程序:Ecshop,Dedecms,Cmseasy ...  
    lib\plugins\pay\alipay.php 
    
    ```
    
    我们看看cmseasy怎么被自己人干掉的吧 在alipay.php中 自己定义的数据库函数在rec_update中
    
    ```
    public function rec_update($row , $where){ ... 省略无关代码 ...
                $sql="UPDATE `".$this->tablename."` SET ".$sqlud." WHERE ".$where; //漏洞出在这里 一只裸体的$where
                return $this->simpledb->execute($sql);
            }
    
    ```
    
    可以看到 where 是没有单引号的,我们看看where从哪里能传进来。
    
    ```
    public static function changeorders($id,$orderlog) {
            //file_put_contents('logs.txt', $id);
            $where=array();
            $where['id']=$id;
            $where['status']=4;
            //$where['orderlog']=serialize($orderlog);
            $update=orders::getInstance()->rec_update($where,$id);
            if($update<1) {
                exit('改变订单状态出错,请联系管理员');
            }
        }
    
    ```
    
    继续追踪changeorders
    
    ```
    function respond() {
            if (!empty($_POST)) {
                foreach($_POST as $key =>$data) {
                    $_GET[$key] = $data;
                }
            }
            $payment  = pay::get_payment($_GET['code']);
            $seller_email = rawurldecode($_GET['seller_email']);
            $order_sn = str_replace($_GET['subject'],'',$_GET['out_trade_no']);
            $order_sn = trim($order_sn);
                             ....省略....
                pay::changeorders($order_sn,$_GET);
    
    ```
    
    看到没$order_sn 没有过滤 注入于是产生了 由于没有显示点 延时注入即可。
    
    **第二弹 Tenpay支付插件也疯狂**
    
    ```
    lib\plugins\pay\tenpay.php 
    
    ```
    
    阿里巴巴那么疯狂,BAT的三弟腾讯怎么能示弱?  组队坑厂商什么的最有爱了~ 由于Cmseasy最信任插件了。。所以又是他受伤。。。
    
    ```
    class tenpay {
    ......
        function respond() {
            require_once ("tenpay/PayResponseHandler.class.php");
            $resHandler = new PayResponseHandler();
            $sp_billno = $resHandler->getParameter("sp_billno"); //腾讯的函数,类似于$_GET['sp_billno']或者$_POST['sp_billno']
    
    
    //上面谈到GET不受过滤影响,本地问价内包含POST,GET提交都可,但是注入的话必须提交POST,因为GET是URL码.
    //sp_lillno=sp_billno=-1-1-../../../demo 包含根目录的demo.php
    
            preg_match_all("/-(.*)-(.*)-(.*)/isu",$sp_billno,$oidout);
            $paytype = $where['pay_code'] = $oidout[3][0];
            include_once ROOT.'/lib/plugins/pay/'.$paytype.'.php';//匹配上面正则就行,包含之,触发,但是实在找不到能截断的PHP文件了,所以鸡肋了。
            $pay = pay::getInstance()->getrows($where); //SQL注入,跟进0x02
        ......
    
    }
    
    ```
    
    看到where没? 难道又是一只裸体动物? 答案是NO 这次是Cmseasy本身代码和API共同狼狈为奸 看getrows
    
    ```
    function getrow($condition,$order='1 desc',$cols='*') {
            $this->condition($condition); //OMG跟进,又是这个函数
            return $this->rec_select_one($condition,'*',$order);
        }
    
    function condition(&$condition) {
            if (isset($condition) &amp;&amp;is_array($condition)) {
                $_condition=array();
                foreach ($condition as $key=>$value) {
                    $value=str_replace("'","\'",$value);//问题函数在这里 MARK
                    $_condition[]="`$key`='$value'";
                }
                $condition=implode(' and ',$_condition);
            }
            ......
        }
    
    ```
    
    试想一下,当我们POST提交`'evalsql`由于gpc作用 会变成`\'evalsql`经过刚刚mark的那一行代码 就变成了`\\'evalsql`单引号被解救了 - -
    
    所以卖队友成功
    
    ![2014081318305040309.jpg](http://drops.javaweb.org/uploads/images/66536f8eebbcbc739118117094297579c1191c88.jpg)
    
    **第三弹 腾讯连弹 换个姿势咱们继续卖队友**
    
    微信sdk接口(weixin.php) 影响程序:数不清,这个太多了点,从大到小
    
    挑个超级典型的对象 PHPYUN  
    /_phpyun的全局过滤堪称典范 所有特殊字符入库一律被替换成&acute 再加上360wscan 铜墙铁壁的防御  
    那么壮士yun是不是革命成功? 错! 汪精卫weixin笑而不语。。。_/  
    这里引入一个古老而又粗暴的漏洞 XML实体注入,也许下一篇drop文就是这个的科普
    
    ```
    //weixin/model/index.class.php  
    private function responseMsg()
        {
            $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
            if (!empty($postStr)){
                    $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
                    $fromUsername = $postObj->FromUserName;
                    $toUsername = $postObj->ToUserName;
                    $keyword = trim($postObj->Content);
                    $time = time();
                    $textTpl = "<xml>
                                <ToUserName><![CDATA[%s]]></ToUserName>
                                <FromUserName><![CDATA[%s]]></FromUserName>
                                <CreateTime>%s</CreateTime>
                                <MsgType><![CDATA[%s]]></MsgType>
                                <Content><![CDATA[%s]]></Content>
                                <FuncFlag>0</FuncFlag>
                                </xml>";
    
                    if(!empty( $keyword ))
                    {
                        $msgType = "text";
                        $contentStr = "Welcome to wechat world!";
                        $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                        echo $resultStr;
                    }else{
                        echo "Input something...";
                    }
            }else {
                echo "";
                exit;
            }
        }
    
    ```
    
    首先介绍下
    
    `$GLOBALS["HTTP_RAW_POST_DATA"]`这个变量是讲POST提交来的数据原封不动的接收 比如POST a=1
    
    `$GLOBALS["HTTP_RAW_POST_DATA"]`就是“a=1” 不受gpc 360wscan的影响 然后接收到$postStr之后 又没有处理 于是乎...
    
    ```
    if($MsgType=='event')
                  {
                    $MsgEvent = $postObj->Event;
                    if ($MsgEvent=='subscribe')
                    {
                        $centerStr = "<Content><![CDATA[欢迎您关注".iconv('gbk','utf-8',$this->config['sy_webname'])."!\n 1:您可以直接回复关键字如【销售】、【南京 销售】、【南京 销售 XX公司】查找您想要的职位\n绑定您的账户体验更多精彩功能\n感谢您的关注!]]></Content>";
                        $this->MsgType = 'text';
                    }elseif ($MsgEvent=='CLICK')
                    {
                        $EventKey = $postObj->EventKey;
                        if($EventKey=='我的帐号'){
                            $centerStr = $this->bindUser($fromUsername);
    
    ```
    
    传进bindUser之后 最终到达
    
    ```
    isBind private function isBind($wxid='')
        {
            if($wxid)
            {
                $User = $this->obj->DB_select_once("member","`wxid`='".$wxid."'","`uid`,`username`");
    
    ```
    
    wxid就是一开始POST来的数据里面的参数 注入赤果果的产生
    
    ![2014081318470353538.jpg](http://drops.javaweb.org/uploads/images/611ee8ab2f77435db3038f40b960a6289995d457.jpg)
    
    微信团队开发这个API的时候,其实是验证了来源的  
    验证了signature(签名)  
    验证的条件是 检查signature是否非空 非空 非空...
    
    0x03 卷尾语
    --------
    
    * * *
    
    这样的第三方插件 应该成为程序员或者白帽子重点检查对象,原因如上0x02 这货们如同后门一般有魅力...  
    一般来讲 造成此类漏洞的原因有两种  
    一,双方相互信任 (你不打单引号,咱不过滤,好基友一起死)  
    二,签名未设置或者验证  
    为各位客官提供点思路
    
    links
    file_download