# Inside Kali365, a Device Code Phishing Ecosystem

Kali365 PhaaS kit abuses Microsoft device code flow to phish M365 tokens, then uses Electron apps to mint ESTSAUTH cookies and run BEC from victim mailboxes.

- Payout: Real-world hack
- Severity: high
- Program: Microsoft
- Analysis by: Huntress
- Disclosed: 2026-06-11
- Write-up quality: 8/10
- Original: https://www.huntress.com/blog/kali365-device-code-phishing-kit
- Library page: https://bugbountylibrary.com/r/blog%3Ahuntress-com-blog-kali365-device-code-phishing-kit
- Tags: area:cloud domain:cloud impact:ato impact:data-leak kind:incident platform:threat-intel sev:high tech:azure tech:electron tech:oauth technique:recon vuln:account-takeover vuln:auth-bypass vuln:mfa-bypass

**Target:** Microsoft Entra ID device code flow / M365 mailboxes
**Root cause:** The device authorization grant lets an attacker-initiated code be entered by a victim, binding the attacker's session to the victim's account; tokens survive MFA and password resets. Captured refresh tokens are then exchanged inside a real Chromium window to obtain ESTSAUTH session cookies.
**Steps:**
1. Victim receives email linking to Canva-hosted page claiming encrypted document
2. Page shows a device code and a 'View' button opening the legitimate Microsoft login page
3. Victim logs in and enters the code; attacker's device authorization completes and attacker receives tokens
4. Victim is redirected to a 'Document Expired' page while attacker gains persistent access in ~42 seconds
5. Operator loads token in OctoLink Live; app sends access_token to `outlook.office365.com/owa/auth.owa` with token_type=Bearer to get OWA cookies
6. If redirected to login, app redeems refresh_token twice at `/common/oauth2/v2.0/token`: first openid profile offline_access, then service scope (e.g. https://admin.microsoft.com/.default offline_access) inside a hidden BrowserWindow to set ESTSAUTH
7. OctoLink Sender refreshes token with client_id d3590ed6-52b3-4102-aeff-aad2292ab01c and scope graph.microsoft.com/.default offline_access, then sends via Graph using draft-create -> send-draft -> delete/verify
**How found:** SOC noticed a spike of over 80 successful UserLoggedIn device code events from Tencent Cloud AS132203 on May 20; pivoting with Validin found 150+ panel IPs, and F12 on React SPAs exposed ~100k lines of JS revealing panel features.
**Exploitation:** Operators phish device codes via 33 Microsoft-branded lures on Cloudflare Workers domains, capture tokens into a vault, then use OctoLink Live to convert tokens into real browser sessions (bypassing MFA) and OctoLink Sender to send lateral phishing at up to 2500/token/day in throttled waves. E2 adds AI BEC drafting, mailbox credential scanning, and Exchange Admin connector/mail-flow manipulation for fraud.
**Escalation:** Token capture escalates to mailbox read, contact harvesting, BEC wire-fraud drafting, rogue Exchange connectors, DKIM toggling, and mass lateral phishing from the victim's own account.
**Insight:** Monitor Entra sign-in logs for device code authentication successes from hosting/VPS ASNs and non-browser user agents; a burst of UserLoggedIn events with no interactive MFA is the tell, and blocking device code flow tenant-wide removes the primitive.

## Proof of concept (copied exactly from the original report)

**KQL hunting rule for operator ASN/CIDR sign-ins**

```
let operatorASNs = dynamic(["132203"]);

let operatorCIDR = "43.173.64.0/20";

SigninLogs

| where ResultType == "0"

| where AutonomousSystemNumber in (operatorASNs)

or ipv4_is_in_range(IPAddress, operatorCIDR)

| where UserAgent contains "python-requests"

| project TimeGenerated, UserPrincipalName, IPAddress, AutonomousSystemNumber,

UserAgent, AppDisplayName, AppId, AuthenticationProtocol,

ResourceDisplayName, Location

| sort by TimeGenerated desc
```

**OWA bearer token to ESTSAUTH cookie exchange**

```
For OWA, it sends the stolen access_token directly to outlook.office365.com/owa/auth.owa with token_type=Bearer, copying the form-based authentication-style login flow and getting OWA session cookies in a single redirect.
```

**OctoLink Sender draft-create/send/verify sequence**

```
It creates a draft with POST /me/messages, sends it with POST /me/messages/{id}/send, waits 1.5 seconds, then checks with GET /me/messages/{id}?$select=id,isDraft to see if the draft is gone (404 means it sent), and deletes any orphan draft Exchange left behind.
```

