65.9K
CodeProject 正在变化。 阅读更多。
Home

SQL Server 暴力破解攻击检测: 第三部分

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.48/5 (5投票s)

2018年3月26日

CPOL

4分钟阅读

viewsIcon

11578

downloadIcon

97

更多配置和审计工具

系列文章

引言

在发布本系列的第一部分后不久,我偶然发现了更多关于如何破解 SQL Server 数据库以及读取事件日志的各种方法的信息。除了事件 ID 18456 之外,还有其他事件,DBA 可能希望检测代码能够考虑这些事件,它们可能表明我们暴露在外的数据库存在恶意的端口扫描。

背景

在深入了解 SQL Server 记录的各种事件类型时,我发现事件 ID 1783217836 也可能指示恶意活动,我们可能希望将其作为暴力破解攻击检测的一部分进行阻止。事实上,当数据库收到来自常见端口扫描工具的数据包时,通常会记录这些错误,因为这些工具会枚举端口列表并等待响应。我不知道其他系统管理员的情况,但在我的经验中,来自公共 IP 地址的针对面向 Internet 的服务的任何端口扫描都应立即被阻止,除非是已知的渗透测试的一部分。

我在家庭网络上使用 pfSense 作为我的防火墙(我强烈推荐它,因为它拥有高度的社区支持和附加组件),并使用 pfBlockerNG 附加组件来管理几个 IPv4 阻止列表,这些列表可从大量提供商免费获得。在我对 BruteForceAttackDetection 的测试中,SQL Server 的入站连接绕过了我订阅的所有恶意软件阻止列表,因此我收到了大量恶意的登录尝试。果然,我看到了许多带有 17832 和 17836 事件 ID 的事件日志条目;有时某个 IP 地址根本不尝试登录,而是反复发送错误的数据包。我不是端口扫描工具的专家;我曾几次使用 NMap 和 Wireshark 进行基本的端口扫描和数据包嗅探,所以我不能确切地说所有这些错误数据包意味着什么。不过,我不想让它们进入我的网络,因此代码的最新修订版本具有可选阻止它们的配置。

当然,这一切只有在从 SQL Server 事件日志中提取事件 ID 信息后才可能实现。我发现 SQL Server 有一种相当奇怪的方法来使用 xp_readerrorlog 查询事件日志的一些部分(它们本质上是 XML)。在原始代码中,我们只获取事件描述,其中包含 IP 地址。显然,在 xp_readerrorlog 中,每个日志条目显示为两行,第一行包含事件 ID、状态和严重性。这需要对转储事件数据的临时表进行自连接(基于事件时间戳)才能将所有内容整合在一起。附加的数据片段对我们来说意义不大,但事件 ID 和状态为我们提供了一个一致的结构来映射其他配置,并提供微调哪些事件被视为恶意活动并应触发阻止恶意远程 IP 地址的功能。

Using the Code

首先,我们需要添加一个新表 ConfigEvent,并用我们希望可选阻止的三个事件 ID 的一些配置来填充它。对于 18456,我填充了区分不同类型失败登录尝试的各种状态码。对于 1783217836,我找不到该事件会引发的所有不同状态码的明确列表,所以我只设置代码以阻止全部或不阻止任何。

CREATE TABLE ConfigEvent
(
    ErrorCode INT NOT NULL,
    StateCode INT NOT NULL,
    Block BIT NOT NULL DEFAULT 1,
    Description VARCHAR(4000),
  PRIMARY KEY(ErrorCode, StateCode)
);

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (17832, -1, 1, N'The login packet used to open the connection is structurally invalid; _
        the connection has been closed. Please contact the vendor of the client library.%.*ls')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (17836, -1, 1, N'Length specified in network packet payload did not match number of bytes read; _
        the connection has been closed. Please contact the vendor of the client library.%.*ls')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 1, 1, N'Error information is not available. _
This state usually means you do not have permission to receive the error details. _
Contact your SQL Server administrator for more information.')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 2, 1, N'User ID is not valid.')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 5, 1, N'User ID is not valid.')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 6, 1, N'An attempt was made to use a Windows login name with SQL Server Authentication.')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 7, 1, N'Login is disabled, and the password is incorrect.')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 8, 1, N'The password is incorrect.')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 9, 1, N'Password is not valid.')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 11, 1, N'Login is valid, but server access failed. _
One possible cause of this error is when the Windows user has access to SQL Server _
as a member of the local administrators group, but Windows is not providing _
administrator credentials. To connect, start the connecting program using the _
Run as administrator option, and then add the Windows user to SQL Server as a specific login.')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 12, 1, N'Login is valid login, but server access failed.')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 18, 1, N'Password must be changed.')

INSERT ConfigEvent(ErrorCode, StateCode, Block, Description)
VALUES (18456, 58, 1, N'An attempt to login using SQL authentication failed. _
        Server is configured for Integrated authentication only. ')

下面是查询事件日志数据的修改后代码(有关 CheckFailedLogins 存储过程中的全部修改后代码,请参见附件脚本);如上所述,这需要自连接才能将事件 ID、状态和严重性数据附加到描述中,并获取整个日志条目。我还将提取所需数据的代码移到了标量函数中,主要是为了保持代码的整洁和结构化。

WITH Logs AS
(
    SELECT e.LogDate,
        CASE
            WHEN e.Message LIKE 'Error: 18456%' THEN 18456 -- Failed login events
            WHEN e.Message LIKE 'Error: 17836%' THEN 17836 -- Bad packets sent, 
                                               -- typically from certain port-scanning tools.
            WHEN e.Message LIKE 'Error: 17832%' THEN 17832 -- Bad packets sent, 
                                               -- typically from certain port-scanning tools.
        END AS ErrorCode,
        dbo.ExtractStateCode(e.Message) AS StateCode,
        dbo.ExtractIPAddress(x.Message) as IPAddress,
        dbo.ExtractUserID(x.Message) AS UserId,
        e.Message + CHAR(13) + CHAR(10) + x.Message as Message
    FROM @FailedLogins AS e
    INNER JOIN @FailedLogins AS x
        ON e.LogDate = x.LogDate
    WHERE (e.Message LIKE 'Error: 18456%' AND x.Message LIKE 'Login%')
        OR (e.Message LIKE 'Error: 17836%' AND x.Message LIKE 'Length%')
        OR (e.Message LIKE 'Error: 17832%' AND x.Message LIKE 'The login%')
)
INSERT INTO @FailedLoginClientDtl
SELECT l.IPAddress,
    l.LogDate,
    l.UserId,
    l.Message
FROM Logs l
INNER JOIN ConfigEvent e  -- Filter based on ones we've set by StateCode in ConfigEvent
    ON e.ErrorCode = l.ErrorCode
    AND (e.StateCode = l.StateCode OR e.StateCode = -1)
    AND e.Block = 1
WHERE IPAddress IS NOT NULL
    AND LogDate >= @LookbackDate

关注点

关于事件日志数据自连接,我注意到的一点是时间戳不是唯一的(至少在 xp_readerrorlog 提取的精度级别上,它似乎是 datetime 而不是 datetime2)。这会导致具有相同时间戳的记录重复,但经过测试,这似乎影响不大。对于大多数情况,这只会重复一个 IP 地址在 SQL Server Agent 作业运行的 10 秒时间窗口内 40-50 多个失败登录尝试中的一两个条目(这显然是恶意的!)。

参考文献

© . All rights reserved.