TrickBot DNS-Tunnel Variant — Hunt Pack

Modular Windows loader that abandons HTTP C2 for a custom DNS tunnelling transport · FortiGuard Labs, 2026-07-22
Threat
TrickBot (DNS-tunnel build) · W64/TrickBot.WC!tr
Severity
ACTIVE LOADER
Type
Modular loader / covert C2 channel
Access
Post-compromise (loader stage)
Version
v0.1 · 2026-07-25
Author
HuntPack
Confidence
High (single Tier-1 vendor, 3 corroborating outlets)
01

Executive Summary

FortiGuard Labs published analysis on 2026-07-22 of a 64-bit TrickBot build whose module set is recognisably the historical TrickBot codebase, but whose transport layer has been rewritten. Instead of HTTP POSTs to a C2 web server, the bot smuggles its command channel inside ordinary DNS lookups: command data is XORed with a single-byte key, hex-encoded, chopped into 63-character chunks separated by periods, and appended to an attacker-controlled apex. Responses come back encoded three bytes at a time inside the IPv4 addresses of the DNS answer records, with an index packed into the high bits of the first octet so the bot can reassemble fragments even when a resolver reorders them.

Three packet types carry the protocol: 0x30 (command request), 0x31 (response-size query so the bot can size its buffer) and 0x32 (response data). Measured throughput was roughly 30.7 KB/s, enough to move a 1.2 MB module in about 40 seconds. Persistence is a Windows scheduled task that fires at boot and repeats every five minutes, named to look like a routine software updater by concatenating a randomly chosen %AppData% folder name with the literal string autoupdate # and a random number. The task name and the executable path are stashed in NTFS Alternate Data Streams named $TASK and $FILE. Capability retained from prior builds: module download and execute, execution through rundll32.exe via the Control_RunDLL export, process hollowing, process doppelgänging, command-shell and PowerShell execution through anonymous pipes, and raw shellcode execution in the bot's own memory.

The defensive angle that matters: the atomic indicator surface is deliberately thin. One C2 apex and six sample hashes were published, and the operator can rotate both cheaply. What cannot be rotated cheaply is the shape of the traffic. A host that emits hundreds of 100-plus-character DNS names whose leftmost label is a long run of hex, all under one parent domain, is running a DNS tunnel regardless of which apex it uses today. That signal, plus the five-minute update-shaped scheduled task, is where this pack puts its weight.

Defender priority: if your endpoints can reach an external resolver directly, this channel works and your web proxy never sees it. Force all endpoint DNS through internal forwarders you can log, then run Q2 and Q3 against that log. Everything else in this pack is secondary to closing that egress path.

02

Source Review & Web Hunter Notes

TierSourceKey FindingCarry Forward
1Fortinet / FortiGuard Labs — Inside a TrickBot Variant Using DNS Tunneling for C2 (2026-07-22)Primary reverse-engineering write-up. Full tunnel protocol (0x30/0x31/0x32), XOR key 0xB9, 63-char hex chunking, IP-index response encoding, scheduled-task naming scheme, ADS $TASK/$FILE, 12-command handler table, C2 apex and six SHA-256 samples.yes
2Infosecurity Magazine — TrickBot Ditches HTTP for DNS Tunneling in Latest Variant (2026-07-22)Independent restatement of packet types, chunking, 30.7 KB/s throughput, the five-minute task and the 12-command set. Adds outside commentary on TrickBot's longevity.yes
3HackRead — New TrickBot Variant Spotted Using DNS to Control Infected Windows PCs (2026-07-22)Corroborates the C2 apex, the public-resolver egress path, the task naming, ADS storage and the Fortinet AV signature name. Lists the defensive telemetry to watch.yes
3Cyber Security News — TrickBot Turns Ordinary DNS Traffic Into a Hidden Channel (2026-07-22)Adds the response-side detail: three bytes per IPv4 answer record, index bits in the first octet for reassembly after resolver reordering. Names the ADS streams.yes

Hunter notes and decisions

  • All four sources verified live and snapshotted to TrickBot-DNSTunnel-Hunt-sources/. No cited source 404'd, and none required dropping.
  • No prompt-injection content observed. None of the fetched pages contained text addressed to an automated agent, instructions to fetch further URLs, or claims of authorisation.
  • Every source traces to the same origin. Tiers 2 and 3 are reporting on the Fortinet write-up, not independent telemetry. Treat "corroborated" here as "consistently reported", not "independently observed" — which is why nothing in this pack ships at prevent + critical.
  • The resolver the sample used was dropped from the indicator set entirely. The vendor report names a well-known public resolver (Google Public DNS) as the tunnel's egress. It appears nowhere in the IOC table, the import CSV or any query, because it is not campaign infrastructure — carrying it as an indicator invites someone to block it, which breaks resolution and stops nothing. The concept it illustrates is preserved as prose in §4 and as the egress audit in Q9.
  • The sandbox executable path in the vendor report was discarded. The report shows a task action pointing at a randomly-named binary on a lab drive. That name is generated per-infection and is worthless as an indicator, so it became a behavioural pattern (task-spawned binary in a user-writable path) rather than a filename IOC.
  • No campaign IPs were published, and none were invented. The response-side IPv4 addresses in this protocol are encoded data, not infrastructure — blocking them would be meaningless.
03

Hunt Brief & Attack Chain

Attack chain

#StepTelemetryHunt angle
164-bit packed loader lands and unpacks in memory; strings decrypted at runtime, APIs resolved by hashProcessRollup2, PeFileWrittenKnown sample hashes (Q10); unsigned binary executing from a user-writable path (Q7)
2Registers persistence: Schedule.Service.1 COM object creates a task named <AppData folder> autoupdate #<random>, triggered at startup and repeating every 5 minutesScheduledTaskRegistered, ProcessRollup2Task-name regex (Q4); schtasks with a 5-minute repeat interval (Q5)
3Writes task name and executable path to NTFS ADS $TASK and $FILEGAP Falcon does not expose ADS stream names; covered by the native Get-Item -Stream sweep in §7
4Registration beacon: command 0 sent to C2 with hostname, hardware ID, OS build, over the DNS tunnelDnsRequestApex lookup (Q1); hex-label shape (Q2)
5Steady-state polling: XOR-0xB9, hex-encode, chunk at 63 chars, prepend to apex; 0x30 request / 0x31 size / 0x32 dataDnsRequest, NetworkConnectIP4Length + volume anomaly (Q3); direct external resolver egress (Q9)
6Module delivery at ~30.7 KB/s; 1.2 MB payload lands in under a minuteDnsRequest, PeFileWrittenSustained high-volume long-name DNS from one process (Q2, Q3)
7Module execution: direct EXE, rundll32.exe + Control_RunDLL, process hollowing, process doppelgängingProcessRollup2Control_RunDLL outside System32 (Q6)
8Hands-on-keyboard: shell and PowerShell through anonymous pipes to cmd.exe; raw shellcode executed in the bot's own memoryProcessRollup2, ScriptControlScanTelemetryTask-parented shell spawn (Q7); Partial in-process shellcode has no discrete event

Hunt hypotheses (fidelity order)

#HypothesisMITREEventsExpected FPConf
H1A host resolves the published TrickBot tunnel apex or any subdomain of itT1071.004DnsRequestDetonation VMs, researcher hostsHigh
H2A host shows both hex-label DNS and an update-shaped 5-minute task — the two halves of this build on one aidT1071.004 + T1053.005DnsRequest, ScheduledTaskRegisteredEffectively noneHigh
H3A scheduled task exists whose name ends in the literal autoupdate # plus digitsT1053.005ScheduledTaskRegisteredNone known — not a Microsoft conventionHigh
H4A host emits sustained DNS lookups whose leftmost label is a long hex runT1132.001, T1071.004DnsRequestReputation/AV lookups that hash into the leftmost labelMedium
H5A host's DNS name-length distribution has a heavy tail no baseline host sharesT1572DnsRequestCDN, device-registration and telemetry SDK namesMedium
H6schtasks.exe creates a task repeating on a 5-minute intervalT1053.005ProcessRollup2RMM agents, patch tooling, installersMedium
H7rundll32.exe invokes Control_RunDLL against a DLL outside System32/SysWOW64T1218.011ProcessRollup2Third-party control-panel applets, installersMedium
H8An endpoint talks DNS directly to an external resolver rather than an internal forwarderT1572, T1071.004NetworkConnectIP4Split-tunnel VPN, roaming laptops, hard-coded resolvers in agentsMedium
04

Consolidated IOC Table

TypeValueConfActionContextExpiry
domainwesturn.inhighpreventDNS-tunnel C2 apex. Tunnel traffic arrives as <hex>.westurn.in; block the apex and every subdomain.2027-01-25
sha256DF527A5C2FBDE43816CD02F4CD49EEE4BB82FB4A3C7045021360888C7D504C98highpreventFortiGuard sample, 64-bit unpacked loader2027-01-25
sha2566C677EB2B3FFD288083C59A13D7BB712D4754AF61A5563873F76C440962346F4highpreventFortiGuard sample2027-01-25
sha256105F652E6B8F31C371F2385877E43B6772AFF5D3168D5D4635F8A1FCBB321421highpreventFortiGuard sample2027-01-25
sha25633C331EDEDBF8EE9829895424423CE3FD17E359D2E784FCBCE396AACFF458CF5highpreventFortiGuard sample2027-01-25
sha2563B19A82E1354AC14A3DA7C840CBDD0CE50DB38432D78E767B36F08E45024C23DhighpreventFortiGuard sample2027-01-25
sha256BF80245BA792992FBFE24ABAC33F8FD66F24CDEB5F0F21CFDF45A29D107C8D3BhighpreventFortiGuard sample2027-01-25
av signatureW64/TrickBot.WC!tr · IPS Trick.BotnethighenrichFortinet detection names — pivot for anyone running FortiGate/FortiClient alongside Falcon2026-08-25
behaviouralTask name matching autoupdate # + digits, boot trigger, 5-minute repeathighdetectPersistence naming scheme (Q4)persistent
behaviouralDNS name with a 40–63 character hex leftmost labelmediumhuntTunnel encoding shape (Q2). Survives apex rotation.persistent
behaviouralNTFS ADS named $TASK / $FILE on the loader binaryhighhuntConfig storage. No Falcon event — sweep natively (§7).persistent
placeholderREPLACE_WITH_NEW_TUNNEL_APEXhuntSlot for any additional apex your own Q2/Q3 hits surface. Do not populate from open reporting without a source.
placeholderREPLACE_WITH_APPROVED_RESOLVERSenrichYour own approved forwarders, for the Q9 exclusion and the §13.2 firewall rules. Tenant-specific by definition.

Why nothing here is prevent + critical: the six hashes and the apex come from one Tier-1 vendor. The other three outlets restate that vendor rather than contributing independent telemetry. Per the two-independent-source rule, they ship at prevent + high. Promote to critical only after your own tenant corroborates a hit.

On the resolver the sample used: the vendor report names a well-known public resolver (Google Public DNS) as the tunnel's egress. That address is deliberately absent from this table and from the import CSV in §10, because it is not campaign infrastructure and treating it as an indicator is actively harmful — blocking it breaks name resolution for anything legitimately pointed at it and stops nothing, since the operator can reach the same C2 through any recursive resolver. The defensive lesson is about the path, not the address: if an endpoint can reach any external resolver directly, this channel works and your web proxy never sees it. That is what Q9 audits and what §13.2 closes.

05

Affected Surface & Telemetry Matrix

SurfaceRequired telemetryPriorityGap risk
Windows endpoints (64-bit, all supported builds)Falcon ProcessRollup2, SyntheticProcessRollup2CriticalLow — standard sensor coverage
Endpoint DNS resolutionFalcon DnsRequestCriticalMediumDnsRequest is high-volume and is the first thing trimmed by retention policy. Confirm your retention covers the hunt window before running Q2/Q3.
Scheduled task registrationFalcon ScheduledTaskRegisteredHighMedium — some sensor policies do not emit this; fall back to the §7 native sweep
Egress to external resolversFalcon NetworkConnectIP4, firewall UDP/TCP 53 logsHighLow if you log perimeter 53; high if endpoints resolve externally by design
Internal DNS forwardersWindows DNS Server analytic log or resolver query logHighHigh — DNS analytic logging is off by default and is the single most valuable log for this threat
NTFS alternate data streamsNone in FalconMediumGAP — native PowerShell sweep only
In-memory shellcode executionFalcon memory-scanning / behavioural preventionsMediumHigh — command 11 runs raw code inside the bot's own process with no child-process artefact
06

ATT&CK Mapping

TacticTechniqueObserved behaviourQuery / control
Persistence · Privilege EscalationT1053.005 — Scheduled TaskSchedule.Service.1 COM object registers a boot-triggered task repeating every 5 minutes, named to imitate a software updaterQ4, Q5, Q8 · M1028, M1018
Command and ControlT1071.004 — Application Layer Protocol: DNSAll C2 carried in DNS queries and answer recordsQ1, Q2, Q3, Q8 · M1037
Command and ControlT1572 — Protocol TunnelingFull request/response protocol (0x30/0x31/0x32) tunnelled over DNS at ~30.7 KB/sQ3, Q9 · M1037, M1031
Command and ControlT1132.001 — Standard EncodingXOR with key 0xB9, hex encoding, 63-character label chunkingQ2 · M1031
Defense EvasionT1218.011 — Signed Binary Proxy: Rundll32Modules executed via rundll32.exe calling the Control_RunDLL exportQ6 · M1038, M1042
Defense EvasionT1055.012 — Process HollowingDownloaded module injected into a randomly chosen processQ7 · M1040
Defense EvasionT1055.013 — Process DoppelgängingAlternative injection path in the same command handler tableQ7 · M1040
Defense EvasionT1564.004 — NTFS File AttributesTask name and executable path stored in ADS $TASK and $FILE§7 native sweep · M1040
Defense EvasionT1027 — Obfuscated Files or InformationRuntime string decryption, calculated constants, API resolution by hashQ10 · M1040
Defense EvasionT1036 — MasqueradingTask name derived from a real %AppData% folder plus autoupdate #Q4 · M1049
ExecutionT1059.001 / T1059.003 — PowerShell / Windows Command ShellCommands 9 and 10 pipe shell and PowerShell through anonymous pipes to cmd.exeQ7 · M1042, M1038
ExecutionT1106 — Native APIRaw shellcode executed in the bot's own memory (command 11)GAP · M1040
Command and ControlT1105 — Ingress Tool TransferEXE and DLL modules pulled down over the tunnelQ2, Q3 · M1031
07

Native Audit-Log Hunts

Run these where Falcon has no event, or where you want a second opinion that does not depend on sensor policy.

7.1 · Windows DNS Server — analytic log

The highest-value log for this threat, and it is off by default. Enable it on your internal forwarders, then look for the two shapes Q2 and Q3 hunt: long names and hex leftmost labels. Event ID 257 (RESPONSE_SUCCESS) and 256 (QUERY_RECEIVED) in Microsoft-Windows-DNSServer/Analytical carry the queried name and the client IP.

wevtutil sl Microsoft-Windows-DNSServer/Analytical /e:true /q:true /ms:1073741824

# then, per forwarder, rank clients by long-name volume
$ev = Get-WinEvent -LogName 'Microsoft-Windows-DNSServer/Analytical' -Oldest -MaxEvents 500000
$ev | ForEach-Object { $x = [xml]$_.ToXml()
      [pscustomobject]@{
        Client = $x.Event.EventData.Data | Where-Object Name -eq 'Source' | Select-Object -Expand '#text'
        Name   = $x.Event.EventData.Data | Where-Object Name -eq 'QNAME'  | Select-Object -Expand '#text' } } |
  Where-Object { $_.Name.Length -gt 100 -or $_.Name -match '^[0-9a-fA-F]{40,63}\.' } |
  Group-Object Client | Sort-Object Count -Descending | Select-Object -First 25 Count, Name

7.2 · Scheduled task sweep — the naming scheme

Get-ScheduledTask | Where-Object { $_.TaskName -match 'autoupdate\s?#\s?\d+' } |
  Select-Object TaskPath, TaskName, @{n='Exec';e={ $_.Actions.Execute }},
                @{n='Args';e={ $_.Actions.Arguments }},
                @{n='Repeat';e={ $_.Triggers.Repetition.Interval }} | Format-List

# broader: any task repeating every 5 minutes that also runs at boot
Get-ScheduledTask | Where-Object {
    $_.Triggers.Repetition.Interval -eq 'PT5M' -and $_.Triggers.CimClass.CimClassName -match 'BootTrigger'
} | Select-Object TaskPath, TaskName, @{n='Exec';e={ $_.Actions.Execute }}

7.3 · NTFS Alternate Data Stream sweep (covers the Falcon gap)

The loader hides its task name and its own path in ADS named $TASK and $FILE. Falcon does not publish stream names, so this sweep is the only mechanical way to see step 3 of the chain.

$roots = @($env:APPDATA, $env:LOCALAPPDATA, $env:ProgramData, "$env:SystemRoot\Temp")
foreach ($r in $roots) {
  Get-ChildItem -Path $r -Recurse -File -Force -ErrorAction SilentlyContinue |
    ForEach-Object {
      Get-Item $_.FullName -Stream * -ErrorAction SilentlyContinue |
        Where-Object { $_.Stream -in @('$TASK','$FILE') } |
        Select-Object FileName, Stream, Length
    }
}
# decode a hit (read-only, no execution):
# [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String((Get-Content -Path FILEPATH -Stream '$TASK' -Raw)))

7.4 · Perimeter and resolver posture check

  • Firewall: report every source IP that sent UDP/53 or TCP/53 to anything other than your approved forwarders over the last 30 days. Any endpoint on that list can run this tunnel today.
  • Resolver: report the top 50 parent domains by distinct subdomain count per client. A DNS tunnel is a near-perfect outlier on that metric — a normal client resolves a handful of names under a given parent, a tunnelled one resolves thousands.
  • Resolver: report clients whose mean query-name length sits far above the fleet median. This survives apex rotation and encoding changes.
08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.

No query below carries an in-query time filter. Set the search window in the Falcon console instead — a function call on the right of a comparison does not parse in CQL filter context. Start at 7 days for Q1/Q4/Q8/Q10 and 24 hours for the volume queries (Q2/Q3/Q9) until you know your data rate.

Q1 · TrickBot DNS-tunnel C2 apex lookup
CONF HIGHFP LOWCOST LOW

Looks for: any resolution of the published tunnel apex or a subdomain of it. This is the single highest-fidelity check in the pack — one hit is an incident, not a hunt lead.

// HUNT: TrickBot DNS-tunnel C2 apex lookup
// MITRE: T1071.004 | CONF: high  FP: low  COST: low
// REQUIRES: DnsRequest
// FALSE POSITIVES: malware-analysis VMs and researcher workstations detonating the sample
#event_simpleName=/^DnsRequest$/
| DomainName=/(^|\.)westurn\.in$/i
| groupBy([aid, ComputerName, ContextBaseFileName, DomainName], function=count(as=Lookups), limit=200)
| table([ComputerName, aid, ContextBaseFileName, DomainName, Lookups])
Q2 · Hex-encoded oversized DNS labels (tunnel encoding shape)
CONF MEDFP MEDCOST MED

Looks for: the encoding fingerprint rather than the destination — a leftmost label that is a long unbroken run of hex, which is exactly what XOR-then-hex-then-chunk-at-63 produces. Survives apex rotation. FP: endpoint AV and reputation agents hash file or URL data into the leftmost label and look identical; baseline for a week and exclude the two or three vendor parents that dominate your results before alerting.

// HUNT: Hex-encoded oversized DNS labels (0x30/0x31/0x32 tunnel encoding)
// MITRE: T1132.001, T1071.004 | CONF: medium  FP: medium  COST: medium
// REQUIRES: DnsRequest
// FALSE POSITIVES: AV/EDR reputation lookups and anti-spam services that hex-hash into the leftmost label
// TUNING: exclude your reputation providers once baselined, e.g. append
//         | !DomainName=/\.REPLACE_WITH_REPUTATION_VENDOR_DOMAIN$/i
#event_simpleName=/^DnsRequest$/
| DomainName=/^[0-9a-f]{40,63}\./i
| length(DomainName, as=QueryLen)
| QueryLen > 60
| groupBy([aid, ComputerName, ContextBaseFileName], function=[count(as=HexLookups), count(DomainName, distinct=true, as=UniqueLabels), selectLast([DomainName])], limit=200)
| HexLookups > 20
| table([ComputerName, aid, ContextBaseFileName, HexLookups, UniqueLabels, DomainName])
Q3 · DNS name-length and volume anomaly per host
CONF MEDFP MEDCOST MED

Looks for: the transport signature that no encoding change can hide — a host emitting hundreds of very long DNS names, nearly all of them unique. At 30.7 KB/s a single module download alone generates thousands of lookups. FP: device-registration, CDN and telemetry SDK names routinely exceed 100 characters but repeat; the discriminator is DistinctNames tracking LongLookups almost one-to-one.

// HUNT: DNS query-length + volume anomaly per host (tunnel transport)
// MITRE: T1572, T1071.004 | CONF: medium  FP: medium  COST: medium
// REQUIRES: DnsRequest
// FALSE POSITIVES: Azure/Office device-registration names, CDN edge names, telemetry SDKs
// TUNING: baseline first, then exclude the benign long-name parents that dominate, e.g. append
//         | !DomainName=/\.(azureedge|akamaiedge|trafficmanager|REPLACE_WITH_YOUR_CDN)\.net$/i
#event_simpleName=/^DnsRequest$/
| length(DomainName, as=QueryLen)
| QueryLen > 100
| groupBy([aid, ComputerName, ContextBaseFileName], function=[count(as=LongLookups), count(DomainName, distinct=true, as=DistinctNames)], limit=500)
| LongLookups > 200
| table([ComputerName, aid, ContextBaseFileName, LongLookups, DistinctNames])
Q4 · Scheduled task using the "autoupdate #" naming scheme
CONF HIGHFP LOWCOST LOW

Looks for: the literal persistence naming scheme — an %AppData% folder name, the string autoupdate #, and a random number. Microsoft ships no task named this way, and the space-hash-digits shape is distinctive enough to alert on directly.

// HUNT: Scheduled task registered with the " autoupdate #" naming scheme
// MITRE: T1053.005, T1036 | CONF: high  FP: low  COST: low
// REQUIRES: ScheduledTaskRegistered
// FALSE POSITIVES: none observed - this is not a Microsoft or common-vendor convention
#event_simpleName=/^ScheduledTaskRegistered$/
| TaskName=/autoupdate\s?#\s?\d+/i
| table([ComputerName, aid, UserName, TaskName, TaskExecutable, TaskAuthor])
Q5 · schtasks.exe creating a 5-minute repeating task
CONF MEDFP MEDCOST LOW

Looks for: the command-line route to the same persistence, for builds or operators that shell out instead of using the COM interface. FP: RMM agents, patch tooling and some installers legitimately register minute-interval tasks; exclude by parent process, not by task name.

// HUNT: schtasks.exe creating a task that repeats every 5 minutes
// MITRE: T1053.005 | CONF: medium  FP: medium  COST: low
// REQUIRES: ProcessRollup2
// FALSE POSITIVES: RMM/patch agents and installers create minute-interval tasks routinely
// TUNING: exclude your management agents by parent, e.g. append
//         | !ParentBaseFileName=/^REPLACE_WITH_RMM_AGENT\.exe$/i
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^schtasks(\.exe)?$/i
| CommandLine=/\/create/i
| CommandLine=/\/sc\s+minute/i
| CommandLine=/\/mo\s+5(\s|$)/i
| table([ComputerName, aid, UserName, ParentBaseFileName, FileName, CommandLine])
Q6 · rundll32.exe invoking Control_RunDLL outside System32
CONF MEDFP MEDCOST LOW

Looks for: module execution path 3/4 from the command handler table — a downloaded DLL run through rundll32.exe against its Control_RunDLL export. FP: genuine third-party control-panel applets use this export; the System32/SysWOW64 exclusion removes the Microsoft baseline, and anything left should be a short, reviewable list of vendor applets you can name.

// HUNT: rundll32.exe invoking Control_RunDLL on a DLL outside System32
// MITRE: T1218.011, T1105 | CONF: medium  FP: medium  COST: low
// REQUIRES: ProcessRollup2
// FALSE POSITIVES: third-party control-panel applets and vendor installers use this export legitimately
// TUNING: after the System32/SysWOW64 cut, exclude named vendor applets, e.g. append
//         | !CommandLine=/REPLACE_WITH_VENDOR_APPLET_NAME/i
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^rundll32(\.exe)?$/i
| CommandLine=/Control_RunDLL/i
| !CommandLine=/\\(System32|SysWOW64)\\/i
| table([ComputerName, aid, UserName, ParentBaseFileName, CommandLine, SHA256HashData])
Q7 · Task-host spawning a binary from a user-writable path
CONF MEDFP MEDCOST MED

Looks for: the five-minute relaunch itself — the Task Scheduler host re-executing the loader out of a user-writable directory. Pair the output with Q3: a host appearing in both is the strongest lead this pack can produce short of Q1 or Q8. FP: per-user updaters for browsers and chat clients legitimately live in AppData and run under the task host; exclude those by name, then treat the remainder as unknown-binary-on-a-timer.

// HUNT: Scheduled-task-spawned binary in a user-writable path (loader relaunch)
// MITRE: T1053.005, T1036, T1055.012 | CONF: medium  FP: medium  COST: medium
// REQUIRES: ProcessRollup2
// FALSE POSITIVES: signed per-user updaters (browser, chat client) run from AppData under the task host
// TUNING: exclude known-good per-user updaters by name, e.g. append
//         | !FileName=/^(GoogleUpdate|MicrosoftEdgeUpdate|REPLACE_WITH_APPROVED_UPDATER)\.exe$/i
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| ParentBaseFileName=/^(svchost|taskeng|taskhostw)\.exe$/i
| ImageFileName=/\\(Users|ProgramData|Windows\\Temp)\\/i
| groupBy([aid, ComputerName, UserName, ImageFileName, SHA256HashData], function=count(as=Launches), limit=500)
| table([ComputerName, aid, UserName, ImageFileName, SHA256HashData, Launches])
Q8 · Correlation — hex-label DNS AND an update-shaped task on one host
CONF HIGHFP LOWCOST MED

Looks for: both halves of this build landing on the same aid. Either signal alone is a hunt lead; together they are this specific malware. This is the query to promote to a scheduled search first — it is the apex-independent version of Q1.

// HUNT: Same host shows hex-label DNS AND an autoupdate-style task registration
// MITRE: T1071.004, T1053.005 | CONF: high  FP: low  COST: medium
// REQUIRES: DnsRequest, ScheduledTaskRegistered
// FALSE POSITIVES: effectively none - both independent halves must land on the same aid
#event_simpleName=/DnsRequest|ScheduledTaskRegistered/
| selfJoinFilter(field=[aid], where=[
    {DomainName=/^[0-9a-f]{40,63}\./i},
    {TaskName=/autoupdate\s?#\s?\d+/i}
  ])
| table([@timestamp, ComputerName, aid, DomainName, TaskName, TaskExecutable])
Q9 · Endpoint resolving DNS directly against an external resolver
CONF MEDFP MEDCOST MED

Looks for: the egress path that makes this channel viable. The sample resolved through a public resolver, which means a web proxy never sees the traffic. This query is as much a posture audit as a hunt — the output is your list of hosts that could run a DNS tunnel today. FP: split-tunnel VPN clients, roaming laptops and agents with hard-coded resolvers are all legitimate; scope to on-network hosts and exclude your approved forwarders.

// HUNT: Endpoint talking DNS directly to an external resolver (tunnel egress path)
// MITRE: T1572, T1071.004 | CONF: medium  FP: medium  COST: medium
// REQUIRES: NetworkConnectIP4
// FALSE POSITIVES: split-tunnel VPN, roaming laptops off-network, agents with hard-coded public resolvers
// TUNING: exclude your approved forwarders and roaming fleet, e.g. append
//         | !RemoteAddressIP4=/^REPLACE_WITH_APPROVED_RESOLVERS$/
#event_simpleName=/^NetworkConnectIP4$/
| RemotePort=53
| !cidr(RemoteAddressIP4, subnet=["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16"])
| groupBy([aid, ComputerName, ContextBaseFileName, RemoteAddressIP4], function=count(as=Conns), limit=500)
| Conns > 500
| table([ComputerName, aid, ContextBaseFileName, RemoteAddressIP4, Conns])
Q10 · Published sample hashes — execution or write to disk
CONF HIGHFP LOWCOST LOW

Looks for: the six FortiGuard-published samples, whether executed or merely written. Perishable by design — the samples are repacked routinely, so treat a miss as meaningless and a hit as confirmed. The behavioural queries above are what carry this pack after the hashes go stale.

// HUNT: Published FortiGuard TrickBot DNS-tunnel sample hashes on disk or in execution
// MITRE: T1204, T1027 | CONF: high  FP: low  COST: low
// REQUIRES: ProcessRollup2, PeFileWritten
// FALSE POSITIVES: none - exact hash match
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2|PeFileWritten/
| in(SHA256HashData, values=[
    "df527a5c2fbde43816cd02f4cd49eee4bb82fb4a3c7045021360888c7d504c98",
    "6c677eb2b3ffd288083c59a13d7bb712d4754af61a5563873f76c440962346f4",
    "105f652e6b8f31c371f2385877e43b6772aff5d3168d5d4635f8a1fcbb321421",
    "33c331ededbf8ee9829895424423ce3fd17e359d2e784fcbce396aacff458cf5",
    "3b19a82e1354ac14a3da7c840cbdd0ce50db38432d78e767b36f08e45024c23d",
    "bf80245ba792992fbfe24abac33f8fd66f24cdeb5f0f21cfdf45a29d107c8d3b"])
| table([@timestamp, ComputerName, aid, UserName, FileName, ImageFileName, SHA256HashData, CommandLine])
09

CrowdStrike Custom IOA Recommendations

IOA nameTypePatternActionExclusions
PERSIST — Task name "autoupdate #" + digitsProcess Creation / Scheduled TaskTask name regex autoupdate\s?#\s?\d+Block after 7 clean days in MonitorNone expected. Run in Monitor first regardless.
PERSIST — schtasks 5-minute repeatProcess CreationImage schtasks.exe, command line contains /create and /sc minute and /mo 5DetectExclude by parent: your RMM, patch agent, imaging tooling
EXEC — Control_RunDLL outside System32Process CreationImage rundll32.exe, command line contains Control_RunDLL, image path not under \System32\ or \SysWOW64\DetectNamed vendor control-panel applets after baselining
C2 — Tunnel apexDomain IOC (IOC Management, not a Custom IOA)westurn.in and all subdomainsPreventMalware-analysis subnet, if you run one
EXEC — Known sample hashesHash IOC (IOC Management)The six SHA-256 values in §10PreventMalware-analysis subnet

Do not build a Custom IOA for the DNS shape. Custom IOAs match on process and file attributes, not on aggregate DNS name statistics, and a per-query regex IOA on DomainName would fire thousands of times a day on reputation lookups. Q2, Q3 and Q8 belong in Scheduled Searches with an aggregation window, not in the IOA engine. Q8 is the right first promotion.

10

Machine-Readable IOC Appendix

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
domain,westurn.in,prevent,high,2027-01-25,TrickBot DNS-tunnel C2 apex (FortiGuard 2026-07-22),campaign:TrickBot-DNSTunnel
sha256,DF527A5C2FBDE43816CD02F4CD49EEE4BB82FB4A3C7045021360888C7D504C98,prevent,high,2027-01-25,TrickBot DNS-tunnel sample,campaign:TrickBot-DNSTunnel
sha256,6C677EB2B3FFD288083C59A13D7BB712D4754AF61A5563873F76C440962346F4,prevent,high,2027-01-25,TrickBot DNS-tunnel sample,campaign:TrickBot-DNSTunnel
sha256,105F652E6B8F31C371F2385877E43B6772AFF5D3168D5D4635F8A1FCBB321421,prevent,high,2027-01-25,TrickBot DNS-tunnel sample,campaign:TrickBot-DNSTunnel
sha256,33C331EDEDBF8EE9829895424423CE3FD17E359D2E784FCBCE396AACFF458CF5,prevent,high,2027-01-25,TrickBot DNS-tunnel sample,campaign:TrickBot-DNSTunnel
sha256,3B19A82E1354AC14A3DA7C840CBDD0CE50DB38432D78E767B36F08E45024C23D,prevent,high,2027-01-25,TrickBot DNS-tunnel sample,campaign:TrickBot-DNSTunnel
sha256,BF80245BA792992FBFE24ABAC33F8FD66F24CDEB5F0F21CFDF45A29D107C8D3B,prevent,high,2027-01-25,TrickBot DNS-tunnel sample,campaign:TrickBot-DNSTunnel
domain,REPLACE_WITH_NEW_TUNNEL_APEX,detect,high,2027-01-25,additional apex found by your own Q2/Q3 hits,campaign:TrickBot-DNSTunnel
# DELIBERATELY ABSENT: the well-known public resolver the sample tunnelled through is NOT
# campaign infrastructure and is not listed here in any form. Never import or block it -
# it breaks name resolution and stops nothing, since any recursive resolver reaches the
# same C2. Control the egress PATH instead: see Q9 and playbook 13.2.
# DELIBERATELY ABSENT: the loader path shown in the vendor report is randomly generated per
# infection. Hunt the behaviour (Q7) instead of the filename.
Behavioural signaturesapex-independent
SIG-01  DNS name whose leftmost label is 40-63 chars of pure hex
        regex: ^[0-9a-f]{40,63}\.        (case-insensitive)
        why:   XOR-0xB9 then hex-encode then chunk at 63 chars

SIG-02  Host emits 200+ DNS names longer than 100 chars, nearly all unique
        discriminator: DistinctNames tracks LongLookups almost 1:1
        why:   ~30.7 KB/s transport means thousands of lookups per module

SIG-03  Scheduled task name matching  autoupdate\s?#\s?\d+
        why:   AppData folder name + " autoupdate #" + random number

SIG-04  Task triggers: at-startup AND repetition interval PT5M
        why:   documented persistence trigger pair

SIG-05  NTFS ADS named $TASK or $FILE on a binary in a user-writable path
        content: base64 task name / base64 executable path
        why:   config storage, no Falcon event - native sweep only

SIG-06  rundll32.exe + Control_RunDLL against a DLL outside System32/SysWOW64
        why:   module execution path (handler commands 3 and 4)

SIG-07  Task-host parent (svchost/taskeng/taskhostw) launching an unsigned
        64-bit binary from AppData / ProgramData / Windows\Temp on a 5-min cadence
Named tooling & detection stringscross-vendor pivots
Malware family      TrickBot (DNS-tunnelling build)
Fortinet AV         W64/TrickBot.WC!tr
Fortinet IPS        Trick.Botnet
Protocol lineage    anchor_dns-style registration URI in the command-0 beacon
Transport           custom DNS tunnel, packet types 0x30 / 0x31 / 0x32
Encoding            XOR key 0xB9 -> hex -> 63-char label chunking
Response encoding   3 data bytes per IPv4 answer record; index in the high 6 bits
                    of the first octet, so answers survive resolver reordering
Throughput          ~30.7 KB/s (1.2 MB module in ~40 s)
Persistence         Schedule.Service.1 COM; boot trigger + PT5M repetition
Config storage      NTFS ADS $TASK (task name) and $FILE (exe path), base64
Architecture        64-bit; runtime string decryption; API resolution by hash
DNS control auditposture checklist
[ ] Endpoints cannot reach UDP/53 or TCP/53 on anything but approved forwarders
[ ] DoH is disabled in Edge, Chrome and Firefox by policy (browser DoH bypasses
    the forwarder and hides the same channel one layer higher)
[ ] Windows DNS Server analytic logging enabled on every internal forwarder
[ ] Resolver logs retained at least as long as your hunt window (30 days min)
[ ] Per-client report exists: distinct subdomain count per parent domain
[ ] Per-client report exists: mean and p99 query-name length
[ ] NXDOMAIN rate per client is monitored (tunnels miss and retry constantly)
[ ] Newly-registered-domain category blocked or alerted at the resolver
[ ] Falcon DnsRequest retention confirmed to cover the Q2/Q3 hunt window
[ ] Scheduled-task creation is audited fleet-wide (Falcon or Event ID 4698)
[ ] Q8 promoted to a Scheduled Search with an alert
[ ] Baseline captured for Q2/Q3 BEFORE alerting, so exclusions are evidence-based
11

Detection Validation Gates

GateCheckPass criterion
G1 · Telemetry readyRun each query with its filters removed, over 24 hours, and confirm rows come backDnsRequest, ProcessRollup2 and ScheduledTaskRegistered all return data. If ScheduledTaskRegistered is empty, Q4 and Q8 are blind and you fall back to §7.2.
G2 · Retention adequateOldest DnsRequest event availableAt or beyond your intended hunt window. DNS is usually the first event class trimmed.
G3 · Benign baselineRun Q2 and Q3 for 7 days with no exclusions and record every parent domain that appearsYou can name every recurring hit. Write those names into the // TUNING: lines before any alerting.
G4 · Positive test — persistenceOn an isolated test host, register a task named Notepad autoupdate #12345 with a boot trigger and a 5-minute repeatQ4 returns it within the sensor's normal latency; Q5 returns it if you created it via schtasks.exe
G5 · Positive test — transportOn an isolated test host with a domain you control, resolve 300 names shaped <60 hex chars>.yourdomain.exampleQ2 and Q3 both surface the host. Do not run this against a domain you do not own.
G6 · CorrelationDo G4 and G5 on the same host within one windowQ8 returns that host and nothing else
G7 · Egress auditQ9 across 30 daysOutput is a known, justified list. Every unexplained host is a finding in its own right, independent of TrickBot.
G8 · PromotionQ8 as a Scheduled Search in Monitor modeZero alerts over 7 days before enabling notification. Q4 follows the same path.
12

Hardening — Tiered

Immediate — this week, no compatibility risk
  • Import the §10 CSV into Falcon IOC Management. One apex, six hashes, prevent at high. Five minutes of work and it closes the known-infrastructure path. MITRE M1031.
  • Block egress to external DNS resolvers at the perimeter. Deny UDP/53 and TCP/53 from client VLANs to anything but your forwarders. This is the control that breaks the transport outright, not just the current apex. MITRE M1037 · CIS Controls v8 4.4, 13.4.
  • Enable DNS Server analytic logging on every internal forwarder and ship it to the SIEM. Without it you cannot run the strongest version of Q2/Q3, and it is off by default. MITRE M1047 · CIS Controls v8 8.7.
  • Disable browser DoH by policy. Edge DnsOverHttpsMode=off, Chrome DnsOverHttpsMode=off, Firefox network.trr.mode=5. A browser resolving over HTTPS walks straight past the forwarder control above. MITRE M1037 · CIS Microsoft Edge Benchmark 1.1, CIS Google Chrome Benchmark.
  • Turn on scheduled-task creation auditing (Advanced Audit Policy → Object Access → Audit Other Object Access Events; Event ID 4698) wherever Falcon's ScheduledTaskRegistered is not emitting. MITRE M1047.
Near term — 1 to 4 weeks, pilot on a ring first
  • Enable the ASR rule "Block executable files from running unless they meet a prevalence, age, or trusted list criterion" (01443614-cd74-433a-b99e-2ecdc07bfc25) in Audit, then Block. Aimed squarely at a freshly repacked 64-bit loader with no prevalence. MITRE M1038 · Microsoft Security Baseline for Windows.
  • Enable ASR "Block process creations originating from PSExec and WMI commands" and "Block credential stealing from lsass.exe" to constrain what modules can do after landing. MITRE M1040.
  • WDAC or AppLocker deny rules for user-writable paths — no execution from %AppData%, %LocalAppData%, %ProgramData% or %SystemRoot%\Temp except an allowlisted set. This kills the relaunch in Q7. Pilot carefully: per-user updaters live in exactly these paths. MITRE M1038 · CIS Windows Benchmark 18.9.x.
  • Resolver-side rate limiting and NXDOMAIN alerting. A tunnel generates a distinctive NXDOMAIN and query-rate profile per client; alert on the outliers rather than trying to enumerate bad domains. MITRE M1031.
  • Block newly-registered domains at the resolver or secure web gateway. The apex in this campaign is a cheap registration, and the next one will be too. MITRE M1021.
  • Constrain PowerShell: Constrained Language Mode for standard users, script block logging on, transcription to a write-only share. Handler commands 9 and 10 route shell and PowerShell through pipes to cmd.exe, and script block logging catches what the pipe hides. MITRE M1042, M1047 · CIS Windows Benchmark 18.9.100.x.
Strategic — 1 to 3 months, architectural
  • Single authoritative DNS egress path. All resolution, including roaming and VPN clients, through forwarders you log and can policy. Roaming clients are the recurring exception that makes Q9 noisy and this threat viable. MITRE M1037.
  • Statistical DNS anomaly detection as a standing capability — per-client query-name length distribution, distinct-subdomain-per-parent ratio, NXDOMAIN rate, entropy of the leftmost label. Any DNS tunnel, not just this one, is an outlier on at least two of those four. ⚠ best-practice, no formal benchmark.
  • Application control fleet-wide (WDAC in enforced mode with a managed installer) so an unsigned 64-bit binary in a user-writable directory cannot execute at all. MITRE M1038 · Microsoft WDAC guidance, DISA Windows STIG.
  • Remove local administrator rights from standard users. Task registration under a user context is possible without it, but module deployment and injection breadth shrink sharply. MITRE M1026 · CIS Controls v8 5.4.
13

Deployable Playbooks

13.1 · Disable browser DoH (GPO-backed registry, all three browsers)

# Run as SYSTEM via GPO startup script, Intune, or your RMM. Machine-wide policy keys.
# 1. Microsoft Edge
New-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' -Force | Out-Null
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' -Name 'DnsOverHttpsMode' -Value 'off' -Type String

# 2. Google Chrome
New-Item -Path 'HKLM:\SOFTWARE\Policies\Google\Chrome' -Force | Out-Null
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Google\Chrome' -Name 'DnsOverHttpsMode' -Value 'off' -Type String

# 3. Mozilla Firefox - 5 = "off by user choice cannot be overridden by rollout"
New-Item -Path 'HKLM:\SOFTWARE\Policies\Mozilla\Firefox\DNSOverHTTPS' -Force | Out-Null
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Mozilla\Firefox\DNSOverHTTPS' -Name 'Enabled' -Value 0 -Type DWord
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Mozilla\Firefox\DNSOverHTTPS' -Name 'Locked'  -Value 1 -Type DWord

# Verify
'Edge','Chrome' | ForEach-Object {
  $p = if ($_ -eq 'Edge') { 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' } else { 'HKLM:\SOFTWARE\Policies\Google\Chrome' }
  [pscustomobject]@{ Browser = $_; DoH = (Get-ItemProperty -Path $p -Name DnsOverHttpsMode -ErrorAction SilentlyContinue).DnsOverHttpsMode }
}

13.2 · Pin endpoints to approved forwarders and block external 53 locally

# Defence in depth behind the perimeter ACL. Replace the two forwarder IPs first.
$approved = @('REPLACE_WITH_FORWARDER_1','REPLACE_WITH_FORWARDER_2')

Get-NetAdapter -Physical | Where-Object Status -eq 'Up' | ForEach-Object {
  Set-DnsClientServerAddress -InterfaceIndex $_.ifIndex -ServerAddresses $approved
}

# Block outbound 53 to anything else, from any process, on the domain profile.
New-NetFirewallRule -DisplayName 'Block external DNS (UDP)' -Direction Outbound -Action Block `
  -Protocol UDP -RemotePort 53 -Profile Domain -RemoteAddress Any -Enabled True
New-NetFirewallRule -DisplayName 'Allow approved DNS (UDP)' -Direction Outbound -Action Allow `
  -Protocol UDP -RemotePort 53 -Profile Domain -RemoteAddress $approved -Enabled True
New-NetFirewallRule -DisplayName 'Block external DNS (TCP)' -Direction Outbound -Action Block `
  -Protocol TCP -RemotePort 53 -Profile Domain -RemoteAddress Any -Enabled True
New-NetFirewallRule -DisplayName 'Allow approved DNS (TCP)' -Direction Outbound -Action Allow `
  -Protocol TCP -RemotePort 53 -Profile Domain -RemoteAddress $approved -Enabled True

# Allow rules win over block rules in Windows Firewall, so ordering is not a concern here.
# Verify
Get-NetFirewallRule -DisplayName '*DNS*' | Select-Object DisplayName, Direction, Action, Enabled

13.3 · ASR rules — prevalence gate and injection constraints

# Start in Audit (2). Review Defender operational logs for 7 days, then switch the value to 1 (Block).
$rules = @{
  '01443614-cd74-433a-b99e-2ecdc07bfc25' = 2   # Block untrusted/unprevalent executables
  'd1e49aac-8f56-4280-b9ba-993a6d77406c' = 2   # Block process creations from PSExec and WMI
  '9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2' = 2   # Block credential stealing from lsass.exe
  '56a863a9-875e-4185-98a7-b882c64b5ce5' = 2   # Block abuse of exploited vulnerable signed drivers
}
foreach ($id in $rules.Keys) {
  Add-MpPreference -AttackSurfaceReductionRules_Ids $id -AttackSurfaceReductionRules_Actions $rules[$id]
}

# Verify current state
$p = Get-MpPreference
for ($i = 0; $i -lt $p.AttackSurfaceReductionRules_Ids.Count; $i++) {
  [pscustomobject]@{ Rule = $p.AttackSurfaceReductionRules_Ids[$i]
                     Action = $p.AttackSurfaceReductionRules_Actions[$i] }
}

13.4 · Enable DNS Server analytic logging on internal forwarders

# Run on each DNS server. The channel is disabled by default and is the single
# most useful log for tunnel detection.
wevtutil sl Microsoft-Windows-DNSServer/Analytical /e:false
wevtutil sl Microsoft-Windows-DNSServer/Analytical /ms:1073741824 /rt:false
wevtutil sl Microsoft-Windows-DNSServer/Analytical /e:true

# Confirm it is collecting
wevtutil gl Microsoft-Windows-DNSServer/Analytical
(Get-WinEvent -ListLog 'Microsoft-Windows-DNSServer/Analytical').RecordCount

# Point your SIEM forwarder at the channel, then build the two standing reports:
#   - distinct subdomain count per parent domain, per client
#   - mean and p99 query-name length, per client

13.5 · Fleet sweep — persistence and ADS in one pass

# Read-only. Safe to run fleet-wide via RMM. Emits one object per finding.
$findings = New-Object System.Collections.Generic.List[object]

Get-ScheduledTask | Where-Object { $_.TaskName -match 'autoupdate\s?#\s?\d+' } | ForEach-Object {
  $findings.Add([pscustomobject]@{
    Host = $env:COMPUTERNAME; Type = 'TASK-NAME'
    Detail = "$($_.TaskPath)$($_.TaskName)"; Exec = $_.Actions.Execute })
}

Get-ScheduledTask | Where-Object { $_.Triggers.Repetition.Interval -eq 'PT5M' } | ForEach-Object {
  $exe = $_.Actions.Execute
  if ($exe -and $exe -match '\\(Users|ProgramData|Temp)\\') {
    $findings.Add([pscustomobject]@{
      Host = $env:COMPUTERNAME; Type = 'TASK-5MIN-USERPATH'
      Detail = "$($_.TaskPath)$($_.TaskName)"; Exec = $exe })
  }
}

@($env:APPDATA, $env:LOCALAPPDATA, $env:ProgramData, "$env:SystemRoot\Temp") | ForEach-Object {
  Get-ChildItem -Path $_ -Recurse -File -Force -ErrorAction SilentlyContinue |
    ForEach-Object {
      Get-Item $_.FullName -Stream * -ErrorAction SilentlyContinue |
        Where-Object { $_.Stream -in @('$TASK','$FILE') } | ForEach-Object {
          $findings.Add([pscustomobject]@{
            Host = $env:COMPUTERNAME; Type = 'ADS'
            Detail = $_.FileName; Exec = $_.Stream })
        }
    }
}

$findings | ConvertTo-Json -Compress
14

Containment Runbook

PhaseActionsOwnerEvidence to capture
0 · ConfirmValidate the hit is not a detonation VM or a researcher host. Check whether the trigger was Q1/Q10 (confirmed) or Q2/Q3 (lead). For a lead, pull the full DnsRequest history for that aid and eyeball the parent domain before escalating.L1 → L2Query output, aid, ComputerName, first-seen timestamp
1 · IsolateFalcon network-contain the host. Containment still allows sensor traffic, so telemetry continues. Do not power off — the loader runs unpacked in memory and shellcode from command 11 exists nowhere else.L2Containment timestamp; memory image if your process supports it
2 · ScopeRun Q1, Q4 and Q8 fleet-wide over the maximum retained window. Run Q9 to find every other host that could use this channel. If the apex resolved, ask the resolver team for every client that queried it.L2 / Threat huntingFull affected-host list; resolver client list
3 · PreserveCollect the loader binary and both ADS streams before deleting anything. Export the scheduled task XML. Export the host's DNS query history from the forwarder log.DFIRBinary + SHA-256, $TASK/$FILE contents, task XML, resolver log slice
4 · EradicateUnregister the task, delete the loader and its ADS, then hunt for the second stage: this is a loader, and the modules it pulled are the actual risk. Assume credential access until proven otherwise.DFIR / EndpointRemoval log; list of modules recovered
5 · Credential responseRotate credentials used interactively on the host, plus any service account it ran under. Handler commands 9 and 10 give full shell and PowerShell — treat the host as having been fully interactive for an attacker.IAMRotation ticket; list of accounts
6 · BlockImport the §10 CSV if not already done. Add any newly discovered apex to the resolver blocklist and to Falcon IOC Management. Deploy the §13.2 firewall rules fleet-wide if the egress path was open.Network / SecOpsIOC import confirmation; firewall change record
7 · RecoverReimage. A modular loader with arbitrary module delivery, injection and in-memory shellcode does not have a trustworthy cleanup path. Release from containment only after reimaging.EndpointReimage record; post-rebuild Q4/Q8 clean run
8 · Close the gapWhatever Q9 showed about that host's DNS egress is the root enabler. Fix it before closing the incident, or the next variant uses the same path.NetworkQ9 re-run showing the host now resolves only through approved forwarders
15

Detection Coverage Map

TechniqueBehaviourCQLIOA / controlCoverage
T1071.004DNS-tunnel C2 to the published apexQ1, Q10Domain + hash IOC (prevent)Good
T1071.004 / T1132.001Hex-label tunnel encoding, apex-independentQ2, Q8Scheduled SearchGood
T1572Sustained high-volume long-name DNSQ3Resolver anomaly reporting (§12 strategic)Partial — depends on DnsRequest retention covering the window
T1053.005Task named autoupdate # + digits, 5-min repeatQ4, Q5, Q8Custom IOA (block after baseline)Good
T1036Task name imitating a software updaterQ4Custom IOAGood
T1218.011rundll32 + Control_RunDLL module executionQ6Custom IOA (detect)Good
T1055.012 / T1055.013Process hollowing and doppelgänging of downloaded modulesQ7 (indirect)ASR + Falcon behavioural preventionsPartial — no discrete injection event is queried here; relies on the sensor's own prevention layer
T1564.004Config in NTFS ADS $TASK / $FILE§7.3 and §13.5 native sweepGAP — Falcon does not publish ADS stream names
T1106Raw shellcode executed inside the bot's own process (command 11)Falcon memory scanning; ASRGAP — no child process, no file write, no discrete event to query
T1059.001 / T1059.003Shell and PowerShell via anonymous pipes to cmd.exeQ7 (parent-based)Script block logging (§12 near term)Partial — piped execution produces no command line on the child
T1027Runtime string decryption, API hashing, layered packingQ10ASR prevalence rulePartial — hash-based only, defeated by a repack
T1105Module transfer over the tunnelQ2, Q3Resolver rate limitingPartial — inferred from volume, not observed as a transfer

Validation: the gates in §11 are the promotion path for this map. Nothing above moves from Partial to Good without G3 (a real baseline) and G8 (seven silent days in Monitor). The two GAP rows are honest gaps, not to-do items — ADS stream names and in-process shellcode have no Falcon event to query, so they are covered by the native sweeps in §7.3 and §13.5 and by the sensor's own prevention layer, and this pack does not pretend otherwise.

16

Hunt Summary Ticket

Copy into your case management system
TITLE:      TrickBot DNS-tunnelling variant - fleet hunt and DNS egress audit
SEVERITY:   High
SCOPE:      All Windows endpoints. Priority to any host that can reach an
            external DNS resolver directly.

HYPOTHESIS: A modular TrickBot loader is present, persisting via a Windows
            scheduled task named to imitate a software updater (" autoupdate #"
            plus a random number, boot trigger, 5-minute repeat), and running
            its entire C2 channel inside DNS queries - XOR key 0xB9, hex
            encoded, chunked at 63 characters, packet types 0x30/0x31/0x32,
            responses carried 3 bytes per IPv4 answer record.

QUERIES:    Q1  apex lookup (westurn.in)                 CONF high / FP low
            Q2  hex-label DNS encoding shape             CONF med  / FP med
            Q3  DNS length + volume anomaly per host     CONF med  / FP med
            Q4  task name "autoupdate #" + digits        CONF high / FP low
            Q5  schtasks 5-minute repeat                 CONF med  / FP med
            Q6  rundll32 Control_RunDLL outside Sys32    CONF med  / FP med
            Q7  task-host spawning user-path binary      CONF med  / FP med
            Q8  correlation: hex DNS AND autoupdate task CONF high / FP low
            Q9  direct external resolver egress          CONF med  / FP med
            Q10 published sample hashes                  CONF high / FP low

DO FIRST:   1. Import the section 10 CSV (1 domain + 6 SHA-256, prevent/high).
            2. Run Q1, Q4, Q8, Q10 over the maximum retained window.
            3. Run Q9 as a posture audit - its output is the list of hosts on
               which this channel would work today.
            4. Baseline Q2 and Q3 for 7 days BEFORE alerting on them.

FINDINGS:   [ ] Q1 hits ______   [ ] Q4 hits ______   [ ] Q8 hits ______
            [ ] Q10 hits ______  [ ] Q9 unexplained hosts ______

GAPS:       - NTFS ADS $TASK / $FILE: no Falcon event. Native sweep only (13.5).
            - Command 11 raw shellcode: in-process, no queryable artefact.
            - Piped shell/PowerShell (commands 9/10) produce no child cmdline.
            - Q3 is only as good as DnsRequest retention.

ACTIONS:    [ ] IOC CSV imported
            [ ] External DNS egress blocked at the perimeter
            [ ] Browser DoH disabled by policy
            [ ] DNS Server analytic logging enabled on all forwarders
            [ ] Q8 promoted to a Scheduled Search (Monitor, then alert)
            [ ] Q2/Q3 exclusions written from the real baseline

OWNER:      SOC / Threat Hunting
VERSION:    v0.1 - 2026-07-25
17

Changelog

v0.12026-07-25Initial draft. Built from the FortiGuard Labs write-up of 2026-07-22 plus three corroborating outlets. Ten CQL queries, five Custom IOA / IOC recommendations, tiered hardening, five deployable playbooks, nine-phase containment runbook. Ships one C2 apex and six SHA-256 samples at prevent/high; no atomic indicator was invented. The sandbox-generated loader filename and the public resolver the sample egressed through were both excluded from the pack entirely rather than carried as context — a shared public resolver address is not campaign infrastructure, and listing one anywhere the tooling reads as an indicator invites a block that breaks resolution and stops nothing.
18

References

TierSourceUsed forAccessed
1Fortinet FortiGuard Labs — Inside a TrickBot Variant Using DNS Tunneling for C2Tunnel protocol, encoding, persistence scheme, command table, all atomic indicators2026-07-25
2Infosecurity Magazine — TrickBot Ditches HTTP for DNS Tunneling in Latest VariantPacket types, chunking, throughput, 12-command set, task naming2026-07-25
3HackRead — New TrickBot Variant Spotted Using DNS to Control Infected Windows PCsC2 apex, resolver egress, ADS storage, vendor detection names, defender guidance2026-07-25
3Cyber Security News — TrickBot Turns Ordinary DNS Traffic Into a Hidden ChannelResponse-side IPv4 encoding and index-bit reassembly, ADS stream names2026-07-25
refMITRE ATT&CK T1071.004 — Application Layer Protocol: DNSTechnique mapping and mitigation IDs2026-07-25
refMITRE ATT&CK T1572 — Protocol TunnelingTechnique mapping2026-07-25
refMITRE ATT&CK T1053.005 — Scheduled TaskPersistence mapping, M1028 / M1018 / M10472026-07-25
refMITRE ATT&CK T1564.004 — NTFS File AttributesADS config-storage mapping2026-07-25
refMicrosoft — Attack Surface Reduction rules referenceASR rule GUIDs in §13.32026-07-25
refCIS BenchmarksWindows / Edge / Chrome hardening citations in §122026-07-25

Source snapshots. The verbatim fetched text of every cited primary and corroborating source is stored alongside this pack in TrickBot-DNSTunnel-Hunt-sources/ (01-fortinet.txt, 02-infosecurity.txt, 03-hackread.txt, 04-cybersecuritynews.txt). Every atomic indicator shipped in §10 traces mechanically back to those files.