Windows应急响应 - 05 账户安全排查

05-账户安全排查 (Account Security Investigation)

账户是攻击者进入系统后的第一个落脚点。无论是新建隐藏账户、提权到管理员、还是通过 RDP 横向移动,都会在账户体系中留下痕迹。作为 Linux IR 背景的工程师,你会发现 Windows 的账户体系远比 /etc/passwd + /etc/shadow 复杂——SAM 数据库、SID 体系、域账户、登录类型(Logon Type)都是 Windows 特有的概念。

前置知识01-系统基础与注册表 | 03-事件日志分析

关联章节06-进程与网络分析 | 09-注册表持久化审计

Linux 对照04-账户安全排查


1. Windows 账户体系基础

1.1 与 Linux 账户体系的对比

特征 Linux Windows
账户数据库 /etc/passwd + /etc/shadow SAM (Security Account Manager)
账户标识 UID/GID (数字) SID (S-1-5-21-…)
管理员 root (UID 0) Administrator (SID 以 -500 结尾)
用户组 /etc/group Local Groups / Domain Groups
域账户 LDAP (可选) Active Directory (NTDS.dit)
密码哈希 /etc/shadow (SHA-512) SAM/NTDS.dit (NTLM Hash)
远程登录 SSH RDP / WinRM / PsExec / SMB

1.2 SAM 数据库

SAM 文件位于 C:\Windows\System32\config\SAM

系统运行时被锁定,无法直接复制

存储本地用户账户的密码哈希(NTLM)

离线提取方法:

1
2
3
4
5
6
7
8
9
10
# 方法 1:通过注册表导出 SAM(需管理员权限)
reg save HKLM\SAM C:\IR\SAM_backup
reg save HKLM\SYSTEM C:\IR\SYSTEM_backup

# 方法 2:使用 Volume Shadow Copy
vssadmin list shadows
# 然后从 shadow copy 中复制 SAM 文件

# 方法 3:使用 secretsdump(离线分析)
# impacket-secretsdump -sam SAM_backup -system SYSTEM_backup LOCAL

1.3 SID (Security Identifier) 体系

Windows 使用 SID 而非数字 UID 来标识账户

SID 格式S-1-5-21-<域标识>-<RID>

常见 Well-Known SID

SID 对应账户/组
S-1-5-18 LOCAL SYSTEM
S-1-5-19 LOCAL SERVICE
S-1-5-20 NETWORK SERVICE
S-1-5-21-…-500 Administrator
S-1-5-21-…-501 Guest
S-1-5-21-…-502 krbtgt (域)
S-1-5-21-…-512 Domain Admins
S-1-5-21-…-544 Administrators (内置组)

查看 SID 的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看当前用户 SID
whoami /user

# 查看所有本地用户 SID
wmic useraccount get name,sid

# PowerShell 方式
Get-LocalUser | Select-Object Name, SID, Enabled, LastLogon

# 根据 SID 反查用户名
$objSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-21-xxx-500")
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$objUser.Value

2. 本地用户枚举

2.1 基础枚举命令

CMD 方式

1
2
3
4
5
6
7
8
9
10
11
:: 列出所有本地用户
net user

:: 查看单个用户详细信息
net user Administrator

:: 关注字段:
:: Account active - 账户是否启用
:: Password last set - 密码上次修改时间
:: Last logon - 上次登录时间
:: Group memberships - 所属组

PowerShell 方式(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 列出所有本地用户及关键属性
Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordLastSet,
PasswordRequired, UserMayChangePassword, Description | Format-Table -AutoSize

# 查找已启用的账户
Get-LocalUser | Where-Object { $_.Enabled -eq $true }

# 查找最近创建的账户(可能是攻击者创建的)
Get-LocalUser | Where-Object { $_.PasswordLastSet -gt (Get-Date).AddDays(-7) } |
Select-Object Name, Enabled, PasswordLastSet, Description

# 查找从未登录过的账户(可能是预置后门)
Get-LocalUser | Where-Object { $_.LastLogon -eq $null -and $_.Enabled -eq $true }

WMI 方式

1
2
3
4
5
6
7
# WMI 查询(兼容老系统)
Get-WmiObject -Class Win32_UserAccount -Filter "LocalAccount=True" |
Select-Object Name, SID, Disabled, Lockout, PasswordRequired, PasswordChangeable |
Format-Table -AutoSize

# CIM 方式(推荐,PowerShell 3.0+)
Get-CimInstance -ClassName Win32_UserAccount -Filter "LocalAccount=True"

2.2 隐藏账户检测

隐藏账户的几种方式

方式 1:账户名以 $ 结尾

net user 不会显示以 $ 结尾的账户名

这是最常见的隐藏账户手法

方式 2:修改注册表隐藏

修改 HKLM\SAM\SAM\Domains\Account\Users 下的注册表项

使账户在管理工具和 net user 中不可见

方式 3:克隆管理员账户

将普通用户的 SID 修改为与 Administrator 相同的 RID(500)

登录时获得管理员权限

检测隐藏账户的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 方法 1:PowerShell 可以看到 $ 结尾的账户(net user 看不到)
Get-LocalUser | Select-Object Name, Enabled, SID

# 方法 2:直接查询注册表 SAM(需要 SYSTEM 权限)
# 先提权到 SYSTEM,例如通过 PsExec
PsExec.exe -s -i powershell.exe
# 然后查看注册表
Get-ChildItem "HKLM:\SAM\SAM\Domains\Account\Users\Names" |
Select-Object PSChildName

# 方法 3:对比 net user 和注册表的账户列表
# net user 看到的
$netUsers = (net user | Select-Object -Skip 4 | Select-Object -SkipLast 2) -split '\s+' |
Where-Object { $_ -ne '' }
# 注册表看到的(需 SYSTEM)
$regUsers = (Get-ChildItem "HKLM:\SAM\SAM\Domains\Account\Users\Names").PSChildName
# 差异即为隐藏账户
Compare-Object $netUsers $regUsers

# 方法 4:检查 SID 异常(克隆账户检测)
Get-WmiObject -Class Win32_UserAccount -Filter "LocalAccount=True" |
Group-Object { $_.SID.Split('-')[-1] } |
Where-Object { $_.Count -gt 1 } |
Select-Object -ExpandProperty Group

注册表隐藏账户的深层检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 需要 SYSTEM 权限执行
# 检查 Users 下每个子项的 F 和 V 值
$usersKey = "HKLM:\SAM\SAM\Domains\Account\Users"
$names = Get-ChildItem "$usersKey\Names"

foreach ($name in $names) {
$username = $name.PSChildName
$rid = (Get-ItemProperty $name.PSPath).'(Default)'
$ridHex = '{0:X8}' -f $rid
Write-Host "User: $username | RID: $rid (0x$ridHex)"
}

# 检查 F 值中的 RID 是否与账户名对应的 RID 一致
# 不一致说明可能是克隆账户

2.3 事件日志中的账户创建记录

Event ID 4720:用户账户已创建

Event ID 4722:用户账户已启用

Event ID 4732:成员已添加到安全启用的本地组

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
# 查找最近的账户创建事件
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4720
} -MaxEvents 50 | ForEach-Object {
$xml = [xml]$_.ToXml()
[PSCustomObject]@{
TimeCreated = $_.TimeCreated
TargetUserName = $xml.Event.EventData.Data |
Where-Object { $_.Name -eq 'TargetUserName' } | Select-Object -Expand '#text'
SubjectUserName = $xml.Event.EventData.Data |
Where-Object { $_.Name -eq 'SubjectUserName' } | Select-Object -Expand '#text'
}
} | Format-Table -AutoSize

# 查找账户被添加到管理员组的事件
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4732
} -MaxEvents 50 | ForEach-Object {
$xml = [xml]$_.ToXml()
$groupName = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text'
$memberName = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'MemberSid' }).'#text'
if ($groupName -eq 'Administrators') {
[PSCustomObject]@{
TimeCreated = $_.TimeCreated
GroupName = $groupName
MemberSid = $memberName
}
}
}

3. 管理员组与特权组审计

3.1 本地管理员组枚举

1
2
3
4
5
6
7
8
:: CMD 方式
net localgroup Administrators

:: 其他关键组也要检查
net localgroup "Remote Desktop Users"
net localgroup "Remote Management Users"
net localgroup "Backup Operators"
net localgroup "Hyper-V Administrators"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# PowerShell 方式
Get-LocalGroupMember -Group "Administrators"

# 枚举所有本地组及成员
Get-LocalGroup | ForEach-Object {
$group = $_.Name
$members = Get-LocalGroupMember -Group $group -ErrorAction SilentlyContinue
if ($members) {
foreach ($member in $members) {
[PSCustomObject]@{
GroupName = $group
MemberName = $member.Name
ObjectClass = $member.ObjectClass
PrincipalSource = $member.PrincipalSource
}
}
}
} | Format-Table -AutoSize

3.2 高危特权组清单

组名 风险说明 排查要点
Administrators 完全控制系统 应仅包含必要账户
Remote Desktop Users 允许 RDP 登录 检查是否有异常成员
Remote Management Users 允许 WinRM 远程管理 横向移动常用
Backup Operators 可读取任意文件 可用于导出 SAM/NTDS.dit
Hyper-V Administrators 虚拟化管理 可逃逸到宿主机
Print Operators 加载打印驱动 PrintNightmare 利用
Server Operators 服务器操作 可修改服务配置
Account Operators 账户管理 域环境中可创建账户
DnsAdmins DNS 管理 可加载任意 DLL

3.3 检查特权令牌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看当前用户权限
whoami /priv

# 关注的高危权限
# SeDebugPrivilege - 调试任意进程(可注入进程、dump 凭据)
# SeImpersonatePrivilege - 模拟客户端(Potato 提权)
# SeAssignPrimaryTokenPrivilege - 替换进程令牌
# SeBackupPrivilege - 读取任意文件
# SeRestorePrivilege - 写入任意文件
# SeTcbPrivilege - 作为操作系统一部分(最高权限)
# SeLoadDriverPrivilege - 加载内核驱动

# 检查所有用户的权限分配(需要管理员)
secedit /export /cfg C:\IR\secpol.cfg
# 查看 secpol.cfg 中的 [Privilege Rights] 段

4. 域用户排查(概述)

4.1 域环境基本信息收集

1
2
3
4
5
6
7
8
9
10
11
12
13
# 确认是否在域环境
systeminfo | findstr /i "domain"

# 或
(Get-WmiObject Win32_ComputerSystem).Domain
(Get-WmiObject Win32_ComputerSystem).PartOfDomain

# 获取域控制器信息
nltest /dclist:
nltest /dsgetdc:

# 当前域信任关系
nltest /domain_trusts

4.2 域用户基本枚举

1
2
3
4
5
:: CMD 方式
net user /domain
net group "Domain Admins" /domain
net group "Enterprise Admins" /domain
net group "Schema Admins" /domain
1
2
3
4
5
6
7
8
9
10
11
12
13
# PowerShell AD 模块(需安装 RSAT)
Import-Module ActiveDirectory

# 查找最近创建的域用户
Get-ADUser -Filter * -Properties WhenCreated, LastLogonDate, Enabled |
Where-Object { $_.WhenCreated -gt (Get-Date).AddDays(-30) } |
Select-Object Name, SamAccountName, WhenCreated, LastLogonDate, Enabled |
Sort-Object WhenCreated -Descending

# 查找域管理员
Get-ADGroupMember -Identity "Domain Admins" -Recursive |
Get-ADUser -Properties LastLogonDate, Enabled |
Select-Object Name, SamAccountName, LastLogonDate, Enabled

详细的域环境排查将在 Windows应急响应/11-Active-Directory-排查 中展开


5. RDP 会话分析

5.1 RDP 登录的事件日志体系

RDP 登录涉及多个事件日志源,需要交叉分析才能还原完整登录过程

Security 日志

Event ID 说明 关键字段
4624 (Type 10) 远程交互式登录成功 源 IP (IpAddress)、用户名
4625 (Type 10) 远程登录失败 源 IP、失败原因
4634 会话注销 登录 ID (LogonId)
4647 用户发起的注销 用户名
4778 会话重新连接 客户端名称、客户端地址
4779 会话断开 客户端名称、客户端地址

TerminalServices-RemoteConnectionManager/Operational

Event ID 说明
1149 RDP 用户认证成功(包含源 IP 和用户名)
261 监听器接收了连接

TerminalServices-LocalSessionManager/Operational

Event ID 说明
21 远程桌面会话登录成功
22 远程桌面 Shell 启动通知
23 远程桌面会话注销
24 远程桌面会话断开
25 远程桌面会话重新连接

5.2 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
43
44
45
46
47
# 查询 RDP 登录成功事件(Security Log - Type 10)
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624
} | ForEach-Object {
$xml = [xml]$_.ToXml()
$logonType = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonType' }).'#text'
if ($logonType -eq '10') {
[PSCustomObject]@{
Time = $_.TimeCreated
User = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text'
Domain = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetDomainName' }).'#text'
SourceIP = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' }).'#text'
LogonID = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetLogonId' }).'#text'
}
}
} | Format-Table -AutoSize

# 查询 TerminalServices-RemoteConnectionManager 1149(RDP 认证成功)
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

# 查询 LocalSessionManager(会话生命周期)
Get-WinEvent -LogName "Microsoft-Windows-TerminalServices-LocalSessionManager/Operational" |
Where-Object { $_.Id -in @(21,22,23,24,25) } |
Select-Object TimeCreated, Id,
@{N='EventType'; E={
switch ($_.Id) {
21 { 'Session Logon' }
22 { 'Shell Start' }
23 { 'Session Logoff' }
24 { 'Session Disconnect' }
25 { 'Session Reconnect' }
}
}},
@{N='User'; E={ $_.Properties[0].Value }},
@{N='SessionID'; E={ $_.Properties[1].Value }},
@{N='SourceIP'; E={ $_.Properties[2].Value }} |
Format-Table -AutoSize

5.3 当前 RDP 会话查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
:: 查看当前活跃的 RDP 会话
qwinsta
:: 或
query user
:: 或
query session

:: 输出示例:
:: SESSIONNAME USERNAME ID STATE TYPE DEVICE
:: services 0 Disc
:: console Admin 1 Active
:: rdp-tcp#0 attacker 2 Active
:: rdp-tcp 65536 Listen

:: 强制断开可疑会话
:: logoff <SessionID>
1
2
3
4
5
6
7
8
9
10
11
12
13
# PowerShell 查看 RDP 会话详情
Get-CimInstance -ClassName Win32_LogonSession |
Where-Object { $_.LogonType -eq 10 } |
ForEach-Object {
$session = $_
$user = Get-CimAssociatedInstance -InputObject $session -ResultClassName Win32_Account
[PSCustomObject]@{
LogonId = $session.LogonId
StartTime = $session.StartTime
User = $user.Caption
AuthenticationPackage = $session.AuthenticationPackage
}
} | Format-Table -AutoSize

5.4 RDP 缓存与 Bitmap 分析

RDP 客户端会在本地缓存远程桌面的 Bitmap 数据

缓存位置C:\Users\<user>\AppData\Local\Microsoft\Terminal Server Client\Cache\

文件名格式:bcache24.bmc, cache0000.bin

工具bmc-tools.py 可从缓存文件中提取 Bitmap 图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 检查每个用户的 RDP 缓存
Get-ChildItem "C:\Users\*\AppData\Local\Microsoft\Terminal Server Client\Cache" -ErrorAction SilentlyContinue

# 检查 RDP 连接历史(注册表)
# 记录了该机器曾经 RDP 连接过哪些远程主机
Get-ChildItem "HKCU:\Software\Microsoft\Terminal Server Client\Servers" -ErrorAction SilentlyContinue |
ForEach-Object {
[PSCustomObject]@{
Server = $_.PSChildName
UsernameHint = (Get-ItemProperty $_.PSPath).UsernameHint
}
}

# 检查默认 RDP 连接文件
Get-ChildItem "C:\Users\*\Documents\*.rdp" -ErrorAction SilentlyContinue

6. 登录类型(Logon Type)深度解析

6.1 完整登录类型对照表

Event ID 4624/4625 中的 LogonType 字段标识了登录方式,是判断攻击路径的关键

Type 名称 说明 常见场景 IR 关注度
2 Interactive 本地控制台登录 物理登录、VM 控制台 ★★★
3 Network 网络登录 SMB 共享、PsExec、WMI ★★★★★
4 Batch 批处理登录 计划任务执行 ★★★
5 Service 服务登录 Windows 服务启动 ★★★
7 Unlock 解锁工作站 屏幕解锁
8 NetworkCleartext 网络明文登录 IIS Basic Auth、PowerShell WinRM (HTTP) ★★★★
9 NewCredentials 新凭据登录 runas /netonly ★★★★
10 RemoteInteractive 远程交互式 RDP 登录 ★★★★★
11 CachedInteractive 缓存凭据登录 域控不可达时的域账户登录 ★★
12 CachedRemoteInteractive 缓存远程登录 RDP + 域缓存 ★★
13 CachedUnlock 缓存解锁 域缓存解锁

6.2 各登录类型的攻击场景

Type 3 (Network) —— 横向移动最常见的登录类型:

PsExec 远程执行 → Type 3

net use \\target\C$ 映射共享 → Type 3

WMI 远程命令 → Type 3

Pass-the-Hash (PTH) → Type 3

关键特征:Type 3 登录不会在目标系统上缓存凭据

Type 10 (RemoteInteractive) —— RDP 登录:

标准 RDP 连接 → Type 10

攻击者获取凭据后 RDP 到目标 → Type 10

注意:如果使用 mstsc /restrictedAdmin(受限管理模式),登录类型会变成 Type 3

Type 9 (NewCredentials) —— runas /netonly

本地进程使用当前凭据,网络访问使用新凭据

Mimikatz sekurlsa::pth (Pass-the-Hash) 会生成 Type 9

Cobalt Strike 的 steal_token 操作也会触发 Type 9

Type 4 (Batch) —— 计划任务:

攻击者通过 schtasks 创建持久化任务 → Type 4

与 Event ID 4698(计划任务创建)交叉分析

6.3 按登录类型查询事件日志

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
43
44
# 统计各登录类型的登录次数
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624
} -MaxEvents 10000 | ForEach-Object {
$xml = [xml]$_.ToXml()
($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonType' }).'#text'
} | Group-Object | Sort-Object Count -Descending |
Select-Object @{N='LogonType';E={$_.Name}}, Count | Format-Table

# 查找所有 Type 3 (Network) 登录,排除机器账户
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624
} -MaxEvents 5000 | ForEach-Object {
$xml = [xml]$_.ToXml()
$logonType = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonType' }).'#text'
$user = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text'
if ($logonType -eq '3' -and $user -notlike '*$') {
[PSCustomObject]@{
Time = $_.TimeCreated
User = $user
SourceIP = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' }).'#text'
LogonProcess = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonProcessName' }).'#text'
AuthPackage = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'AuthenticationPackageName' }).'#text'
}
}
} | Format-Table -AutoSize

# 查找 Type 9 (NewCredentials) —— 可能是 Pass-the-Hash
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624
} -MaxEvents 5000 | ForEach-Object {
$xml = [xml]$_.ToXml()
$logonType = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonType' }).'#text'
if ($logonType -eq '9') {
[PSCustomObject]@{
Time = $_.TimeCreated
User = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text'
Process = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'ProcessName' }).'#text'
}
}
} | Format-Table -AutoSize

7. 凭据缓存与凭据窃取检测

7.1 Windows 凭据存储位置

存储位置 内容 攻击工具
LSASS 进程内存 NTLM Hash, Kerberos Ticket Mimikatz sekurlsa::logonpasswords
SAM 数据库 本地用户 NTLM Hash secretsdump, hashdump
LSA Secrets 服务账户密码、Auto-Logon 密码 Mimikatz lsadump::secrets
DPAPI 浏览器密码、WiFi 密码、凭据管理器 Mimikatz dpapi::
Credential Manager 保存的网络凭据 vaultcmd, cmdkey
Domain Cache 域缓存凭据 (DCC2) secretsdump
NTDS.dit (域控) 所有域用户 Hash secretsdump, ntdsutil

7.2 凭据窃取指标检测

LSASS 访问检测

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
# Event ID 4663 - LSASS 进程被访问
# Sysmon Event ID 10 - ProcessAccess(更精确)
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-Sysmon/Operational'
ID = 10
} -MaxEvents 1000 -ErrorAction SilentlyContinue | Where-Object {
$_.Properties[5].Value -like '*lsass.exe'
} | ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
SourceProcess = $_.Properties[3].Value # SourceImage
TargetProcess = $_.Properties[5].Value # TargetImage
GrantedAccess = $_.Properties[7].Value
}
} | Format-Table -AutoSize

# 检查 LSASS 进程的 MiniDump 文件(攻击者可能 dump 后外传)
Get-ChildItem -Path "C:\", "C:\Users\*\Desktop", "C:\Users\*\Downloads",
"C:\Windows\Temp", "C:\Temp" -Filter "*.dmp" -Recurse -ErrorAction SilentlyContinue

# 检查可疑的 comsvcs.dll MiniDump(不需要 Mimikatz 的 LSASS dump 方法)
# 攻击者常用:rundll32.exe comsvcs.dll MiniDump <lsass_pid> dump.dmp full
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4688
} -MaxEvents 5000 | Where-Object {
$_.Properties[8].Value -like '*comsvcs*MiniDump*' -or
$_.Properties[8].Value -like '*procdump*lsass*'
}

凭据管理器检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 列出已存储的凭据
cmdkey /list

# Vault 凭据查询
vaultcmd /listcreds:"Windows Credentials" /all
vaultcmd /listcreds:"Web Credentials" /all

# 检查注册表中的 Auto-Logon 配置(明文密码!)
$autoLogon = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -ErrorAction SilentlyContinue
if ($autoLogon.DefaultPassword) {
Write-Warning "[!] Auto-Logon 配置发现明文密码!"
Write-Host "User: $($autoLogon.DefaultUserName)"
Write-Host "Domain: $($autoLogon.DefaultDomainName)"
Write-Host "Password: $($autoLogon.DefaultPassword)"
}

7.3 Credential Guard 和 LSA Protection 状态检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 检查 Credential Guard 是否启用
Get-CimInstance -ClassName Win32_DeviceGuard -Namespace "root\Microsoft\Windows\DeviceGuard" -ErrorAction SilentlyContinue |
Select-Object SecurityServicesRunning, VirtualizationBasedSecurityStatus

# 检查 LSA Protection (RunAsPPL) 是否启用
$lsaPPL = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "RunAsPPL" -ErrorAction SilentlyContinue
if ($lsaPPL.RunAsPPL -eq 1) {
Write-Host "[+] LSA Protection (PPL) 已启用 - 可阻止大部分凭据窃取" -ForegroundColor Green
} else {
Write-Warning "[-] LSA Protection (PPL) 未启用 - LSASS 进程可被直接访问"
}

# 检查 WDigest 明文密码缓存(Windows 8.1+ 默认关闭)
$wdigest = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest" -Name "UseLogonCredential" -ErrorAction SilentlyContinue
if ($wdigest.UseLogonCredential -eq 1) {
Write-Warning "[!] WDigest 明文密码缓存已开启 - 攻击者可能已修改此配置!"
}

8. 一键用户安全审计脚本

8.1 综合审计脚本

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<#
.SYNOPSIS
Windows 账户安全一键审计脚本
.DESCRIPTION
枚举本地用户、检测隐藏账户、审计管理员组、检查 RDP 登录、检测凭据窃取指标
.NOTES
需要管理员权限运行
#>

$OutputDir = "C:\IR\AccountAudit_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null

Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Windows 账户安全审计" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan

# ---- 1. 本地用户枚举 ----
Write-Host "`n[*] 1. 枚举本地用户..." -ForegroundColor Yellow
$localUsers = Get-LocalUser | Select-Object Name, Enabled, SID, LastLogon,
PasswordLastSet, PasswordRequired, Description
$localUsers | Format-Table -AutoSize
$localUsers | Export-Csv "$OutputDir\LocalUsers.csv" -NoTypeInformation -Encoding UTF8

# ---- 2. 隐藏账户检测 ----
Write-Host "[*] 2. 检测隐藏账户($ 结尾)..." -ForegroundColor Yellow
$hiddenUsers = $localUsers | Where-Object { $_.Name -like '*$' -and $_.Name -ne 'DefaultAccount$' }
if ($hiddenUsers) {
Write-Host "[!] 发现隐藏账户:" -ForegroundColor Red
$hiddenUsers | Format-Table -AutoSize
} else {
Write-Host "[+] 未发现 $ 结尾的隐藏账户" -ForegroundColor Green
}

# ---- 3. 管理员组成员 ----
Write-Host "[*] 3. 审计管理员组成员..." -ForegroundColor Yellow
$adminMembers = Get-LocalGroupMember -Group "Administrators" -ErrorAction SilentlyContinue
$adminMembers | Format-Table -AutoSize
$adminMembers | Export-Csv "$OutputDir\AdminGroupMembers.csv" -NoTypeInformation -Encoding UTF8

# ---- 4. 高危组审计 ----
Write-Host "[*] 4. 审计高危组成员..." -ForegroundColor Yellow
$highRiskGroups = @("Remote Desktop Users", "Remote Management Users",
"Backup Operators", "Hyper-V Administrators")
foreach ($group in $highRiskGroups) {
$members = Get-LocalGroupMember -Group $group -ErrorAction SilentlyContinue
if ($members) {
Write-Host " [!] $group 组成员:" -ForegroundColor Red
$members | Format-Table -AutoSize
}
}

# ---- 5. 最近账户创建事件 ----
Write-Host "[*] 5. 检查最近 30 天的账户创建事件 (4720)..." -ForegroundColor Yellow
try {
$accountCreation = Get-WinEvent -FilterHashtable @{
LogName = 'Security'; ID = 4720
StartTime = (Get-Date).AddDays(-30)
} -ErrorAction SilentlyContinue | ForEach-Object {
$xml = [xml]$_.ToXml()
[PSCustomObject]@{
Time = $_.TimeCreated
NewUser = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text'
CreatedBy = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'SubjectUserName' }).'#text'
}
}
if ($accountCreation) {
$accountCreation | Format-Table -AutoSize
$accountCreation | Export-Csv "$OutputDir\AccountCreation.csv" -NoTypeInformation -Encoding UTF8
} else {
Write-Host " [+] 无最近账户创建事件" -ForegroundColor Green
}
} catch {
Write-Host " [-] 无法读取 Security 日志" -ForegroundColor Red
}

# ---- 6. RDP 登录事件 ----
Write-Host "[*] 6. 检查 RDP 登录事件..." -ForegroundColor Yellow
try {
$rdpLogins = Get-WinEvent -LogName "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational" -MaxEvents 100 -ErrorAction SilentlyContinue |
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
}
}
if ($rdpLogins) {
$rdpLogins | Format-Table -AutoSize
$rdpLogins | Export-Csv "$OutputDir\RDP_Logins.csv" -NoTypeInformation -Encoding UTF8
}
} catch {
Write-Host " [-] 无 RDP 日志" -ForegroundColor Red
}

# ---- 7. Auto-Logon 检查 ----
Write-Host "[*] 7. 检查 Auto-Logon 配置..." -ForegroundColor Yellow
$autoLogon = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -ErrorAction SilentlyContinue
if ($autoLogon.DefaultPassword) {
Write-Host "[!!!] Auto-Logon 明文密码:User=$($autoLogon.DefaultUserName) Pass=$($autoLogon.DefaultPassword)" -ForegroundColor Red
} else {
Write-Host " [+] 未发现 Auto-Logon 明文密码" -ForegroundColor Green
}

# ---- 8. 凭据保护状态 ----
Write-Host "[*] 8. 检查凭据保护配置..." -ForegroundColor Yellow
$lsaPPL = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "RunAsPPL" -ErrorAction SilentlyContinue
Write-Host " LSA Protection (PPL): $(if($lsaPPL.RunAsPPL -eq 1){'已启用'}else{'未启用'})"
$wdigest = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest" -Name "UseLogonCredential" -ErrorAction SilentlyContinue
if ($wdigest.UseLogonCredential -eq 1) {
Write-Host " [!] WDigest 明文缓存:已开启(高危)" -ForegroundColor Red
} else {
Write-Host " WDigest 明文缓存:已关闭" -ForegroundColor Green
}

Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host " 审计完成,结果保存至:$OutputDir" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan

9. 实战练习:发现隐藏管理员账户

9.1 场景描述

某 Windows Server 2019 疑似被入侵,管理员发现有异常远程登录记录

你的任务

  1. 枚举所有本地用户,找出隐藏账户

  2. 确认哪些账户具有管理员权限

  3. 追踪异常账户的创建时间和创建者

  4. 分析 RDP 登录记录,确定攻击来源 IP

9.2 排查步骤

Step 1:基础用户枚举

1
2
3
4
5
6
7
# 先用 net user 看看
net user
# 假设输出只有:Administrator, Guest, DefaultAccount, WDAGUtilityAccount

# 再用 PowerShell 枚举
Get-LocalUser | Select-Object Name, Enabled, SID, LastLogon
# 可能发现额外的账户:support$(Enabled: True)

Step 2:确认管理员组

1
2
Get-LocalGroupMember -Group "Administrators"
# 发现 support$ 也在管理员组中

Step 3:追踪账户创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 搜索 Event ID 4720
Get-WinEvent -FilterHashtable @{ LogName='Security'; ID=4720 } |
ForEach-Object {
$xml = [xml]$_.ToXml()
$target = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text'
if ($target -like '*support*') {
[PSCustomObject]@{
Time = $_.TimeCreated
NewUser = $target
CreatedBy = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'SubjectUserName' }).'#text'
}
}
}
# 输出:2024-01-15 03:22:41, NewUser=support$, CreatedBy=Administrator

Step 4:RDP 来源分析

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查找 support$ 账户的 RDP 登录
Get-WinEvent -FilterHashtable @{ LogName='Security'; ID=4624 } -MaxEvents 10000 |
ForEach-Object {
$xml = [xml]$_.ToXml()
$user = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text'
$type = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonType' }).'#text'
if ($user -eq 'support$' -and $type -eq '10') {
[PSCustomObject]@{
Time = $_.TimeCreated
SourceIP = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' }).'#text'
}
}
} | Sort-Object Time | Format-Table -AutoSize

9.3 Linux 工程师注意事项

Linux 习惯 Windows 差异
cat /etc/passwd 看所有用户 net user 可能遗漏隐藏账户,用 Get-LocalUser
grep :0: /etc/passwd 找 root 等权账户 检查 SID 以 -500 结尾 + Administrators 组成员
last / lastlog 查看登录记录 Event ID 4624 + TerminalServices 日志
auth.log / secure 查看认证日志 Security Event Log (4624/4625/4634)
SSH key 认证 RDP + NLA (Network Level Authentication)
who / w 查看在线用户 qwinsta / query user

10. 快速参考卡片

10.1 账户排查命令速查

目标 命令
列出所有本地用户 Get-LocalUser
查看用户详情 net user <username>
查看管理员组 Get-LocalGroupMember -Group "Administrators"
查看当前登录会话 qwinsta / query user
查看用户 SID wmic useraccount get name,sid
查看用户权限 whoami /priv
查看保存的凭据 cmdkey /list
检查 Auto-Logon reg query "HKLM\...\Winlogon" /v DefaultPassword

10.2 关键事件 ID 速查

Event ID 日志源 说明
4720 Security 用户账户创建
4722 Security 账户启用
4724 Security 密码重置
4725 Security 账户禁用
4726 Security 账户删除
4732 Security 添加到本地组
4624 Security 登录成功
4625 Security 登录失败
1149 TerminalServices-RCM RDP 认证成功
21 TerminalServices-LSM RDP 会话登录
25 TerminalServices-LSM RDP 会话重连


上一章 目录 下一章
04-取证制品分析 Windows应急响应 06-进程与网络分析