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 publisertp=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 deliveryAvsender-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å serverenFunn: 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=noneer 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.