Hopp til hovedinnhold
Erik Nilsen
4 min lesing

Da WordPress holdt, men e-posten åpnet døren

  • #wordpress
  • #pentest
  • #email-security
  • #dmarc

FIKTIVT: Alle firmanavn, personnavn, domener og e‑postadresser i dette innlegget er oppdiktet. Eventuell likhet med ekte virksomheter eller personer er tilfeldig. Detaljer fra reelle oppdrag er sanert.

Jeg fikk i oppdrag å teste nettstedet til Måneskinn AS (maneskinnas.no) – en WordPress-side på managed hosting. Jeg gikk inn med forventning om å finne en sårbar plugin et sted. Det gjorde jeg ikke. Web-laget holdt. Det som ikke holdt, var e-posten – og det var nok til å ta over hele nettstedet på papiret.

Kontekst

Black-box-test av én WordPress-side. Målet var det vanlige: finne lekkasje av kundedata, credentials, og «har vi bugs». Siden lå bak en CDN med WAF, på managed WordPress-hosting, og brukte en typisk stack med Elementor, JetEngine og noen addons.

Arbeidsmåten min er moderne pentest med AI i loopen: jeg styrer retning, scope og prioritering, og lar AI-en gjøre selve gravingen – skrive scripts, kjøre sweeps, prøve angrepsvektorer og lese output. Jeg plukker ut det interessante og bryter inn der det trengs menneskelig dømmekraft.

Det jeg fant

Web-laget var kjedelig solid

Jeg lot AI-en kjøre wpscan og nuclei mot siden, og krysse pluginversjoner mot CVE-databasene. Alt var på nyeste, patchede versjon. De interessante endepunktene fantes – men var stengt:

  • Code Snippets-pluginen (kjører vilkårlig PHP) hadde REST-endepunkter bak auth:
POST /wp-json/code-snippets/v1/snippets
-> 401 rest_forbidden
  • Jeg ba AI-en prøve klassisk uautentisert filopplasting og LFI gjennom de vanlige Elementor/JetEngine-AJAX-actionene. Traversering ble stoppet av WAF-en, og JetEngine-versjonen var patchet mot den uautentiserte SQL-injeksjonen som ellers hadde vært en åpning.
  • Brute-force mot login traff en rate-limiter etter tre forsøk. Jeg stoppat der – å hamre videre ville bare låst ut den ekte brukeren.

Dette er sånn et nettsted bør se ut: oppdaterte komponenter, WAF, rate limiting, stengte REST-endepunkter. Ingen rød tråd å trekke i på web-laget.

REST API-et delte litt for villig

Det første som sprakk var brukerlisten. Standard WordPress REST-endepunkt, uautentisert:

curl -s https://maneskinnas.no/wp-json/wp/v2/users
[
  {"id": 1, "name": "Kari Berg", "slug": "kariberg"},
  {"id": 4, "name": "[email protected]", "slug": "kariberg-maneskinnas-no"}
]

Der har jeg både et gyldig brukernavn og administrators e-postadresse. Bruker 4 hadde til og med e-posten som offentlig visningsnavn.

Ingen andre faktor

AI-en sjekket etter de vanlige MFA-pluginene (Two-Factor, Wordfence Login Security, miniOrange og flere). Ingen treff. Admin-pålogging så ut til å være passord alene. Isolert sett dempet rate-limiteren dette – men jeg hadde nå en e-postadresse, et brukernavn, og ingen andre faktor i veien.

E-posten var den åpne døren

Jeg fikk AI-en til å slå opp e-postoppsettet for domenet. SPF var stramt (-all), men DMARC fortalte en annen historie:

SPF    : v=spf1 include:spf.protection.outlook.com -all
DMARC  : v=DMARC1; p=none;
DKIM   : ingen selector publisert

p=none betyr at mottakere ikke avviser forfalsket e-post – policyen overvåker bare. Kombinert med manglende DKIM er domenet i praksis spoofbart. For å bekrefte det skrev AI-en et lite Python-script som koblet seg rett mot mottakerens MX og sendte en melding med forfalsket avsender:

import smtplib
from email.message import EmailMessage
 
msg = EmailMessage()
msg["From"] = "[email protected]"   # forfalsket
msg["To"] = "[email protected]"
msg["Subject"] = "[PENTEST PoC] Spoof (DMARC p=none)"
msg.set_content("Autorisert pentest – proof of concept. Ikke sendt av Maneskinn AS.")
 
with smtplib.SMTP("mx.eksempel.no", 25, timeout=40) as smtp:
    smtp.ehlo("mail.pentest-poc.example")
    smtp.send_message(msg)

Mottakerens server tok imot den uten å blunke:

-> MAIL FROM:<[email protected]>
<- 250 2.1.0 Sender OK
-> RCPT TO:<[email protected]>
<- 250 2.1.5 Recipient OK
<- 250 2.6.0 Queued mail for delivery

Avsender-IP-en var ikke i SPF-posten, det fantes ingen DKIM-signatur, og DMARC-justeringen feilet. Likevel: levert. På en Microsoft 365-mottaker havnet den i innboksen, ikke i søppelpost. Det var poenget. En forfalsket e-post fra [email protected] lander der folk faktisk leser den.

Angrepskjeden

Ingen av delene er spektakulær alene. Til sammen er de en vei inn:

1. Hent admin-epost   ->  /wp-json/wp/v2/users
2. Forfalsk avsender  ->  DMARC p=none, ingen DKIM  ->  innboks
3. Phishing-lenke     ->  falsk wp-admin / M365-login  ->  passord fanges
4. Ingen MFA          ->  passordet alene gir admin
5. Admin              ->  Code Snippets  ->  vilkårlig PHP på serveren

Funn: High for e-postforfalskningen, Medium for brukerenumerering og manglende MFA hver for seg. Den patchede WordPress-siden ble aldri brutt – kjeden går via identitet og menneske.

Et nettsted kan være teknisk solid og likevel være enkelt å ta over, hvis e-postdomenet kan forfalskes og kontoene mangler MFA. Web-skanneren ser ikke den kjeden.

Hva jeg lærte

  • Ikke stopp på web-laget. Den beste veien inn var DNS og e-post, ikke en plugin-CVE. Sjekk alltid SPF/DKIM/DMARC som en del av en webtest.
  • p=none er ikke «DMARC er på». Det er overvåkning. Uten enforcement og DKIM er domenet spoofbart.
  • Brukerenumerering er drivstoff. Et lekket brukernavn og en e-postadresse gjør phishing presist.
  • MFA er det billigste laget som faktisk stopper kjeden. Hadde det vært påtvunget, stoppet et fanget passord ved andre faktor.
  • La AI gjøre sweepene, ta selv vurderingen. AI-en fant de tekniske trådene fort; prioritering, scope-grense (jeg stoppet brute-force) og selve angrepskjeden er min vurdering.

Veien videre

Anbefalingene til kunden var enkle og rimelige: DMARC til p=quarantine og deretter p=reject, aktiver DKIM, legg på rua-rapportering, påtving MFA på admin, og steng brukerenumereringen i REST. Det kollapser hele kjeden. Web-laget trengte de knapt å røre – det var aldri problemet.