如何使用PowerShell跟踪重要的Windows安全事件


许多组织依赖Microsoft技术来完成工作。与此同时,威胁行为者可以利用诸如Windows之类的操作系统。幸运的是,Windows记录操作系统安全事件,以帮助您追踪此类行为。

Windows生成的安全事件在事件响应过程中充当关键资源。诸如Microsoft的Windows事件查看器之类的工具为您提供了访问权限,以审查捕获的事件,但通过手动滚动繁杂的日志来检测异常是不现实的。

在本文中,您将学习如何通过学习审计策略、Windows事件日志以及使用PowerShell分析安全事件来追踪Windows中潜在的安全漏洞。

先决条件

本文旨在传达有关如何使用PowerShell分析Windows安全事件的信息。如果您想跟随演示进行操作,您将需要:

  • A Windows 10+ PC – This PC will be used to generate and track down potential security events in the event log. This tutorial will be using Windows PowerShell 5.1.
  • 在Windows PC上拥有管理员权限
  • A PowerShell code editor such PowerShell ISE or Visual Studio (VS) Code.

Windows存储安全事件的位置

当在Windows操作系统上执行操作时,Windows会将该操作记录为一个或多个事件日志中的事件。Windows事件日志默认存储在文件系统中的%SystemRoot%\system32\winevt\logs目录中。可以通过修改相应事件日志的EventLog注册表子键来更改此位置。

如果您想查看系统上存储的最重要的事件日志(应用程序、安全和系统),请将以下代码复制并粘贴到PowerShell控制台中,或将其保存为脚本。

<diy6要访问安全日志文件的存储位置,您需要以管理员身份运行代码。

#将应用程序、安全和系统日志呈现为数组。
 $arrLogs = @(
     "Application"
     "Security"
     "System"
 )
 #使用ForEach-Object cmdlet来针对每个相应的日志使用Get-ItemProperty cmdlet。
 $arrLogs | ForEach-Object {
     #使用Get-ItemProperty cmdlet列出应用程序、安全和系统日志的配置文件路径。
     Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\$_ -Name File | Select-Object PSChildName,File
 }

以下屏幕截图显示了代码的预期输出,显示了应用程序安全系统日志文件的日志名称和存储位置。

Application, Security, and System audit log location

审计策略:定义要记录的事件

默认情况下,Windows 并未捕获可能需要用于检测或调查违规行为的所有安全事件。要控制 Windows 记录或不记录的内容,您必须定义并应用审核策略。审核策略是一组传递给 Windows 的指令,告诉它要记录哪些事件。

有几种不同的方法可以分配和处理审核策略,例如组策略。如果您需要在多台计算机上实施审核策略,那么组策略是一个很好的选择。但在本文中,您将专注于单个设备,因此将使用auditpol 工具。auditpol 工具已经随 Windows 安装,并允许您在 Windows 系统上查找和设置审核策略。

查找审核策略

例如,要查找 Windows 系统上所有审核策略的状态,可以使用如下所示的/get参数。使用/category参数后跟通配符告诉 auditpol 查找所有审核策略的状态;而不仅仅是匹配特定类别或子类别的一个。

#获取系统的审计策略配置。
auditpol /get /category:*

以下截图显示了代码预期输出的缩略版本,显示了帐户管理审计策略类别、子类别和状态(设置)。

Audit policy category, subcategory, and configuration

A Setting that is configured as No Auditing means that all events associated with that audit policy subcategory will not be logged.

设置审计策略

auditpol工具不仅可以查看审计策略设置,还可以使用auditpol /set命令修改它们。为了演示本教程中的未来部分,请以管理员身份打开PowerShell控制台并运行以下命令。此命令开始记录所有登录子类别中的事件(成功和失败)。

配置登录子类别会强制系统记录以下事件:

#设置登录事件以捕获成功/失败的活动。
auditpol /set /subcategory:"Logon" /success:enable /failure:enable

有许多资源可用于帮助您进行最佳实践的审计策略配置,包括互联网安全中心(CIS)基准国防信息系统局(DISA)安全技术实施指南(STIG),以及由Microsoft发布的指南。

生成用于分析的登录失败日志

本文将是一个教程,并期望您跟着操作。如果您已配置Windows以审计上述登录事件,请现在生成一些安全事件,以便稍后进行分析。更具体地说,让我们生成35次失败的登录尝试,这将记录在您系统的安全日志中,模拟暴力破解活动。

1. 打开您喜欢的代码编辑器。

2. 将以下代码复制并粘贴到代码编辑器中。此代码片段尝试使用虚假的用户名和密码使用Start-Process命令打开PowerShell.exe进程。

#定义5个用户名来记录登录失败。
 $arrUsers = @(
     "AtaBlogUser1"
     "AtaBlogUser2"
     "AtaBlogUser3"
     "AtaBlogUser4"
     "AtaBlogUser5"
 )
 #使用ForEach-Object循环遍历用户名,为每个用户名生成一次登录失败。
 $arrUsers | ForEach-Object {
     $securePassword = ConvertTo-SecureString "AtA810GRu13Z%%" -AsPlainText -Force 
     $storedCredential = New-Object System.Management.Automation.PSCredential($_, $securePassword)
     Start-Process -FilePath PowerShell -Credential $storedCredential
 }
 #为用户AtaBlogUser生成30次登录失败。
 $i = 0
 Do {
     $securePassword = ConvertTo-SecureString "AtA810GRu13Z%%" -AsPlainText -Force 
     $storedCredential = New-Object System.Management.Automation.PSCredential("AtaBlogUser", $securePassword)
     Start-Process -FilePath PowerShell -Credential $storedCredential
     $i++
 } Until ($i -eq 30)

3. 将PowerShell脚本保存为Invoke-BogusEvents.ps1或您喜欢的其他名称,并执行该脚本。

执行时,您将注意到预期的错误重复35次,指示用户名或密码不正确

Authentication failure due to incorrect user name or password.

如果未收到预期的输出,请确保辅助登录服务处于运行状态。

使用PowerShell访问Windows事件

现在您确保至少有35个Windows安全事件,让我们深入了解如何使用PowerShell的Get-WinEvent命令查找它们。

您可能熟悉 PowerShell的Get-EventLog命令,该命令也用于以编程方式访问事件日志。Get-EventLog使用了已弃用的Win32应用程序编程接口(API),本文不讨论此内容。

打开 PowerShell 控制台作为管理员,并调用 Get-WinEvent cmdlet,将 FilterHashtableMaxEvents 参数传递给它,如下所示。

下面的命令查询您系统的安全日志(LogName='Security')中的 事件 ID 4625ID=4625),并返回最新的 10 个实例(MaxEvents 10)。

# 过滤安全日志以获取事件 ID 4625 的前 10 个实例
Get-WinEvent -FilterHashtable @{LogName='Security';ID=4625} -MaxEvents 10

如果成功,您应该会看到类似以下的输出:

10 instances of Event ID 4625

使用 Get-WinEvent 访问事件属性

在上面的部分中,您使用了 Get-WinEvent 来查看 Windows 安全事件的概要,但是 Windows 事件包含了更多信息。每个 Windows 事件都有有价值的属性,您可以用于深入分析。

Windows 事件作为 XML

当 Windows 记录一个事件时,它以 XML 格式存储。如果是这样的话,为什么您的 Get-WinEvent 命令返回典型的 PowerShell 对象呢?Get-WinEvent cmdlet 读取本机 Windows API,并将事件转换为 PowerShell 对象,以增加功能性。

每个 Windows 事件都有遵循特定的 XML 模式或结构的各种属性。

您将看到下面每个事件都遵循特定的结构,具有三个属性:

  • name – 属性名称
  • inType – 输入类型定义或事件接受值的方式
  • outputType – 输出类型定义或事件记录方式

使用 PowerShell 查找事件 XML 模板

如上所述,每个 Windows 安全事件都存储在 XML 中,并具有特定的模式,但该模式是什么样的呢?让我们来看看。

在之前的某个部分中,您生成了安全事件日志中 ID 4625 的一些事件。这种类型的事件具有仅适用于它的特定属性。要找到这些属性以及模板的样子:

1. 如果尚未打开,请以管理员身份打开 PowerShell 控制台。

2. 再次运行 Get-WinEvent,但这次使用 ListProvider 参数指定 Windows 用于记录事件到安全事件日志的提供程序,并仅返回 Events 属性。

Events 属性包含列表提供程序记录的所有事件,并公开每个事件的 XML 模板。

(Get-WinEvent -ListProvider 'Microsoft-Windows-Security-Auditing').Events
Get Win Event List Provider

3. 现在您已经有了用于查找所有事件类型的模板的代码,通过仅返回与 ID 4625 关联的事件来缩小范围。4. 一旦您仅返回事件 ID 4625 的登录事件类型,请将其限制为仅显示如下的 Template 属性。

(Get-WinEvent -ListProvider 'Microsoft-Windows-Security-Auditing').Events | Where-Object -Property ID -eq 4625

#获取事件 ID 4625 的事件属性的事件 XML 模板。
 ((Get-WinEvent -ListProvider 'Microsoft-Windows-Security-Auditing').Events | Where-Object -Property ID -eq 4625).Template

以下是代码输出的截断版本的屏幕截图,标识了事件属性名称、输入类型和输出类型。您可以看到事件 ID 4625 具有具有各种输入和输出定义的事件属性。

下面的屏幕截图突出显示了事件 ID 4625 的SubjectUserSid属性。此特定事件接受输入类型(inType)为win:SID,并呈现输出(outType)为string,这就是它存储在安全日志中的方式。

XML template example

PowerShell如何将XML转换为对象

现在您已经看到了Windows如何在XML中存储事件以及如何在PowerShell中查看这些模板,让我们转向PowerShell如何将该XML转换为对象。

1. 再次运行Get-WinEvent命令以返回我们的事件 ID 4625。到目前为止,这没什么新的。请注意,PowerShell只显示四个属性,TimeCreatedIdLevelDisplayNameMessage

Get-WinEvent -FilterHashtable @{LogName='Security';ID=4625} -MaxEvents 1
Get-WinEvent FilterHashtable

默认情况下,Get-WinEvent cmdlet 不会将事件的 XML 数据源中的所有属性作为 PowerShell 对象返回。

2. 现在,将上述命令的输出传递到Select-Object cmdlet,并指定Property参数并传递一个值以显示所有属性。

Get-WinEvent -FilterHashtable @{LogName='Security';ID=4625} -MaxEvents 1 | Select-Object -Property *

请注意,PowerShell 隐藏了许多不同的属性。更具体地说,有一个名为Properties的属性。Properties属性包含您之前在XML模板中看到的每个事件属性的值。

Powershell hiding Properties

3. 限制上述 Get-WinEvent 命令的输出,以显示 Properties 属性。该属性存储所有事件属性,而不是 PowerShell 对象属性,存储在一个数组中。

# 输出事件属性数组,用于第一次出现的事件 ID 4625
 $eventProperties = (Get-WinEvent -FilterHashtable @{LogName='Security';ID=4625} -MaxEvents 1).properties
 $eventProperties

在下面的截图左侧是上述命令的输出。该数组包含了截图右侧 XML 模板中每个 XML 属性的

截图中的代码输出表明,用户 AtaBlogUserTargetUserName)在系统 Desktop-XXXXXWorkstationName)上发生了身份验证失败,使用的 IP 地址为 ::1IpAddress)。

Expected output correlating property values to Event ID 4625’s XML template.

也许你只想返回 TargetUserName 事件属性的值。由于你已经将所有事件属性存储在一个名为 $eventProperties 的变量中,可以引用第五个索引,该索引保存了 TargetUserName 的值。

你必须引用单个事件属性对象上的 value 属性,以仅返回该值(AtaBlogUser)。 $eventProperties[5].value

$eventProperties[5].value
Event attribute property positions

本节中描述的做法将在后续的部分中用于跟踪你在本文中模拟的暴力攻击。

检测暴力攻击

你现在准备使用你的 PowerShell 技能追踪之前在这个帖子中复制的暴力攻击!让我们通过模拟追踪一个基于特定时间范围的 暴力攻击,来测试一下你的技能。

假设你收到了一个警报,你的组织认为有人试图使用管理账户登录到一个重要的 Windows 服务器。这个活动是从昨天开始的。你需要发现过去24小时内发生的事件 ID 4625: 账户登录失败 的次数,并确定每个事件的登录类型。

1. 查找在 Windows 安全日志 (LogName="Security") 中过去 24 小时 (StartTime=((Get-Date).AddDays(-1).Date) 到当前时间 (Get-Date) 内的所有事件 ID 4625 (ID=4625) 的事件。

$events = Get-WinEvent -FilterHashTable @{LogName="Security";ID=4625;StartTime=((Get-Date).AddDays(-1));EndTime=(Get-Date)}

2. 现在,计算存储在变量中的所有事件,以确定失败的登录事件是否超过了预期的次数。

$events.Count

现在,你应该看到一个数字值,表示在过去 24 小时内在安全事件日志中找到的事件 ID 4625 的次数。

3. 所以,你已经确定发生了暴力攻击,现在追踪这些 Windows 安全事件的更多信息。为此,只返回你感兴趣的每个事件的属性。

如前所述,特定事件的每个值都存储在具有特定索引的数组中。此演示的有趣事件属性如下。

  • 目标用户名 索引:[5]
  • 登录类型 索引:[10]
  • 工作站名称 索引:[13]
  • IP地址 索引:[19]

下面的代码示例读取$events变量中的每个对象,仅收集有趣的属性,并将它们连接成一行。

#从过去24小时内Event ID 4625的所有实例中提取目标用户名、登录类型、工作站名称和IP地址事件属性。
 $events | ForEach-Object {
     ## 引用属性对象属性
     ## 仅从属性数组中返回索引5、10、13和19的值
     ## 通过用逗号连接它们将所有值合并在一起
     $_.properties[5,10,13,19].value -join ", "
 }

以下截图显示了代码预期输出的截断版本,详细说明了目标用户名登录类型工作站名称IP地址的逗号分隔列表。

A truncated version of the code’s output, detailing TargetUserName, LogonType, WorkstationName, and IpAddress property values.

4. 正如您之前从XML模板中看到的,事件ID 4625的模板具有登录类型属性。此属性指示帐户尝试进行身份验证的方法。通过进一步调查,您注意到登录类型在某些情况下是不同的。

LogonType attribute

LogonType值是一个从2到11的数字值,但是这代表什么意思呢?您进行了一些研究,并发现了每个值的含义。

2 – 交互式 – 用户登录到此计算机。

3 – 网络 – 用户或计算机通过网络登录到此计算机。

4 – 批处理 – 批处理登录类型用于批处理服务器,其中的进程可能代表用户而无需其直接干预。

5 – 服务 – 服务由服务控制管理器启动。

7 – 解锁 – 此工作站已解锁。

8 – NetworkCleartext – 用户通过网络登录到此计算机。用户的密码以其未散列的形式传递给身份验证包。所有内置身份验证包在将凭据发送到网络之前都会对其进行哈希处理。凭据不会以明文(也称为清晰文本)形式在网络中传输。

9 – NewCredentials – 调用者克隆了其当前令牌,并为出站连接指定了新凭据。新的登录会话具有相同的本地标识,但对其他网络连接使用不同的凭据。

10 – RemoteInteractive – 调用者克隆了其当前令牌,并为出站连接指定了新凭据。新的登录会话具有相同的本地标识,但对其他网络连接使用不同的凭据。

11 – CachedInteractive – 用户使用在计算机本地存储的网络凭据登录到此计算机。未联系域控制器以验证凭据。

现在你对每个LogonType都有了很好的了解,而不是在输出中看到一个数值,你想要一个更具描述性的字符串。要在PowerShell中创建“映射”,请使用散列表。

$logonTypes = @{
     [uint32]2 = "Interactive"
     [uint32]3 = "Network"
     [uint32]4 = "Batch"
     [uint32]5 = "Service"
     [uint32]7 = "Unlock"
     [uint32]8 = "NetworkCleartext"
     [uint32]9 = "NewCredentials"
     [uint32]10 = "RemoteInteractive"
     [uint32]11 = "CachedInteractive"
 }

5. 结合Get-WinEventLogonType散列表,使用ForEach-Object创建一个脚本,该脚本将仅返回你所需的属性,并带有用户友好的LogonType值,如下所示。 Format-Table cmdlet通过将PowerShell的响应格式化为表格,增加了用户友好的输出。

#使用Get-WinEvent访问事件ID 4625的每个已登录实例的属性
$events = Get-WinEvent -FilterHashTable @{LogName="Security";ID=4625;StartTime=((Get-Date).AddDays(-1).Date);EndTime=(Get-Date)}
## 创建数值值到字符串“映射”
$logonTypes = @{
    [uint32]2 = "Interactive"
    [uint32]3 = "Network"
    [uint32]4 = "Batch"
    [uint32]5 = "Service"
    [uint32]7 = "Unlock"
    [uint32]8 = "NetworkCleartext"
    [uint32]9 = "NewCredentials"
    [uint32]10 = "RemoteInteractive"
    [uint32]11 = "CachedInteractive"
}
## 开始处理$events数组中的每个对象
$events | ForEach-Object {
    ## 在散列表中查找数值值
    $logonType = $logonTypes[$_.properties[10].value] 
    #创建自定义的PowerShell对象以输出相关的事件属性
    [PSCustomObject]@{     
        TimeCreated = $_.TimeCreated     
        TargetUserName = $_.properties[5].value     
        LogonType = $logonType     
        WorkstationName = $_.properties[13].value     
        IpAddress = $_.properties[19].value 
    }
} | Format-Table -Wrap

到目前为止,你现在有一个能够返回PSCustomObject类型对象的脚本,使你能够执行许多不同类型的分析!要完成本教程的分析,通过TargetUserName优先处理身份验证失败的尝试。要通过Group-Object cmdlet将上述代码与Sort-Object及其Descending开关相结合,以识别最高违规用户。

#使用Get-WinEvent访问每个Event ID 4625的已记录实例的属性
$events = Get-WinEvent -FilterHashTable @{LogName="Security";ID=4625;StartTime=((Get-Date).AddDays(-1).Date);EndTime=(Get-Date)}
## 创建将数字值转换为字符串的“映射”
$logonTypes = @{
    [uint32]2 = "Interactive"
    [uint32]3 = "Network"
    [uint32]4 = "Batch"
    [uint32]5 = "Service"
    [uint32]7 = "Unlock"
    [uint32]8 = "NetworkCleartext"
    [uint32]9 = "NewCredentials"
    [uint32]10 = "RemoteInteractive"
    [uint32]11 = "CachedInteractive"
}
## 开始处理$events数组中的每个对象
$events | ForEach-Object {
    ## 在哈希表中查找数字值
    $logonType = $logonTypes[$_.properties[10].value] 
    #创建自定义PowerShell对象以输出相关事件属性
    [PSCustomObject]@{     
        TimeCreated = $_.TimeCreated     
        TargetUserName = $_.properties[5].value     
        LogonType = $logonType     
        WorkstationName = $_.properties[13].value     
        IpAddress = $_.properties[19].value 
    }
} | Group-Object -Property TargetUserName | Sort-Object -Property Count -Descending

做得好!您刚刚使用PowerShell检测了您在本文中模拟的暴力破解尝试。根据输出,AtaBlogUser 在过去的24小时内未能通过身份验证 30 次!

AtaBlogUser Logon Failures

下一步

在本教程中,您学到了Windows如何记录事件,如何为特定事件类型启用事件记录,以及如何构建一个PowerShell工具来查询这些事件。

有了您现在拥有的PowerShell脚本,您如何使它更好?您将如何利用今天学到的代码构建更好的工具?

Source:
https://adamtheautomator.com/windows-security-events/