两端的防火墙都是干净的:一次内网「直连不通」的完整复盘

说明:文中所有 IP、主机名与账号均已做替换,示例地址为占位。

凌晨快到十二点,有人跟我说:那台天天直连的机器,连不上了。

我第一反应是「不可能」——那台机器就在本机 Hyper-V 里,同一个二层网络,中间没有路由器,没有 NAT,连 VLAN 都没配。它昨天还是好的。

于是从最简单的开始:ping 不通。换 TCP 探端口,也全不通。2259854458888,一个都不通。同一时刻,隔壁另一台同类虚拟机一切正常

这就很有意思了。同一台交换机上的两台虚拟机,一台通一台不通,而且不是「慢」,是彻底静默

故障时的数据路径:SYN 发出去了,却没有回音 源主机(管理 OS)10.0.0.10Hyper-V 管理适配器外部虚拟交换机二层直连,无路由 / 无 NATSYN ×N(重传)ARP 有回虚拟机 A(目标机)10.0.0.20防火墙丢弃日志:空虚拟机 B(对照)10.0.0.30一切正常SYN 到达网卡正常往返丢包点:WFP 的「IP Packet」层第三方过滤驱动在这里静默丢包 ARP 不在 WFP 的管辖范围内 —— 所以链路「看起来」是通的,TCP 却全灭 这正是「防火墙规则干净、丢弃日志为空,却连不上」的原因
图 1 故障时的数据路径

一、三个反直觉的事实

按常规剧本,网络不通先看防火墙。但这次,越查越不对劲:

检查项结果说明
目标机 Windows 防火墙入站 Block 规则Get-NetFirewallRule -Enabled True -Direction Inbound -Action Block 返回空
目标机放行规则作用域remote=Any22 与 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 /renew

10.0.0.10 回到了 InterfaceIndex 17,默认路由也跟着回来了。直连当场恢复。

把「丢在哪一层」钉死:六步流程 ① 连通性矩阵ping / TCP 多目标对照;只有一台不通 ⇒ 排除本机出站策略② 打开目标机防火墙丢弃日志日志为空 ⇒ 防火墙根本没看见包(极有价值的否定结论)③ 双侧 pktmon 抓包源主机 + 目标机同时抓:包到底停在哪一段④ auditpol(必须用 GUID)+ 安全日志 5152让 WFP 自己说出:是谁、在哪一层丢的⑤ 列出 WFP 内核驱动在 wfp / fw / flt 里点名第三方过滤驱动⑥ 兜底通道PowerShell Direct:走 VMBus,不依赖网络,断网也能管两条关键判据③ 看「谁看得到 SYN」目标侧看不到 ⇒ 二层 / 交换机看得到、无 SYN-ACK ⇒ WFP / 第三方④ 看 Layer NameInbound Transport v4 ⇒ Windows 防火墙IP Packet ⇒ 第三方驱动判据对上了,故障范围就只剩一层。
图 2 定位丢包层次的六步流程

六、可复用清单

如果你哪天遇到「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,中文会直接把脚本解析成语法错误。这次的验收脚本就是这么炸的。

五、假信号一定要复测。排查过程中,端口探测工具出现过一次「该通不通的被报成通」。所有关键结论至少复测一遍,尤其是那些「太好了,看来就是这个问题」的结论。

结语

这次故障从头到尾没有一行代码的问题,也没有任何硬件故障。它是两件事叠加的结果:一个过于尽职的安全软件,和一个过于自信的我

有意思的是,那个安全软件的判断其实是「对」的——它看到的确实是一个内网地址在连别人的管理共享,那正是横向移动的典型特征。错的是我的动作,不是它的规则。

所以与其说这是一次排障复盘,不如说是一次关于「权限边界」的提醒:在内网里做探索性操作时,你以为自己在做无害的检查,而别人(或者别人的安全软件)看到的可能是另一种东西。

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇