QiYuanXueTang's Class

2021Web安全实战系列笔记 | 文件上传实战和分析(七)

Word count: 2.9kReading time: 10 min
2021/10/08

靶机环境搭建

Upload-Labs 环境要求
操作系统:windows、Linux
php版本:推荐5.2.17(其它版本可能会导致部分Pass无法突破)
php组件:php_gd2,php_exif(部分Pass需要开启这两个扩展)
apache:以moudel方式连接
项目地址:https://github.com/c0ny1/upload-labs
直接使用phpstudy搭建,将下载的文件放在对应的根目录即可。访问如下:

解题思路

pass-01

首先看第一关,查看提示和源码,发现是前端JS验证,而我们知道,前端验证,有和没有基本一样。

绕过也非常简单,可以浏览器直接禁用JS,先按F12,然后按F1,找到禁用JS,如下图:

然后直接上传php木马。结果如下:

这是一种方法,当然也可以直接burp抓包,修改后缀直接上传。

pass-02

看第二关,首先分析源码

分析代码,发现只是检查了content-type类型,这个也是很好绕过的,直接抓取数据包,修改类型就可以了。抓的数据包如下:

将类型改为image/jpeg,直接上传PHP木马,结果如下

木马上传成功。

pass-03

看第三关,分析源码

发现第三关是基于黑名单防护,不允许上传’.asp’,’.aspx’,’.php’,’.jsp’后缀的文件,这个黑名单是不全的,比如可以尝试上传.phtml .phps .php5 .pht的后缀文件,也是可以当做php脚本解析的,如果Web服务器是apache的话,也可以上传.htaccess文件,来绕过黑名单。首先尝试上传.phtml,发现上传成功,再响应包中找到上传路径。这里要注意一下,要在apache的httpd.conf中有如下配置代码
AddType application/x-httpd-php .php .phtml .phps .php5 .pht

pass-04

看第四关,分析源码

发现这里黑名单基本过滤了后缀,但是,没有.htaccess,可以上传.htaccess文件,来达到解析脚本的目的。也可以用另外的一种方法,看代码6到10行,这里是对文件后缀的一些处理,包括删除结尾的点,删除空格,全都转换成小写等,然而这么处理肯定有想不到的地方,**比如构造后缀.php. .中间是一个空格(后面的第八关和第十关是同样的思路)**,即达到了绕过黑名单,又经过检查使得脚本木马可以解析。如下图:

访问脚本木马,成功解析

pass-05

第五关和第四关一样,只是黑名单里添加了.htaccess,所以只能使用构造后缀的方式了,四关的方法五官通用,所以这里就不详细赘述了。

pass-06

看第六关,分析源码

看第5至10行,里面的过滤没有限制大小写,所以可以尝试大小写组合绕过。比如.pHP等。尝试上传,如下图:

上传成功,脚本木马成功解析:

pass-07

看第七关,分析源码:

通过观察源码,发现没有对空格进行处理,可以直接在末尾添加空格来绕过黑名单。如下:

上传成功,成功解析。

pass-08

看第八关,分析源码:

这里的黑名单限制了所有可以解析的后缀,所以只能构造特殊后缀来绕过防护,这里去除了空格,并用strrchr函数查找指定小黑点最后一次出现,所以构造的后缀为.php. (点php点加空格),成功绕过,如下图:

上传成功,成功解析。

pass-09

看第九关,分析源码:

看第5行到第10行,源码中未过滤::$DATA,可以利用::$DATA来绕过过滤,到这里小伙伴可能就要问了,为什么::$DATA可以绕过黑名单呢?这其实是利用了windows的特性,在window的时候如果文件名+”::$DATA”会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名。例如:”test.php::$DATA”Windows会自动去掉末尾的::$DATA变成”test.php”。
现在抓取数据包验证。上传成功,如下图。

上传成功,成功解析。

pass-10

看第十关,分析源码

这一关和第八关类似,就比第八关多了一行,多删除了一个点,所以还是采用构造后缀的方式,这次构造的后缀为.php. .(点php点空格点)正好绕过过滤。
构造好后,上传成功

解析成功

pass-11

看第十一关,分析源码

这里的重点在第八行,这里使用了str_ireplace函数将匹配到的字符替换为空,所以绕过思路就很简单了,只需要双写就可以了,删除字符后,剩余的又重新拼接为后缀,达到绕过的目的,抓取数据包测试如下,上传成功。

访问,解析成功。

pass-12

看十二关,分析源码

分析源码我们可以知道,这里是基于白名单过滤,只允许上传’jpg’,’png’,’gif’,但是这里注意第八行,上传路径是可以控制的,所以可以利用%00截断,来达到上传木马的目的。这里要注意一下,%00截断想要利用成功,php版本小于5.3.4(高版本php官方修复了这个漏洞),php的magic_quotes_gpc为OFF状态。抓取数据包,进行尝试,如下图:

访问,解析成功

pass-13

看十三关,分析源码

同样是白名单,也是上传位置可控,不过是由GET传输变为POST,还是利用%00截断。不过因为POST不会进行自动解码,所以要自己在16进制中进行修改,如下图:


上传成功,解析成功

pass-14

第十四关是上传图片马,配合解析漏洞

分析代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch($typeCode){
case 255216:
$fileType = 'jpg';
break;
case 13780:
$fileType = 'png';
break;
case 7173:
$fileType = 'gif';
break;
default:
$fileType = 'unknown';
}
return $fileType;
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_type = getReailFileType($temp_file);

if($file_type == 'unknown'){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}

下面是文件包含的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
include.php
<?php
/*
本页面存在文件包含漏洞,用于测试图片马是否能正常运行!
*/
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file;
}else{
show_source(__file__);
}
?>

分析代码可以知道它只读2字节,只需要将木马后缀改为图片格式,内容加个图片头部,然后在返回包中找到路径,然后写在file参数后,因为file参数include的原因都会直接执行。如下:

长传成功,配合文件包含漏洞解析图片格式的木马。

pass-15

先看源码:

这里使用getimagesize()函数来验证是否是图片,这里说一下getimagesize(),这个函数功能会对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求的。getimagesize()返回结果中有文件大小和文件类型,如果用这个函数来获取类型,从而判断是否是图片的话,会存在问题。是可以被绕过的,因为图片头可以被伪造。这里伪造gif的图片头,来进行上传,如下

图片木马上传成功。

pass-16

这一关需要开启php_exif

看源码

这里使用exif_imagetype函数来检查是否是图片,这里说一下exif_imagetype(),它是读取一个图像的第一个字节并检查其签名。所以也是可以通过伪造图片头来进行绕过的。这里同样伪造gif的图片头,来进行上传,如下

上传成功

pass-17

看源码

这一关比较综合,判断了后缀名、content-type,以及利用imagecreatefromgif判断是否为gif图片,最后再做了一次二次渲染。还是按照第16关的思路走一下,看是否成功。显然失败了。

那就做一个图片木马,上传一个正常图片,抓取数据包,在图片末尾插入恶意代码,如下:

然后,进行上传操作。上传成功,如下。

因为这里进行了二次渲染,所以还没有结束,我们将上传的图片下载到本地查看。

这里发现在进行二次渲染的过程中,我们插入到图片的恶意代码被清理掉了,所以需要对比渲染前后,哪些地方没有变化,我们将恶意代码插入到那里,来绕过二次渲染。对比发现,这里渲染前后没有发生变化。

所以,我将恶意代码插入到这里,如下图:

重新上传,上传成功,再次下载到本地,发现恶意代码没有被清除,绕过了二次渲染。

pass-18

看源码

这里的代码逻辑是先将文件上传到服务器,然后判断文件后缀是否在白名单里,如果在则重命名,否则删除。这样就存在一个安全问题,那就是我同时上传多个相同文件,在它删除之前访问就可以了。也就是条件竞争问题。可以借助burp的暴力破解模块。这里复现失败了=_=”。

pass-19

这里同样存在条件竞争问题,不过就是需要换成图片木马。其他和第十八关一样。

pass-20

看源码

这里发现move_uploaded_file()函数中的img_path是由post参数save_name控制的,因此可以在save_name利用00截断绕过,和前面关卡的00截断类似。如下:

上传成功。

pass-21

看源码

说实话,这个代码看的我有点懵。参考网上文章一点点分析吧,首先第五行以白名单的形式检查MIME这个可以直接在数据包中修改,然后向下分析,到第十行,这里的含义是如果POST接收的save_name值为空则赋值给$_FILES[‘upload_file’][‘name’],否则是本身。接着是用explode() 函数把字符串打散为数组,然后解释一下下面涉及到的函数的含义。

end()函数将 array的内部指针移动到最后一个单元并返回其值
reset()函数将 array 的内部指针倒回到第一个单元并返回第一个数组单元的值
count() 函数计算数组中的单元数目或对象中的属性个数,这里要注意,数组下标从0开始

然后这里用end函数将接收的后缀与白名单比较,如果符合,继续执行,然后数组第一位和$file[count($file) - 1]进行拼接,产生保存文件名file_name。
所以这里采用数组绕过,save_name[0]=pass21.php, save_name[2]=jpg,$ext=jpg过白名单,reset($file)=pass21.php
$file[1]=null,这样就成功上传pass21.php.(windows多个点不影响)
抓包测试。上传成功。

结语

这篇文章对于upload-labs靶场进行了全通关思路讲解,如有错误请斧正。

CATALOG
  1. 1. 靶机环境搭建
  • 解题思路
    1. 1. pass-01
    2. 2. pass-02
    3. 3. pass-03
    4. 4. pass-04
    5. 5. pass-05
    6. 6. pass-06
    7. 7. pass-07
    8. 8. pass-08
    9. 9. pass-09
    10. 10. pass-10
    11. 11. pass-11
    12. 12. pass-12
    13. 13. pass-13
    14. 14. pass-14
    15. 15. pass-15
    16. 16. pass-16
    17. 17. pass-17
    18. 18. pass-18
    19. 19. pass-19
    20. 20. pass-20
    21. 21. pass-21
    22. 22. 结语