# GhostLoot part 2: the Microsoft cookie mess, the inbox, and the Google wall

Part 1 got the loot out of Evilginx's session list. Part 2 is what I learned the hard way after that: which Microsoft cookie is actually money, which "TTL" is a fiction, how the panel had to stop being a spreadsheet, and why a stock Google phishlet will waste your week.

Evilginx 3.3.0 Community. GhostLoot 1.6.1. Still authorized work only. Still a read-only viewer.

## The Tuesday that looked like a broken tool

Smoke-test. Personal Microsoft account into an o365-shaped phishlet. Capture comes back. I open the cookies. `MSPAuth: Disabled`. Or worse, a stub that looks like a session until you stare at it.

First thought: GhostLoot is wrong. It wasn't. Microsoft never issued a reusable session on the path I was on.

"Microsoft login" is two IdPs sharing a brand.

## Entra is not MSA

**Entra ID** is work and school. Host: `login.microsoftonline.com`. The cookies that matter are `ESTSAUTH` (non-persistent: about 24 hours or until the browser closes) and `ESTSAUTHPERSISTENT` (persistent SSO: up to 90 days of inactivity, rolling, until revoked). Replay means import on `https://login.microsoftonline.com`, then open `https://www.office.com`. Outlook on the web is Entra territory. A personal `MSAAUTHP` will not SSO you into it.

**MSA** is personal Microsoft accounts — including a Gmail you registered as a Microsoft account, including passwordless Authenticator-only. Host: `login.live.com`. For passwordless, the cookie that matters is `__Host-MSAAUTHP`. It persists across browser close. Microsoft does not publish a Max-Age. Anyone quoting "one year" is guessing. `__Host-MSAAUTH=11` is a deleted stub, not loot. Password MSA can show `__Host-MSAAUTH` with a real long value; that one is not persistent.

Replay for MSA: import on `https://login.live.com`, then `https://account.microsoft.com`. Avoid `https://outlook.live.com` — you do not get SSO from `MSAAUTHP` there.

Throw a personal account at an Entra-wired phishlet and Microsoft quietly routes you into the consumer stack. Different hosts. Different cookies. The proxy collects flow junk. The session never closes cleanly. You get `Disabled`. That is a coverage problem, not "AiTM fails on personal accounts."

I ended up with one `microsoft.yaml` covering both stacks, redirect-on-capture after loot. Separate o365-only files for personal accounts were the wrong shape.

## The stock Evilginx completion lie

Community Evilginx has a footgun: `AllCookieAuthTokensCaptured` wants **all** listed tokens.

A live MSA capture will never carry `ESTSAUTH`. A live Entra capture will never carry `MSAAUTHP`. So the session never "completes," even when you are holding the one cookie that actually replays.

The patch that made ops sane: treat the session as captured when **any** required non-junk token is present.

Junk that must never count as loot: empty, `Disabled`, `estsfd`, `11`, length ≤ 2.

## The name-fold bug that refused to match

Go lowercases cookie names. `strings.ToLower("__Host-MSAAUTHP")` becomes `__host-msaauthp` — two a's in `msaa`.

I wrote a switch case as `__host-msauthp` — one a. It never matched. The panel kept calling good MSA loot invalid while I stared at a cookie that was sitting right there.

Tests that earned their keep: PasswordlessMSA, PasswordMSA, TestMSCookieNameFold. If you fold names, count the a's.

## The TTL lie

Evilginx's `CookieToken` does not store `Set-Cookie` Expires. So any countdown you invent in a UI is fan fiction.

GhostLoot's panel only shows kinds it can defend: `parsed` (JWT `exp` when the cookie actually is a JWT), `idle90d`, `idle24h`, `session`, `persistent`, `unknown`. No `now+1y`. No fake 89-day ticker.

TTL lives in the inspector as context. The scan signal is freshness: captures under fifteen minutes get a copper highlight; default sort is recency. When you are juggling Telegram alerts, "is this still warm" beats "is this theoretically ninety days."

## Turning the panel into an inbox

After part 1 I kept bolting columns. Then I ripped them out. Then I put some back. The only job that mattered: Telegram ping → about twenty seconds → replay or bounce → notes. Manual. No auto-replay. GhostLoot still never writes phishlets and never talks to live Evilginx — copy `~/.evilginx/data.db` (buntdb), parse, delete the copy. Default listen `127.0.0.1:8090`. One Go binary, `index.html` embedded with `go:embed`. Version in production: 1.6.1.

The panel is an inbox now.

Tabs: Inbox, Captures, Lures, Settings. Dashboard collapsed into a period strip on Lures — not a place you live. Keyboard: `1–4`, `j`/`k`, `c` copy cookies, `b` brief, `h` Cookie header, `u` done, `e` bounced, `?`.

Inbox columns that survived: account + status tag, loot type (Entra ID / Microsoft account), password blurred, from (CC · city · UA), expires (honest label), when (relative + absolute). Geo is optional via ip-api.com with a local JSON cache; `-nogeo` turns it off. Still never send lure URLs to Safe Browsing, VirusTotal, or urlscan.

Statuses are three: `open`, `bounced`, `done`. I tried `copied` / `replayed`. They confused me mid-op, so they folded back into `open`. Bounce reasons persist: CA / IP mismatch / UA / token dead.

Replay inspector is a checklist, not a pretty card:

1. VPN/exit — country · city · AS… and Copy UA  
2. Import on `login.microsoftonline.com` or `login.live.com`  
3. Then open `office.com` / `account.microsoft.com`

One primary **Copy cookies**. Accept-Language (suggested from country, not captured), Cookie header, long briefing — behind More.

Telegram on the wire is Spanish and thin: `#46 · hace 40s`, type, persist, user (unless minimal mode), country, UA, import/then/avoid. Never the cookie. Never the password. Minimal mode drops account and IP when that alert leaves your perimeter.

## One command from the laptop

`ghostloot` on the laptop: if `localhost:8090` is down, it builds `ssh -L 8090:127.0.0.1:8090` with ControlMaster. It does not `pkill` ssh. `ghostloot down` stops the panel systemd unit and closes that tunnel. `ghostloot console` attaches the Evilginx tmux — and yes, root and ubuntu use different sockets (`/tmp/tmux-0` vs `/tmp/tmux-1000`); the CLI finds the session owner instead of guessing.

Server side: systemd unit `ghostloot` on `127.0.0.1:8090`, config `/etc/ghostloot.conf`. Laptop config under `~/.ghostloot/`. `sudo ./install.sh` hunts `data.db`, the Evilginx binary, and a free loopback port in `8090–8094`.

Bugs that only show up when you actually run it:

- Template called `.size` on an Array (`ips.size` → undefined IPs).  
- API said `update` / `landing`; UI expected `update_time` / `landing_url`.  
- `go.mod` wanted 1.24; VPS had 1.22 — lowered the module.  
- Copying the binary onto itself while it was running → `ETXTBSY`. Kill whatever is on 8090 first. Do not `pkill` the SSH tunnel that is keeping you in.

## The Google wall

I will say this once, without a tutorial.

Public `google.yaml` files (the usual Community collections, `min_ver: 2.3.0` and friends) will load. Login becomes WebLiteSignIn / `/v3/signin/identifier`. Next is a JS button (`identifierNext`). `bgresponse` defaults toward `js_disabled`. Botguard / reCAPTCHA Enterprise sits on the path. Force a POST with the email and you still land on `/v3/signin/rejected?...&rhlk=js`.

Hostname rewrite, extra `proxy_hosts`, Cloudflare Origin in front of the lure — none of that mints Botguard. Community cannot do Google properly. Pro uses Evilpuppet. YAML will not save you. I am not publishing a bypass.

Cloudflare in front of a lure hides origin in DNS. New hostnames need origin certs. Let's Encrypt HTTP-01 against orange-cloud returns 403. Missing origin cert and the visitor gets 525. That is ops, not a Google breakthrough.

## What actually stops AiTM

Password plus SMS, OTP, push, or emailed code does not. You capture after the second factor is done.

Phishing-resistant MFA does: FIDO2, passkeys, Windows Hello for Business, bound to the real origin. The browser will not hand that credential to your look-alike domain. Nothing to steal. If a tenant enforces that, the run dies at the door — write it up as a strength.

If the tenant has no 2FA at all, that is the tenant. It is not your phishlet "skipping" MFA.

## OPSEC, still boring on purpose

Bind loopback, tunnel in. Off-localhost without `-user`/`-pass` refuses to start. CSRF origin check on POSTs. State files `0600`. Do not park a second web app (GoPhish and friends) on `0.0.0.0:8080` on the same box. Mail from a warmed mailbox, not the proxy IP. Replay from a VPN in the victim's country with a cloned UA — not from the VPS. Optional geo is fine; shipping lure URLs to Google Safe Browsing is how infra dies.

## Grab it

GhostLoot is MIT: [github.com/marcocarolasec/ghostloot](https://github.com/marcocarolasec/ghostloot)

Read-only viewer for a `data.db` you already have, on an engagement you are cleared to run. No Evilginx in the repo. No phishlets. No auto-replay.

If you have run Community Evilginx against Microsoft in 2026: did the Entra vs MSA split burn you too, or has a CE Google Next flow ever actually completed for you?
