图片马与内存马:原理认知与全面防御指南
本文面向「完全零基础」读者,用防御者视角讲清两件事:图片马和内存马分别是什么、为什么难查、以及每一类风险对应哪些可落地的防护与排查方法。

重要声明
- 本文只写原理认知与攻击防范/检测/应急,不提供任何可复现的植入、构造、利用步骤或恶意样本。
- 「仅用于学习」不能成为未授权攻击的理由。请在自建靶场或明确书面授权环境中做合法实验。
- 「每个字都带来源」在技术写作中不可行;下文对关键结论给出可点击的权威或可考证链接,便于你自行核对。
一、先建立一张总地图
把两者放在同一条「Web 被入侵后」的链路里理解:
1 | |
| 对比维度 | 图片马(常见形态) | 内存马(常见形态) |
|---|---|---|
| 主要载体 | 看起来像图片的上传文件 | JVM / Web 容器内存中的组件 |
| 是否落盘 | 通常有文件 | 往往无独立恶意文件 |
| 传统杀软/扫目录 | 有时能扫到可疑脚本特征 | 经常扫不到 |
| 重启服务后 | 文件一般还在 | 多数会消失(除非再次注入或写入启动链路) |
| 防御重心 | 上传校验 + 禁止执行 + 重编码 | 修入口漏洞 + 运行时检测 + 组件审计 |
一句话记忆:图片马是「把恶意逻辑藏进看起来像图片的文件里」;内存马是「把后门挂进 Web 框架正在跑的请求处理链里」。
二、图片马:原理(防御者需要懂到什么程度)
2.1 它到底是什么?
「图片马」不是一种官方标准术语,而是运维/安全圈对一类现象的俗称:
上传内容在表面上通过「像图片」的检查,但在后续错误配置或二次漏洞下,仍可能被服务器当成脚本执行或被包含执行。
社区与安全问答中,常见讨论包括:上传目录可执行脚本、双重扩展名、PATH_INFO 误解析、以及本地文件包含(LFI)把上传文件当代码读入等。参见:
2.2 为什么「看起来像图片」仍危险?(概念层)
从防御视角,只需记住三条机制,不必学如何制作:
扩展名/MIME 可被伪造
浏览器提交的Content-Type、用户起的文件名都不可信。OWASP 明确要求:不要信任Content-Type,要用服务端内容检测与白名单。
来源:OWASP File Upload Cheat Sheet「能通过图片头检查」≠「文件里没有别的东西」
仅检查魔数或getimagesize一类「像不像图」的函数,无法证明文件安全;恶意内容可藏在元数据、注释块或尾部拼接区。PHP 官方也提醒:getimagesize()不适合作为「是否安全图片」的充分证明。
来源:PHP 手册 · getimagesize、Security SE 同上帖真正致命的往往是「执行条件」
即使磁盘上有可疑字节,若上传目录永远不能当脚本执行、且没有文件包含把该文件当代码加载,风险会大幅下降。反过来:弱校验 + 可执行目录 + 包含漏洞,才是连环事故。
2.3 风险链条(示意,非操作步骤)
flowchart LR
A["用户上传接口"] --> B{"校验是否过弱?"}
B -->|是| C["可疑图片落盘"]
C --> D{"上传目录可执行脚本?"}
C --> E{"存在文件包含等二次漏洞?"}
D -->|是| F["可能被当成脚本处理"]
E -->|是| F
F --> G["服务器被远程控制风险"]
B -->|否且纵深防护| H["风险被切断"]
三、图片马:全面攻击防范方法(逐项写清)
下面每一项都是防御动作。按「优先级从高到低」排列。权威总纲见:OWASP File Upload Cheat Sheet、OWASP Input Validation · File Upload。
方法 A:业务白名单(扩展名 + 真实类型)
做什么
- 只允许业务真正需要的类型(如
jpg/png/webp),用允许列表,不要只用「禁止 php/jsp」黑名单。 - 用服务端
finfo/ libmagic 等读取文件内容魔数,映射到允许的 MIME,再决定扩展名。 - 不要信任
$_FILES['type']或客户端声明。
为什么有效
黑名单永远赶不上变形;白名单把攻击面压到最小。OWASP 与多份安全上传实践均强调这一点。
参考:Secure File Uploads in PHP 实践清单
落地检查清单
- 代码里是否存在「禁止列表」却没有「允许列表」?
- MIME 是否来自服务端内容嗅探?
- 最终保存扩展名是否由服务端根据嗅探结果决定?
方法 B:随机文件名 + 长度/字符限制
做什么
- 丢弃用户原始文件名(或仅作展示名存数据库)。
- 磁盘文件名用密码学安全随机串 + 白名单扩展名。
- 限制文件名长度与字符集。
为什么有效
避免 ../、双扩展名、特殊解析字符等「靠文件名骗过 Web 服务器」的路径。OWASP 明确建议应用生成文件名。
来源:OWASP File Upload Cheat Sheet
方法 C:文件大小与上传权限控制
做什么
- 限制单文件大小、单用户频率、总配额。
- 上传接口必须先认证、再授权(谁可以传、能传到哪)。
为什么有效
既防存储耗尽型拒绝服务,也缩小「谁能往服务器扔文件」的面。
来源:OWASP DoS Cheat Sheet · 上传限制、File Upload Cheat Sheet · User Permissions
方法 D:存储位置——Web 根目录之外
做什么
- 上传文件存到网站文档根以外的目录。
- 对外访问时,用受控的下载/读图接口输出,并强制设置正确的
Content-Type与X-Content-Type-Options: nosniff。
为什么有效
用户即使猜到文件名,也不能靠「直接访问 URL」触发脚本引擎。这是纵深防御的核心层。
来源:Security SE 建议、OWASP File Upload · 存到 webroot 外
方法 E:上传目录禁止脚本执行(Web 服务器层)
做什么(概念配置目标,按你的服务器选型查阅官方文档落实)
- Apache:对该目录关闭脚本引擎、拒绝可执行扩展名映射。
- Nginx:该
location只提供静态文件,不要把该路径fastcgi_pass到 PHP/其他解释器。 - 容器/云存储场景:对象存储 + CDN,源站不解释用户内容。
为什么有效
即使有人传进了「带脚本尾巴的伪图片」,服务器也不会执行它。
实践讨论见:PHP 仅允许图片上传的安全校验说明
方法 F:图片解码并重编码(最强内容净化之一)
做什么
- 用 GD / ImageMagick 等库真正解码像素,再写出一张新图。
- 失败则拒绝上传。
- 缩略图同样走「解码 → 缩放 → 再编码」。
为什么有效
重编码会丢弃 EXIF 注释、尾部拼接、大量非像素负载;多份安全开发实验材料将其视为破坏嵌入载荷的关键手段。
来源:Secure PHP Development · Lab File Upload Security、OWASP Input Validation · Image rewriting、PHPPot:生产级上传指南中的 Re-Encode
方法 G:修复文件包含与错误解析面
做什么
- 审计并消除本地/远程文件包含类缺陷。
- 关闭危险的动态包含、严格校验被包含路径为应用内固定白名单。
- 检查 Web 服务器对「奇怪 URL」的解析配置,避免把静态资源路径误交给脚本引擎。
为什么有效
图片马经常不是「单独就能打穿」,而是「上传 + 包含/解析错误」组合拳。LFI 测试方法见 OWASP WSTG(上文链接)。
方法 H:SVG 等特殊格式单独策略
做什么
- 若业务不需要 SVG,直接禁止。
- 若需要,按「不可执行、消毒、隔离域」策略处理(SVG 可内嵌脚本,性质不同于位图)。
为什么有效
Security SE 明确提醒:SVG 设计上就可含脚本。
来源:同上 SE 回答
方法 I:持续检测与内容安全
做什么
- 上传管道接入恶意软件/Webshell 特征扫描(注意:仅特征扫描不够)。
- CSP、下载响应头、仅登录可读私有图等。
- 定期扫上传目录异常新建文件、异常时间戳、异常权限。
图片马防御「最小闭环」
1 | |
缺任何一环都可能被绕过;不要只做其中一两项就宣布安全。
四、内存马:原理(防御者需要懂到什么程度)
4.1 它到底是什么?
「内存马」同样多为中文圈俗称,对应英文常说的 memory shell / in-memory webshell:
恶意逻辑以动态加载的类 / 挂钩组件的形式,注册进 Tomcat、Spring 等 Web 运行时的请求处理链,不一定在磁盘上留下独立后门文件。
学术与工程材料会把它描述为「无文件或少文件、依赖容器组件模型」的 Webshell 形态。例如程序分析方向的检测研究:JShellDetector 论文 PDF。
4.2 它挂在哪里?(只记组件名,不学植入)
Java Web 常见被「挂马」关注的组件类型(便于你排查时知道该查什么):
| 组件类型 | 在请求链中的大致角色 | 防御者为何要盯它 |
|---|---|---|
| Filter | 请求进入业务前可拦截/改写 | 异常 Filter 可对几乎所有流量动手脚 |
| Servlet | 处理特定路径 | 多出「源码里没有的路由」很可疑 |
| Listener | 监听会话/请求等事件 | 可在事件回调里执行逻辑 |
| Valve(Tomcat) | 容器管道阀门 | 更靠近容器层,更隐蔽 |
| Spring Interceptor / Controller 映射 | MVC 层拦截与路由 | 业务应用常见植入面之一 |
| WebSocket 端点等 | 长连接通道 | 传统「扫 JSP」容易漏 |
蓝队视角的对比表(文件马 vs 内存马)可参考公开复盘文:蓝队视角:Arthas 在 Tomcat 入侵分析中的应用(CN-SEC 转载)。
4.3 为什么难查?(概念)
- 无文件或文件与内存不一致 → 目录扫描、mtime 告警经常失灵。
- 活在合法进程里 → 看起来仍是正常的
java/tomcat进程。 - 入口常常是「能执行代码」的漏洞(反序列化、表达式注入、任意上传 JSP 后二次提权到内存等)→ 只删文件不修洞,会再回来。
- 重启可能暂时清除 → 若只重启不取证、不修洞,等于「赶走症状、留下病因」。
研判思路综述(框架识别 → 异常类 → 日志):Java 内存马研判分析(CN-SEC)。
4.4 风险链条(示意)
flowchart TD
V["可远程代码执行类漏洞"] --> L["运行时加载可疑字节码"]
L --> R["注册进 Filter/Servlet/Interceptor 等"]
R --> H["后门只活在内存请求链"]
H --> X{"传统扫盘"}
X -->|经常无效| Y["需运行时组件审计 / Agent 检测 / RASP"]
H --> Z["修复入口漏洞 + 取证清理 + 加固"]
五、内存马:全面攻击防范方法(逐项写清)
同样:只写防、查、清的流程,不写如何注入。
方法 1:优先消灭「能进内存」的入口漏洞
做什么
- 修补已知反序列化、表达式语言注入、模板注入、不安全的动态类加载接口等。
- 依赖组件升级与 SCA(软件成分分析)。
- 关闭不必要的调试端点、Actuator 未鉴权接口、随意开放的 Jolokia/JMX。
为什么有效
内存马是「结果」,代码执行漏洞才是「因」。不修洞,清一次还会回来。
公开应急文普遍强调「同步修复漏洞防止二次注入」:研判分析文结论段。
方法 2:部署 RASP / 运行时防护
做什么
- 在 JVM 侧对
defineClass、危险命令执行、异常 Filter 注册等行为做策略拦截(按厂商产品落地)。 - 将告警接入 SOC。
为什么有效
内存马依赖运行时挂钩;在挂钩点拦截比事后扫盘更对症。蓝队材料将其视为对抗内存马的重要手段:Arthas 实战文中的 RASP 讨论。
方法 3:最小权限与容器隔离
做什么
- 应用进程不以 root 跑;文件系统只读镜像 + 明确可写卷。
- 网络出站白名单,限制「马」连回外网的价值。
- 不同应用分不同 JVM / 容器,缩小爆炸半径。
方法 4:配置与组件基线(「应有清单」)
做什么
- 维护一份「本应用合法 Filter / Servlet / Listener / Interceptor / 路由」基线。
- 发布流程变更该清单需走变更单。
- 运行时定期 diff:多出来的组件 → 进入应急。
为什么有效
内存马的强特征之一就是「多了一个不该存在的挂载点」。检测工具也常从容器注册表入手,例如开源扫描器说明:JavaShellRadar、JavaMemHunter。
方法 5:定期运行时巡检(Agent / 专用扫描器)
做什么(防御向使用)
- 在授权变更窗口对生产 JVM 做只读巡检。
- 使用社区/自研的 Java Agent 类内存马扫描工具,关注:
- 容器已注册组件 vs 磁盘/配置是否一致
- CodeSource 是否为空或异常路径
- 可疑类名、包伪装、字节码与磁盘 jar 不一致
- 工具示例(自行评估信任与许可证后再用):
注意
在生产 Attach Agent 有稳定性与合规风险,先在预发验证;优先选你们安全团队认可的方案。
方法 6:应急排查动作清单(发现可疑后)
原则:先取证,再清理;不要一上来重启把现场冲掉。
建议顺序(蓝队常见做法,可结合 Arthas 实战文、研判分析文):
- 隔离:限制出网、必要时摘流量,保留进程。
- 取证:进程列表、监听端口、近期访问日志、JVM 线程、必要时 heap/dump(按法务与隐私规范)。
- 核对路由与组件:对照基线,找出「源码/配置没有、运行时却有」的 Filter/Servlet/Interceptor。
- 核对类来源:类是否来自预期 jar;CodeSource 是否异常;类加载器是否怪异。
- 日志狩猎:异常 URI、高频 404/200 交替、非常规 User-Agent、业务高峰外的管理路径访问。
- 清理:按预案卸载恶意组件或滚动重启(重启是兜底,不是唯一手段)。
- 根治:修漏洞、轮换密钥与会话、全量扫同集群、复盘。
方法 7:开发侧减少「动态注册滥用」
做什么
- 避免在生产暴露可任意注册 Filter/Controller 的管理接口。
- 代码评审关注反射、
defineClass、从网络读字节码再加载等模式。 - 使用安全编码规范与 SAST。
方法 8:监控与告警
做什么
- WAF / 网关:异常路径、已知内存马管理特征流量(特征会变,不能当唯一手段)。
- 主机:Java 进程异常子进程(如意外拉起系统命令)。
- 应用:新路由自动发现告警。
内存马防御「最小闭环」
1 | |
六、把两者放进同一套安全运营
| 场景 | 你该立刻想到什么 |
|---|---|
| 有用户上传头像/附件 | 走第三节图片马防护闭环 |
| Java/Tomcat/Spring 被告警「可疑类执行」 | 走第五节内存马应急,而不是只删 JSP |
| 只发现磁盘 webshell | 仍要查是否已被提升为内存驻留 |
| 只重启后暂时「好了」 | 高度怀疑内存马 + 入口洞仍在 |
flowchart TB
subgraph daily["日常"]
U["上传防护纵深"] --> B["组件与依赖基线"]
B --> R["RASP/巡检"]
end
subgraph incident["事件"]
A["告警"] --> F["取证"]
F --> D{"落盘还是内存?"}
D -->|落盘| P["图片/文件马清除 + 上传加固"]
D -->|内存| M["组件卸载/重启 + 修洞"]
end
daily --> incident
七、零基础 30 天学习路线(合法)
| 周次 | 学什么 | 怎么证明学会了 |
|---|---|---|
| 第 1 周 | HTTP、MIME、Web 根目录、Nginx/Apache 静态与脚本分离 | 能画出「为什么上传目录不能执行脚本」 |
| 第 2 周 | 按 OWASP 清单给一个演示站做上传加固(仅防御) | 对照本文第三节打满检查清单 |
| 第 3 周 | Java Servlet 过滤器链、Spring MVC 请求流程(官方文档) | 能口述 Filter 与 Controller 差异 |
| 第 4 周 | 在自建靶场用公开检测工具做「巡检演练」 | 能写一份无漏洞细节的应急报告模板 |
推荐权威入口:
- OWASP File Upload Cheat Sheet
- OWASP Input Validation Cheat Sheet
- OWASP Testing Guide · LFI
- Jakarta Servlet 规范(了解 Filter/Servlet 模型)
八、参考与可考证链接汇总
图片马 / 上传安全
- OWASP File Upload Cheat Sheet
- OWASP Input Validation Cheat Sheet(含 Image rewriting)
- Security Stack Exchange:图片中的 Webshell 风险与缓解
- PHP 手册:getimagesize
- Secure PHP Development · 文件上传实验说明
- PHP 安全上传实践清单
- PHPPot:PHP 8 生产级上传指南
内存马 / 检测与研判
- JShellDetector 论文(程序分析检测无文件 Webshell)
- Java 内存马研判分析
- 蓝队视角:Arthas 与 Tomcat 内存马排查
- 内存马排查笔记
- JavaShellRadar
- JavaMemHunter
- MemoryShellHunter
- mem-shell-detector
- Tomcat 组件检测思路笔记:xu-an.gitbook · Tomcat 内存马检测
博客转载与个人笔记质量参差,请以 OWASP / 官方手册 / 论文为主,其余作补充对照。
九、结语
- 图片马:问题本质是「不可信上传内容」碰上「可执行或可被包含的环境」。
- 内存马:问题本质是「能在运行时改请求链」碰上「只扫磁盘的防御习惯」。
- 学会防御,不需要学会造马;把第三节与第五节的检查清单落到你的系统上,比收藏一百篇攻击教程更有用。
若你下一步想针对某一技术栈(例如纯 Nginx+PHP,或 Spring Boot 3)把检查清单改成「可直接贴进变更单」的配置级加固清单,可以指定栈名,我按同一防御边界继续写专篇。