大概两年前的一次应急响应,让我彻底记住了“无文件攻击”这个名字。某公司的一台对外 Web 服务器半夜开始向外回传数据,流量监控看得很清楚,可当团队登录服务器排查时,全盘扫描干干净净,临时目录里没有 webshell,上传目录连可疑文件都翻不出来。机器看起来“很干净”,数据却在不断被带走。后来靠内存分析和进程行为才确认,攻击者早把恶意代码注进了 Java 进程的内存,指令和数据全部通过 Web 请求参数传递,磁盘上自始至终没有出现过恶意样本。这就是无文件攻击(Fileless Attack)在 Web 渗透中最典型的场景:不是没有攻击,而是没有“文件形态”的攻击。
这篇文章主要想和做安全运维、蓝队应急、Web 开发的读者聊聊无文件攻击的原理与应用边界:它为什么这么能藏,在 Web 场景里通常长什么样,攻击者说的“免杀”究竟在对抗什么,以及防守端怎么从“找文件”转向“追痕迹”。为了保证内容可公开分享,文里不会展开任何具体攻击代码和利用步骤,重心放在原理认知和防御落地这两件事上。
1. 为什么“无文件”反而最危险:传统查杀检查不到载荷
1.1 大多数安全产品先要拿到一个“文件”才有事可做
很多朋友把“装了杀毒软件、每周全盘扫描”等同于安全,这个习惯在常规病毒面前够用,但在无文件攻击面前基本等于没有防线。原因很直接:主流防病毒产品的工作方式,无论是特征库匹配、启发式查杀,还是云沙箱行为分析,第一步几乎都是“先找到一个文件”。特征库要拿样本去比对,启发式要在文件里找可疑结构,云沙箱还要把文件丢进一个隔离环境观察运行行为。也就是说,检测链条的入口建立在“恶意代码会落地成文件”这个假设上。
可无文件攻击恰恰绕开了这个假设。恶意代码要么直接放进内存,要么以脚本字符串形式存在于命令行参数里,要么藏进系统自带的注册表项、计划任务和 WMI 订阅中。杀毒软件全盘扫描时,它找不到可以扫描的对象。网上有个说法很形象:安检员一直在检查行李箱里的物品,有人把违禁品缝在衣服上直接过检,安检机对着行李箱再仔细也看不见。这些年我参与过的应急里,好几起最后确认失陷的系统,唯一“干净”的证据就是全盘扫描报告。
1.2 可检测的痕迹只有三种,无文件攻击砍掉其中一种
抛开各种产品名词,一次攻击最终能留给检测方的痕迹其实只有三类:文件实体、进程行为、网络流量。传统杀软主要盯第一类,EDR 和主机审计盯第二类,流量设备盯第三类。常规病毒攻击会把一个恶意文件放到磁盘,再通过某个进程把它运行起来,最后回连攻击者。三道检测层都有机会感知到它。
无文件攻击的策略是把第一类直接抹掉,然后把第二类和第三类压到最小。脚本可以通过远端加载后直接在内存里运行,进程本身是系统自带工具或正常业务的进程,行为上看起来只是“多跑了一段命令”;外联又刻意控制在低频、加密、混入正常 HTTPS 流量的程度。三个检测源同时失手,告警自然就不会出现。这也是为什么很多单位明明部署了不止一层安全设备,遇到无文件攻击时还是只能通过最原始的渠道、比如业务方收到异常账单或数据泄露被曝光才发现。
1.3 “无文件”不等于“无痕迹”,这个纠偏很关键
关于无文件攻击,我见过最大的误区是把“无文件”理解成“无痕迹”。做应急的时候,最容易让人心里发毛的不是看到一堆恶意文件,而是什么都找不到,找不到反而让人怀疑自己是不是漏了。实际上,无文件攻击只是没有传统意义上的恶意文件,它的痕迹全都换了一副样子:进程启动链异常、命令行参数里出现可疑的脚本代码、内存里有不在部署清单中的类、网络会话指向陌生目标。
这些痕迹需要靠不同工具去挖,但只要你接受了“无文件也有痕迹”这个前提,排查方向就不会跑偏。反过来,如果脑子里只有“找文件”这一个思路,遇到这种情况就会反复全盘扫描,然后把环境弄得越来越糟。在我看,对防守方来说最重要的一句话是:杀毒软件报告“干净”,不等于主机“没被入侵”,只能说明它的样本库里没有能匹配的东西。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Web 渗透场景下,无文件攻击常见的几种形态
2.1 内存马:恶意逻辑住进了运行进程
Web 渗透里最典型的无文件形态就是内存马。以 Java Web 为例,业务程序运行时需要把 class 字节码加载成对象,这个过程叫类加载。攻击者拿到漏洞利用入口后,会构造一段精心编写的字节码,把它直接加载进运行中的 JVM,再注册成一个额外的过滤器、监听器或者 Servlet。之后每次 HTTP 请求进来,这个恶意组件会先于业务代码执行,从请求参数里解析指令,再通过 HTTP 响应把执行结果带出去。
为什么这种形态很难发现?因为进程还是那个正常的业务进程,端口还是 80 或 443,流量还是 HTTP 协议,唯一多出来的东西活在进程内部。你把磁盘翻一百遍也看不到它,除非把 JVM 的内存抓下来分析,或者在运行时防护层面做类加载对比。从防御视角看,它留下的特征其实很明确:运行时加载的类名和部署包对不上、请求路径出现非业务约定的参数、类加载时间和业务发布时间对不上。问题在于这些特征默认不被记录,平时看不到。
2.2 脚本型载荷:命令执行漏洞只是开始
另一类常见形态是利用 Web 漏洞拿到命令执行入口,然后调用系统自带的脚本引擎加载远端载荷。场景大概是这样:某个 Web 组件存在命令注入或表达式注入漏洞,攻击者只拿到了一个“能执行命令”的口子,他不会停在入口处,而是会调用 PowerShell、Python 或系统自带的脚本解释器,去执行一段由远端拉取或直接解码出来的脚本。
这里有个普遍误解:很多人以为无文件攻击一定要用到加密的恶意文件。实际上很多案例连文件都不用,攻击者把脚本做一次编码塞进请求参数,服务端执行时解码,恶意代码就像流水一样过了内存。磁盘上没有新增文件,进程也可能是系统自带的合法解释器进程。从检测角度看,关键特征变成了“Web 进程为什么拉起了一个脚本解释器”“脚本解释器的命令行里为什么有一大段编码内容”“这个脚本进程为什么短时间内执行了系统管理命令”。这些行为细节平时如果只记日志不分析,那确实是盲区。
2.3 持久化与驻留:为什么清完进程还会复发
很多团队处置无文件攻击时最容易栽的跟头,是只清表面不挖持久化。攻击者辛苦打进来,为的是长期留用,他会把“启动开关”放到一些系统机制里:Windows 的注册表启动项、WMI 事件订阅、计划任务,Linux 下的 systemd 服务、定时器、启动脚本。这些位置都没有传统意义上的恶意文件,但它们会在系统重启、定时触发或满足特定条件时,把攻击者留下的载荷重新拉起来。
我见过不止一次这样的情况:应急时确认了进程异常,杀掉恶意进程,封了外联地址,以为处置完事。结果第二天早上业务又来电话,说服务器又开始往外连,原因就是攻击者留下的某个计划任务还躺在系统里,到点自动执行。处理持久化必须有一个固定动作清单,不能靠临场记忆。应优先检查计划任务、systemd 定时器、WMI 订阅、启动项、启动脚本这些位置,看到新增的非业务项目,先确认来源再清理,别一上来就把系统配置删乱。
2.4 一条典型的 Web 入口攻击链路
把上面这些形态串起来,可以看清一条完整的 Web 入口攻击链路:攻击者先通过某个对外的 Web 组件漏洞,比如一次反序列化或模板注入,获得远程代码执行能力;然后调用系统脚本引擎加载内存载荷,这个载荷负责信息收集并回连;确认控制之后,再往计划任务或启动项里写一个持久化配置;后续的攻击者只需要在某个时间点触发这个开关,或者直接通过 Web 请求继续下达指令。
整条链路里没有任何一个环节强制要求恶意文件落地,所以传统文件查杀体系处处都断。防守方要做的,恰恰是在这条链路上布上对应的检测点位:入口侧靠补丁管理和 WAF,载荷侧靠内存检测和 RASP,回连侧靠网络外联审计,持久化侧靠配置基线核查。四环如果缺一环,中间就可能断掉。
3. 攻击者视角的“免杀”逻辑:不躲病毒库,而是躲检测源
3.1 传统免杀与无文件思路的关键差异
标题里的“免杀”这两个字很容易被误解成传统杀软对抗技术。传统免杀的做法,是拿一个恶意文件做加壳、混淆、改特征,让它不被杀软识别,但最终还是要带着这个文件落地执行。无文件攻击的思路完全不是一回事:它默认不让恶意代码变成一个可供扫描的样本,等于整个绕开了文件查杀体系。
类比来说,传统小偷研究的是怎么让撬锁工具看起来不像撬锁工具;无文件攻击者干脆不用锁,他利用主人自己留的窗户爬进去,而且这扇窗户还是主人亲手打开的。所以“免杀”在无文件攻击语境里,本质上不是某个加密或混淆技巧,而是一整套对抗思路的转移:对抗重点从“文件特征”挪到了“行为隐蔽”。防御侧要理解这一点,不必去逐字研究攻击者的混淆手法,而是应该想一想:如果我不依赖文件样本,我还能靠什么发现它?
3.2 系统白名单工具为什么会成为攻击者的帮手
无文件攻击还有一个常用策略,是利用系统自带工具和脚本完成恶意操作,这类工具专业上被称为“Living Off the Land”。操作系统本身自带远程管理、脚本执行、网络传输等能力,安全体系默认它们是可信的,正常运维也会用到,检测产品一般不会一看到 PowerShell 或计划任务就告警。攻击者恰恰利用这种信任,把恶意操作包装成“系统工具的常规使用”。
举例说,攻击者想让目标机器下载一段脚本,他不会去上传一个被扫描的 exe,而是直接调用系统自带的网络工具去拉取数据。这些行为在日志层面长得和运维人员日常操作很像,所以单纯靠“允许名单”很难拦住。这里想提醒一点:白名单不是万能的,同样的工具在晚上十一点被一个 Web 进程拉起,和白天空闲时被运维人员在固定机器上使用,风险完全不一样。名单之外还需要行为基线,这才是有意义的检测维度。
3.3 “免杀”不等于“隐身”:行为痕迹依然可追
攻击者能抹掉文件,但抹不掉“事情发生的经过”:谁在什么时间拉起了哪个进程,这个进程执行了什么命令,命令行里带有哪些参数,它又主动连去了哪个地址。这些痕迹一段一段散落在主机日志、进程审计、网络日志和应用日志里,单个看都不起眼,串起来就能还原一整条攻击路径。
所以我在给团队做培训时经常说,不要追求“什么都能实时拦截”,那既不现实也很贵。更实际的目标是:告警错过可以,但我必须保证事后能找回时间线。无文件攻击很少是“进来就爆发”的,它更像一种慢性渗透,只要日志还在、时间线能重建,止损就来得及。真正让防守方被动的,往往不是不知道攻击手法,而是日志被截断、关键进程被提前重启、内存证据被清掉,导致无从下手。
4. 防御者反制:以痕迹为中心的检测与应急排查
4.1 把检测重心从“文件特征”挪到“行为三件套”
搞清楚无文件攻击的特性之后,防御策略自然要跟着调整。我建议把检测建设分成两条线:一条线照旧做传统杀毒和防病毒,管住常规文件型攻击;另一条线重点盯“行为三件套”,专门应对无文件场景。
第一件是进程启动链。攻击者哪怕不落文件,也总要经过某个进程来执行指令,所以父进程是谁、谁拉起了谁、为什么一个 Web 进程会去启动脚本解释器,这些问题本身就是强信号。第二件是命令行和脚本块。无文件攻击的大量操作都藏在命令行参数里,Windows 环境的脚本块日志、Linux 环境的 shell 审计、Web 中间件的访问日志,都要长期开启并纳入集中分析。第三件是网络外联。低频、加密、凌晨时段、目标地址陌生,这些特征单看可能不触发告警,但组合起来就足够可疑。
4.2 日志闭环:最便宜但最容易断头的基础设施
做应急这些年,我发现很多单位不是没有日志,而是日志“断头”。Web 日志在应用服务器上,主机审计在终端上,网络流量数据在防火墙那边,三块各存各的,告警系统提示“这台主机异常”,分析师还得手动切换三四个平台才能拼出初步时间线。真正等人工拼完,攻击者可能早就完成了横向移动。
日志闭环这件事听起来不性感,但性价比极高。不需要一开始就上很贵的大数据平台,先把三类日志统一格式、统一时区、集中存储,保留至少九十天,就已经能覆盖大多数无文件攻击的溯源需求。这里还有个很容易忽略的细节:Web 访问日志里除了记 URL,一定要记原始请求来源、请求体大小和响应状态,这样才能从“某接口被访问”准确推到“某参数触发了异常执行”。日志不是为了应付合规检查,是为了出事时能回答三个问题:谁进来了、从哪个入口进来的、进来之后做了什么。
4.3 一份可直接落地的无文件攻击应急排查清单
做应急预案最怕全是空话。下面这份清单是我在实际项目中沉淀下来的版本,你可以直接拿去当应急手册的骨架。
| 检查对象 | 重点内容 | 常见异常信号 |
|---|---|---|
| 进程列表与父进程链 | 谁拉起了谁、进程启动时间 | Web 进程拉起解释器、非业务进程外联 |
| 内存加载模块 / JVM 类 | 对比部署包与运行时加载的类 | 不在部署清单中的类、加载时间异常 |
| 网络外联会话 | 凌晨低频、加密协议、陌生目标 | 境外或非常见地址、持续时间长 |
| 计划任务 / systemd 定时器 | 非业务创建的定时项 | 名称伪装成系统组件、指向脚本路径 |
| WMI 订阅 / 注册表启动项 | 新增加载项、可疑命令行 | 启动命令带编码参数或远端地址 |
| Web 目录与中间件缓存 | 排除常规落盘后再查非文件实体 | 模板缓存、类缓存出现新内容 |
| 数据库与中间件扩展 | 存储过程、触发器、外部脚本 | 新增非业务函数或扩展 |
注意:应急时顺序别搞反。先隔离外联并保全内存证据,再分析取证,最后才能清理。很多团队急着“修”,一上来就重启进程、删文件,结果内存里最关键的载荷信息全没了。
4.4 日常加固里性价比最高的几件事
防守不能只靠应急,日常加固里有些动作花不了多少钱,但能直接把无文件攻击的生存空间压小。第一,对外组件的补丁优先级一定要排在最前面,补丁跟不上,WAF 规则至少要先顶上;第二,脚本引擎权限做最小化,执行策略收紧、宏默认禁止,开发确实需要再单独申请;第三,针对 Web 中间件做类加载和组件白名单,运行时防护手段对内存马这类形态有直接杀伤力;第四,定期做“假设已失陷”演练,按上面这份清单完整走一遍,把排查练成肌肉记忆。
5. 一次模拟应急演练复盘:从一条网络告警到收敛
5.1 事件背景:一台“干净”的服务器在半夜外联
为了更好地展示无文件攻击的排查思路,我整理了一次内部模拟演练的过程。某公司一台对外提供服务的 Java Web 服务器,凌晨两点被网络监控发现向一个外部地址发起低频 HTTPS 连接,持续时间超过四十分钟,数据包不大,但频率很规律。值班人员先登录主机做了一遍常规检查:磁盘全盘扫描无恶意文件,杀毒软件零告警,业务进程看起来也正常。这台机器给人的第一印象就是“干净”。
可网络侧的行为不会说谎。结合时间点、业务状态、外联目标地址,值班人员判断这不是业务行为,按预案进入应急流程。这个场景和真实无文件攻击案例的告警形态几乎一样:网络层先发现异常,主机层却拿不出文件证据。如果在主机层反复纠结于文件扫描,时间就会在犹豫中消耗掉。
5.2 排查链路:从告警到锁定的每一步
这次演练的排查链路可以分成六个步骤,每一步都对应前面讲到的痕迹维度。
第一步是网络隔离与证据保全。确认外联不是业务行为后,先切断服务器对外的网络连接,但保留业务访问入口,同时绝对不关机。原因很简单,内存里住着完整的攻击载荷,一关机等于销毁第一手证据。
第二步是进程与外联分析。通过进程树检查,发现外联连接归属于一个 Java 业务进程,可这个进程在凌晨不该有任何主动外联动作,端口也不是业务声明的常用端口。到这里基本可以判断,进程本身已经被植入了恶意逻辑。
第三步是内存快照与类加载分析。把 JVM 内存做成快照后,对比部署包里的类清单和运行时加载的类集合,很快找到一个不在部署包里的可疑类,反编译后确认这是攻击者注入的内存马组件。
第四步是日志回推入口。顺着 Web 访问日志往前翻,找到当晚第一次触发可疑请求的 URL,指向一个组件接口。再对比补丁记录,发现该组件在两周前爆过漏洞,当时因为业务排期一直没修复,入口就这样被确认了。
第五步是持久化清理。在计划任务和启动项里找到攻击者写下的异常配置,内容是一段指向外部的拉取指令。清理掉后端、封禁外部地址,完成组件补丁修复,这次清理才算真正结束。
第六步是全内网扩散排查。检查其他主机是否有相同特征,确认无横向扩散迹象后才恢复完全开放。
5.3 复盘:哪些环节原本可以更早发现
这次演练虽然最终收敛成功,但复盘时还是能挑出不少本可以更早发现问题的环节。最大隐患是补丁空窗期长达两周,等于把入口敞开着等人进来;其次是 Web 日志和主机审计没有自动关联,大量时间花在人工跨系统拼接时间线上;还有一个典型问题,就是团队对“没有文件也可能是攻击”缺少预案,值班人员一开始反复全盘扫描文件,走了一段弯路。
正面经验也同样明显:网络外联监控足够灵敏,虽然它拦不住无文件攻击,却提供了一个明确的发现窗口。很多团队的现状是重入口防御、轻外联检测,这就等于只看门锁,不看屋里有没有进人。外联监控恰恰是从“被动挨打”到“主动发现”的转折点。
5.4 过后我留下的三条实操心得
第一,面对疑似无文件攻击,取证顺序应该是内存、进程树、外联记录、持久化配置,不要一上来就清理环境,清理前先保证证据还在。第二,应急处置的第一个动作不是“修”,而是“隔离和保全”,把外联切断、把数据留住,后面才有得查。第三,平时把这些排查动作做成脚本化、剧本化,遇事直接按单子走。蓝队真正拼的不是临场反应,而是有没有把常见场景提前练过——这比我临时翻十篇文档都有用。
