1. 1. 一、RDP 攻击面概述
    1. 1.1. 1.1 RDP 基本架构
    2. 1.2. 1.2 NLA(Network Level Authentication)对攻击的影响
    3. 1.3. 1.3 常见 RDP 攻击场景
  2. 2. 二、RDP 相关事件日志详解
    1. 2.1. 2.1 Security Log — 登录事件
      1. 2.1.1. Event ID 4624(成功登录)— Logon Type 10
      2. 2.1.2. Event ID 4625(登录失败)— 暴力破解检测核心
      3. 2.1.3. Event ID 4648(显式凭据登录)
    2. 2.2. 2.2 TerminalServices 日志(RDP 专用)
      1. 2.2.1. Event ID 1149 — RDP 连接认证成功
      2. 2.2.2. Event ID 21 — RDP 登录成功(会话创建)
      3. 2.2.3. Event ID 22 — RDP Shell 启动(桌面就绪)
      4. 2.2.4. Event ID 23 — RDP 用户注销
      5. 2.2.5. Event ID 24 — RDP 会话断开
      6. 2.2.6. Event ID 25 — RDP 重连成功
      7. 2.2.7. TerminalServices 事件时间线汇总
    3. 2.3. 2.3 安全日志中其他 RDP 相关事件
  3. 3. 三、RDP Bitmap Cache 取证
    1. 3.1. 3.1 什么是 RDP Bitmap Cache
    2. 3.2. 3.2 提取和分析 Bitmap Cache
    3. 3.3. 3.3 RDP 连接历史(注册表)
  4. 4. 四、RDP 暴力破解完整案例分析
    1. 4.1. 4.1 场景还原
    2. 4.2. 4.2 第一步:检查登录失败事件
    3. 4.3. 4.3 第二步:确认是否有成功登录
    4. 4.4. 4.4 第三步:追踪攻击者登录后的行为
    5. 4.5. 4.5 第四步:检查 TerminalServices 日志交叉验证
    6. 4.6. 4.6 第五步:后渗透行为检测
  5. 5. 五、RDP 暴力破解防护
    1. 5.1. 5.1 Windows 原生防护
    2. 5.2. 5.2 第三方 Fail2Ban 等效方案
    3. 5.3. 5.3 纵深防御建议
  6. 6. 六、RDP 会话劫持检测
    1. 6.1. 6.1 tscon 会话劫持原理
    2. 6.2. 6.2 检测方法
  7. 7. 七、自动化排查脚本
    1. 7.1. 7.1 一键 RDP 安全审计脚本
  8. 8. 八、与 Linux SSH 暴力破解对比
  9. 9. 九、关键检查清单(Checklist)

Windows应急响应 - 11 RDP暴力破解与未授权访问

RDP暴力破解与未授权访问

前置说明

RDP(Remote Desktop Protocol,远程桌面协议)是 Windows 环境中最常被攻击的远程访问服务

相当于 Linux 世界的 SSH,是攻击者获取初始访问的首选目标

本页覆盖:暴力破解检测、未授权访问取证、RDP 位图缓存取证、防护加固

关联页面:03-事件日志分析 | 04-取证制品分析 | 10-SSH暴力破解与未授权访问


一、RDP 攻击面概述

1.1 RDP 基本架构

默认端口:TCP 3389(很多管理员会改端口,但 Shodan/Censys 可轻松扫出)

协议层次:

RDP → TPKT → X.224 → MCS → 加密层(TLS/CredSSP/RDP Security)

Windows 默认组件:

TermService(Terminal Services)服务

svchost.exe -k NetworkServicesvchost.exe -k termsvcs

确认 RDP 服务状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 检查 RDP 服务状态
Get-Service -Name TermService | Select-Object Status, StartType

# 检查 RDP 是否启用
Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections
# 0 = 允许连接,1 = 拒绝连接

# 检查 RDP 端口
Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name PortNumber

# 检查 NLA 是否启用
Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name UserAuthentication
# 1 = NLA 启用,0 = 禁用

1.2 NLA(Network Level Authentication)对攻击的影响

NLA 启用时(推荐配置):

认证在 RDP 会话建立之前完成(CredSSP 协议)

暴力破解产生 Event ID 4625 失败日志,但 不会 产生 TerminalServices 1149 事件

攻击者无法看到登录界面就被拒绝

资源消耗小,服务器不会因暴力破解导致 GUI 资源耗尽

NLA 禁用时(危险配置):

攻击者先建立 RDP 会话,然后在 Windows 登录界面输入凭据

每次尝试都建立完整 RDP 会话,产生 TerminalServices 1149 事件

服务器资源消耗大,可能导致拒绝服务

攻击者可以看到操作系统版本等信息

关键区别对照:

1
2
NLA 启用:  Client → CredSSP Auth → [成功才建立会话]
NLA 禁用: Client → RDP Session → [登录界面认证]

检测 NLA 状态:

1
2
3
4
5
6
7
8
# 方法1:注册表
(Get-ItemProperty 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp').UserAuthentication

# 方法2:WMI
(Get-WmiObject -Class Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices).UserAuthenticationRequired

# 方法3:GPO 设置
# 计算机配置 → 管理模板 → Windows组件 → 远程桌面服务 → 远程桌面会话主机 → 安全

1.3 常见 RDP 攻击场景

场景一:互联网暴力破解

RDP 直接暴露在公网(最常见也最危险)

攻击者使用 Hydra/Crowbar/NLBrute 等工具

通常在数小时到数天内完成,取决于密码复杂度

场景二:内网横向移动

攻击者已获取某台主机权限,使用窃取的凭据 RDP 到其他主机

使用 Pass-the-Hash 或 Restricted Admin 模式

详见 15-横向移动检测

场景三:RDP 劫持(Session Hijack)

利用 tscon.exe 劫持断开的 RDP 会话

需要 SYSTEM 权限:tscon <SessionID> /dest:console

无需知道目标用户密码

场景四:BlueKeep/CVE-2019-0708 类漏洞利用

预认证远程代码执行

不需要任何凭据

影响 Windows 7/2008 R2 及更早版本


二、RDP 相关事件日志详解

2.1 Security Log — 登录事件

Event ID 4624(成功登录)— Logon Type 10

RDP 成功登录的核心标识是 Logon Type 10(RemoteInteractive)

关键字段:

1
2
3
4
5
6
7
8
9
EventID:      4624
LogonType: 10 (RemoteInteractive)
TargetUserName: 登录的用户名
TargetDomainName: 域名或计算机名
IpAddress: IP 地址(关键!)
IpPort: 源端口
LogonProcessName: User32
AuthenticationPackageName: Negotiate
WorkstationName: 源主机名(可能为空)

查询命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 查询所有 RDP 成功登录
Get-WinEvent -FilterHashtable @{
LogName='Security'
Id=4624
} | Where-Object {
$_.Properties[8].Value -eq 10 # LogonType = 10
} | ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
User = $_.Properties[5].Value # TargetUserName
Domain = $_.Properties[6].Value # TargetDomainName
SourceIP = $_.Properties[18].Value # IpAddress
SourcePort = $_.Properties[19].Value # IpPort
LogonID = $_.Properties[7].Value # TargetLogonId
}
} | Format-Table -AutoSize

注意: RDP 重连(断开后重新连接)可能产生 Logon Type 7(Unlock)而非 Type 10

Event ID 4625(登录失败)— 暴力破解检测核心

RDP 暴力破解最直接的证据

关键字段:

1
2
3
4
5
6
7
EventID:      4625
LogonType: 10 (RemoteInteractive) 3 (Network,NLA场景)
TargetUserName: 尝试的用户名
Status: 失败状态码
SubStatus: 失败子状态码
IpAddress: 攻击者 IP
FailureReason: 失败原因描述

Status/SubStatus 常见组合:

1
2
3
4
5
6
7
8
9
10
0xC000006D / 0xC000006A  →  密码错误(最常见的暴力破解特征)
0xC000006D / 0xC0000064 → 用户名不存在
0xC000006E / 0xC000006A → 账户限制 + 密码错误
0xC0000234 / 0x00000000 → 账户被锁定
0xC0000072 / 0x00000000 → 账户被禁用
0xC000006F / 0x00000000 → 登录时间限制外
0xC0000070 / 0x00000000 → 工作站限制
0xC0000193 / 0x00000000 → 账户过期
0xC0000071 / 0x00000000 → 密码过期
0xC000015B / 0x00000000 → 未授予登录类型权限

暴力破解检测查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 统计每个源 IP 的登录失败次数(暴力破解检测)
Get-WinEvent -FilterHashtable @{
LogName='Security'
Id=4625
} -MaxEvents 50000 | ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
User = $_.Properties[5].Value
SourceIP = $_.Properties[19].Value
Status = '0x{0:X8}' -f [int]$_.Properties[7].Value
SubStatus= '0x{0:X8}' -f [int]$_.Properties[9].Value
}
} | Group-Object SourceIP | Sort-Object Count -Descending |
Select-Object Count, Name, @{N='Users';E={($_.Group.User | Sort-Object -Unique) -join ','}} |
Format-Table -AutoSize

暴力破解判断标准(经验值):

同一源 IP 在 1 小时内 > 50 次 4625 → 高度疑似暴力破解

同一源 IP 尝试多个不同用户名 → 字典攻击/密码喷洒

同一用户名来自多个 IP → 分布式暴力破解

Event ID 4648(显式凭据登录)

当使用 mstsc /v:target 并手动输入不同于当前会话的凭据时产生

说明有人在使用 非当前登录用户 的凭据进行 RDP 连接

可用于检测凭据盗用后的横向移动

1
2
3
4
5
6
7
8
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4648} |
Where-Object { $_.Properties[8].Value -like '*termsrv*' -or $_.Properties[11].Value -ne '-' } |
Select-Object TimeCreated,
@{N='SubjectUser';E={$_.Properties[1].Value}},
@{N='TargetUser';E={$_.Properties[5].Value}},
@{N='TargetServer';E={$_.Properties[8].Value}},
@{N='TargetIP';E={$_.Properties[12].Value}} |
Format-Table -AutoSize

2.2 TerminalServices 日志(RDP 专用)

Event ID 1149 — RDP 连接认证成功

日志通道:Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational

仅在 NLA 禁用时记录每次连接尝试,NLA 启用时仅记录成功连接

关键字段:

1
2
User:          域\用户名
Source Network Address: 源 IP

查询命令:

1
2
3
4
5
6
7
8
9
10
11
Get-WinEvent -LogName 'Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational' |
Where-Object { $_.Id -eq 1149 } |
ForEach-Object {
$xml = [xml]$_.ToXml()
[PSCustomObject]@{
Time = $_.TimeCreated
User = $xml.Event.UserData.EventXML.Param1
Domain = $xml.Event.UserData.EventXML.Param2
SourceIP = $xml.Event.UserData.EventXML.Param3
}
} | Format-Table -AutoSize

Event ID 21 — RDP 登录成功(会话创建)

日志通道:Microsoft-Windows-TerminalServices-LocalSessionManager/Operational

表示 RDP 会话已成功创建

关键字段:User、Session ID、Source Network Address

1
2
3
4
5
6
7
8
9
10
Get-WinEvent -LogName 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational' |
Where-Object { $_.Id -eq 21 } |
ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
User = $_.Properties[0].Value
SessionID = $_.Properties[1].Value
SourceIP = $_.Properties[2].Value
}
} | Format-Table -AutoSize

Event ID 22 — RDP Shell 启动(桌面就绪)

表示 RDP 用户的桌面(explorer.exe shell)已经启动

时间比 Event 21 稍晚,确认用户已到达桌面

Event ID 23 — RDP 用户注销

用户正常注销(而非断开连接)

Event ID 24 — RDP 会话断开

用户断开 RDP 连接(关闭窗口/网络中断),但会话可能仍然活跃

注意: 断开 ≠ 注销,断开的会话可以被重连或劫持

Event ID 25 — RDP 重连成功

用户重新连接到之前断开的会话

如果非原始用户重连,可能是会话劫持

TerminalServices 事件时间线汇总

1
2
3
4
5
6
7
8
9
10
11
正常 RDP 会话生命周期:
1149 (RemoteConnectionManager) → 认证成功
21 (LocalSessionManager) → 会话创建
22 (LocalSessionManager) → Shell 启动
...用户操作...
24 (LocalSessionManager) → 会话断开

23 (LocalSessionManager) → 用户注销

会话重连:
25 (LocalSessionManager) → 重新连接

完整时间线查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 综合查询 RDP 相关所有事件
$rdpEvents = @()

# RemoteConnectionManager (1149)
$rdpEvents += Get-WinEvent -LogName 'Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational' -MaxEvents 500 |
Where-Object { $_.Id -eq 1149 } |
Select-Object TimeCreated, Id, @{N='Detail';E={"[认证成功] User=$($_.Properties[0].Value) IP=$($_.Properties[2].Value)"}}

# LocalSessionManager (21,22,23,24,25)
$rdpEvents += Get-WinEvent -LogName 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational' -MaxEvents 500 |
Where-Object { $_.Id -in 21,22,23,24,25 } |
Select-Object TimeCreated, Id, @{N='Detail';E={
$action = switch($_.Id) { 21 {"会话创建"} 22 {"Shell启动"} 23 {"注销"} 24 {"断开"} 25 {"重连"} }
"[$action] User=$($_.Properties[0].Value) Session=$($_.Properties[1].Value) IP=$($_.Properties[2].Value)"
}}

$rdpEvents | Sort-Object TimeCreated -Descending | Select-Object -First 100 | Format-Table -AutoSize

2.3 安全日志中其他 RDP 相关事件

Event ID 描述 IR 用途
4624 Type 10 RDP 成功登录 确认 RDP 登入
4625 Type 10/3 RDP 登录失败 暴力破解检测
4634/4647 注销 会话结束时间
4648 显式凭据登录 凭据盗用检测
4778 会话重连 包含源IP和主机名
4779 会话断开 包含源IP和主机名

4778/4779 特别重要 — 包含客户端名称(ClientName)和客户端地址(ClientAddress),即使 4624 中 IP 字段为空也可能在这里找到源信息

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查询 RDP 会话重连/断开(4778/4779)
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4778,4779} -MaxEvents 200 |
ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
EventID = $_.Id
Action = if($_.Id -eq 4778){'重连'}else{'断开'}
User = $_.Properties[0].Value
SessionName= $_.Properties[1].Value
ClientName = $_.Properties[4].Value
ClientIP = $_.Properties[5].Value
}
} | Format-Table -AutoSize

三、RDP Bitmap Cache 取证

3.1 什么是 RDP Bitmap Cache

RDP 协议会在客户端缓存远程桌面的图像碎片(bitmap tiles),用于减少网络传输

关键: 缓存存储在 客户端 而非服务器端

这意味着如果攻击者从某台机器 RDP 到目标,攻击者的机器上会留下目标桌面的图像碎片

缓存位置:

1
2
3
4
5
6
7
%USERPROFILE%\AppData\Local\Microsoft\Terminal Server Client\Cache\
├── bcache22.bmc # RDP 8.0 之前
├── bcache24.bmc
└── Cache0000.bin # RDP 8.0+ (Windows 8/2012 起)
Cache0001.bin
Cache0002.bin
...

3.2 提取和分析 Bitmap Cache

工具一:bmc-tools(ANSSI 开发,推荐)

1
2
3
4
5
:: 提取 bitmap cache 为独立图片
python bmc-tools.py -s "C:\Users\attacker\AppData\Local\Microsoft\Terminal Server Client\Cache" -d C:\output\bitmap_cache

:: 生成拼接后的大图(collage 模式)
python bmc-tools.py -s "C:\Users\attacker\AppData\Local\Microsoft\Terminal Server Client\Cache" -d C:\output -b

工具二:RdpCacheStitcher(GUI 拼接工具)

将 bmc-tools 提取的碎片导入

手动/半自动拼接还原完整屏幕截图

可以还原攻击者在远程桌面上看到的内容

取证价值:

可以看到攻击者通过 RDP 访问了什么内容

可能包含:打开的文件、命令行窗口、浏览器页面、敏感数据

即使攻击者清理了日志,bitmap cache 往往被遗忘

3.3 RDP 连接历史(注册表)

客户端连接历史(攻击者机器上):

1
2
3
4
5
6
7
8
9
10
11
12
# 查看 RDP 连接历史(每个用户)
Get-ChildItem 'HKCU:\Software\Microsoft\Terminal Server Client\Servers' |
ForEach-Object {
[PSCustomObject]@{
Server = $_.PSChildName
Username = (Get-ItemProperty $_.PSPath).UsernameHint
}
} | Format-Table -AutoSize

# MRU(最近连接列表)
Get-ItemProperty 'HKCU:\Software\Microsoft\Terminal Server Client\Default' |
Select-Object MRU*

服务器端(被连接的机器上):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# NTUSER.DAT 中的 RDP 连接历史(离线分析用)
# 路径: NTUSER\Software\Microsoft\Terminal Server Client\Servers

# 检查所有用户的 RDP 连接历史
Get-WmiObject Win32_UserProfile | ForEach-Object {
$sid = $_.SID
$path = $_.LocalPath
try {
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteSubKey('Users')
$userKey = $reg.OpenSubKey("$sid\Software\Microsoft\Terminal Server Client\Servers")
if ($userKey) {
$userKey.GetSubKeyNames() | ForEach-Object {
Write-Output "User: $path → Connected to: $_"
}
}
} catch {}
}

四、RDP 暴力破解完整案例分析

4.1 场景还原

背景: 某企业 Windows Server 2019 服务器,RDP 端口 3389 直接暴露在公网

发现: 管理员发现服务器运行异常缓慢,CPU 持续 100%

初步判断: 疑似被入侵

4.2 第一步:检查登录失败事件

1
2
3
4
5
6
7
8
9
10
11
12
13
# 先看最近 24 小时的登录失败统计
$startTime = (Get-Date).AddHours(-24)
Get-WinEvent -FilterHashtable @{
LogName='Security'
Id=4625
StartTime=$startTime
} | Group-Object @{E={$_.Properties[19].Value}} |
Sort-Object Count -Descending |
Select-Object -First 20 Count, @{N='SourceIP';E={$_.Name}},
@{N='FirstAttempt';E={($_.Group | Sort-Object TimeCreated | Select-Object -First 1).TimeCreated}},
@{N='LastAttempt';E={($_.Group | Sort-Object TimeCreated -Descending | Select-Object -First 1).TimeCreated}},
@{N='Users';E={($_.Group | ForEach-Object {$_.Properties[5].Value} | Sort-Object -Unique | Select-Object -First 5) -join ','}} |
Format-Table -AutoSize

预期输出示例:

1
2
3
4
5
Count SourceIP         FirstAttempt          LastAttempt           Users
----- -------- ------------ ----------- -----
15847 185.xxx.xxx.12 2026-03-31 02:15:33 2026-04-01 18:22:41 Administrator,admin,test,user
8234 91.xxx.xxx.45 2026-03-31 08:44:12 2026-04-01 16:33:22 administrator,admin,guest
3421 103.xxx.xxx.78 2026-04-01 00:12:55 2026-04-01 14:55:33 Administrator,sa,root

分析: 明显的暴力破解特征 — 大量失败、多用户名尝试、持续时间长

4.3 第二步:确认是否有成功登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 检查暴力破解 IP 是否有成功登录
$bruteForceIPs = @('185.xxx.xxx.12', '91.xxx.xxx.45', '103.xxx.xxx.78')

Get-WinEvent -FilterHashtable @{
LogName='Security'
Id=4624
} -MaxEvents 50000 | Where-Object {
$_.Properties[8].Value -eq 10 -and # LogonType 10
$_.Properties[18].Value -in $bruteForceIPs
} | ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
User = $_.Properties[5].Value
SourceIP = $_.Properties[18].Value
LogonID = $_.Properties[7].Value
}
} | Format-Table -AutoSize

如果发现成功登录,说明攻击者已经突破!必须立即进入后续排查

4.4 第三步:追踪攻击者登录后的行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 使用 Logon ID 追踪攻击者会话中的所有操作
$attackerLogonId = '0x1234ABCD' # 从上一步获取

# 查询该会话中的进程创建(需要 4688 审计已启用)
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4688} -MaxEvents 100000 |
Where-Object { $_.Properties[1].Value -eq $attackerLogonId } |
ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
Process = $_.Properties[5].Value
CmdLine = $_.Properties[8].Value
Parent = $_.Properties[13].Value
}
} | Format-Table -AutoSize

# 查询该会话中的特权使用(4672 — Special Privileges)
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4672} -MaxEvents 10000 |
Where-Object { $_.Properties[1].Value -eq $attackerLogonId } |
Select-Object TimeCreated, @{N='User';E={$_.Properties[0].Value}} |
Format-Table -AutoSize

4.5 第四步:检查 TerminalServices 日志交叉验证

1
2
3
4
5
6
7
8
9
10
11
12
13
# 交叉验证 — 检查 TerminalServices 日志
Get-WinEvent -LogName 'Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational' |
Where-Object { $_.Id -eq 1149 } |
ForEach-Object {
$xml = [xml]$_.ToXml()
[PSCustomObject]@{
Time = $_.TimeCreated
User = $xml.Event.UserData.EventXML.Param1
Domain = $xml.Event.UserData.EventXML.Param2
SourceIP = $xml.Event.UserData.EventXML.Param3
}
} | Where-Object { $_.SourceIP -in $bruteForceIPs } |
Format-Table -AutoSize

4.6 第五步:后渗透行为检测

攻击者 RDP 登录成功后常见操作:

1
2
3
4
5
6
1. whoami / net user / net localgroup administrators  → 信息收集
2. net user hacker P@ssw0rd /add → 创建后门账户
3. net localgroup administrators hacker /add → 提权
4. reg add ... fDenyTSConnections /d 0 → 确保 RDP 持续可用
5. netsh advfirewall set allprofiles state off → 关闭防火墙
6. 下载并执行挖矿/勒索软件 → 最终目的

检查后门账户创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Event ID 4720 — 用户账户创建
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4720} |
ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
Creator = $_.Properties[4].Value
NewAccount = $_.Properties[0].Value
}
} | Format-Table -AutoSize

# Event ID 4732 — 成员被添加到本地组(特别关注 Administrators 组)
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4732} |
ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
AddedUser = $_.Properties[0].Value
GroupName = $_.Properties[2].Value
AddedBy = $_.Properties[6].Value
}
} | Where-Object { $_.GroupName -like '*admin*' } |
Format-Table -AutoSize

五、RDP 暴力破解防护

5.1 Windows 原生防护

账户锁定策略(Group Policy)

1
2
3
4
5
计算机配置 → Windows 设置 → 安全设置 → 账户策略 → 账户锁定策略

账户锁定阈值: 5 次无效登录
账户锁定时间: 30 分钟
重置账户锁定计数器: 30 分钟后

PowerShell 配置:

1
2
3
4
5
# 查看当前账户锁定策略
net accounts

# 设置账户锁定策略
net accounts /lockoutthreshold:5 /lockoutduration:30 /lockoutwindow:30

Windows 防火墙规则限制 RDP 源 IP

1
2
3
4
5
# 仅允许特定 IP/网段访问 RDP
New-NetFirewallRule -DisplayName "RDP-Allow-Trusted" -Direction Inbound -Protocol TCP -LocalPort 3389 -RemoteAddress 10.0.0.0/8,192.168.1.0/24 -Action Allow

# 阻止所有其他 RDP 访问
New-NetFirewallRule -DisplayName "RDP-Block-All" -Direction Inbound -Protocol TCP -LocalPort 3389 -Action Block

5.2 第三方 Fail2Ban 等效方案

与 Linux 上的 fail2ban 类似,Windows 上有以下方案:

wail2ban(开源,PowerShell 实现)

原理:监控 Security Log 中的 4625 事件,自动创建防火墙规则封禁 IP

GitHub: https://github.com/glasnt/wail2ban

配置要点:

1
2
3
4
# wail2ban 核心配置
$CHECK_WINDOW = 120 # 检查窗口(秒)
$CHECK_COUNT = 5 # 窗口内允许的最大失败次数
$MAX_BANDURATION = 172800 # 最长封禁时间(秒)

RdpGuard(商业软件)

功能类似 fail2ban,但有 GUI 管理界面

支持 RDP、FTP、SMTP 等多种协议

支持白名单和自动解封

IPBan(开源替代)

https://github.com/DigitalRuby/IPBan

跨平台(Windows/Linux),功能最完善的开源方案

安装:

1
2
# 下载并以服务方式安装
# 支持配置文件自定义阈值、封禁时间、白名单

对比 Linux fail2ban:

特性 Linux fail2ban wail2ban IPBan RdpGuard
开源
日志监控 文本文件 Event Log Event Log Event Log
封禁方式 iptables Windows Firewall Windows Firewall Windows Firewall
配置难度 最低
企业支持 社区 社区 社区 商业

5.3 纵深防御建议

第一层:网络层

不要将 RDP 直接暴露在公网(最重要的一条)

使用 VPN 或 RD Gateway 作为入口

配置网络级 ACL 限制源 IP

第二层:认证层

启用 NLA(Network Level Authentication)

强制复杂密码策略

启用账户锁定策略

使用多因素认证(MFA)— Azure MFA / DUO 等

第三层:审计层

确保 Security Log 大小足够(建议 ≥ 1GB)

启用审计策略:登录/注销、进程创建、对象访问

将日志转发到 SIEM

第四层:监控层

配置 RDP 登录失败告警

监控非工作时间的 RDP 登录

监控来自异常地理位置的 RDP 连接

第五层:加固层

1
2
3
4
5
6
7
8
9
10
# 限制可 RDP 登录的用户(移除 Everyone/Users)
# 计算机配置 → Windows 设置 → 安全设置 → 本地策略 → 用户权限分配
# → 允许通过远程桌面服务登录:仅添加需要的用户/组

# 修改默认端口(简单但非安全措施,仅增加扫描难度)
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name PortNumber -Value 33890
# 需要重启 TermService 服务并更新防火墙规则

# 启用 Restricted Admin Mode(防止凭据在目标服务器上缓存)
reg add "HKLM\System\CurrentControlSet\Control\Lsa" /v DisableRestrictedAdmin /t REG_DWORD /d 0 /f

六、RDP 会话劫持检测

6.1 tscon 会话劫持原理

攻击者拥有 SYSTEM 权限后,可以劫持任意已断开的 RDP 会话

不需要知道目标用户的密码

1
2
3
4
5
6
7
8
:: 攻击方式
:: 1. 列出所有会话
query user

:: 2. 以 SYSTEM 权限执行(例如通过 PsExec 或服务)
tscon <TargetSessionID> /dest:console
:: 或
tscon <TargetSessionID> /dest:rdp-tcp#<AttackerSessionNumber>

6.2 检测方法

检测 tscon.exe 的执行:

1
2
3
4
5
6
7
8
9
10
11
# Sysmon Event ID 1 或 Security Event ID 4688
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4688} -MaxEvents 100000 |
Where-Object { $_.Properties[5].Value -like '*tscon*' } |
ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
Process = $_.Properties[5].Value
CmdLine = $_.Properties[8].Value
User = $_.Properties[1].Value
}
} | Format-Table -AutoSize

检测创建劫持服务(常见攻击方式):

1
2
3
4
5
6
7
8
# 攻击者常创建一个服务来以 SYSTEM 权限执行 tscon
# 例如: sc create sesshijack binpath= "cmd.exe /k tscon 2 /dest:rdp-tcp#0"
# 检测 Event ID 7045(新服务安装)
Get-WinEvent -FilterHashtable @{LogName='System';Id=7045} |
Where-Object { $_.Properties[1].Value -like '*tscon*' } |
Select-Object TimeCreated, @{N='ServiceName';E={$_.Properties[0].Value}},
@{N='ImagePath';E={$_.Properties[1].Value}} |
Format-Table -AutoSize

检测异常会话切换:

正常情况下 tscon.exe 很少被使用

任何 tscon.exe 的执行都应该触发告警


七、自动化排查脚本

7.1 一键 RDP 安全审计脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# RDP 安全审计一键脚本
Write-Host "===== RDP 安全审计 =====" -ForegroundColor Cyan

# 1. RDP 配置检查
Write-Host "`n[1] RDP 配置状态" -ForegroundColor Yellow
$rdpEnabled = (Get-ItemProperty 'HKLM:\System\CurrentControlSet\Control\Terminal Server').fDenyTSConnections
$nlaEnabled = (Get-ItemProperty 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp').UserAuthentication
$rdpPort = (Get-ItemProperty 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp').PortNumber
Write-Host " RDP 启用: $(if($rdpEnabled -eq 0){'是 [!]'}else{'否'})"
Write-Host " NLA 启用: $(if($nlaEnabled -eq 1){'是 [OK]'}else{'否 [!]'})"
Write-Host " RDP 端口: $rdpPort"

# 2. 当前 RDP 会话
Write-Host "`n[2] 当前 RDP 会话" -ForegroundColor Yellow
query user 2>$null

# 3. 最近 24h 登录失败 Top 10 IP
Write-Host "`n[3] 最近24h RDP登录失败 Top 10" -ForegroundColor Yellow
try {
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4625;StartTime=(Get-Date).AddHours(-24)} -ErrorAction SilentlyContinue |
ForEach-Object { $_.Properties[19].Value } |
Where-Object { $_ -and $_ -ne '-' } |
Group-Object | Sort-Object Count -Descending | Select-Object -First 10 Count, Name |
Format-Table -AutoSize
} catch { Write-Host " 无登录失败记录或日志不可用" }

# 4. 最近成功的 RDP 登录
Write-Host "`n[4] 最近 RDP 成功登录 Top 20" -ForegroundColor Yellow
try {
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4624} -MaxEvents 50000 -ErrorAction SilentlyContinue |
Where-Object { $_.Properties[8].Value -eq 10 } |
Select-Object -First 20 TimeCreated,
@{N='User';E={$_.Properties[5].Value}},
@{N='SourceIP';E={$_.Properties[18].Value}} |
Format-Table -AutoSize
} catch { Write-Host " 无法读取安全日志" }

# 5. 账户锁定策略
Write-Host "`n[5] 账户锁定策略" -ForegroundColor Yellow
net accounts 2>$null | Select-String "Lockout"

Write-Host "`n===== 审计完成 =====" -ForegroundColor Cyan

八、与 Linux SSH 暴力破解对比

维度 Windows RDP Linux SSH
默认端口 3389 22
日志位置 Security.evtx + TerminalServices /var/log/auth.log
失败事件 Event ID 4625 “Failed password”
成功事件 Event ID 4624 Type 10 “Accepted password/publickey”
防暴力破解 wail2ban/IPBan/RdpGuard fail2ban
认证方式 NTLM/Kerberos/CredSSP 密码/密钥/证书
会话缓存 Bitmap Cache(取证金矿) ~/.bash_history
会话劫持 tscon.exe screen/tmux attach
NLA 等效 NLA 无直接等效

详见 10-SSH暴力破解与未授权访问 了解 Linux 侧的对应内容


九、关键检查清单(Checklist)

事件收集:

[ ] Security Log — 4624 Type 10, 4625, 4648, 4778, 4779

[ ] TerminalServices-RemoteConnectionManager — 1149

[ ] TerminalServices-LocalSessionManager — 21, 22, 23, 24, 25

[ ] System Log — 7045(服务安装,检测 tscon 劫持)

暴力破解确认:

[ ] 统计 4625 按源 IP 分组的数量和时间分布

[ ] 检查是否有成功登录(4624 Type 10)来自暴力破解 IP

[ ] 确认攻击时间窗口

后渗透排查:

[ ] 新建用户账户(4720)

[ ] 组成员变更(4732)

[ ] 进程执行记录(4688)

[ ] RDP Bitmap Cache 分析

[ ] 注册表 RDP 连接历史

防护确认:

[ ] NLA 是否启用

[ ] 账户锁定策略是否配置

[ ] 防火墙规则是否限制 RDP 源 IP

[ ] RDP 是否暴露在公网


上一章 目录 下一章
10-PowerShell日志与脚本分析 Windows应急响应 12-IIS-Web应用入侵