ToddyCat — Umbrij .NET Backdoor & the Shadow Token via Remote Debug (STRD) Technique
Executive Summary
Umbrij is a .NET DLL backdoor (ConfuserEx-obfuscated) attributed by Kaspersky to ToddyCat, an Asia-nexus espionage actor known for targeting high-profile government and defence entities across Europe and Asia. Umbrij is purpose-built for one thing: surreptitiously reading a victim's Gmail / Google Workspace mailbox without ever needing the account password.
The novel tradecraft — which Kaspersky names Shadow Token via Remote Debug (STRD) — works by copying the victim's live browser profile, then launching Chrome/Edge in --headless mode with a --remote-debugging-port. Umbrij drives that debug port through PuppeteerSharp (the .NET Chrome DevTools Protocol library) to visit a Google OAuth URL for a legitimate Google Workspace migration client_id (GWMMO 279448736670, or GWSMO 1095133494869 with the -sync switch). Because the copied profile is still signed in, Google issues an authorization code with no password prompt; Umbrij exchanges it for an access token and reads mail via the Google API (scopes include https://mail.google.com/, gmail.insert, gmail.labels, drive, and admin.directory.user).
Umbrij is delivered by DLL side-loading: legitimate signed hosts (GoogleDesktop.exe, BDSubWiz.exe, VSTestVideoRecorder.exe) are dropped to user-writable staging paths (C:\Users\Public, C:\Windows\Vss, C:\Windows\Temp) alongside a malicious companion DLL (GoogleServices.DLL, log.dll, or the VideoRecorderEngine DLL). The launcher accepts switches such as -regex (find a target user by email substring), -browser, -debugport, -sync, and -deepsearch. Persistence is a scheduled task masquerading as KasperskyEndpointSecurityEDRAvp.
Defender priority: The highest-value, lowest-FP angles are a browser launched with both --remote-debugging-port and --headless (especially with --user-data-dir pointing at a BackupFiles copy), the three DLL-sideload host binaries running from Public/Vss/Temp, the Kaspersky-published Umbrij hashes, and the KasperskyEndpointSecurityEDRAvp scheduled task. Enforce Chrome policy DeveloperToolsAvailability=2 to break the remote-debug primitive outright.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Fwd |
|---|---|---|---|
| 1 · Vendor | Kaspersky Securelist — "How the ToddyCat APT group gains access to Gmail accounts" (2026-07-02) | Primary technical analysis: Umbrij .NET backdoor, STRD technique, PuppeteerSharp/CDP remote-debug flow, OAuth client_ids & scopes, DLL-sideload trios, MD5 hashes, scheduled-task persistence, Chrome-policy mitigation | yes |
| 2 · Press | The Hacker News — "ToddyCat-Linked Umbrij Malware Abuses OAuth to Access Gmail via Google API" (2026-07-02) | Corroborates STRD, PuppeteerSharp usage, session-riding (no password needed), migration-tool client_id abuse | partial |
| 2 · Press | GBHackers / CyberPress — ToddyCat "Shadow Token" & ConfuserEx tooling | Confirm ConfuserEx obfuscation, headless remote-debug port, automated Gmail compromise | partial |
Every atomic IOC below (MD5 hashes, host/DLL filenames, install paths, OAuth client_ids, scheduled-task name) traces to the Kaspersky Securelist indicator set. The OAuth client_ids and Google scopes are behavioural constants of the technique; the sideloaded filenames and staging paths are durable, while the debug-port number (e.g. 11111) is arbitrary and should be treated as illustrative, not a fixed indicator.
Hunt Brief & Attack Chain
Hunt hypotheses (fidelity-ordered)
- H1 (high): A browser was launched with both
--remote-debugging-portand--headless— the core STRD primitive. Covers T1550.001 / T1528. - H2 (high): A headless remote-debug browser used
--user-data-dirpointing at a copied profile (BackupFiles). STRD profile-clone tell. - H3 (high): One of the three DLL-sideload host binaries executed from
C:\Users\Public,C:\Windows\Vss, orC:\Windows\Temp. Covers T1574.001. - H4 (high): A file matching a Kaspersky-published Umbrij / sideloaded-DLL hash executed or was written. Atomic.
- H5 (high): A scheduled task named
KasperskyEndpointSecurityEDRAvpwas registered. Covers T1053.005 masquerade. - H6 (high): An Umbrij launcher ran with STRD switches (
-regex+-deepsearch/-debugport/-browser). Command-line tell. - H7 (medium): A non-browser parent spawned a headless remote-debug browser. Catches renamed/unknown host binaries.
- H8 (medium): A sideload host binary or headless browser resolved
accounts.google.comfor the OAuth code exchange. Covers T1114 / T1071.001.
Attack chain
| Step | Behavior | Telemetry | Hunt Angle |
|---|---|---|---|
| 1 · Delivery | DLL side-load: signed host (GoogleDesktop/BDSubWiz/VSTestVideoRecorder) + malicious DLL dropped to Public/Vss/Temp | ProcessRollup2, PeFileWritten | Q3, Q4 |
| 2 · Execution | Umbrij launcher runs with -regex / -browser / -debugport / -deepsearch to pick a target user | ProcessRollup2 | Q6 |
| 3 · Profile clone | Copies live Chrome/Edge profile (Login Data, Local State, Web Data) into a BackupFiles dir | FileCreateInfo / PeFileWritten | Q2 (via user-data-dir), native hunts |
| 4 · STRD | Launches browser --headless --remote-debugging-port; PuppeteerSharp drives CDP to Google OAuth URL | ProcessRollup2 | Q1, Q2, Q7 |
| 5 · Token mint | Rides signed-in session → authorization code for GWMMO/GWSMO client_id → access token | DnsRequest, NetworkConnectIP4 | Q8 |
| 6 · Collection | Reads mailbox via Google API (mail.google.com, gmail.insert, drive scopes) | NetworkConnectIP4 (API egress) | Q8, native hunts |
| 7 · Persistence | Scheduled task masquerading as KasperskyEndpointSecurityEDRAvp | ScheduledTaskRegistered | Q5 |
Consolidated IOC Table
All values published by Kaspersky Securelist. Hashes and sideloaded filenames are durable; the debug-port number is arbitrary (illustrative 11111). Validate against your own retention window before bulk-blocking, and note the host binaries are legitimate signed files abused only when they run from user-writable staging paths.
| Type | Value | Conf | Action | Context |
|---|---|---|---|---|
| md5 | 1AB58838E5790EFB22F2D35AB98C0B7D | high | detect | Umbrij (ver. a) |
| md5 | A7D7D6C4C3F227F7117261C63B9E23A9 | high | detect | Umbrij (ver. a) |
| md5 | 3D3A621F852C42D97FD7260681E42508 | high | detect | Umbrij (ver. a) |
| md5 | 3432DD9AC0DF80EF86EB80BD080F839B | high | detect | Umbrij (ver. a) |
| md5 | 22AAEB4946BA6D2F2E27FEB7DBB295DE | high | detect | Umbrij (ver. b) |
| md5 | F61FBFB7AA1CD5DC8F70B055B51563E2 | high | detect | Umbrij (ver. b) |
| md5 | F169D6D172DFB775895A5E2B1540C854 | high | detect | Umbrij (ver. c) |
| md5 | 9F5F2F0FB0A7F5AA9F16B9A7B6DAD89F | high | detect | GoogleServices.DLL (sideloaded) |
| md5 | 28CB7B261F4EB97E8A4B3B0D32F8DEF1 | high | detect | log.dll (sideloaded) |
| md5 | BAE82A15D1DBFB024617B9B56A8E5F66 | high | detect | VideoRecorderEngine.dll (sideloaded) |
| filename | GoogleDesktop.exe / BDSubWiz.exe / VSTestVideoRecorder.exe (from Public/Vss/Temp) | high | hunt | DLL-sideload host binaries |
| path | C:\Users\Public\ , C:\Windows\Vss\ , C:\Windows\Temp\ | high | hunt | staging / execution paths |
| task | KasperskyEndpointSecurityEDRAvp | high | hunt | scheduled-task persistence (masquerade) |
| oauth | client_id 279448736670 (GWMMO) / 1095133494869 (GWSMO) | high | hunt | Workspace migration app abused for token mint |
| behavior | browser --headless --remote-debugging-port (STRD) | high | detect | core technique primitive |
Affected Surface & Telemetry Matrix
| Surface | Required Telemetry | Priority | Gap Risk |
|---|---|---|---|
| Windows endpoints (Chrome/Edge + .NET present) | ProcessRollup2 / SyntheticProcessRollup2 | Critical | Low — default Falcon coverage |
| File writes (profile clone / sideloaded DLL) | PeFileWritten / FileCreateInfo | High | Low |
| Scheduled tasks | ScheduledTaskRegistered | High | Low |
| DNS / egress (OAuth code exchange, Google API) | DnsRequest, NetworkConnectIP4 | Med | High — accounts.google.com is ubiquitous; only useful scoped to a suspicious parent |
| Google Workspace (identity side) | Workspace Admin login/token audit logs | Med | High — not in Falcon EDR; needs Workspace telemetry (§7) |
ATT&CK Mapping
| Tactic | Technique | Observed Behavior | Coverage |
|---|---|---|---|
| Defense Evasion | T1574.001 DLL Side-Loading | Signed host (GoogleDesktop/BDSubWiz/VSTestVideoRecorder) loads malicious companion DLL | Q3, Q4 |
| Defense Evasion | T1036.005 Masquerading: Match Legitimate Name | Scheduled task named KasperskyEndpointSecurityEDRAvp; migration-app client_id reuse | Q5 |
| Credential Access | T1528 Steal Application Access Token | OAuth authorization code minted from a signed-in session | Q1, Q2, Q7 |
| Defense Evasion / Lateral | T1550.001 Use Alternate Auth Material: App Access Token | Rides existing Gmail session; no password needed | Q1, Q8 |
| Privilege Escalation | T1134.003 Make and Impersonate Token | Runs browser in a targeted user's context (-user / -regex) | Q6 |
| Persistence | T1053.005 Scheduled Task | KasperskyEndpointSecurityEDRAvp task | Q5 |
| Collection | T1114 Email Collection | Reads mailbox via Google API (mail.google.com / gmail scopes) | Q8, native hunts |
| Command & Control | T1071.001 Web Protocols | OAuth code exchange + Google API over HTTPS | Q8 |
Native Audit-Log Hunts (non-CQL)
- Google Workspace Admin — Token audit: In the Admin console, review OAuth token grants and app activity for the migration apps Google Workspace Migration for Microsoft Outlook (client_id
279448736670) and Google Workspace Sync for Microsoft Outlook (1095133494869). Legitimate use is rare and admin-driven; unexpected grants tied to a workstation IP are the identity-side tell. - Google Workspace — Login audit: Hunt for API access to Gmail from a
localhostredirect flow or new device/session immediately after an endpoint alert; users can self-audit atmyaccount.google.com/connections. - Chrome/Edge policy state: Confirm
DeveloperToolsAvailabilityis enforced (value2= disabled) via GPO/MDM reporting; any host where it is unset is exposed to STRD. - Scheduled task inventory: Sweep for a task literally named
KasperskyEndpointSecurityEDRAvpon hosts that do not run Kaspersky (the name is a masquerade). - File-system sweep: Look for a
BackupFilesdirectory under%LOCALAPPDATA%\Google\Chromeor%LOCALAPPDATA%\Microsoft\Edge— Umbrij's copied-profile staging folder.
CrowdStrike LogScale CQL Hunt Queries
Looks for: a Chromium-based browser launched with both --remote-debugging-port and --headless — the STRD primitive Umbrij needs to drive the DevTools protocol. Very rare on user workstations.
// HUNT: Headless browser with a remote-debugging port (STRD) // MITRE: T1528, T1550.001 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: rare — some automated testing/CI harnesses; scope out known runners // TUNING: exclude sanctioned QA/automation hosts and puppeteer-based CI agents #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^(chrome|msedge|brave|chromium)\.exe$/i | CommandLine=/--remote-debugging-port/i | CommandLine=/--headless/i | table([@timestamp, aid, ComputerName, UserName, FileName, ParentBaseFileName, CommandLine], limit=200)
Looks for: a remote-debug browser whose --user-data-dir points at a copied-profile BackupFiles folder — Umbrij clones the live profile so the debug session inherits the signed-in Google cookies. Near-zero FP.
// HUNT: STRD browser using a cloned profile (BackupFiles user-data-dir) // MITRE: T1528, T1550.001, T1539 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^(chrome|msedge|brave|chromium)\.exe$/i | CommandLine=/--remote-debugging-port/i | CommandLine=/--user-data-dir=.{0,120}BackupFiles/i | table([@timestamp, aid, ComputerName, UserName, FileName, ParentBaseFileName, CommandLine], limit=200)
Looks for: the three signed host binaries Umbrij abuses running from C:\Users\Public, C:\Windows\Vss, or C:\Windows\Temp — legitimate files in illegitimate locations.
// HUNT: Umbrij DLL-sideload host binaries from staging paths // MITRE: T1574.001 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: none expected — these binaries do not normally live in Public/Vss/Temp #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^(GoogleDesktop|BDSubWiz|VSTestVideoRecorder|bds)\.exe$/i | FilePath=/\\(Users\\Public|Windows\\Vss|Windows\\Temp)\\/i | table([@timestamp, aid, ComputerName, UserName, FileName, FilePath, CommandLine, ParentBaseFileName], limit=200)
Looks for: execution or write of any Kaspersky-listed Umbrij or sideloaded-DLL MD5. Atomic — promote straight to a Custom IOC block (see §10).
// HUNT: Umbrij / sideloaded-DLL known-bad MD5 hashes (Kaspersky) // MITRE: T1574.001, T1528 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 / PeFileWritten #event_simpleName=/ProcessRollup2|PeFileWritten|NewExecutableWritten/ | in(MD5HashData, values=["1AB58838E5790EFB22F2D35AB98C0B7D", "A7D7D6C4C3F227F7117261C63B9E23A9", "3D3A621F852C42D97FD7260681E42508", "3432DD9AC0DF80EF86EB80BD080F839B", "22AAEB4946BA6D2F2E27FEB7DBB295DE", "F61FBFB7AA1CD5DC8F70B055B51563E2", "F169D6D172DFB775895A5E2B1540C854", "9F5F2F0FB0A7F5AA9F16B9A7B6DAD89F", "28CB7B261F4EB97E8A4B3B0D32F8DEF1", "BAE82A15D1DBFB024617B9B56A8E5F66"]) | table([@timestamp, aid, ComputerName, UserName, FileName, FilePath, MD5HashData], limit=max)
Looks for: registration of a scheduled task named KasperskyEndpointSecurityEDRAvp — Umbrij's persistence masquerade. On any host without a genuine Kaspersky install this is a strong signal.
// HUNT: Umbrij scheduled-task persistence (Kaspersky EDR masquerade) // MITRE: T1053.005, T1036.005 // CONF: high FP: low COST: low | REQUIRES: ScheduledTaskRegistered // TUNING: on genuine Kaspersky fleets, confirm the task's backing executable path is legitimate #event_simpleName=/ScheduledTaskRegistered/ | TaskName=/KasperskyEndpointSecurityEDRAvp/i | table([@timestamp, aid, ComputerName, UserName, TaskName, TaskExecutable], limit=200)
Looks for: a process invoked with the distinctive Umbrij switch pairing — a -regex user selector alongside -deepsearch, -debugport, or -browser. FP: generic -regex exists in other tools; the pairing with browser/debug switches is what makes this specific.
// HUNT: Umbrij launcher switches (-regex + -deepsearch/-debugport/-browser) // MITRE: T1134.003, T1528 // CONF: high FP: medium COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: unrelated CLI tools that accept a -regex flag // TUNING: require the -regex + browser/debug pairing (below); exclude sanctioned admin scripts #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | CommandLine=/\s-regex\s/i | CommandLine=/\s-(deepsearch|debugport|browser|sync)\b/i | table([@timestamp, aid, ComputerName, UserName, FileName, FilePath, CommandLine, ParentBaseFileName], limit=200)
Looks for: a headless remote-debug browser whose parent is not a normal browser/updater — catches Umbrij even under a renamed host binary. FP: automation frameworks (node.exe, python.exe) legitimately launch headless Chrome — exclude sanctioned CI/QA parents.
// HUNT: Headless remote-debug browser launched by a non-browser parent // MITRE: T1528, T1574.001 // CONF: medium FP: medium COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: node/python automation harnesses driving Puppeteer/Playwright // TUNING: exclude approved automation parents and CI runner hosts #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^(chrome|msedge|brave|chromium)\.exe$/i | CommandLine=/--remote-debugging-port/i | ParentBaseFileName!=/^(chrome|msedge|brave|chromium|explorer|GoogleUpdate|MicrosoftEdgeUpdate|services)\.exe$/i | table([@timestamp, aid, ComputerName, UserName, FileName, ParentBaseFileName, CommandLine], limit=200)
Looks for: DNS to accounts.google.com / oauth2.googleapis.com initiated by one of the sideload host binaries (not the real browser) — the token-mint egress. FP: these domains are ubiquitous, so this query is scoped by ContextBaseFileName to the abused hosts; broaden only during active IR.
// HUNT: OAuth token-mint DNS from an Umbrij sideload host binary // MITRE: T1114, T1071.001, T1550.001 // CONF: medium FP: medium COST: medium | REQUIRES: DnsRequest // FALSE POSITIVES: accounts.google.com is common — scoping to the host binaries keeps it tight // TUNING: during IR, drop the ContextBaseFileName filter to see all OAuth egress on a suspect host #event_simpleName=DnsRequest | DomainName=/^(accounts\.google\.com|oauth2\.googleapis\.com)$/i | ContextBaseFileName=/^(GoogleDesktop|BDSubWiz|VSTestVideoRecorder|bds)\.exe$/i | table([@timestamp, aid, ComputerName, DomainName, ContextBaseFileName], limit=200)
Looks for: a Chrome/Edge binary running from Public/Vss/Temp rather than Program Files — a portable browser Umbrij can drop to guarantee a debuggable instance. Distinctive path → very low FP.
// HUNT: Chromium browser running from a user-writable staging path // MITRE: T1528, T1574.001 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^(chrome|msedge|brave|chromium)\.exe$/i | FilePath=/\\(Users\\Public|Windows\\Vss|Windows\\Temp)\\/i | table([@timestamp, aid, ComputerName, UserName, FileName, FilePath, CommandLine], limit=200)
CrowdStrike Custom IOA Recommendations
| IOA Name | Pattern | Action | Exclusions |
|---|---|---|---|
| STRD — Headless Remote-Debug Browser | Chromium image with --remote-debugging-port + --headless (Q1) | Detect (high) | Sanctioned QA/CI automation hosts |
| Umbrij — Sideload Host in Staging Path | GoogleDesktop/BDSubWiz/VSTestVideoRecorder from Public/Vss/Temp (Q3) | Detect → Block (after baseline) | None expected — abnormal location |
| Umbrij — EDR-Masquerade Task | ScheduledTaskRegistered TaskName KasperskyEndpointSecurityEDRAvp (Q5) | Detect (high) | Genuine Kaspersky fleets — verify backing path |
| Umbrij — Launcher Switches | -regex + -deepsearch/-debugport/-browser (Q6) | Detect (medium) | Sanctioned admin scripts using -regex |
Promotion path: validate the high-CONF/low-FP queries (Q1, Q3, Q5) against 7–14 days of baseline, confirm zero benign hits, then promote to Custom IOAs in detect mode; escalate Q3 to block once clean. Q4 hashes → Custom IOC Management (block).
Machine-Readable IOC Appendix
One-click blocks for SOC tooling. Hashes are Kaspersky-published; swap expirations to fit your policy. Note the host EXEs are legitimate signed binaries — hunt on location/behaviour, do not blanket-block by name.
type,value,action,severity,expiration,description,tags md5,1AB58838E5790EFB22F2D35AB98C0B7D,prevent,critical,2026-12-31,Umbrij ver.a,actor:ToddyCat;tool:Umbrij md5,A7D7D6C4C3F227F7117261C63B9E23A9,prevent,critical,2026-12-31,Umbrij ver.a,actor:ToddyCat;tool:Umbrij md5,3D3A621F852C42D97FD7260681E42508,prevent,critical,2026-12-31,Umbrij ver.a,actor:ToddyCat;tool:Umbrij md5,3432DD9AC0DF80EF86EB80BD080F839B,prevent,critical,2026-12-31,Umbrij ver.a,actor:ToddyCat;tool:Umbrij md5,22AAEB4946BA6D2F2E27FEB7DBB295DE,prevent,critical,2026-12-31,Umbrij ver.b,actor:ToddyCat;tool:Umbrij md5,F61FBFB7AA1CD5DC8F70B055B51563E2,prevent,critical,2026-12-31,Umbrij ver.b,actor:ToddyCat;tool:Umbrij md5,F169D6D172DFB775895A5E2B1540C854,prevent,critical,2026-12-31,Umbrij ver.c,actor:ToddyCat;tool:Umbrij md5,9F5F2F0FB0A7F5AA9F16B9A7B6DAD89F,prevent,critical,2026-12-31,GoogleServices.DLL sideload,actor:ToddyCat;tool:Umbrij md5,28CB7B261F4EB97E8A4B3B0D32F8DEF1,prevent,critical,2026-12-31,log.dll sideload,actor:ToddyCat;tool:Umbrij md5,BAE82A15D1DBFB024617B9B56A8E5F66,prevent,critical,2026-12-31,VideoRecorderEngine.dll sideload,actor:ToddyCat;tool:Umbrij
[ Umbrij / STRD behaviors ] - Browser launched with --headless AND --remote-debugging-port - --user-data-dir pointing at a *\BackupFiles\* cloned profile - GoogleDesktop.exe / BDSubWiz.exe / VSTestVideoRecorder.exe / bds.exe running from C:\Users\Public, C:\Windows\Vss, or C:\Windows\Temp - Sideloaded DLL: GoogleServices.DLL / log.dll / VideoRecorderEngine.dll - Launcher switches: -regex + -deepsearch / -debugport / -browser / -sync - Scheduled task named KasperskyEndpointSecurityEDRAvp - OAuth code exchange for client_id 279448736670 or 1095133494869 - Non-browser parent spawning a headless remote-debug Chrome/Edge
Actor: ToddyCat (Asia-nexus espionage APT)
Implant: Umbrij (.NET DLL, ConfuserEx-obfuscated)
Verdicts: HEUR:Trojan-PSW.MSIL.Umbrij.gen
HEUR:Trojan.MSIL.Agent.gen
Technique: STRD - Shadow Token via Remote Debug
Library: PuppeteerSharp (Chrome DevTools Protocol)
OAuth: GWMMO client_id 279448736670
GWSMO client_id 1095133494869 (-sync)
Scopes: mail.google.com, gmail.insert, gmail.labels,
drive, admin.directory.user
[ ] Chrome/Edge policy DeveloperToolsAvailability = 2 (disabled) enforced [ ] Workspace: restrict/deny GWMMO (279448736670) & GWSMO (1095133494869) [ ] Workspace: review OAuth token grants + localhost-redirect API access [ ] Scheduled-task sweep for KasperskyEndpointSecurityEDRAvp [ ] WDAC/AppLocker blocks EXEs under C:\Users\Public, \Windows\Vss, \Windows\Temp [ ] ASR: block credential stealing / LSASS + audit DLL side-loading hosts [ ] Users audit third-party access at myaccount.google.com/connections
Detection Validation Gates
- Telemetry ready: Confirm ProcessRollup2, ScheduledTaskRegistered, PeFileWritten, and DnsRequest are flowing from in-scope Windows hosts running Chrome/Edge.
- Benign baseline: Run Q1 and Q7 (headless remote-debug) over 7–14 days; record any sanctioned Puppeteer/Playwright CI or QA hosts, and exclude those parents before alerting.
- Positive test: In a lab, launch
chrome.exe --headless --remote-debugging-port=9222 --user-data-dir=C:\Temp\BackupFilesand confirm Q1 and Q2 fire; register a task namedKasperskyEndpointSecurityEDRAvpand confirm Q5 fires. - Promotion: Only promote Q1/Q3/Q5 to detect/block after zero benign hits across the baseline window.
Hardening — Tiered & Deployable
- Enforce Chrome/Edge
DeveloperToolsAvailability = 2via GPO/MDM — disables DevTools and the remote-debugging primitive STRD depends on. (MITRE M1042 Disable or Remove Feature.) - Restrict the Workspace migration apps (GWMMO
279448736670, GWSMO1095133494869) to trusted admins in Google Admin console API controls, or block them outright if unused. (M1018 User Account Management.) - Sweep + alert on the
KasperskyEndpointSecurityEDRAvptask across the fleet. (M1047 Audit.)
- WDAC/AppLocker: block execution of binaries under
C:\Users\Public,C:\Windows\Vss, andC:\Windows\Temp(deny-by-path). (M1038 Execution Prevention.) - Audit DLL side-loading hosts: alert when GoogleDesktop/BDSubWiz/VSTestVideoRecorder load a DLL from a non-standard directory. (M1052 User Account Control / M1038.)
- Enforce phishing-resistant MFA and re-auth for OAuth grants in Google Workspace so a stolen cookie alone cannot mint high-scope tokens. (M1032 Multi-factor Authentication.)
- Context-aware access / device trust in Workspace so API sessions from unmanaged or anomalous device contexts are denied. (M1035 Limit Access to Resource Over Network.)
- Application allow-listing for OAuth apps (block unverified/unneeded third-party and migration apps org-wide). (M1018.)
- Least-privilege on admin scopes — restrict
admin.directory.userand Drive-wide scopes to a minimal, monitored admin set. (M1026 Privileged Account Management.)
Deployable Playbooks
P1 · Enforce Chrome DeveloperToolsAvailability = 2 (disable DevTools / remote debug)
# HKLM policy (applies to all users; Edge uses ...\Policies\Microsoft\Edge) New-Item -Path 'HKLM:\SOFTWARE\Policies\Google\Chrome' -Force | Out-Null Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Google\Chrome' ` -Name 'DeveloperToolsAvailability' -Type DWord -Value 2 New-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' -Force | Out-Null Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' ` -Name 'DeveloperToolsAvailability' -Type DWord -Value 2 # Verify: Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Google\Chrome' -Name DeveloperToolsAvailability
P2 · Hunt the KasperskyEndpointSecurityEDRAvp scheduled task
$t = Get-ScheduledTask -TaskName 'KasperskyEndpointSecurityEDRAvp' -ErrorAction SilentlyContinue
if ($t) {
$a = $t.Actions | Select-Object Execute, Arguments
[pscustomobject]@{ Host=$env:COMPUTERNAME; Found=$true; Exec=$a.Execute; Args=$a.Arguments }
} else {
[pscustomobject]@{ Host=$env:COMPUTERNAME; Found=$false }
}
P3 · Flag Chromium binaries / EXEs running from staging paths
$dirs = 'C:\Users\Public','C:\Windows\Vss','C:\Windows\Temp'
foreach ($d in $dirs) {
if (Test-Path $d) {
Get-ChildItem -Path $d -Filter *.exe -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match '^(chrome|msedge|brave|chromium|GoogleDesktop|BDSubWiz|VSTestVideoRecorder|bds)\.exe$' } |
Select-Object FullName, Length, LastWriteTime
}
}
P4 · Locate cloned-profile BackupFiles staging folders
$roots = @(
"$env:LOCALAPPDATA\Google\Chrome",
"$env:LOCALAPPDATA\Microsoft\Edge"
)
foreach ($r in $roots) {
if (Test-Path $r) {
Get-ChildItem -Path $r -Directory -Filter 'BackupFiles' -ErrorAction SilentlyContinue |
Select-Object FullName, CreationTime
}
}
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Network-contain the host in Falcon; preserve memory, the BackupFiles profile copy, and the staging directories; do NOT reboot (lose volatile token/CDP state). | SOC | RTR capture, process tree, netconns |
| Eradicate | Remove the KasperskyEndpointSecurityEDRAvp task; delete sideloaded host+DLL pairs and the cloned profile; block Umbrij hashes (Q4). | IR | Task diff, deleted-file hashes |
| Identity recovery | In Google Workspace: revoke the user's OAuth tokens/sessions, force password reset + re-MFA, and revoke the GWMMO/GWSMO grants; review Gmail rules/labels for tampering (gmail.insert scope). | IR + IdP | Workspace token-revocation log |
| Hunt-back | Sweep the fleet with Q1–Q9; pivot on the sideloaded-DLL hashes and any host with a BackupFiles clone or the masquerade task; review Workspace audit for other abused mailboxes. | Threat Hunt | Fleet query results, Workspace audit |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1528 / T1550.001 | Headless remote-debug browser (STRD) | Q1, Q2 | Yes | Good |
| T1574.001 | DLL-sideload host in staging path | Q3, Q9 | Yes | Good |
| Atomic hashes | Known-bad Umbrij / DLL files | Q4 | IOC | Good |
| T1053.005 | EDR-masquerade scheduled task | Q5 | Yes | Good |
| T1134.003 | Umbrij launcher switches | Q6 | Yes | Partial (FP-tuned) |
| T1528 (evasion) | Non-browser parent → headless browser | Q7 | — | Partial |
| T1114 / T1071.001 | OAuth token-mint egress | Q8 | — | Partial (scoped) |
| Profile clone | Live browser-profile copy | Q2 (via user-data-dir) | — | Partial — full file-copy detection needs FileCreateInfo hunt (§7) |
| Identity side | Workspace token grant / API read | — | — | Gap — out of EDR scope; covered by Workspace audit (§7) |
Detection is strongest at the STRD primitive (headless + remote-debug), DLL-sideload location, atomic hashes, and the masquerade task — all low-FP. The launcher-switch and non-browser-parent hunts need environment tuning for automation frameworks. The mailbox-read itself happens in Google's cloud and is a gap best covered upstream via Workspace token/login audit (§7).
Hunt Summary Ticket
TITLE: ToddyCat Umbrij / STRD OAuth-token backdoor hunt
SEVERITY: Critical - active nation-state espionage (mailbox theft)
SCOPE: Windows endpoints (Chrome/Edge + .NET); DNS/egress;
Google Workspace identity audit
HYPOTHESIS: ToddyCat ran Umbrij to clone a signed-in browser profile,
drive a headless --remote-debugging-port session via
PuppeteerSharp, and mint a Google OAuth token to read mail.
QUERIES: Q1 headless+debug | Q2 BackupFiles profile | Q3 sideload
hosts | Q4 hashes | Q5 EDR-masq task | Q6 launcher switches
Q7 non-browser parent | Q8 OAuth egress | Q9 browser in staging
DO FIRST: Q1, Q3, Q5 (lowest FP, structural) then Q4, Q2.
FINDINGS: <record hosts / hashes / tasks / Workspace grants here>
GAPS: Mailbox read in Google cloud; profile file-copy (use §7).
ACTIONS: Enforce DeveloperToolsAvailability=2; restrict GWMMO/GWSMO;
block Q4 hashes; revoke tokens + re-MFA; isolate + IR
confirmed hosts.
OWNER: HuntPack
VERSION: v0.2 · 2026-07-04
Changelog
table() calls now carry an explicit row limit: the default is 200 and truncation is silent, so a capped result was indistinguishable from a complete one. Atomic-IOC sweeps (filters over 5+ hashes or C2 IPs) use limit=max so a wide infection is never silently under-scoped; behavioural hunts use limit=200, where exceeding the cap indicates the query needs tuning. Where present, event names that do not exist in the Falcon data model were corrected (e.g. ServiceInstalled is a Sysmon concept, not a Falcon event; ElfFileWritten is ELFFileWritten) — such queries could never return a row. No detection logic, fields, or IOCs changed.References
| Tier | Source | Used For | Access Date |
|---|---|---|---|
| 1 · Vendor | Kaspersky Securelist — How the ToddyCat APT group gains access to Gmail accounts | Umbrij analysis, STRD technique, IOCs, hashes, OAuth client_ids, scheduled-task persistence, Chrome-policy mitigation | 2026-07-04 |
| 2 · Press | The Hacker News — ToddyCat-Linked Umbrij Malware Abuses OAuth to Access Gmail | Technique corroboration, PuppeteerSharp/CDP, session-riding, migration-app abuse | 2026-07-04 |
| 2 · Press | GBHackers — ToddyCat Uses Shadow Token via Remote Debug | STRD naming, ConfuserEx, headless remote-debug detail | 2026-07-04 |
| Ref | MITRE ATT&CK — ToddyCat (G1022) | Technique mapping, mitigations | 2026-07-04 |