文件上传与upload-labs

前言

文件上传也是ctf中相当常见的一个考点,通过upload-labs的学习我们可以掌握各种不同的文件上传姿势。

关于文件上传

文件上传漏洞往往产生于服务器配置不当,导致任意文件的上传,或者在开放文件上传时没有对文件进行严格过滤。就会导致不法分子恶意写入一些webshell,窃取服务器内的文件,造成严重的危害。

webshell及一句话木马

webshell就是以asp、php、jsp或者cgi等网页文件形式存在的一种代码执行环境,主要用于网站管理、服务器管理、权限管理等操作。使用方法简单,只需上传一个代码文件,通过网址访问,便可进行很多日常操作,极大地方便了使用者对网站和服务器的管理。正因如此,也有小部分人将代码修改后当作后门程序使用,以达到控制网站服务器的目的。

而利用文件上传漏洞进行攻击时最常用的便是一句话木马,通过上传一句话木马文件配合菜刀、蚁剑等webshell连接工具等,获得目标服务器的读取权。

这篇博客详细记录了常用的一句话木马,之后做upload-labs靶场一般用到的是普通php一句话木马:
常用的一句话木马

对于一句话木马类型的文件上传,可以参考这篇博客学习:
Web安全-一句话木马

upload-labs

刷靶场建议先从黑盒开始,等到做不出来了就翻提示和源码做白盒,当然下面是直接从白盒的角度写的。如果文件名被更改,可以在Repeater中Go一下,并在返回中找到文件名。

Pass-01

首先在txt文件中写入一句话代码,但php后缀的文件被过滤了,因此我们改后缀格式为jpg并上传。

这题涉及到了JS检测,这题起过滤使用的是JS代码,而php代码在js中被读取,因此我们可以在f12审查元素中删去JS代码,或者直接禁用JS调用就可以直接上传php文件了。

不过一般我们使用另一个方法,使用burp suite抓包:

在蓝线位置我们发现我们上传的文件,在这里将jpg后缀改为php并forward,就成功上传了文件,我们可以在靶场根目录中的upload文件夹看到我们上传的这个文件。

由于本地靶场我们知道文件的绝对路径,因此直接蚁剑连接就可以了:

记得做完之后删除上传的文件。

Pass-02

这关涉及到的是MINE,即文件类型绕过,我们同样抓包查看:

我们可以发现这个Content-Type,这里就对你上传的文件进行了一次类型检查,图中网页检查的类型为image/jpeg,这是因为因为我们上传的本身就是jpg文件,因此和第一关一样更改后缀为php就可以了。

但还有另一种方法,如果我们直接上传php文件的话,我们会发现这次的Content-Type变成了application/octet-stream,导致检查不通过,因此我们将其更改为image/jpeg也可以绕过检查。

部分源代码:

1
2
3
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']

MINE的原理是判断$_Files["upload_file"]["type"]是不是图片格式(image/gif、imge/jpeg、image\png),不是则不允许上传。$_Files["upload_file"]["type"]的值是从请求数据包中Content-Type中获取。

Pass-03

提示告诉我们这题过滤了.asp|.aspx|.php|.jsp这四个后缀,不过没了这四个后缀,我们还有很多其他替代品,例如:

  • ASP:asa/cer/cdx
  • ASPX:ashx/asmx/ascx
  • PHP:php4/php5/phtml/php3/pht
  • JSP:jspx/jspf

当然在使用这些后缀之前可能要先对apache进行配置,我们也可以fuzz测试其他没有被过滤的后缀。

这里也放一下源码:

1
2
3
4
5
6
7
8
9
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array('.asp','.aspx','.php','.jsp');
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空

这串代码的意义是删去文件名末尾的点,并截取后缀,将后缀转换为小写,删去前面定义的字符串数组里的内容并收尾去空。

Pass-04

这关过滤的是真的狠,把我们前面讲的几个全部过滤了,当然我们也可以fuzz试试有没有漏网之鱼。在这关中,我们要使用到.htaccess文件来帮助我们过关。

这里截取了一段网络上关于.htaccess的介绍:

htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

其中,.htaccess文件中内容SetHandler application/x-httpd-php用来设置当前目录所有文件都使用PHP解析,无论上传任何文件,只要符合php语言代码规范,就会被当做php文件执行。

首先我们要创建一个txt文件,在这个文件中写入SetHandler application/x-httpd-php,接着更改后缀为.htaccess,但是由于windows的命名规则导致文件不能没有文件名,因此我们先随意取一个文件名。当然也可以使用FilesMatch指定文件,示例如下:

1
2
3
<FilesMatch "muma">
SetHandler application/x-httpd-php
</FilesMatch>

这意味着文件名字中带有muma的文件会被解析成php。在上传.htaccess文件时要使用burp suite将这个文件的文件名删去。接着只要上传我们的muma1.jpg,用蚁剑连接就可以了。

Pass-05

这一关连.htaccess都过滤了,但是我们查看源码发现,这题只过滤了一次.和空格,因此我们构建muma1.php. .文件名就可以成功绕过了,进入upload发现文件依然是muma1.php

Pass-06

查看源码发现没有大小写转化,意味着这题可以进行大小写绕过,将文件后缀改为.phP或者.pHp等等都是可以的,当然更改.htaccess的大小写进行绕过同样可行。

Pass-07

查看源码发现没有对后缀名去空,那么我们上传一个muma1.php文件,并使用burp suite抓包,将文件名改成muma1.php ,后缀名后添加空格即可绕过后端php脚本的检测,再上传到windows服务器上,会自动去除后缀名后的空格。

Pass-08

查看源码发现没有过滤后缀中的点,那么我们上传一个php文件后抓包将文件名后缀改成.php.就可以了。windows系统特性会自动忽略最后一个点,添加点即可绕过后端的php检测,而且可以正常解析。

Pass-09

这一关没有过滤::$DATA,我们在上传php文件后抓包,在文件后缀最后加上::$DATA。在windows中::$DATA会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,起到绕过后端检测的效果。

Pass-10

这关解法和Pass-05是一样的,muma1.php. ,而且还真没啥区别,严重怀疑是传重了。

Pass-11

看源码:

1
2
3
4
5
6
7
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess","ini");

$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;

这关起到过滤作用的是str_ireplace()函数,它会检测文件名中包含$deny_ext的字符并替换为空。在做sqli-labs时我们也遇到了类似的情况,同样的,使用双写绕过,将文件后缀改为.pphphp这类的上传就可以了。这样,后缀在后端过滤掉php后,剩下的内容仍然为php。

而如果上传.pphhpp这种是无法绕过成功的,因为没有连续的php,导致这个后缀没有被过滤而成功上传,但上传后的结果仍然是.pphhpp,webshell工具无法连接这种后缀。

Pass-12

前面的题目均为黑名单绕过,意思是我们不能上传黑名单中的含有后缀的文件。而这一关往后几关是白名单绕过,意味着我们只能上传白名单中含有后缀的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}

我们发现$img_path直接拼接,而且GET的save_path可控,这里要运用到截断上传的原理。

url中,%00对应的ascii码值为0,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束,而忽略后面上传的文件或图片,只上传截断前的文件或图片,这和sqli-labs的23关原理是一致的,相信刷完sqli-labs对截断这个操作已经不陌生了。

因此,我们首先上传一个muma1.jpg,接着抓包,修改GET内容为../upload/muma1.php%00

查看upload文件夹发现上传成功。

ps.php版本要小于5.3.4,magic_quotes_gpc需要为Off状态。

Pass-13

这关和上关思路是一致的,也是%00的截断,但是由于这题是POST注入,POST注入不会自动解码%00,因此我们需要修改16进制码来进行截断。

首先我们上传muma1.jpg并抓包,然后将抓包内容发送至Repeater中,在POST内容最后添加上muma1.php:

接着我们在Hex中找到对应的十六进制码(php的十六进制码为70 68 70),因此我们将在这串编码后的值更改为00,Go一次之后我们就可以在upload文件夹中发现muma1.php了:

Pass-14

这关涉及到了上传图片码,首先我们要了解什么是图片码。图片码,又名图片一句话。顾名思义,就是将一句话木马藏匿在图片之中,从而达到绕过检查的效果。我们来介绍两种制作图片码的方法:

  • cmd命令制作
    将你的php木马文件和图片放在同一个目录之下,shift+右键在这个目录下打开cmd,输入以下命令:

    1
    copy tupian.jpg /b + muma1.php /a shell.jpg

    这里的tupian.jpg是我们的原始图片,muma1.php是一句话木马文件,最后生成的文件是shell.jpg。我们可以用010打开这个文件,发现木马已经被成功写入图片中:

    同样的方法我们也可以制作png图片码和gif图片码。

  • 二进制软件应用(010、winhex等)
    我们可以用010打开一张图片,直接在图片最后加上一句话木马,要注意不要破坏原文件的文件尾,导致文件上传时无法识别。

有了图片码,就可以直接进行文件上传了,但是由于菜刀和蚁剑等shell工具无法解析图片文件,因此我们要利用到文件包含漏洞。关于文件包含漏洞之前也写过了一篇博客,也可以参考下面这篇博客:
Web安全实战系列:文件包含漏洞

在upload文件夹中写入下面这个php文件:

1
2
3
4
5
6
7
8
9
<?php
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file;
}else{
show_source(__file__);
}
?>

这里我把文件命名为include.php

我们构建一个url读取图片:

1
127.0.0.1/upload-labs/upload/include.php?file=9220211125204748.gif

读取成功,但是读取出来一段乱码,结果不明显,我们写入phpinfo()测试:


成功。

为了和第15关区别,我们进行源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}

本关源码以二进制读取模式打开文件,并读取两个字节。而unpack的作用是解包,用什么打包就用什么解包,这里是将$bin中的以二进制的形式解包输出到chars中,接着使用intval()函数将值转换为十进制,并对比转换出来的值是否为对应文件的文件头值,最后将文件类型返回导$fileType中。因此我们只能上传jpg、png或gif格式的文件,否则检查不会通过。

Pass-15

这关直接上传上关制作的图片码,用同样的方法就可以解除题目。这两关的差别主要在于源码的检查方式不同,我们查看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}

本关使用了getimagesize()函数获取到图片信息,接着使用image_type_to_extension()函数获取图片后缀,最后判断文件中是否存在.jpeg|.png|.gif文件(这个检查利用文件的十六进制码进行),如果有则成功通过检查,否则上传失败。因此我们制作的图片码可以绕过,因为图片码本身就是一张图片,前面的文件内容可以通过检查。

Pass-16

这关由于用到了exif_imagetype(),因此要开启php的php_exif模块。这关的检查就比较简单粗暴了,直接使用exif_imagetype()来判断图像的类型:函数读取一个图像的第一个字节并检查其签名,如果发现了恰当的签名则返回一个对应的值,如果不为.jpeg|.png|.gif文件则返回false阻止文件上传:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function isImage($filename){
$image_type = exif_imagetype($filename);
switch ($image_type) {
case IMAGETYPE_GIF:
return "gif";
break;
case IMAGETYPE_JPEG:
return "jpg";
break;
case IMAGETYPE_PNG:
return "png";
break;
default:
return false;
break;
}
}

不过由于本质上还是检查.jpeg|.png|.gif文件,因此我们上传图片码并利用文件包含就可以顺利解题了。

Pass-17

这题提示我们对图片进行了二次渲染,这意味着我们之前写进图片中的一句话木马会被抹掉,我们上传一个图片码试试:

我们发现原本的一句话木马被抹去了,这就意味着写入失败,我们来看看源代码:

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];

$target_path=UPLOAD_PATH.'/'.basename($filename);

// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);

//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);

if($im == false){
$msg = "该文件不是jpg格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagejpeg($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}

}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);

if($im == false){
$msg = "该文件不是png格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagepng($im,$img_path);

@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}

}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagegif($im,$img_path);

@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}

这里二次渲染用到了几个和imagecreat有关的函数,例如imagecreatefromgif(),作用就是创建一个画布,并从GIF文件或URL地址中载入一副图像。渲染后的图像利用move_uploaded_file()将上传的文件移动到新位置下,最后用unlink()删除原文件。

关于这关的写入方法,由于gif文件在二次渲染之后会保留一段和渲染前相同的内容,而jpg与png则没有这段内容。因此我们选择gif文件,将木马写入渲染前后内容一致的部分中,利用文件包含漏洞解题就可以了。

Pass-18

这里涉及到了文件上传的竞争条件,2020年的黑盾杯就有这个考点。提示说这题需要代码审计,那么我们就分析代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$is_upload = false;
$msg = null;

if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = UPLOAD_PATH . '/' . $file_name;

if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传出错!';
}
}

分析代码,这里程序用move_uploaded_file()对文件进行转存后读取后缀并进行对比,如果后缀不为.jpg|.png|.gif的话则使用unlink函数删除文件。而条件竞争漏洞就运用到了在多线程情况下,就有可能出现还没处理完,我们就访问了原文件,这样就会导致防护被绕过。

网站允许上传任意文件,然后检查上传文件是否包含webshell,如果包含删除该文件,或者网站允许上传任意文件,但是如果不是指定类型,那么使用unlink删除文件,在类似这两种情况下我们就可以考虑使用竞争条件漏洞解题。

那么我们要怎么利用这个时间差访问文件呢?答案就是爆破上传。使用burp suite营造很多人上传info.php(<?php phpinfo();?>)的场景,同时我们用蚁剑连接,下面是具体流程:
首先我们上传文件并抓包,将抓包内容发送到Intruder中,随意构建一个payload爆破点,例如我选定GET中的action内容进行爆破,设置字典为1-9999的数字,设置10线程。


设置完之后进行攻击,在攻击的同时不断访问127.0.0.1/upload/info.php,将phpinfo的页面刷新出来后在upload文件夹中就可以发现info.php也成功上传。

Pass-19

这关同样也是竞争条件漏洞,不过这关除了检测后缀,还检测了文件大小、文件名是否重复、目录是否可写等等,将文件上传后,对文件重新命名。本关上传图片码然后用文件包含伪协议读取即可。这告诉我们,只要我们访问的够快,程序就来不及重命名我们的文件。

我们可以写一个python脚本帮助我们读取文件:

1
2
3
4
5
6
7
8
9
import requests
url = "http://127.0.0.1/upload/include.php?info.jpg"
while True:
r = requests.get(url)
if "phpinfo" in r.text:
print("OK")
break
else:
print("NO")

跑到脚本出现OK后就成功了。

Pass-20

本关又一次用到了move_uploaded_file(),而且是由POST方式获取文件,因此可以使用13关的过关方式:%00截断,更改Hex中的十六进制码即可。

我们来看看本关的源码:

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
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

$file_name = $_POST['save_name'];
$file_ext = pathinfo($file_name,PATHINFO_EXTENSION);

if(!in_array($file_ext,$deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
}else{
$msg = '上传出错!';
}
}else{
$msg = '禁止保存为该类型文件!';
}

} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

这关检验的是黑名单,如果后缀与黑名单的内容一致则禁止保存,反之则以设定的保存名称作为文件名保存。介绍另一个方法,这里move_uploaded_file()除了%00截断之外,还会忽略掉文件末尾的/.,这里我们可以利用这一点构建文件名:

发现info.php已经成功写入upload文件夹中。

Pass-21

这题是数组绕过与/.绕过相结合,我们审计一遍代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}

$ext = end($file);
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
$msg = "禁止上传该后缀文件!";
}else{
$file_name = reset($file) . '.' . $file[count($file) - 1];
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;

我们注意到这段代码中有一个判断语句,如果$file不为数组则进行白名单检测,否则不检测。同时发现$file_name经过reset($file) . '.' . $file[count($file) - 1];处理,如果上传的是数组的话,会跳过$file = explode('.', strtolower($file));,因此我们可以让$file为数组:

首先绕过MINE,我们的目的是让文件成功命名为info.php/,并通过move_uploaded_file()出去/.。而命名的规则为$file_name = reset($file) . '.' . $file[count($file) - 1];,因此我们要让reset($file)返回info.php/,$file[count($file) - 1]返回空值,即$file[0]为smi1e.php/,$file[2]为白名单中的jpg用于绕过过滤。这样就成功绕过了过滤。

但是这个看着真的很绕,也很难彻底理解,这里也就理解了个大概,具体原理还要继续深入研究。

.user.ini

在之前的关卡中,我们用到了.htaccess文件,但是这个文件的使用是有局限性的,只有在apache服务器环境下才可以使用。因此这里补充一个绕过方法:上传.user.ini文件。

只要服务器脚本语言为php,对应目录下有可用php文件,服务器使用CGI/FastCGI模式,就可以通过上传.user.ini绕过黑名单检验:

  • 首先构建.user.ini文件,内容为:

    1
    auto_prepend_file=a.jpg //为了绕过过滤需要添加例如jpg的文件头
  • 然后依次上传.uesr.ini和图片码,图片便会被解析成php文件

总结

系统地刷完了upload-labs,学习了文件上传的各种绕过姿势:

  • MINE绕过
  • 更改后缀('.php. .''.phP''.phtml''.php ''.php.'等等
  • 上传.htaccess或.user.ini文件
  • 竞争条件
  • 上传图片码
  • %00截断
  • /.截断

同时这个靶场也锻炼了代码审计能力,还是很有收获的。