BUUCTF:[XDCTF 2015]filemanager —php代码审计 + 文件上传 + sql闭合语句注入数据库 + 二次注入

参考自:https://blog.csdn.net/qq_36618918/article/details/107768282

0、前置知识点:

1. pathinfo()

在这里插入图片描述

一、自己做

发现了 extension参数没有二次addslashes,可能有二次注入的机会,

拿着源码,弄了好一会,在本地的phpstudy上搭建成功了,然后就寻找sql的注入点了。
之前有说过,文件名没有二次注入的漏洞,可以看一下:

刚试了很多次, 发现了一个我自己误认为的一个失误:文件上传时,使用的是$_FILE这个全局变量,不再common.inc.php的过滤中,所以从文件上传这里上传的任意一个点,都会导致sql的注入风险,但是 后缀有白名单不能够注入,所以只有文件名能够注入了。

1. delete中的二次注入

下面是我自己做的时候的分析,后来看了WP。发现自己错了,报错确实是能够报错,也确实能够插入数据,但是这个的命令是一个delete命令,况且执行这个delete命令的切入点还是我们无法控制的fid。
退一步来看,假如delete语句参考的是我们可控的filename的化,也没有什么利用价值,因为这个是一个delete命令呀!。我们没办法去修改数据,所以这个delete.php中无法进行突破。

在这里插入图片描述我们在删除恶意文件的时候,ha\'ck,没有报错,

可见,$req['filename']中的ha\'ck是被上了addslashed的。我们在查询filename的时候,是不会产生sql报错的。因为这个ha\'ck的单引号被注释了么,
他这里就很巧妙了,他这里删除的时候,使用的是主键fid进行删除的,倘若利用数据库刚查询出来的filename进行删除的化,就会报错的。
在这里插入图片描述
所以,他这里用fid进行删除,我们就没有办法了。

在这里插入图片描述

什么时候给filename上上\了呢?
用到post方法上的,
在这里插入图片描述

2. upload中的二次注入
	if ($file["error"] == UPLOAD_ERR_OK) {
		$name = basename($file["name"]);  # 上传文件的时候,用到是$_FILE这个全局变量,不在common.inc.php中的过滤中,所以没有反斜杠转义。
		$path_parts = pathinfo($name); # 路径信息,有 filename extension basement dirname
                                       # 文件名(不含拓展名) 拓展名, 文件名(全含) 路径(不含文件名)
		if (!in_array($path_parts["extension"], array("gif", "jpg", "png", "zip", "txt"))) {
			exit("error extension");
		}
		$path_parts["extension"] = "." . $path_parts["extension"]; # 拓展名带上 .
		$name = $path_parts["filename"] . $path_parts["extension"]; # 拼接 name 为全名
		// $path_parts["filename"] = $db->quote($path_parts["filename"]);
		// Fix
		$path_parts['filename'] = addslashes($path_parts['filename']); # 文件名只有一次 addslaches 。 上传用到是$_FILE,没有进行addslashed。
        #这样,插入到是 ha\'ck  ,数据库存放的就是ha'ck。然后我们查询 select * from file where filename = 'ha'ck',就会报错的。
		$sql = "select * from `file` where `filename`='{$path_parts['filename']}' and `extension`='{$path_parts['extension']}'"; # 这个修改不了啊,

。这个也是我看了WP之后的分析:
在这里插入图片描述

看到数据库中插入的数据,我们也就知道了查询数据的时候也是会报错的。但是uplaod中没有查询功能啊,知识能够上传文件,从而这个upload.php可以是我们构造注入的一个点,
那么哪里有查询功能呢,那就是rename.php中的了,然后我们转入WP中

二、学到的

  1. 在PHP将数据放入 数据库中时, 放入之前addslashes()。要来两次,一次的化,从数据库中取出来的时候就会发生二次注入的危险。 所以先对用户的输入进行一次统一的addslashes,然后我们要存入哪个数据,就将哪个数据再一次addslashes。这就就能够防止 数据库的二次注入了。

  2. 同理,我们在做这样的题目的时候,也要留心,是否存在sql的二次注入

  3. 这个找到注入点之后,有坑,就是要细细的去分析PHP代码,他的真实文件的更新和数据库中的更新不同,以及他的数据库查询的filename的字段,以及后来的拼接extension等等,都要细细的去分析,,

三、学习WP:

这个题,wdnm的,,真的可以,,,不愧是xdctf的题。

<?php
require_once "common.inc.php";
if (isset($req['oldname']) && isset($req['newname'])) {
	$result = $db->query("select * from `file` where `filename`='{$req['oldname']}'"); # 这个查询是post会addslashed的
	if ($result->num_rows > 0) {
		$result = $result->fetch_assoc();
	} else {
		exit("old file doesn't exists!");
	}

	if ($result) {

		$req['newname'] = basename($req['newname']); # newname 没有 注意双注入, req[newname] = 12\'34 这样的,又转义符号的。
 		$re = $db->query("update `file` set `filename`='{$req['newname']}', `oldname`='{$result['filename']}' where `fid`={$result['fid']}");
 		# 这个filename在我们upload的时候是可控的,然后我们就 可以构造语句了,
 		# 关键点:
        #$re = $db->query("update `file` set `filename` ='x.jpg', `oldname`=' ',extension='' ,filename='x.jpg' where `fid`={$result['fid']}");
        # filename 字段被更新为x.jpg ,存储的后缀名字段extension也被设置为空,

		if (!$re) {
			print_r($db->error);
			exit;
		}
		$oldname = UPLOAD_DIR . $result["filename"] . $result["extension"];
		$newname = UPLOAD_DIR . $req["newname"] . $result["extension"];
		if (file_exists($oldname)) {
			rename($oldname, $newname);
		}
		$url = "http://localhost/1111111/upload/" . $newname;
		echo "Your file is rename, url:
                <a href=\"{$url}\" target='_blank'>{$url}</a><br/>
                <a href=\"http://localhost/1111111/www/\">go back</a>";
	}
}

看完WP后在 一、 中进行了一些补充,那么这里就直接看rename.php中的能够查询到filename 的语句了,

$req['newname'] = basename($req['newname']); # newname就是我们填入的值,这些会自动addslashes,所以我们就按照上传的文件名填写就好,
 $re = $db->query("update `file` set `filename`='{$req['newname']}', `oldname`='{$result['filename']}' where `fid`={$result['fid']}");

重点来了,这里有个update语句,而且newnamefilename我们都是可控的,

先验证用户是否输入旧文件名(即需要修改的文件名),和输入新文件名。
接着对数据库进行查询,查询在上传功能upload.php中在数据库插入的文件名中是否有用户输入的旧文件名,如果存在,即调用update语句,将查询到存在的文件名更新到数据库oldname字段中,导致文件名$result[‘filename’]再次入库,结果造成二次注入。
在这里验证oldname文件是否存在,是在数据库中去查询filename字段,而不是直接去查询文件系统的文件名验证。而filename字段又是我们可控的。而newname参数又是用户控制输入的,也就是我们也能控制。
但是在upload.php上传文件是对文件后缀进行白名单验证,所以这里不能直接上传恶意文件。

而在rename.php中,重命名的后缀是直接在数据库中提取出上传文件时插入的文件名后缀
在这里插入图片描述
而在上传文件时,我们是可以对后缀进行控制的,也就是说,我们可以通过upload.php插入时构造语句,然后在rename.php中update数据库更新进行二次注入将extension字段的值改为空,同时也可以控制filename的值,那么等于说我能控制rename函数的两个参数的值.。

注意在这里有个坑,这里改名的时候检查了文件是否存在:if(file_exists($oldname))我虽然通过注入修改了filename的值,但我upload目录下上传的文件名是没有改的。 因为利用注入时将extension改为空了,那么实际上数据库中的filename总比文件系统中真实的文件名少一个后缀。 那么这里的file_exists就验证不过。这里可以通过再次上传一个新文件,这个文件名就等于数据库里的filename的值就即可绕过。 所以最后整个getshell的流程,实际上是一个二次注入+二次操作getshell。

具体操作:

1.构造文件名,上传文件
’,extension=’’,filename='x.jpg.jpg
(在右边的.jpg作用是为了绕白名单后缀,左边的.jpg是文件名,为了闭合将filename改为x.jpg)
在这里插入图片描述
在这里插入图片描述

$sql = "insert into `file` ( `filename`, `view`, `extension`) values( '{$path_parts['filename']}', 0, '{$path_parts['extension']}')";
# 那既然有sql注入漏洞的话,我们要利用,就要将语句闭合。
#insert into `file` ( `filename`, `view` ,`extension` ) values( ' \' , extension=\'\' , filename= \'x.jpg',0,'.jpg' )";

在这里插入图片描述

2.接着进行重命名操作
在这里插入图片描述
filename从数据库里面复制出来,其他方法复制的不好使,

在这里插入图片描述

$req['newname'] = basename($req['newname']);
 $re = $db->query("update `file` set `filename`='{$req['newname']}', `oldname`='{$result['filename']}' where `fid`={$result['fid']}");
 # 这个filename在我们upload的时候是可控的,然后我们就 可以构造语句了,
 # 关键点:
 #$re = $db->query("update `file` set `filename` ='x.jpg', `oldname`=' ',extension='' ,filename='x.jpg' where `fid`={$result['fid']}");
# filename 字段被更新为x.jpg ,存储的后缀名字段extension也被设置为空,

在这里插入图片描述

这样了,
在这里插入图片描述

3.接着传入一个真正包含webshell的x.jpg:
在这里插入图片描述这里注意,这个的filename 的值,都是我们上传的全称的左边的,
在这里插入图片描述

4.重命名getshell:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
也就是数据库中修改的数据和真是文件系统中的文件不对应了,
原因就是因为 我们的sql注入,使得extension为空,并且他在真实修改文件名的时候没有直接去搜索文件系统中的文件名,而是用数据库中拼接出来的文件名进行搜索。

** 5. 查看flag:**

啊,,win10本地搭建的,先这样凑合看吧,题目上是一样的目录

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值