说明:文中所有 IP、主机名与账号均已做替换,示例地址为占位。
凌晨快到十二点,有人跟我说:那台天天直连的机器,连不上了。
我第一反应是「不可能」——那台机器就在本机 Hyper-V 里,同一个二层网络,中间没有路由器,没有 NAT,连 VLAN 都没配。它昨天还是好的。
于是从最简单的开始:ping 不通。换 TCP 探端口,也全不通。22、5985、445、8888,一个都不通。同一时刻,隔壁另一台同类虚拟机一切正常。
这就很有意思了。同一台交换机上的两台虚拟机,一台通一台不通,而且不是「慢」,是彻底静默。
一、三个反直觉的事实
按常规剧本,网络不通先看防火墙。但这次,越查越不对劲:
| 检查项 | 结果 | 说明 |
|---|---|---|
| 目标机 Windows 防火墙入站 Block 规则 | 无 | Get-NetFirewallRule -Enabled True -Direction Inbound -Action Block 返回空 |
| 目标机放行规则作用域 | remote=Any | 22 与 5985 两条规则都对任何来源放行 |
| 目标机防火墙丢弃日志 | 空 | 打开 droppedconnections 日志后,一条丢弃记录都没有 |
| 源主机防火墙涉及目标的规则 | 无 | 全量扫过一遍,没有任何规则提到目标地址 |
| ARP | 通 | 源主机看目标机是 Reachable,MAC 也正确 |
| 路由 | 正常 | Find-NetRoute 两侧都指向正确接口 |
三个反直觉的点:
第一,目标机的防火墙根本没看见这些包。日志是空的——不是「放行了」,是「没见过」。
第二,ARP 是通的。如果是二层断了,ARP 应该先断。ARP 能通说明链路是活的。
第三,反向也不通。从目标机 ping 源主机,同样失败。这不是单向封锁,是双向不来电。
正常情况下,到这里我会开始怀疑交换机、怀疑驱动、怀疑人生。但既然 ARP 通、TCP 不通,那问题一定卡在「网卡收到了,但没交给 TCP 栈」这个区间里。
二、把「丢在哪一层」钉死
猜没有用,抓包。
我在源主机和目标虚拟机内部同时开抓包,过滤对端地址,然后手动触发一次连接。
源主机侧的抓包结果很干脆:
AA-BB-CC-DD-EE-FF > 11-22-33-44-55-66, ethertype IPv4
10.0.0.10.12057 > 10.0.0.20.22: Flags [S], seq ..., win 64240同一行重复了很多次——那是 TCP 在按退避重传。没有一个回包。
然后是关键:目标虚拟机内部的抓包,看见了 SYN。
AA-BB-CC-DD-EE-FF > 11-22-33-44-55-66, ethertype IPv4
10.0.0.10.12057 > 10.0.0.20.22: Flags [S], seq ...它还回了 ARP:
11-22-33-44-55-66 > AA-BB-CC-DD-EE-FF, ethertype ARP
Reply 10.0.0.20 is-at 11-22-33-44-55-66但整个抓包窗口里,从目标机发出的 TCP 包数量是 0。
这一步把范围压缩得非常小:
- 包确实进了目标机的网卡(抓到了);
- 目标机的网卡能发包(ARP 回了);
- 但目标机的 TCP 栈从没生成过 SYN-ACK(一个都没有)。
也就是说:包死在「网卡 → TCP 栈」之间。这个区间里,能无声无息吞包的,只有一类东西——WFP(Windows Filtering Platform)上的过滤驱动。
三、让 WFP 自己招供
Windows 防火墙对入站包的丢弃,是会写日志的。既然日志是空的,那就说明不是 Windows 防火墙干的,而是另一个挂载在 WFP 上的过滤驱动。
问题是怎么抓它出来。办法是打开 WFP 的审核策略,然后读安全事件日志。
这里有个坑值得单独记一句:auditpol 的子类别名是本地化的。在中文系统上直接写:
auditpol /set /subcategory:"Filtering Platform Packet Drop" /failure:enable会得到一个非常迷惑的 Error 0x00000057: The parameter is incorrect.。正解是用 GUID:
auditpol /set /subcategory:"{0CCE9225-69AE-11D9-BED3-505054503030}" /failure:enable
auditpol /set /subcategory:"{0CCE9226-69AE-11D9-BED3-505054503030}" /success:enable /failure:enable开好之后触发一次连接,安全日志里立刻出现了事件 5152:
The Windows Filtering Platform has blocked a packet.
Direction: Inbound
Source Address: 10.0.0.10
Destination Address: 10.0.0.20
Layer Name: IP Packet
Filter Run-Time ID: 389248两个关键信息:
Layer Name: IP Packet——这是在 IP 层就被丢了。Windows 防火墙自己的入站拦截发生在 ALE 层(Inbound Transport v4),不在这一层。这就把 Windows 防火墙正式排除了。- 有一个明确的
Filter Run-Time ID,说明这是某个驱动注册的过滤规则。
剩下的就简单了——把当前运行、名字里带 wfp / fw / flt 的内核驱动列出来:
Get-CimInstance Win32_SystemDriver |
Where-Object { $_.State -eq 'Running' -and $_.Name -match 'wfp|fw|flt' } |
Select-Object Name, PathName答案浮出来了:一个第三方安全软件的 WFP 内核驱动。它不是 Windows 防火墙,所以它的拦截既不进 Windows 防火墙的规则表,也不进它的日志——难怪前面所有检查都显示「干净」。
四、真凶,和我自己的手
知道是谁干的之后,还得知道为什么。那套安全软件把自己的行为写进了日志,而且是明文 JSON:
{"detection":"Lateral/HiddenShare",
"raddr":"10.0.0.10",
"lport":445,
"blocked":1,
"uri":"10.0.0.20C$",
"procname":"System"}读到这一行,我沉默了大概三秒。
10.0.0.20C$。这是我在上一轮排查里自己干的事——为了找「这台机器上有什么共享」,我去访问了目标机的管理共享 C$。
那套安全软件的「横向渗透防护」把它判成了攻击行为:一个内网地址突然去连别人的管理共享,这在行为特征上确实很像横向移动。于是它做了一件很果断的事:
把源 IP 整条拉黑——不是封端口,是封 IP。
这一下所有现象都对上了:
- 22 / 5985 / 445 一起死,因为封的是 IP 不是端口;
- 两端的 Windows 防火墙全清白,因为动手的不是它;
- ARP 还是通的,因为 ARP 不在 WFP 的管辖范围内。
于是,「以前都是直连通的」这句话的解释也变得非常朴素:以前没人去摸那个管理共享。
顺带一提,这类安全软件通常带有自我保护。想停它的服务,会得到一句干脆的拒绝:
Service 'XXX Daemon' cannot be stopped due to the following error:
Cannot stop XXX service on computer '.'.它的配置库一般也是加密的,改不了——规则只有它自己的界面能改。
五、第二只靴子:宿主机把自己的地址弄丢了
按理说,把那个安全软件处理掉,事情就该结束了。
结果重启完,还是不通。而且这次的症状变了——连 ARP 都不通了。
这里我要老实交代第二个错误,因为它比第一个更蠢。
在前面排查的过程中,为了验证「是不是只封了这一个源 IP」,我干了一件当时觉得很聪明的事:给源主机加一个临时的第二 IP,然后用这个 IP 去连。
问题出在加的方式上:
New-NetIPAddress -InterfaceAlias "vEthernet (New Virtual Switch)" `
-IPAddress <临时地址> -PrefixLength 24那块网卡本来是 DHCP 的,而且是 Hyper-V 外部交换机的管理 OS 适配器——就是宿主机用来和所有虚拟机通信的那块虚拟网卡。
给一个 DHCP 接口加静态地址,Windows 会顺手把这个接口切成「静态」模式。等我实验做完、把临时地址删掉,这块网卡就既没有 DHCP、也没有静态地址,只剩一个 169.254 开头的自动私有地址(APIPA):
IPAddress InterfaceIndex PrefixOrigin
169.254.x.x 17 WellKnown <- 虚拟网卡,裸的
10.0.0.10 22 Dhcp <- 地址跑到物理网口上去了而那个 DHCP 租约,被物理网口抢走了——因为这两块网卡共用一个 MAC 地址,DHCP 服务器根本分不清是谁在要地址。默认路由也跟着跑到了物理网口上。
结果就是:管理 OS 的虚拟网卡没有同网段地址,宿主机和所有虚拟机之间的通信全部失效。表现出来就是双向 ARP 都解析不出来(Incomplete / Unreachable)。
修复动作很简单:
# 1. 把地址从物理网口上摘掉
Remove-NetIPAddress -InterfaceIndex 22 -AddressFamily IPv4 -Confirm:$false
# 2. 让虚拟网卡恢复 DHCP
Set-NetIPInterface -InterfaceIndex 17 -Dhcp Enabled
# 3. 重新续租
ipconfig /release
ipconfig /renew10.0.0.10 回到了 InterfaceIndex 17,默认路由也跟着回来了。直连当场恢复。
六、可复用清单
如果你哪天遇到「ARP 通、TCP 不通、防火墙干净」这种组合,可以照这个顺序走:
第一步,做连通性矩阵。别只测故障目标,同时测几个已知正常的目标。如果只有一台不通,本机出站策略基本可以排除。
第二步,打开目标机的防火墙丢弃日志。如果日志是空的,说明防火墙没参与——这是个非常有价值的否定结论,别跳过。
第三步,双侧抓包。这个组合能一次性回答「丢在哪一层」:
# 宿主机
pktmon filter remove
pktmon filter add -i <目标IP>
pktmon start --capture --pkt-size 0 --file-name C:Temppkt.etl
# ...触发一次连接...
pktmon stop
pktmon etl2txt C:Temppkt.etl -o C:Temppkt.txt判断口诀:
- 源侧只有 SYN、目标侧啥都没有 → 丢在二层 / 交换机;
- 源侧只有 SYN、目标侧看得到 SYN → 丢在目标的协议栈里;
- 目标侧看得到 SYN、但没有 SYN-ACK → WFP 或第三方过滤驱动。
第四步,用 WFP 审核事件定位。记住要用 GUID:
auditpol /set /subcategory:"{0CCE9225-69AE-11D9-BED3-505054503030}" /failure:enable然后看安全日志的 5152 事件。Layer Name 是关键判据:
| Layer Name | 大致含义 |
|---|---|
Inbound Transport v4 | 大概率是 Windows 防火墙自己的规则 |
IP Packet | 第三方 WFP 驱动在 IP 层拦的 |
第五步,点名第三方。
Get-CimInstance Win32_SystemDriver |
Where-Object { $_.State -eq 'Running' } |
Where-Object { $_.Name -match 'wfp|fw|flt' }另外,如果那套软件把审计记录写在本地(不少安全软件会存成 WAL / SQLite),库本体可能是加密的,但 WAL 里往往是明文,用共享读就能捞出来:
$fs = [IO.File]::Open($logPath, 'Open', 'Read', 'ReadWrite')第六步,能不用网络就不用网络。这次真正救场的是 Hyper-V 的 PowerShell Direct——它走 VMBus,不经过任何网络栈,虚拟机网络彻底断掉也能管理:
Invoke-Command -VMName <虚拟机名> -Credential <凭据> { hostname }在「网络彻底不可用」的场景下,这条通道比任何 SSH / WinRM 都可靠。
七、几条教训
一、别去摸别人的管理共享。hostC$ 这种动作在行为检测模型里和横向渗透没有区别。企业级安全软件会果断把源 IP 整条拉黑,而且它的拦截发生在 WFP 的 IP 层——你在 Windows 防火墙里永远看不到它,日志也是空的。这次故障从头到尾,最贵的一课就是这一条。
二、不要在 Hyper-V 管理 OS 的 DHCP 接口上做加静态 IP 的实验。那个操作会把接口顶成静态并丢掉地址,而且因为共享 MAC,租约可能落到物理网口上去。要测「换个源地址会怎样」,正确做法是在连接对象上显式绑定源地址,而不是改接口配置:
var client = new TcpClient(new IPEndPoint(IPAddress.Parse("<临时源地址>"), 0));
client.Connect("<目标>", 22);三、本地化的子类别名不可靠,脚本里一律用 GUID。中文系统上 auditpol 的子类别名会让脚本以 0x57 失败,而那个报错完全不提示「是名字的问题」。
四、写 PowerShell 脚本别用中文。PowerShell 5.1 在没有 BOM 的情况下按 ANSI 读 .ps1,中文会直接把脚本解析成语法错误。这次的验收脚本就是这么炸的。
五、假信号一定要复测。排查过程中,端口探测工具出现过一次「该通不通的被报成通」。所有关键结论至少复测一遍,尤其是那些「太好了,看来就是这个问题」的结论。
结语
这次故障从头到尾没有一行代码的问题,也没有任何硬件故障。它是两件事叠加的结果:一个过于尽职的安全软件,和一个过于自信的我。
有意思的是,那个安全软件的判断其实是「对」的——它看到的确实是一个内网地址在连别人的管理共享,那正是横向移动的典型特征。错的是我的动作,不是它的规则。
所以与其说这是一次排障复盘,不如说是一次关于「权限边界」的提醒:在内网里做探索性操作时,你以为自己在做无害的检查,而别人(或者别人的安全软件)看到的可能是另一种东西。