文件上传漏洞通常发生在程序设计中,由于对用户上传文件的处理不当或存在缺陷,导致用户能够绕过校验限制,向服务器上传具有执行能力的动态脚本文件。这些文件可能包括木马程序、病毒、恶意脚本或WebShell等。尽管文件上传本身并非问题所在,但服务器对上传文件的处理和解释方式不当,可能会引发严重后果。
upload-labs靶场主要用于渗透测试和CTF比赛中的文件上传漏洞训练。它通过模拟真实的文件上传场景,帮助用户理解文件上传漏洞的原理、危害以及如何绕过各种检测机制。靶场包含20个关卡,每个关卡都有不同的挑战,从基本的文件类型检测到复杂的黑名单和白名单绕过,逐步提升难度,帮助用户逐步提升对文件上传漏洞的理解和实战能力。
Pass-01
- 我们需要先确定上传上去的路径
- 如何限制我们上传的问题
信息收集测试
1 使用info.php文件上传查看情况,结果有个弹窗提示无法上传,另外,这个时候我们没有拦截到任何发送到服务器端的请求,说明,此监测是客户端监测;

2.使用符合条件的文件上传,如下面,上传正确的图片后,页面上有反显,那么我们就能获取到访问图片的路径,因为我们不仅要上传文件上去,还要能访问。

3.通过chrome的开发者工具既能查看到图片访问的路径,这边上传上去的文件连名字都没有修改,那么我们要访问我们上传的文件路径就是http://ip:端口/Pass-01/upload/文件名
4.由于这个是客户端限制,我们可以直接通过bp向服务器端发送请求,在这之前我们要先拦截下正常的请求,这边image.png是上传的文件名字,而下面啊那些就是这个图片的信息,由于是图片,所以这边看上去像乱码。

分析
已知条件
- 已经知道能访问的路径
- 文件名没有修改
绕过方案:
把我们的info.php文件改成info.png,然后通过bp拦截,把文件名改成info.php,既能访问到这个文件,路径地址是:
http://ip:端口/upload/info.php
实施
1.修改文件名
2.开启bp的拦截

3.发起请求并拦截,第二部分是文件的内容

4.发送到repeat中,并修改文件名

5.访问地址
bash
[http://ip](http://ip/):端口/upload/info.php

总结
文件要能上传,传完要能访问,还要能解析执行!
Pass-02
信息收集测试
1.使用info.php文件,异常提示文件类型不正确,并且有发送请求到服务端,说明是服务器端校验;
、

2.如果是服务器端校验,那么我们就需要获取到服务器端的代码了,我们这边就假设我们已经能获取到服务器端的代码
ini
```
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
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']
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
}
}
```
3.上传一个正常的页面,查看反显的路径,路径与Pass-01一样:http://ip:端口/Pass-01/upload/文件名

分析
这边的判断主要是根据请求过来的Content-Type的值进行判断是否图片,如果我们直接这个值改成其中一个"image/jpeg",其他的偶不变,既能绕过,注意这边的没有修改上传的文件名字。
已知条件:
- 已经知道能访问的路径
- 文件名没有修改
- 文件校验的策略
绕过方案:
通过bp修改Content-Type的值为"image/jpeg"
实施
1.拦截请求或者让请求正常通过后,发送到repeat中;
2.在repeat中修改Content-Type,并发送请求;

3.验证,能正常访问:http://ip:端口/upload/info.php

总结
服务器端的校验主要根据具体的校验方案来进行突破,这边主要是对Content-Type校验的绕过方式。
Pass-03
信息收集测试
1.使用info.php文件,异常提示"提示:不允许上传.asp,.aspx,.php,.jsp后缀文件!",并且有发送请求到服务端,说明是服务器端校验;

2.正常图片,反显图片的名称与源文件不一致,说明文件名有做了修改;

3.查看源码:
ini
```
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
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); //首尾去空
if(!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = UPLOAD_PATH .
'文件夹不存在,请手工创建!';
}
}
```
#### 分析与实施
分析代码,黑名单只有'.php',而apache识别为php脚本文件的扩展名有:.php、 .php3、.php4、 .php5,另外index.php.aaa也是能识别的,但是代码中会把.php删掉,所以使用php3|.php4| .php5。,所以,我把后缀改成php3上传既,并验证如下图,能正常访问
Apache HTTP服务器默认情况下支持解析以下常见的文件扩展名: ![image-20250424180334462]()
apache后缀识别原理:
Apache对文件后缀名的识别是从后向前进行匹配的,以单个.作为分隔符。当遇到未知的文件后缀名时,会继续向前匹配,直到遇到可以识别的后缀名为止。

总结
这题主要让价了解apache解析成php脚本的扩展名有哪些,apache解析原理。
Pass-04
信息收集与分析
根据之前的经验,我们直接看源码
ini
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");
$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); //收尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
这回增加了更多的黑名单,03中的方法应该是不行了。我们可以利用apache中.htaccess文件的特性,把图片解析成php。其中.htaccess(Hypertext Access)是 Apache 服务器的一个分布式配置文件,可以覆盖主配置文件的设置,无需重启服务器即可生效(需要管理员级别权限修改的httpd.conf文件修改后需要重启Apach服务器)它主要用于目录级的配置,是 Web 安全、URL 重写和服务器优化的重要工具。
实施
-
新增一个文件.htaccess,添加下面代码,
bash<FilesMatch "loudong.jpg"> SetHandler application/x-httpd-php </FilesMatch>
2.然后在把info.php改成loudong.jpg文件
3.注意这边上传后的文件名没有改,我们就按照下面回显的路径去访问,访问地址是:http://ip:端口/upload/loudong.jpg,既能看到php的info信息

总结
此题主要的知识点是.htaccess文件。
Pass-05
信息收集与分析
查看了代码后,与04差不多,我们就试试使用04的代码测试下,得到下面的结果:

只能在分析下代码,与04差不多,黑名单中添加了".htaccess",但是没有转换成小写,那么我们PHP就能绕过。
ini
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
实施
1.修改后缀php改成PHP并上传

2.访问对应的地址

总结
根据具体的代码制定绕过计划
Pass-06
信息收集与分析
与05比较,少了去掉收尾空格的代码片段。
ini
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = $_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
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
通过上传的时候通过bp添加空格,在校验文件名是否合格的时候,能绕过验证,并且系统保存末尾有空格的文件名称时,会自动把末尾的空格去掉。
实施

总结
保存的特性系统的特性,把末尾的空格去掉。
Pass-07
信息收集与分析
与06比较缺少了删除末尾的"."的代码段,如果我们再末尾添加一个.那么 <math xmlns="http://www.w3.org/1998/Math/MathML"> f i l e e x t = s t r r c h r ( file_ext = strrchr( </math>fileext=strrchr(file_name, '.'); 中返回的就是".",所以
ini
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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_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); //首尾去空
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 . '文件夹不存在,请手工创建!';
}
}
实施

总结
针对操作系统在后面加.与空格,保存成文件后,都会自动把.与空格去掉
Pass-08
信息收集与分析
代码中缺少对":: <math xmlns="http://www.w3.org/1998/Math/MathML"> D A T A "的处理,在 W i n d o w s 中会把 : : DATA"的处理,在Windows中会把:: </math>DATA"的处理,在Windows中会把::DATA 之后的数据当成文件流处理,不会检测后 缀名,且保持:: <math xmlns="http://www.w3.org/1998/Math/MathML"> D A T A 之前的文件名,类似之前" . "与空格的功效,所以在 b p 中使用 r e p e a t 发送到服务器前把文件名中添加 : : DATA 之前的文件名,类似之前"."与空格的功效,所以在bp中使用repeat发送到服务器前把文件名中添加:: </math>DATA之前的文件名,类似之前"."与空格的功效,所以在bp中使用repeat发送到服务器前把文件名中添加::DATA即可。
ini
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
实施
把请求发送到repeat中后,修改filename,添加::$DATA

访问地址http://IP:端口/upload/info.php

总结
在windows操作系统中添加::#DATA作为文件名末尾,操作系统会忽略。
Pass-09
信息收集与分析
代码:
ini
$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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$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); //首尾去空
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 . '文件夹不存在,请手工创建!';
}
}
根据代码的验证过程,只要 <math xmlns="http://www.w3.org/1998/Math/MathML"> f i l e e x t 中不是黑名单的字符串就行,由于" . "、 " 空格 " 、" : : file_ext中不是黑名单的字符串就行,由于"."、"空格"、":: </math>fileext中不是黑名单的字符串就行,由于"."、"空格"、"::DATA"三个保存时,对最终落地到操作系统的文件名没有影响,那么我们通过这个三个组合添加到我们的文件名后,由于"::$DATA"是整个替换,所以用不了,那么就剩"."与"空格"了
ini
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
逐句分析:
ini
$file_name = trim($_FILES['upload_file']['name']);
头尾去空格,那么我们文件名最后一个必须是"."
ini
$file_name = deldot($file_name);//删除文件名末尾的点
deldot此函数删除末尾的点,一直删除到没有为止,如果有两个".",那么会删除两个,那么我们末尾的字符串是空格+.了:" ."
ini
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
前面两个对我们没有影响,最后一个取首位空格的函数,我们的倒数第二个空格又没有了,那么我们可以倒数三个在加上一个".",完成的文件名为:
info.php. .
实施



总结
需要对代码进行逐条分析,找到逻辑的漏洞。
Pass-10
信息收集与分析
代码:
ini
$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 = 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;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
这段代码的核心是 <math xmlns="http://www.w3.org/1998/Math/MathML"> f i l e n a m e = s t r i r e p l a c e ( file_name = str_ireplace( </math>filename=strireplace(deny_ext,"", $file_name);,它吧所有的黑名单中的关键字全部换成空格,文件都能上传,但是上传后,php的文件名被去掉了,那么也就无法运行了。

注意上面的代码知会执行一次,如果我们再php中插入一个php字符窜,那么一个php被替换成"",剩下的还是php,如pphphp、phphpp(这个不行,替换完会变成hpp),综上,只能是pphphp了。
实施

总结
根据具体的代码具体分析,分析出其中的逻辑漏洞。