blubiu

My Love

上网不网恋,简直浪费电.


AVDemo漏洞平台白盒测试


前言

AVDemo漏洞平台测试第二篇–白盒测试

前段时间写了第一篇,现在来补上第二篇。


目录:


测试环境

phpStudy + php 5.6

工具: Seay + RIPS + VCG


审计流程

在审计代码前,可以先简单看下网站结构

我们可以从中了解到程序的架构、大概的运行流程、包含那些配置文件等,

还能了解程序的业务逻辑,

当然大牛的话可能一眼就能知道程序怎么个走法,

下图这些目录需要重点关注,特别是配置目录。

images


审计方法

审计方法有那么几种: 通读全文法、敏感函数参数回溯法、定向功能分析法

从字面意思能够大概理解,这里我就不多解释了。

因为本人也是第一次审计框架,能力有限,

这里就直接配合工具来审计了。


这里首先使用三种工具交叉扫描,

工具这里我也先不介绍了,各有千秋吧!!

下面是三种工具检测的截图

Seay:

images


RIPS:

images


VCG:

images


1. 文件包含漏洞

使用三种工具交叉扫描发现,

可能存在文件包含漏洞,

images


我们跟进一下看看,

发现是在入口文件 index.php 出现的问题

images


然后根据以往的经验,这种 include($_GET['module'].'.inc') 拼接后缀名的可能存在绕过风险,

像这种情况的话我们可以使用00截断去截断他的拼接后缀,

但是00截断必须要 php < 5.3.4 才可以,

这里我们还有另外一种方法,就是使用 phar://伪协议来绕过,

关于这个协议为什么能够绕过,这里简单讲一下,

这是一种与压缩包有关的协议,他可以打开一个压缩包,并且可以打开其中一个文件,读取某个文件的内容,

使用方法: phar://path/file[文件]/file[里面所压缩的文件]


这里我们来说说如何使用伪协议,

当然,我们知道,不管是什么协议,都需要有一个文件来给他读取,

这里我们就需要找一个上传点了,

这里我们可以直接使用正则快速定位 upload 上传类,

我们可以看到第5行,有个上传函数,

images


然后跟过去看看,

发现是 user目录 下的一个处理上传的php文件,

然后我们可以看到 首先有一个 is_pic 去判断上传的文件名,

如果上传成功后保存在 upload目录下,

文件命名为 u_时间戳_文件名 这样的格式。

images


然后我们再跟进查看 lib 函数库,

发现 is_pic 这个函数只是检查了文件后缀,

那么我们就有办法绕过了,

然后直接将一句话 shell.php 的 后缀 修改为 inc ,

然后将 shell.inc 压缩为 zip 文件,

然后再将 shell.zip 后缀修改为 sehll.jpg ,因为只能上传图片,

然后打开我们的开发者工具,然后上传,

这里可以看到他的一个执行时间,

但是还是时间格式,我们还需要把他转换成时间戳 ,

images


我们可以使用 strtotime 将时间转换成时间戳,

<?php

	date_default_timezone_set('UTC');
	echo strtotime('Tue, 30 Apr 2019 02:18:27 GMT');

?>

输出: 1556590707

现在我们知道文件名是 u_1556590707_shell.jpg


但是在包含的过程中却出现了错误,

我们直接去文件夹下看看上传的文件,

发现名字不一样,uploads 目录下的文件名是 1556590709 , 而我们的是 1556590707

这是服务器执行的时候有差异导致的,因为执行的时间相差也非常短,

所有我们可以尝试去猜测文件名,这里的话只需要加两位就可以了,


现在我们可以构造payload了,

根据上面的 phar:// 使用语法 ,

Payload为: phar://uploads/u_1556590709_shell.jpg/shell

因为上传的时候已经拼接了后缀,所以这里我们就不需要再加后缀名了

然后shell路径为:

http://www.vd.com/index.php?module=phar://uploads/u_1556707346_shell.jpg/shell

然后上菜刀就行了,

images


2. SQL注入

这里我就不一一截图了,三个工具都扫描到了这个sql注入,

那么我们现在就来看看是否真实存在,

Seay工具里说没有单引号保护,

根据经验来看,没有引号保护的更容易注入,因为不需要闭合,

有引号保护的,当我们在闭合引号的时候,可能会被过滤掉,导致无法注入,

images


这里我们跟过去看看 ,

发现这里查看留言详情的页面,

通过接受 id 的值,然后去数据库中查找这条留言,

images


然后我们再跟过去看看这个 sqlwaf的函数 ,

发现把一些注入常用的关键字替换为 sqlwaf

但是后面有些字符却替换为空

images


我们可以来简单的测试一下,and 1=1 ,毫无疑问,被过滤了,

这时我们可以使用 || 来绕过,

输入 a||nd 1=1 成功绕过,

后面的操作就跟黑盒测试中那一样了,就不演示了

images

images


3. 命令执行

我们看第6个,使用 shell_exec 执行 $cmd ,如果 $cmd 可控的话,那么可能会存在命令执行

images


我们跟过去看看

发现这是执行 ping 操作的代码,

通过接受 target 的值来进行ping操作,

最后使用 shell_exec 来执行 $cmd 的命令,

此操作没有任何过滤,存在绕过

images


我们可以直接使用命令管道符进行绕过,

类似的绕过还有 &&&|| 等等。


4. 任意文件读取

我们看到第14行,可能存在任意文件读取,

images


追踪一下代码,

发现是获取头像的代码,

images


然后我们再追踪一下参数 avatar

发现他是在上传头像的地方

images


我们来解读一下这段代码

if(is_pic($_FILES['upfile']['name'])){

	$avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name'];

	if (move_uploaded_file($_FILES['upfile']['tmp_name'], $avatar)) {
		//更新用户信息
		$query = "UPDATE users SET user_avatar = '$avatar' WHERE user_id = '{$_SESSION['user_id']}'";
		mysql_query($query, $conn) or die('update error!');
		mysql_close($conn);
		//刷新缓存
		$_SESSION['avatar'] = $avatar;
		header('Location: edit.php');
	}

$avatar变量是上传图片的路径,

move_uploaded_file() 函数,将上传的文件移动到新位置

语法: move_uploaded_file(file,newloc)

参数:

  1. file   必需。规定要移动的文件。

  2. newloc   必需。规定文件的新位置。

然后将上传的文件进行更新操作,

$query 是设置sql语句进行更新操作,

我们再仔细看里,user_avatar = '$avatar'

他是获取图片的路径然后更新到数据库中,

如果我们能够控制这个地方,那么就能够造成任意文件读取。


既然有思路了,那么我们来试试,

首先开启burp,把流量全部记录下来,

然后进行登录,头像上传一系列操作,这是为了保证 Cookie 一致

后面在历史记录中找到登录、头像上传、头像获取这三个包,然后发送到Repeater

images


我们知道,上传文件处是把新的文件更新到数据库,并且更新的时候没任何过滤,

那么我们尝试去构造一下payload,

重新看一下这个更新语句

$query = "UPDATE users SET user_avatar = '$avatar' WHERE user_id = '{$_SESSION['user_id']}'";

我们需要闭合掉 $avatar 的单引号,

那么我们的payload应该是:

',user_avatar = '2' where user_name = 'test'#.jpg

想当于:

$query = "UPDATE users SET user_avatar = '',user_avatar = '1' where user_name = 'test'#.jpg WHERE user_id = '{$_SESSION['user_id']}'";

我们可以看到这串语句,把前面第一个 user_avatar 闭合掉了,

然后把要修改的用户设置为 test,并把后面注释掉了,

我们进数据库中查看一下,

发现成功修改。

images


images


现在,他获取图片的时候就拿到了这个 1

那么我们可以根据这种情况把我们想要读取的文件路径写进去,

这样他在读取的时候,实际上就读取了某个文件,

我们可以尝试去读取 sys目录下的 config.php,

images


但是在尝试读取的时候出错了,我们去数据库中看看

发现路径变成了 uploads

这是因为,$avatar 存在路径的话,他会直接重命名,然后上传到 uploads 目录下 ,

这时我们可以使用十六进制进行绕过,

../sys/config.php 转换成十六进制,

然后重新发包,发现数据库中已经变成了我们想要的路径。

images


这个时候,我们只需要将登录的Cookie和获取头像的Cookie改个名字,就行了

这是相当于重新登录,不然的话还是原来登录获取的,

只需要在原来Cookie处一个 a 就行了,

根据Cookie组成,只要是数组英文字母都可以,

当然,如果登录的Cookie加了个 a ,那么获取头像处的 Cookie也要加个 a

Cookie要相同

images


改完后重新发送登录包,然后再发送获取头像的包,

这个时候就已经成功读取到了我们想要读取的文件内容

images


5. 逻辑越权

代码审计工具只能查找一些危险函数,

对于逻辑漏洞是扫描不出来的,

包括现在的一些web扫描工具,也是扫描不到逻辑漏洞,

这种漏洞需要人工去发现,

逻辑漏洞一般出现在,登录、支付、修改信息等等。

下面我们来看看这个越权漏洞


这个漏洞位于 updateName.php ,这是一个更新用户名的页面

我们再来看看这个漏洞,

代码:

include_once('../sys/config.php');
if (isset($_POST['submit']) && !empty($_POST['username']) ) {

	if (strlen($_POST['username'])>16) {
		$_SESSION['error_info'] = '用户名過長(用戶名長度<=16)';
		header('Location: edit.php');
		exit;
	}

	$clean_username = clean_input($_POST['username']);
	$clean_user_id = clean_input($_POST['id']);
	
	//判断用户名已是否存在
	$query = "SELECT * FROM users WHERE user_name = '$clean_username'";
    $data = mysql_query($query, $conn);
	if (mysql_num_rows($data) == 1) {
		$_SESSION['error_info'] = '用户名已存在';
		header('Location: edit.php');
		exit;
	}
	
	$query = "UPDATE users SET user_name = '$clean_username' WHERE user_id = '$clean_user_id'";
	mysql_query($query, $conn) or die("update error!");
	mysql_close($conn);
	//刷新缓存
	$_SESSION['username'] = $clean_username;
	header('Location: edit.php');
}
else {
	not_find($_SERVER['PHP_SELF']);
}

这个漏洞位于 $clean_username = clean_input($_POST['username']);

$clean_user_id = clean_input($_POST['id']);

通过接收 $_POST 的值,然后赋值给,$clean_username$clean_user_id

我们知道,$_POST** 是可以构造出来的,使用hackbar就可以,或者抓包修改也行

当然,我们还需要绕过 isset($_POST['submit'])!empty($_POST['username'])

这个两个也是接收 $_POST 的值,我们也可以构造去绕过,

接下来我们来尝试一下看看


首先把必要的参数填进去,

Payload: submit=1&username=aaa&id=9

submit只要有值就行,然后用户名我们修改为 aaa ,要修改 id=9 的用户 ,

然后我们将数据发送到 updateName.php

images


这个时候,我们可以看到,用户名成功被修改

images


6. IP伪造

可以看到第12和13行,可能存在IP伪造,

并且 HTTP_X_FORWARDED_FORHTTP_CLIENT_IP 这两个函数存在绕过,

所以我们很有必要跟过去看看。


首先来看看代码:

function get_client_ip(){
	if ($_SERVER["HTTP_CLIENT_IP"] && strcasecmp($_SERVER["HTTP_CLIENT_IP"], "unknown")){
		$ip = $_SERVER["HTTP_CLIENT_IP"];
	}else if ($_SERVER["HTTP_X_FORWARDED_FOR"] && strcasecmp($_SERVER["HTTP_X_FORWARDED_FOR"], "unknown")){
		$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
	}else if ($_SERVER["REMOTE_ADDR"] && strcasecmp($_SERVER["REMOTE_ADDR"], "unknown")){
		$ip = $_SERVER["REMOTE_ADDR"];
	}else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")){
		$ip = $_SERVER['REMOTE_ADDR'];
	}else{
		$ip = "unknown";
	}
	return($ip);
}

get_client_ip 是一个获取ip的函数,

HTTP_CLIENT_IP 函数可以获取客户端的ip,这个ip存在于http请求的header中

HTTP_X_FORWARDED_FOR 也可以获取ip地址

strcasecmp 比较两个字符串(不区分大小写)函数

strcasecmp($_SERVER["HTTP_CLIENT_IP"], "unknown")

这句的意思就比较 ipunknown 这两个字符串,是不是相同的,

整改代码的意思就是获取 ip ,然后如果没有的话,最后输出是 unknown


接下来,我们再来追踪一下 哪里调用了 get_client_ip 函数,

我们发现在 logCheck.php 调用了,

这是前台用户登录验证的一个页面。

images


现在来试试,

发现在闭合单引号的时候被过滤掉了,

images


我们回到注入点跟过滤函数,

$query = "UPDATE users SET login_ip = '$ip' WHERE user_id = '$row[user_id]'";

这个注入点是字符型注入,需要闭合单引号,

而过滤函数已经把单引号过滤了,

这个注入点已经没办法进行注入了。

不过我们依然可以进行ip伪造,

在某些爆破,登录,访问的时候可能会判断ip,

如果我们能够伪造,是可以绕过这些限制的,

images


总结

这个漏洞平台审计起来还是比较简单的,

不过还是要多审计,多看代码,

不然一扫描报一堆的漏洞,很难去发现那里有漏洞,那些没有漏洞,

那这个来说,如果文件包含路径后面拼了其他路径的,那么基本没戏。

就像下图一样那种,

代码还得要多看,以后的路还很长。

images