长亭百川云 - 文章详情

服务排查脚本完善 | Windows 应急响应

NOP Team

66

2024-07-13

0x01 简介

大家好,我们是 NOP Team,在之前的应急响应相关的文章中,我们提到了服务的隐藏与排查,还有如何验证可执行文件是可靠的,在 《Windows 应急响应手册》中我们给出了进阶性排查的脚本,大概的排查方式就是将所有的服务对应的命令中的启动程序拿出来,检查该程序的签名是否为微软官方的有效签名。

在后期应急响应过程中,发现一个问题: Windows Server 2016 中这个服务对应命令字符串格式比较宽泛,但开发者的使用过程中并不一定遵守什么规范,就导致可能出现一些奇怪的格式,例如存在被双引号包裹的命令,也有没有被双引号包裹的,这部分在之前的脚本中已经特别处理了,真正遇到的问题是文件名称里带空格的,举个例子

这还只是文件名存在空格,更有甚者如下

遇到这类的服务命令,之前的脚本就会报错,今天这篇文章就是测试一下服务的具体执行情况,完善脚本

0x02 问题展示

之前的脚本如下

`$microsoftCNS = @('Microsoft Corporation', 'Microsoft Windows', 'Microsoft Windows Hardware Compatibility Publisher', 'Microsoft Update', 'Microsoft Windows Publisher')      # 定义函数来进行签名校验   function Verify-FileSignature {       param (           [Parameter(Mandatory=$true)]           [ValidateScript({Test-Path $_ -PathType Leaf})]           [string]$FilePath       )              if (Test-Path -Path $FilePath -PathType Leaf) {           $signature = Get-AuthenticodeSignature -FilePath $FilePath                      if ($signature.Status -eq 'Valid') {               $publisher = $signature.SignerCertificate.Subject                              # 解析发布者信息以提取 CN 字段的值               $cnValues = @(($publisher -split ', ' | Where-Object { $_ -like 'CN=*' }).Substring(3))                  if ($cnValues.Count -eq 1) {                   $cnValue = $cnValues[0]                   # Write-Output "CN 字段的值: $cnValue"                              # 判断 CN 字段是否为微软官方                   if ($microsoftCNS -contains $cnValue) {                       # Write-Output "CN 字段值为微软官方。"                       return "Valid"                   }                }            }               return "Invalid"          }               return "File Not Found"      }      $services = Get-WmiObject -Class Win32_Service | Where-Object { $_.PathName -ne $null }   foreach ($service in $services) {       $executablePath = $service.PathName       if ($executablePath.StartsWith('"')) {           $executablePath = $executablePath.Split('"')[1]       } else {           $executablePath = $executablePath.Split(' ')[0]       }             if ($executablePath -ne $null) {           $fileInfo = Get-Item -LiteralPath $executablePath           $result = Verify-FileSignature -FilePath $executablePath           if ($result -ne 'Valid') {               Write-Host "---------------------------------------"               Write-Host "Service Name: $($service.Name)"               Write-Host "Executable Path: $executablePath"               Write-Host "Signature Status: $result"               Write-Host ""           }       }   }   `

我们创建一个服务,服务对应的程序名称里放置一个空格,看看效果怎么样?

`sc create BindService binPath= "C:\Users\helper\Desktop\bi nd s.exe"   `

其中 bi nd s.exe 程序为 Metasploit Framework 生成的木马,传到测试服务器上并重命名为 bi nd s.exe

`msfvenom -p windows/meterpreter/bind_tcp lport=4477 -f exe-service -o bind.exe   `

当程序以服务的形式启动的时候,会自动监听 4477 端口

当前服务未启动,所以此时没有监听相关端口,现在启动该服务

服务启动后,我们的木马程序成功启动,监听 4477 端口,所以 Windows 是可以正确识别 C:\Users\helper\Desktop\bi nd s.exe 这种字符串的,并且可以正确找到要执行的程序

我们现在看一下原来的脚本检查过程中会发生什么?

可以看到,创建新的服务后,检查脚本会报错,报错为 Get-Item : 找不到路径“C:\Users\helper\Desktop\bi”,因为该路径不存在。

这显然是我们的脚本写的时候没有想到会有开发者设置服务的时候,文件名竟然会有空格,所以导致报错,我们直接修改一下,让其匹配完整路径就好了

0x03 问题分析

想让脚本正确匹配到完整路径并不容易,服务对应的命令字符串可能是如下格式

`C:\Windows\system32\lsass.exe   C:\Windows\system32\svchost.exe -k netsvcs   C:\Program Files (x86)\Parallels\Parallels Tools\Services\prl_tools_service.exe   C:\Users\helper\Desktop\bi nd s.exe   `

之前我们对路径做了处理,所以主要问题出现在文件名称以及参数上,如果是单文件名,那么很好办,直接用就可以了;如果遇到第二个这种带参数的,就比较难办了,所以之前是用空格做分隔,取第一个值,但现在出现了第四个这种,名称里带空格的,这种方法明显就有错误了,怎么办呢?

要不遇到空格就分组,之后不断拼接,直到找到程序?

例如下面的顺序寻找 C:\Users\helper\Desktop\bi nd s.exe ,直到找到一个有效可执行文件

  • C:\Users\helper\Desktop\bi

  • C:\Users\helper\Desktop\bi nd

  • C:\Users\helper\Desktop\bi nd s.exe

但这有一个问题,如果要执行的是 bi nd s.exe ,好巧不巧,该路径下刚好有一个叫做 bi 的文件,这种情况下是不是就会导致错误匹配,导致检查不准确呢?

等等,Windows 本身是如何识别包容性这么高的命令字符串的呢?我应该和 Windows 保持一致啊!

0x04 Windows 服务路径解析

说到 Windows 路径解析,大家可能一下子就想到了解析路径导致 DLL 劫持相关的内容,之后大家可能会想起 Windows 路径解析规则,但是我还是喜欢手动测试一遍

测试过程就不给大家展示了,直接说结论吧

当服务路径为  C:\Users\helper\Desktop\bi nd s.exe 时,Windows 服务查找的顺序为

  • C:\Users\helper\Desktop\bi

  • C:\Users\helper\Desktop\bi.exe

  • C:\Users\helper\Desktop\bi nd

  • C:\Users\helper\Desktop\bi nd.exe

  • C:\Users\helper\Desktop\bi nd s.exe

  • C:\Users\helper\Desktop\bi nd s.exe.exe

大家没有看错,Windows 并不是直接找完整路径,而是一点一点来的,而且对于服务程序来说,不需要后缀为 exe 也是可以执行的,而且 Windows 还会自动在文件名称后面加入 .exe 来匹配,也就是上面的 C:\Users\helper\Desktop\bi.exeC:\Users\helper\Desktop\bi nd.exeC:\Users\helper\Desktop\bi nd s.exe.exe

当服务路径为 C:\Users\helper\Desktop\t m p\bi nd s.exe 时的查找顺序如下:

  • C:\Users\helper\Desktop\t

  • C:\Users\helper\Desktop\t.exe

  • C:\Users\helper\Desktop\t m

  • C:\Users\helper\Desktop\t m.exe

  • C:\Users\helper\Desktop\t m p\bi

  • C:\Users\helper\Desktop\t m p\bi.exe

  • C:\Users\helper\Desktop\t m p\bi nd

  • C:\Users\helper\Desktop\t m p\bi nd.exe

  • C:\Users\helper\Desktop\t m p\bi nd s.exe

  • C:\Users\helper\Desktop\t m p\bi nd s.exe.exe

当目录中存在空格时,解析规则也是类似的,只不过目录名字以及目录名字.exe 不会解析

对于常规来说,上面的情况已经足够了,但是我们面对的是攻击者,一群想方设法逃避检测的人,我们需要再多探究一些,如果路径空格不只一个会怎么样?

C:\Users\helper\Desktop\bi   nd   s.exe 为例,这其中都是三个空格,首先看看能不能创建成功

看来创建没有问题,如果放置一个 bi   nd   s.exe 能够正常启动吗?

能够正常启动,功能没问题,现在问题来了,现在匹配的时候最先匹配的是 bi 还是 bi ,Windows图形化修改文件名称最后面添加空格是没用的,试试能不能通过命令行修改

通过命令行 ren 命令无法在文件名称后面加空格,试试 PowerShell

发现 ren 和 PowerShell 修改也没有用,在 Windows 中似乎文件名后面可以随便加空格

看起来这样似乎就与之前单空格的时候一样了,是这样吗?

还是有不少场景需要我们测试一下的

C:\Users\helper\Desktop\biC:\Users\helper\Desktop\bi.exe 是否可以执行?

经过测试,可以执行,顺序与之前一致

C:\Users\helper\Desktop\bi .exeC:\Users\helper\Desktop\bi  .exeC:\Users\helper\Desktop\bi   .exe 是否可以执行?

经过测试,均不可以执行

C:\Users\helper\Desktop\bi ndC:\Users\helper\Desktop\bi  ndC:\Users\helper\Desktop\bi   nd 是否可以执行?

经过测试,只有 C:\Users\helper\Desktop\bi   nd  (三个空格) 可以执行

最终测试查找顺序如下

  • C:\Users\helper\Desktop\bi

  • C:\Users\helper\Desktop\bi.exe

  • C:\Users\helper\Desktop\bi   nd

  • C:\Users\helper\Desktop\bi   nd.exe

  • C:\Users\helper\Desktop\bi   nd   s.exe

  • C:\Users\helper\Desktop\bi   nd   s.exe.exe

与一个空格顺序基本一致

0x05 检测脚本

经过测试,我们基本上已经搞清楚了 Windows 服务路径解析顺序,我们修改脚本,新的脚本如下:

`$microsoftCNS = @('Microsoft Corporation', 'Microsoft Windows', 'Microsoft Windows Hardware Compatibility Publisher', 'Microsoft Update', 'Microsoft Windows Publisher')      # 定义函数来进行签名校验   function Verify-FileSignature {       param (           [Parameter(Mandatory=$true)]           [string]$FilePath       )              if (Test-Path -Path $FilePath -PathType Leaf) {           $signature = Get-AuthenticodeSignature -FilePath $FilePath                      if ($signature.Status -eq 'Valid') {               $publisher = $signature.SignerCertificate.Subject                              # 解析发布者信息以提取 CN 字段的值               $cnValues = @(($publisher -split ', ' | Where-Object { $_ -like 'CN=*' }).Substring(3))                  if ($cnValues.Count -eq 1) {                   $cnValue = $cnValues[0]                              # 判断 CN 字段是否为微软官方                   if ($microsoftCNS -contains $cnValue) {                       return "Valid"                   }                }            }               return "Invalid"       }               return "File Not Found"   }      # 获取所有服务   $services = Get-WmiObject -Class Win32_Service | Where-Object { $_.PathName -ne $null }   foreach ($service in $services) {       $path = $service.PathName              # 去掉双引号       if ($path.StartsWith('"') -and $path.EndsWith('"')) {           $path = $path.Trim('"')       }          # 处理路径中包含空格的情况       $potentialPaths = @()       $parts = $path -split ' '       for ($i = 0; $i -lt $parts.Length; $i++) {           $potentialPath = ($parts[0..$i] -join ' ')                      if (Test-Path -Path $potentialPath -PathType Leaf) {               $potentialPaths += $potentialPath           } elseif (Test-Path -Path "$potentialPath.exe" -PathType Leaf) {               $potentialPaths += "$potentialPath.exe"           }              if ($potentialPaths.Length -ge 1) {               break           }       }          # 逐个验证找到的可执行文件路径       foreach ($potentialPath in $potentialPaths) {           $result = Verify-FileSignature -FilePath $potentialPath           if ($result -ne 'Valid') {               Write-Host "---------------------------------------"               Write-Host "Service Name: $($service.Name)"               Write-Host "Executable Path: $potentialPath"               Write-Host "Signature Status: $result"               Write-Host ""           }        }   }   `

新的脚本可以成功帮我们找到 Windows 服务解析顺序下的第一个文件,并检测其签名

当然,脚本检测没问题不代表真的没问题,毕竟可以白加黑嘛,如果攻击者利用微软签名的程序进行白加黑,那么检测签名也不会检测出来,而且微软官方的 DLL 文件也没有签名,不是很容易判断,如果后续有其他好方法,会再次完善脚本

往期文章

有态度,不苟同

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2