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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
ツール/GitHubGitHub/captain-woof/cve-2026-25243
脆弱性分析エクスプロイトポストエクスプロイトペネトレーションテストレッドチーミングデータベースセキュリティバイナリエクスプロイト
GitHubcaptain-woof/cve-2026-25243

CVE-2026-25243

CVE-2026-25243 的稳定 PoC(Redis RESTORE 双重释放 -> 远程代码执行)

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

人気

すべて見る →

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

すべてのツールを探索

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

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

CVE-2026-25243 — Redis RESTORE の double-free → リモートコード実行

Rocky Linux 8.10、aarch64、Redis 8.6.2、jemalloc 5.3.0 で検証済み。

リファレンス: https://www.zeroday.cloud/blog/redis-cve-2026-25243-deep-dive

TLDR; 安定したエクスプロイトで、さまざまな OS ディストリビューションとアーキテクチャで動作します。


エグゼクティブサマリー

これは何か? Redis におけるメモリ破損の脆弱性で、認証済みの攻撃者が Redis ユーザーとして任意のコマンドを実行できるようにします。この攻撃は現実世界で有効で、通常の Redis 操作であり管理者専用ではない、単一の RESTORE コマンドだけで成立します。このエクスプロイトは 1 秒未満で完全な RCE を実証します。

影響は? 認証済みの任意の Redis クライアントがトリガーでき、被害は全面的です: Redis プロセス内での任意コード実行 (コンテナ内では多くの場合 root で実行)。Redis 自体にパッチを当てない限り、緩和する方法はありません。

概要: どのように動作するのか? Redis には、バイナリデータのブロブを受け取り、それを Redis オブジェクトとして再構築するシリアライズ機能 (RESTORE) があります。ブロブの形式を 検証する コードと、それを デシリアライズする コードは、特定のシーケンスの解析方法について食い違いがあります — 攻撃者がヒープを破損させるために悪用するバグです。ヒープが破損すると、攻撃者は Redis プロセス内の任意のメモリアドレスを読み書きできるようになり、そこからサーバーの内部状態を乗っ取ってシェルコマンドを実行します。

実際のエクスプロイト技術: これは単純なクラッシュではありません。これはヒープエクスプロイトチェーンです: 破損 → オーバーラップ → 任意 R/W → 情報漏えい → server 構造体の発見 → 関数ポインタの乗っ取り → RCE。エクスプロイトは 9 つのステージで構成され、実行時に複数のアドレスをリークし、バイナリ構造を解析し、メモリエイリアシングを検出する必要があります。アーキテクチャ (x86-64、aarch64 など) を超えて動作する理由は、すべてのアドレスが推測ではなくターゲット自身からリークされることです。


1. 脆弱性 — 詳細

CVE-2026-25243 は、単一の認証済み コマンドから到達可能な バグのペアです。 は攻撃者が制御する RDB ブロブをデシリアライズします。両方のバグは、ブロブを検査する と、それを実体化する の間のギャップに存在します。

RESTORE
double-free
RESTORE key ttl <serialized-value>
バリデータ
コンバータ

バグ 1 — レガシー zipmap 変換 (CWE-415、このエクスプロイトが使用する経路)。 zipmap バリデータ (zipmapValidateIntegrity()) とコンバータ (zipmapNext()) は、冗長な長さエンコーディングについて食い違います。小さな長さ 4 は、正当に 5 バイトの長い形式 FE 04 00 00 00 で書くことができます。バリデータはあるバイト数を消費し、コンバータは別のバイト数を消費します — 4 バイトの解析同期ずれです。そのためコンバータは、検証された構造とは異なる構造を走査し、フィールドが辞書に挿入された後に lpSafeToAdd() が失敗し、クリーンアップパスがフィールドを 2 回解放します: 1 回は dictRelease() 経由、もう 1 回は sdsfree() 経由です。

バグ 2 — stream コンシューマー PEL のロード (CWE-415)。 rdbLoadStreamConsumersGroup() では、重複したエントリ ID を含むコンシューマー PEL により、2 回目の raxTryInsert() が失敗し、グループのグローバル PEL がまだ所有している streamNACK に対して streamFreeNACK() が呼び出されます。2 回解放されます。(--vuln-type stream で選択可能。)

どちらのバグも、攻撃者に「解放されつつ参照もされている」メモリチャンクを与えます — ヒープオーバーラップエクスプロイトの古典的な出発点です。

影響: 認証済みの Redis クライアント (管理者権限は不要、RESTORE は通常のデータコマンド) が、redis ユーザーとして任意コード実行を獲得します — デフォルトのコンテナイメージでは root です。

2. エクスプロイトの仕組み

9 つのステージ。それぞれが弱いプリミティブをより強いものに変換します:

ステージ獲得するプリミティブメカニズム
0ターゲットプロファイルINFO server / INFO memory → バージョン、アーキテクチャ、ディストリビューション、pid、実行ファイルパス、開始時刻、アロケータ
1double free不正な zipmap (または stream) による RESTORE
2メモリを共有する 2 つのキー解放されたチャンクにマーカーキーをスプレーし、エイリアシングを検出してから、一方のキーの SDS ヘッダーをその双子を介して上書きし、1 MB の「memview」に拡張
3任意 R/Wmemview 内で INCRBYFLOAT オブジェクトを見つけ、その ptr フィールドを乗っ取る: そのキーに対する GETRANGE/SETRANGE が任意のアドレスを読み書き可能に
4イメージポインタredis-server イメージ内の値を探すためにヒープを逆方向にスキャン
5&serverELF ヘッダーまで下り、プログラムヘッダーを解析し、書き込み可能セグメントをダンプし、server.pid と照合
6メモリ内のペイロード"/bin/sh", "-c", "<cmd>" と argv 配列を memview に書き込み
7乗っ取られた構造体server.executable、server.exec_argv、server.enable_debug_cmd を上書き
8RCEDEBUG CRASH-AND-RECOVER → restartServer() → execve(server.executable, server.exec_argv, environ)

トリガー方法

root@kitploit:~
python3 exploit.py --host 127.0.0.1 --port 6379 \
    --password mypassword --cmd 'id > /tmp/pwned123.txt'

確認:

root@kitploit:~
cat /tmp/pwned123.txt
# uid=0(root) gid=0(root) groups=0(root)

3. 変更履歴

2026-08-06 — 移植性・信頼性・速度の再調整

当初の状態: エクスプロイトは x86-64 専用で、aarch64 ターゲットではステージ 3 で失敗しました。最終状態: aarch64 Rocky Linux 8.10 で 1 秒未満、116 の Redis コマンドによる完全な RCE。

a) 実行時ターゲットフィンガープリンティング (新規、ステージ 0)。 ターゲットについて何も推測しなくなりました。INFO server + INFO memory により、Redis バージョン、CPU アーキテクチャ (os: 行から)、ディストリビューション系 (gcc_version から推測)、アロケータ、そして最も重要な 3 つの検証アンカー (process_id、executable、正確な stat_starttime (server_time_usec/1e6 - uptime_in_seconds)) が得られます。以降のステージは推測ではなく、これらと照合します。

b) アーキテクチャに依存しないメモリレイアウト。 ハードコードされた 4 つの x86-64 定数 (BINARY_ADDR_MIN/MAX、HEAP_ADDR_MIN/MAX) は、x86_64、aarch64 (39 ビットと 48 ビットの両方の VA)、riscv64、ppc64le、s390x をカバーし、それぞれに ET_EXEC と ET_DYN の両方の配置を持つアーキテクチャ別テーブル (ARCH_PROFILES) に置き換えられ、リストにないものには幅広い汎用フォールバックも用意されています。これが実際にこのターゲットでエクスプロイトが失敗した理由です: リークされたポインタ 0x0000ffff8a5fdf32 は x86-64 の範囲チェックで拒否された、完全に正当な aarch64 の mmap アドレスでした。

c) コンセンサスベースのリーク検証 (ステージ 3)。 ハードコードされたヒープウィンドウを信頼する代わりに、スキャンは memview 内の構造的に有効な 1337.NNNNNN オブジェクトをすべて収集し、そのうち少なくとも 2 つが同じ memview ベースアドレス (ptr - offset_of_value) を導出することを要求します。実際には 502 個の候補が一致し、これはレンジテーブルでは提供できない証明です。確認されたポインタは、実行時にヒープウィンドウを校正します。また、形式検証は (ラウンドトリップコストの高い) 書き込み制御テストの前に移動されました。

d) ステージ 3 のスキャン境界 (バグ修正)。 スキャンはハードコードされた 10 MB まで実行されましたが、memview は 1 MB なので、末尾を越えて読み取り、空の応答を受け取り、AssertionError: Empty data from memview で中断しました。現在は memview の実際の STRLEN に制限され、1 ラウンドトリップあたり 64 KB ではなく 256 KB を読み取り、無意味だった 6×1 秒のリトライスリープループは廃止されました。

e) ステージ 5 を書き直し: ELF ガイド方式・クラッシュフリー (最重要)。 旧実装はイメージポインタから前方にスキャンし、アドレスをプローブして、ガベージ SDS ヘッダーが主張する長さだけ読み取っていました。このターゲットでは、読み取り専用セグメントの末尾から 0x715000 の未マップの穴へまっすぐ進み、サーバーを停止させました (getrangeCommand → memcpy 内で SIGSEGV)。盲目的なスキャンを安全にすることはできません。置き換えは決定的です:

  1. イメージベースを見つける。 最も低いリークされたイメージポインタからページ単位で下って行きます。プローブは無料です: すべての ELF64 イメージの最初の 5 バイトは 7f 45 4c 46 02 であり、sdslen() は ptr[-1] からフラグバイトを取得するため、乗っ取ったオブジェクトを base+5 に向けると、e_ident[EI_CLASS]=0x02 がフラグバイト、つまり SDS_TYPE_16 になり、その長さは base+0 の uint16 = 0x457f (0x7f45 ビッグエンディアン) になります。正確に 17791 の STRLEN は、ELF シグネチャそのものです。バイナリのローカルコピーは不要です — ヘッダーはターゲット自身のメモリから読み出されます。
  2. プログラムヘッダーを解析して、すべての PT_LOAD セグメントの正確なランタイム境界を取得します (PIE ターゲットの ET_DYN ロードバイアスを処理)。以降のすべての読み取りは実際のマッピングにクランプされるため、未マップ穴でのクラッシュは構造的に不可能になりました。
  3. 1 つの SDS ヘッダーを偽造します。書き込み可能セグメントのゼロ化されたスロットに偽造することで、数十万回のバイトプローブの代わりに、数回のラウンドトリップで .data/.bss 全体を読み取り可能にします。上書きされたバイトは保存され、復元されます。
  4. server.pid を INFO の pid と照合します — 正確な 8 バイトの等価性テスト — その後、server.executable をデリファレンスし、その文字列を INFO の executable と比較して確認します。旧コードは緩い 7 フィールドの形状ヒューリスティックを受け入れていました。構造体は現在、確実に特定されます。

f) ステージ 4 の強化。 Lua バリデータは完全なアーキテクチャ別レンジリストを受け取り (0x400000 の非 PIE イメージと 0xaaaa… の PIE イメージの両方が認識されます)、校正済みのヒープウィンドウを除外します。1 つではなく複数の候補を返すため、不適切な選択は実行全体ではなくリトライで済みます。

g) ステージ 7 の自己検証。 enable_debug_cmd はハードコードされた stat_starttime - 0x3c で特定されていました。現在は、期待される stat_starttime 値が INFO から正確にわかっており (30 日ではなく 3 秒のウィンドウ)、構造体の読み取りウィンドウは 4 KB から 32 KB に拡大され (stat_starttime はオフセット 0x9e0 にあり、旧制限をはるかに超えています)、そして決定的なことに、各候補オフセットはライブオラクルで検証されます: バイトを設定し、DEBUG SET-ACTIVE-EXPIRE 1 を送信し、サーバーがそれを受け入れるかどうかを確認します。誤った推測は次の試行の前に復元されるため、フラグは推測ではなく任意のビルドで見つかります。-0x3c は依然として最初に試行され、8.6.2 (オフセット 0x9a4) で正しいことが確認されています。

h) 書き込みが実際に到達する (ステージ 5)。 setrangeCommand() は dbUnshareStringValue() を呼び出します。これは encoding == RAW && refcount == 1 でない限り値を複製します。乗っ取ったポインタを通した最初の書き込みの前にエンコーディングバイトがゼロ化されるため、書き込みはプライベートコピーではなくターゲットアドレスに到達します。

i) ペイロードの簡素化。 バックコネクト/リバースシェルの機構、ASCII バナー、追加されていた ;sleep 5 はすべて削除されました。ペイロードは正確に /bin/sh -c '<--cmd>' のみです。--cmd のデフォルトは id > /tmp/pwned123.txt です。

j) 速度。 ステージ 4 は 8 つではなく 3 つの候補を収集します。ステージ 5 は約 10^5 回のバイトプローブを約 40 回のバルク読み取りに置き換えます。ステージ 3 は 256 KB の読み取りを使用し、ローカル検証に失敗した候補のラウンドトリップをスキップします。チェーン全体: 116 コマンド、<1 秒。

結果: ターゲットコンテナ上の /tmp/pwned123.txt に uid=0(root) gid=0(root) groups=0(root)。

移植性に関する注意

  • アーキテクチャは推測ではなく検出されます。ELF ベースのステージ 5 は設計上アーキテクチャに依存せず (ターゲット自身のプログラムヘッダーを読み取ります)、PIE と非 PIE の両方のイメージ、リトルエンディアンとビッグエンディアンの両方を処理します。
  • ディストリビューションはオペレーターの便宜のために報告されます。エクスプロイトはそれに機能的な依存関係を持ちません。唯一のファイルシステム前提は /bin/sh であり、これは POSIX と FHS が要求するものです。
  • バージョン: RDB バージョンと stream 構造体のサイズは redis_version から選択されます (7.x と 8.x をサポート)。構造体フィールドのオフセット (executable=24、exec_argv=32) は LP64 ABI に従い、enable_debug_cmd はハードコードではなく実行時に発見・検証されます。
  • 32 ビットターゲットはステージ 0 で明示的に拒否されます (ペイロードは 64 ビットポインタを構築するため)。後で不明瞭に失敗する代わりです。

2026-08-06 (後半) — 反復実行テスト後の安定性強化

デフォルトの zipmap 経路で 13/13 回の実行が成功 (5 回 + 8 回の連続実行) し、各実行は**≤1 秒**で完了しました。反復実行でのみ表面化した 3 つの問題が現在修正されています:

k) ステージ 0 の SAVE レース。 前回の実行 (または redis 自身) のバックグラウンドセーブがまだ実行中の場合、実行が ERR Background save already in progress で中断される可能性がありました。現在は SAVE を最大 15 秒間リトライし、それでも失敗した場合は中断する代わりにチェックポイントなしで実行を続行します。

l) ターゲット再起動中の再接続 (--connect-retries、デフォルト 10)。 失敗した試行はヒープを破損したままにするため、次の実行の FLUSHALL が汚染されたチャンクを解放し、サーバーを停止させます。サーバーは数秒後に再起動し、完全にエクスプロイト可能な状態になるため、ステージ 0 は失敗する代わりに再接続してリトライします。自らの検証エラー (未サポートのバージョン/アーキテクチャ) は決してリトライされません。これにより、ストレステスト中に約 3 回に 1 回の割合で見られた断続的な「stage 0 failed with an empty error」が解消されました。

m) 8.6.x での sizeof(streamNACK) の修正。 --vuln-type stream 経路は、構造体が 24 または 32 バイトであると想定されていたため、誤った jemalloc サイズクラスをスプレーしていました。8.6.2 では 64 バイトです (delivery_time、delivery_count、consumer、cgroup_ref_node、streamID id、pel_prev、pel_next)。正しいサイズにより、stream 経路は「key overlap not found」でステージ 2 で失敗する代わりに、ステージ 5 に到達します。

既知の制限

  • --vuln-type stream は 8.6.2 では信頼できません。 サイズ修正により、double-free、オーバーラップ、R/W プリミティブ、ELF 解析を通過しますが、その後キースペースが不安定になります: サーバーは setrangeCommand で NULL+8 の o->ptr を読み取る際に停止します。つまり、キールックアップが破損したオブジェクトを返します。解放する 64 バイトのチャンクは他のライブアロケーションと共有されるため、zipmap 経路よりもはるかに巻き添えが多くなります。13/13 の成功率を持つデフォルトの --vuln-type zipmap を使用してください。
  • --random-heap-massage (最初に 10 万個のランダムキー) は成功しますが、常にではありません — スプレーされたヒープは、マーカーキーが置かれない場所に double-free されたチャンクを配置することがあります。再実行すれば成功します。
  • 検証済みなのは aarch64 / Rocky Linux 8.10 / Redis 8.6.2 (非 PIE ET_EXEC) のみです。x86-64 と PIE の経路は実装されており、設計上アーキテクチャに依存しませんが、今回のセッションでは実ターゲットに対して実行されていません。
ツールをダウンロード