ケーススタディ·DownUnderCTF2023

で旗を見つける
CTFバイナリ。

REAを使用して、小さなフラグチェッカーを検査し、そのルールを方程式に変換し、元のプログラムを印刷する入力を見つけます Correct!.

マスクされた正方形の旗チェッカー*簡単·Linux x86-64

オリジナルのチャレンジを見る

バイナリからチェックされた答えへ

  1. 01*チェックを見つける文字列→関数のチェック REAは、プロンプト、その参照、および入力を受け入れるか拒否するコードを検索します。
  2. 02*ルールを抽出する36文字·26和 REAは、各チェックの背後にある命令とデータを返します。 エージェントはそれらを方程式に変えます。
  3. 03*解決して実行する回収フラグ→正解!Pythonスクリプトは方程式を解きます。 REAは、結果を受け入れる元のプログラムをキャプチャします。
課題はjosephによるもので、DownUnderCTFの公式リポジトリに掲載されています。

チェックコードを見つける

配布資料は15KBの実行可能ファイルです。 それはフラグを要求し、次に印刷します Correct! または Incorrect!. あなたの代理店への簡単な要求から始めなさい。

あなたのコーディングエージェント

REAを使用してms_flag_checkerを分析し、フラグを見つけます。 チェックがどのように機能するかを説明し、元のプログラムで答えを確認します。

ローカルで利用可能なダウンロードされたチャレンジと接続されているマシンコードのプログラムプロバイダーの分析との例のプロンプト、。

  1. その機能に入力プロンプトに従ってください

    エージェント→REA
    open_binary
    {"path": "ms_flag_checker"}
    
    search_strings
    {"pattern": "flag|correct|wrong|input",
     "mode": "regex", "case_sensitive": false}
    
    xrefs
    {"address": "0x102004"}
    
    resolve_containing_procedure
    {"address": "0x10126d"}
    REA→エージェント*選択された結果
    0x102004  "What is the flag? "
    0x10201c  "Incorrect!"
    0x102027  "Correct!"
    
    Prompt referenced at: 0x10126d
    Containing function: 0x10124d

    この参照は、実行可能ファイルの元の関数名が削除されていても、エージェントに調査する場所を提供します。

  2. チェッカーとその2つのヘルパーを読む

    エージェント→REA
    analyze_function
    {"procedure": "0x10124d"}
    
    analyze_function
    {"procedure": "0x101189"}
    
    analyze_function
    {"procedure": "0x101217"}
    REA→エージェント*選択された指示
    0x1012c1: CMP EDI, 0x24
    0x1012c6: LEA RBX, [0x1040e0]
    0x1012cd: LEA RBP, [0x104060]
    0x1012d4: LEA R13, [RBX + 0x270]
    0x1012e1: CALL 0x00101189
    0x1012ec: CALL 0x00101217
    0x1012f1: CMP dword ptr [RBP], EAX
    0x1012f4: JNZ 0x00101335
    0x1012f6: ADD RBX, 0x18

    最初のループは36文字コードをコピーします。 チェックループはマスクをデコードし、選択したコードを合計し、結果を格納された番号と比較します。 その 0x18-バイト-ステップ-アクロス 0x270 バイトは26個のマスクを与えます。

これらは、新鮮なREA分析から選択された結果です。 アドレスはREAのインポートされたイメージを参照します。

一つのチェックは、一つの文字を明らかに

チェッカーは入力を6×6グリッドに配置します。 マスクは、どのセルが合計に寄与するかを選択します。 そのコンパクトなエンコーディングは、負の数を使用してセルをスキップし、正の数を使用してセルを選択します。

第七のマスクは、21個のセルをスキップし、位置21を選択し、次に14をスキップします。 そのセルだけが合計に貢献します。 必要な文字コードは55で、文字が7であることを意味します。
7番目のチェックでは、1つの位置のみが選択されます。 オープンフィギュア.

バイナリからマスクとそのターゲットを読み取ります

エージェント→REA*read_bytes
{"address": "0x1040e0", "length": 624}
{"address": "0x104060", "length": 144}
REA→エージェント·セブンスエントリ
Mask at 0x104170:
eb 01 f2 00  →  -21, +1, -14, stop

Target at 0x104078:
37 00 00 00  →  55

命令 MOVSX マスクバイトを符号付き値として読み取ります。 これが理由です eb -21を意味します。 ターゲットはリトルエンディアンの整数です。 一緒に、これらのバイトはエージェントに次のことを伝えます code[21] = 55、その文字は 7.

指示から読み取り可能なチェックまで

加算ヘルパーとその呼び出し元をCの同じルールに接続するステップを選択します。

REA*選択されたオリジナルの指示
0x10121c: MOV ECX, 0x0

0x10122c: CMP dword ptr [RSI + RAX*0x1], 0x0
0x101230: JZ 0x00101223

0x101232: ADD ECX, dword ptr [RDI + RAX*0x1]

0x101223: ADD RAX, 0x4

0x1012f1: CMP dword ptr [RBP], EAX
0x1012f4: JNZ 0x00101335
読み取り可能なC*ワンチェックの概要
int check_one_mask(const int codes[36],
                   const int mask[36],
                   int target) {
    int total = 0;
    for (int i = 0; i < 36; ++i) {
        if (mask[i]) {
            total += codes[i];
        }
    }
    return total == target;
}

01*ゼロから開始します。 ECX 実行中の合計を保持します。

命令は、加算ヘルパーとその呼び出し元から来ています。 Cは、説明的な名前と平坦化された36セルループを持つ説明的な要約です。

26の方程式を解く

ほとんどのマスクは複数のセルを選択します。 たとえば、最初のチェックでは16個の文字コードが追加され、合計1441個が必要です。 重複する選択は、小さなPythonスクリプトが一緒に解くことができる方程式を与えます。

Python*ソルバーの抜粋
from z3 import Int, Or, Solver, Sum, sat

codes = [Int(f"code_{i}") for i in range(36)]
solver = Solver()
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_{}"
for code in codes:
    solver.add(Or(*[code == ord(c) for c in alphabet]))
for code, char in zip(codes, "DUCTF{"):
    solver.add(code == ord(char))
solver.add(codes[-1] == ord("}"))

for selected, target in checks:
    solver.add(Sum([codes[i] for i in selected]) == target)

assert solver.check() == sat
model = solver.model()
print("".join(chr(model.eval(code).as_long()) for code in codes))

Z3は、これらの方程式を満たす値を見つけるソルバーです。 この抜粋は、抽出されたチェックを使用し、aを前提としています DUCTF{…} 文字、数字、アンダースコアと中括弧を持つフラグ。 完全なダウンロードには、26個のマスクとターゲットすべてが含まれています。

完全なソルバーをダウンロードする

元のプログラムで答えを確認してください

REAのプロセスキャプチャは、解決されたフラグを受け入れる元の実行可能ファイルを記録します。 から選択した文字を変更する z に y 最初の合計を1441ではなく1440にし、プログラムはそれを拒否します。

入力 観測された出力 終了コード
ソルバーの旗 Correct! 0
一つの文字が変更されました Incorrect! 255
回収されたフラグとキャプチャされた出力を表示する
REA•オリジナルプログラム出力
What is the flag? DUCTF{ezzzpzzz_07bcda7bfe81faf43caa}
Correct!

回復された値は、バイナリを解決した後にチェックされた、主催者の公開されたチャレンジメタデータのフラグとも一致します。

ソース、ターゲットの識別情報および分析の詳細

2026年10月8日に、REA4.1.0とGhidra12.1.4をLinux x64で使用して、新鮮な分析と2つのプロセスキャプチャを行いました。 バイナリは削除されたx86-64ELF、15,248バイトです。

オリジナルの実行可能ファイル·SHA-256
dcd3bec4f608e11f3a12ce461100aaf5f621851ee63a1f870d7c90d4e3dd51ae

文字列参照、関数、定数は、主催者のソースまたはソリューションを読み取る前に検査されました。 Pythonソルバーとダイアグラムは、これらのREA結果から構築されました。

主催者のソースと比較する · チャレンジメタデータとフラグ · REA証拠ノート

自分で試してみてください

元のチャレンジとソルバーを1つのフォルダにダウンロードします。 上記のプロンプトを使用してエージェントに調査するか、含まれているスクリプトを実行します。

ターミナル*解決して実行
python3 -m venv .venv
.venv/bin/python -m pip install z3-solver
.venv/bin/python solve.py

chmod +x ms_flag_checker
./ms_flag_checker

スクリプトは候補フラグを出力します。プログラムのプロンプトに貼り付けます。 ニーズを解決するPythonと z3-solver. 元の実行可能ファイルを実行するには、Linux x86-64が必要です。

コーディングエージェントでREAをセットアップする

トップ