关于上传中的00截断分析

  关于上传中00截断的细节,很多朋友在渗透中都会发现一些这样的有趣现象,这个站点使用00截断上传的方法上传成功了,而换一个站点又失败了,这是什么原因呢?你看了这篇文章就会明白。

00截断原理

  0x00是字符串的结束标识符,攻击者可以利用手动添加字符串标识符的方式来将后面的内容进行截断,而后面的内容又可以帮助我们绕过检测。

00截断的限制条件

1
PHP<5.3.29,且GPC关闭

00截断的利用方法

上传文件后缀检测代码

1
2
3
4
5
6
7
8
9
10
11
12
13
$uploaded_name = $_FILES[ 'file' ][ 'name' ];
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); // 提取上传文件后缀
$target_name = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; // 对上传文件进行重命名
if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ))
{
move_uploaded_file($_FILES["file"]["tmp_name"],
$dir . $target_name); // 将临时文件移动到指定目录
$result = $dir . $target_name;
echo "Stored in: $result";
}
else{
echo "Invalid file";
}

误区

  有很多朋友喜欢在文件名中加%00进行截断,笔者认为这种方式是不对的,为什么呢?比如攻击者构造文件名:admintony.php%00a.jpg,在提取后缀名的时候遇到%00则认为字符串结束了,那么他提取到的后缀名会是.php.php后缀又不允许上传所以上传失败了(这里有必要提一句,有人可能会说在一些情况下,%00截断文件名可以成功,这种案例你试一下是不是任意文件上传,西普的00截断实验就是一个任意文件上传的上传点,既然是任意文件上传又何必用00截断绕过呢?)

正确用法

  那么00截断应该在什么时候使用呢?笔者认为,数据包中必须含有上传后文件的目录情况才可以用,比如数据包中存在path: uploads/,那么攻击者可以通过修改path的值来构造paylod: uploads/aa.php%00

  为什么修改path才可以,因为程序中检测的是文件的后缀名,如果后缀合法则拼接路径和文件名,那么攻击者修改了path以后的拼接结果为:uploads/aaa.php%00/2018051413370000.php,移动文件的时候会将文件保存为uploads/aaa.php,从而达到Getshell效果。

疑问:

  为什么效果图中打印的结果和预期不符,是因为echo的特殊性质,官方解释的echo

  正是因为echo可以输出多个字符串,一个字符串截断后就变成了两个字符串,所以会输出后面的内容。

%00和%00(urldecode)

  在网上常见用Burp将数据包中的%00进行urldecode的操作,那为什么要进行这一个操作?网上也常见直接放入%00就可以截断成功的案例,为什么呢?

  • %00(urldecode)

  首先解释为什么要进行urldecode操作呢?其原因在于上传的表单中有一个enctype的属性,并且需要enctype="multipart/form-data" (不对表单中数据进行编码),path大多数都是存放在表单中的,因此需要在数据包中进行urldecode操作使%00变成字符串结束符号。

  • %00

  那么为什么网上也有直接添加%00而不进行urldecode操作呢?因为path也可以存放在URL或者Cookie中,而在提交数据的时候,浏览器会对数据做一次urldecode的操作,而到服务端,会对数据进行一次urldecode的操作,因此如果path在非enctype=multipart/form-data的表单中或URL or Cookie中的时候,就可以直接写%00不需要进行URLdecode操作,让服务端对%00进行URL解码即可。