Windows 事件 ID 4625:读懂一次失败登录

逐一解析 4625 事件的每个字段、各状态码的含义,以及如何区分打错密码的用户和正在跑字典的僵尸网络。

阅读约 8 分钟

4625 是什么

事件 ID 4625——「账户登录失败」——会在每一次认证尝试被拒绝时写入 Windows 安全日志,无论原因是什么:密码错误、账户不存在、账户已禁用、在允许时段之外登录。它成功时的对应事件是 4624。

在面向公网的 Windows 服务器上,这是最有用的一个事件,因为暴力破解攻击正是在这里、在得手之前变得可见。防火墙层面的一切防御,都从读取这条流开始。

如果在一台确认可从公网访问的服务器上完全看不到 4625 事件,那是审核被关掉了,而不是服务器很清静。请检查并开启:

cmd
auditpol /get /subcategory:"Logon"
auditpol /set /subcategory:"Logon" /success:enable /failure:enable

真正重要的字段

一条 4625 记录相当冗长,其中大部分是噪音。承载信息的是五个字段:

  • 账户名(在「登录失败的账户」下)——被尝试的用户名。在真实攻击中,这里是一长串您机器上从未存在过的名字。
  • 源网络地址——尝试来自哪个 IP。这是您据以行动的字段。本地控制台登录时它为空。
  • 登录类型——以何种方式尝试登录。10 是 RemoteInteractive(RDP),3 是网络(SMB,或经过某些网关的 RDP),2 是物理控制台,5 是服务,4 是计划任务。
  • 失败原因与状态 / 子状态——解释哪里出错的十六进制代码。有用的区分就藏在这里。
  • 工作站名——客户端自报的主机名。攻击工具常常发送乱码、随机字符串,或干脆留空,这本身就是一个信号。

状态码解读

要读的是「子状态」字段;顶层的「状态」通常是通用的 0xC000006D。记住下面这五个,几乎任何 4625 您都能一眼归类:

  • 0xC0000064——账户名不存在。几乎总是攻击:您自己的用户知道自己的用户名。
  • 0xC000006A——账户存在但密码错误。要么是真实用户输错了,要么是机器人猜中了一个有效用户名。
  • 0xC0000234——账户已被锁定。如果这类记录成串出现,说明您的锁定策略正在被反过来利用。
  • 0xC0000072——账户已禁用。这通常是一个被攻击者发现、仍在继续尝试的休眠账户。
  • 0xC000006F / 0xC0000070——在允许时段之外登录,或来自不被允许的工作站。较为少见,值得调查而不是忽略。

区分攻击与手误

有三个信号可以区分它们,在自动处置之前,三个您都该看到。

速率与持续性。人会在一分钟内尝试两三次,然后要么进去了,要么打电话给您。机器人会连续数小时保持稳定速率,通宵不歇,从不停顿,也从不给任何人打电话。

账户多样性。人只会尝试一个账户:自己的。机器人会顺着清单走——administrator、admin、user、test、sql、backup、scan——而其中大部分账户在您的机器上根本不存在,这正是上面提到的 0xC0000064 特征。

来源。您的同事从少数几个已知地址登录。攻击来自托管商网段,每几百次尝试就换一个地址,而且常常在同一小时内来自多个国家。

下面这条命令按来源地址和账户对最近一天的失败记录分组,让三个信号一次性可见:

powershell
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=(Get-Date).AddDays(-1)} |
  ForEach-Object {
    $d = ([xml]$_.ToXml()).Event.EventData.Data
    [pscustomobject]@{
      IP      = ($d | Where-Object Name -eq 'IpAddress')['#text']
      Account = ($d | Where-Object Name -eq 'TargetUserName')['#text']
      Status  = ($d | Where-Object Name -eq 'SubStatus')['#text']
    }
  } |
  Group-Object IP |
  Sort-Object Count -Descending |
  Select-Object -First 15 Count, Name,
    @{n='Accounts'; e={ ($_.Group.Account | Select-Object -Unique) -join ', ' }}

从读取事件到阻断事件

手工读 4625 是诊断,不是防御——等您跑这条查询的时候,攻击已经持续一周了。真正能改变什么的,是持续不断地执行这个循环,并把阈值转化为防火墙规则。

最朴素的做法是订阅安全日志、按来源统计失败次数,并在某个来源越线时调用 New-NetFirewallRule。它能跑起来,然后就撞上每个这类脚本都会撞上的问题:规则集涨到数千条,防火墙开始爬行;高负载下日志轮转,事件丢失;重启之后脚本悄无声息地死掉;一次误匹配把管理员挡在唯一的入口之外。

RDP Protector 把这个循环作为服务来运行:用一条合并的防火墙规则代替数千条,白名单永远优先于封禁,判断在本地做出——因此网络扛不住的时候它仍然扛得住。它读取的正是上面展示的那条事件流,没有任何东西对您隐藏。

FAQ

事件 4625 和 4624 有什么区别?
4624 记录成功登录,4625 记录失败登录。两者都在安全日志中,字段结构相同,所以上面的查询只要换个 ID 就同样适用。一串 4625 之后紧跟着同一来源地址的 4624,是您最不想看到的序列:猜解成功了。
4625 事件中的登录类型 3 是什么意思?
登录类型 3 是网络登录——通过网络而非控制台完成认证的连接。它涵盖 SMB 文件共享访问,以及取决于前置网关的部分 RDP 路径。直连主机的 RDP 通常记录为类型 10(RemoteInteractive)。大量类型 3 的失败记录通常意味着除了 RDP,SMB 也暴露在外。
为什么「源网络地址」字段是空的或显示为短横线?
这出现在没有网络来源的登录中——物理控制台、部分服务和计划任务登录——偶尔也出现在尝试失败得太早、地址来不及记录的情况下。如果相邻字段显示登录类型为 2、4 或 5,地址为空是预期行为,并不说明有什么问题。
安全日志会保留 4625 事件多久?
直到写满并轮转为止,而在真实攻击下这可能是几小时而非几周——默认的 20 MB 在每天数千条事件的情况下很快就满了。如果您要依赖日志做调查,请调大它的最大容量或把事件转发到主机之外,否则等您去看的时候证据早已过期。

不必再手工翻日志

代理会持续监视这条完全相同的事件流,并把它转化为防火墙规则。一台服务器永久免费,无需信用卡。