TrickBot DNS-Tunnel Variant — Hunt Pack
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.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Forward |
|---|---|---|---|
| 1 | Fortinet / 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 |
| 2 | Infosecurity 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 |
| 3 | HackRead — 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 |
| 3 | Cyber 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.
Hunt Brief & Attack Chain
Attack chain
| # | Step | Telemetry | Hunt angle |
|---|---|---|---|
| 1 | 64-bit packed loader lands and unpacks in memory; strings decrypted at runtime, APIs resolved by hash | ProcessRollup2, PeFileWritten | Known sample hashes (Q10); unsigned binary executing from a user-writable path (Q7) |
| 2 | Registers persistence: Schedule.Service.1 COM object creates a task named <AppData folder> autoupdate #<random>, triggered at startup and repeating every 5 minutes | ScheduledTaskRegistered, ProcessRollup2 | Task-name regex (Q4); schtasks with a 5-minute repeat interval (Q5) |
| 3 | Writes task name and executable path to NTFS ADS $TASK and $FILE | — | GAP Falcon does not expose ADS stream names; covered by the native Get-Item -Stream sweep in §7 |
| 4 | Registration beacon: command 0 sent to C2 with hostname, hardware ID, OS build, over the DNS tunnel | DnsRequest | Apex lookup (Q1); hex-label shape (Q2) |
| 5 | Steady-state polling: XOR-0xB9, hex-encode, chunk at 63 chars, prepend to apex; 0x30 request / 0x31 size / 0x32 data | DnsRequest, NetworkConnectIP4 | Length + volume anomaly (Q3); direct external resolver egress (Q9) |
| 6 | Module delivery at ~30.7 KB/s; 1.2 MB payload lands in under a minute | DnsRequest, PeFileWritten | Sustained high-volume long-name DNS from one process (Q2, Q3) |
| 7 | Module execution: direct EXE, rundll32.exe + Control_RunDLL, process hollowing, process doppelgänging | ProcessRollup2 | Control_RunDLL outside System32 (Q6) |
| 8 | Hands-on-keyboard: shell and PowerShell through anonymous pipes to cmd.exe; raw shellcode executed in the bot's own memory | ProcessRollup2, ScriptControlScanTelemetry | Task-parented shell spawn (Q7); Partial in-process shellcode has no discrete event |
Hunt hypotheses (fidelity order)
| # | Hypothesis | MITRE | Events | Expected FP | Conf |
|---|---|---|---|---|---|
| H1 | A host resolves the published TrickBot tunnel apex or any subdomain of it | T1071.004 | DnsRequest | Detonation VMs, researcher hosts | High |
| H2 | A host shows both hex-label DNS and an update-shaped 5-minute task — the two halves of this build on one aid | T1071.004 + T1053.005 | DnsRequest, ScheduledTaskRegistered | Effectively none | High |
| H3 | A scheduled task exists whose name ends in the literal autoupdate # plus digits | T1053.005 | ScheduledTaskRegistered | None known — not a Microsoft convention | High |
| H4 | A host emits sustained DNS lookups whose leftmost label is a long hex run | T1132.001, T1071.004 | DnsRequest | Reputation/AV lookups that hash into the leftmost label | Medium |
| H5 | A host's DNS name-length distribution has a heavy tail no baseline host shares | T1572 | DnsRequest | CDN, device-registration and telemetry SDK names | Medium |
| H6 | schtasks.exe creates a task repeating on a 5-minute interval | T1053.005 | ProcessRollup2 | RMM agents, patch tooling, installers | Medium |
| H7 | rundll32.exe invokes Control_RunDLL against a DLL outside System32/SysWOW64 | T1218.011 | ProcessRollup2 | Third-party control-panel applets, installers | Medium |
| H8 | An endpoint talks DNS directly to an external resolver rather than an internal forwarder | T1572, T1071.004 | NetworkConnectIP4 | Split-tunnel VPN, roaming laptops, hard-coded resolvers in agents | Medium |
Consolidated IOC Table
| Type | Value | Conf | Action | Context | Expiry |
|---|---|---|---|---|---|
| domain | westurn.in | high | prevent | DNS-tunnel C2 apex. Tunnel traffic arrives as <hex>.westurn.in; block the apex and every subdomain. | 2027-01-25 |
| sha256 | DF527A5C2FBDE43816CD02F4CD49EEE4BB82FB4A3C7045021360888C7D504C98 | high | prevent | FortiGuard sample, 64-bit unpacked loader | 2027-01-25 |
| sha256 | 6C677EB2B3FFD288083C59A13D7BB712D4754AF61A5563873F76C440962346F4 | high | prevent | FortiGuard sample | 2027-01-25 |
| sha256 | 105F652E6B8F31C371F2385877E43B6772AFF5D3168D5D4635F8A1FCBB321421 | high | prevent | FortiGuard sample | 2027-01-25 |
| sha256 | 33C331EDEDBF8EE9829895424423CE3FD17E359D2E784FCBCE396AACFF458CF5 | high | prevent | FortiGuard sample | 2027-01-25 |
| sha256 | 3B19A82E1354AC14A3DA7C840CBDD0CE50DB38432D78E767B36F08E45024C23D | high | prevent | FortiGuard sample | 2027-01-25 |
| sha256 | BF80245BA792992FBFE24ABAC33F8FD66F24CDEB5F0F21CFDF45A29D107C8D3B | high | prevent | FortiGuard sample | 2027-01-25 |
| av signature | W64/TrickBot.WC!tr · IPS Trick.Botnet | high | enrich | Fortinet detection names — pivot for anyone running FortiGate/FortiClient alongside Falcon | 2026-08-25 |
| behavioural | Task name matching autoupdate # + digits, boot trigger, 5-minute repeat | high | detect | Persistence naming scheme (Q4) | persistent |
| behavioural | DNS name with a 40–63 character hex leftmost label | medium | hunt | Tunnel encoding shape (Q2). Survives apex rotation. | persistent |
| behavioural | NTFS ADS named $TASK / $FILE on the loader binary | high | hunt | Config storage. No Falcon event — sweep natively (§7). | persistent |
| placeholder | REPLACE_WITH_NEW_TUNNEL_APEX | — | hunt | Slot for any additional apex your own Q2/Q3 hits surface. Do not populate from open reporting without a source. | — |
| placeholder | REPLACE_WITH_APPROVED_RESOLVERS | — | enrich | Your 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.
Affected Surface & Telemetry Matrix
| Surface | Required telemetry | Priority | Gap risk |
|---|---|---|---|
| Windows endpoints (64-bit, all supported builds) | Falcon ProcessRollup2, SyntheticProcessRollup2 | Critical | Low — standard sensor coverage |
| Endpoint DNS resolution | Falcon DnsRequest | Critical | Medium — DnsRequest 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 registration | Falcon ScheduledTaskRegistered | High | Medium — some sensor policies do not emit this; fall back to the §7 native sweep |
| Egress to external resolvers | Falcon NetworkConnectIP4, firewall UDP/TCP 53 logs | High | Low if you log perimeter 53; high if endpoints resolve externally by design |
| Internal DNS forwarders | Windows DNS Server analytic log or resolver query log | High | High — DNS analytic logging is off by default and is the single most valuable log for this threat |
| NTFS alternate data streams | None in Falcon | Medium | GAP — native PowerShell sweep only |
| In-memory shellcode execution | Falcon memory-scanning / behavioural preventions | Medium | High — command 11 runs raw code inside the bot's own process with no child-process artefact |
ATT&CK Mapping
| Tactic | Technique | Observed behaviour | Query / control |
|---|---|---|---|
| Persistence · Privilege Escalation | T1053.005 — Scheduled Task | Schedule.Service.1 COM object registers a boot-triggered task repeating every 5 minutes, named to imitate a software updater | Q4, Q5, Q8 · M1028, M1018 |
| Command and Control | T1071.004 — Application Layer Protocol: DNS | All C2 carried in DNS queries and answer records | Q1, Q2, Q3, Q8 · M1037 |
| Command and Control | T1572 — Protocol Tunneling | Full request/response protocol (0x30/0x31/0x32) tunnelled over DNS at ~30.7 KB/s | Q3, Q9 · M1037, M1031 |
| Command and Control | T1132.001 — Standard Encoding | XOR with key 0xB9, hex encoding, 63-character label chunking | Q2 · M1031 |
| Defense Evasion | T1218.011 — Signed Binary Proxy: Rundll32 | Modules executed via rundll32.exe calling the Control_RunDLL export | Q6 · M1038, M1042 |
| Defense Evasion | T1055.012 — Process Hollowing | Downloaded module injected into a randomly chosen process | Q7 · M1040 |
| Defense Evasion | T1055.013 — Process Doppelgänging | Alternative injection path in the same command handler table | Q7 · M1040 |
| Defense Evasion | T1564.004 — NTFS File Attributes | Task name and executable path stored in ADS $TASK and $FILE | §7 native sweep · M1040 |
| Defense Evasion | T1027 — Obfuscated Files or Information | Runtime string decryption, calculated constants, API resolution by hash | Q10 · M1040 |
| Defense Evasion | T1036 — Masquerading | Task name derived from a real %AppData% folder plus autoupdate # | Q4 · M1049 |
| Execution | T1059.001 / T1059.003 — PowerShell / Windows Command Shell | Commands 9 and 10 pipe shell and PowerShell through anonymous pipes to cmd.exe | Q7 · M1042, M1038 |
| Execution | T1106 — Native API | Raw shellcode executed in the bot's own memory (command 11) | GAP · M1040 |
| Command and Control | T1105 — Ingress Tool Transfer | EXE and DLL modules pulled down over the tunnel | Q2, Q3 · M1031 |
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.
CrowdStrike LogScale CQL Hunt Queries
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.
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])
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])
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])
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])
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])
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])
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])
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])
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])
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])
CrowdStrike Custom IOA Recommendations
| IOA name | Type | Pattern | Action | Exclusions |
|---|---|---|---|---|
| PERSIST — Task name "autoupdate #" + digits | Process Creation / Scheduled Task | Task name regex autoupdate\s?#\s?\d+ | Block after 7 clean days in Monitor | None expected. Run in Monitor first regardless. |
| PERSIST — schtasks 5-minute repeat | Process Creation | Image schtasks.exe, command line contains /create and /sc minute and /mo 5 | Detect | Exclude by parent: your RMM, patch agent, imaging tooling |
| EXEC — Control_RunDLL outside System32 | Process Creation | Image rundll32.exe, command line contains Control_RunDLL, image path not under \System32\ or \SysWOW64\ | Detect | Named vendor control-panel applets after baselining |
| C2 — Tunnel apex | Domain IOC (IOC Management, not a Custom IOA) | westurn.in and all subdomains | Prevent | Malware-analysis subnet, if you run one |
| EXEC — Known sample hashes | Hash IOC (IOC Management) | The six SHA-256 values in §10 | Prevent | Malware-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.
Machine-Readable IOC Appendix
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.
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
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
[ ] 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
Detection Validation Gates
| Gate | Check | Pass criterion |
|---|---|---|
| G1 · Telemetry ready | Run each query with its filters removed, over 24 hours, and confirm rows come back | DnsRequest, ProcessRollup2 and ScheduledTaskRegistered all return data. If ScheduledTaskRegistered is empty, Q4 and Q8 are blind and you fall back to §7.2. |
| G2 · Retention adequate | Oldest DnsRequest event available | At or beyond your intended hunt window. DNS is usually the first event class trimmed. |
| G3 · Benign baseline | Run Q2 and Q3 for 7 days with no exclusions and record every parent domain that appears | You can name every recurring hit. Write those names into the // TUNING: lines before any alerting. |
| G4 · Positive test — persistence | On an isolated test host, register a task named Notepad autoupdate #12345 with a boot trigger and a 5-minute repeat | Q4 returns it within the sensor's normal latency; Q5 returns it if you created it via schtasks.exe |
| G5 · Positive test — transport | On an isolated test host with a domain you control, resolve 300 names shaped <60 hex chars>.yourdomain.example | Q2 and Q3 both surface the host. Do not run this against a domain you do not own. |
| G6 · Correlation | Do G4 and G5 on the same host within one window | Q8 returns that host and nothing else |
| G7 · Egress audit | Q9 across 30 days | Output is a known, justified list. Every unexplained host is a finding in its own right, independent of TrickBot. |
| G8 · Promotion | Q8 as a Scheduled Search in Monitor mode | Zero alerts over 7 days before enabling notification. Q4 follows the same path. |
Hardening — Tiered
- Import the §10 CSV into Falcon IOC Management. One apex, six hashes,
preventathigh. 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, ChromeDnsOverHttpsMode=off, Firefoxnetwork.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
ScheduledTaskRegisteredis not emitting. MITRE M1047.
- 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%\Tempexcept 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.
- 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.
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
Containment Runbook
| Phase | Actions | Owner | Evidence to capture |
|---|---|---|---|
| 0 · Confirm | Validate 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 → L2 | Query output, aid, ComputerName, first-seen timestamp |
| 1 · Isolate | Falcon 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. | L2 | Containment timestamp; memory image if your process supports it |
| 2 · Scope | Run 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 hunting | Full affected-host list; resolver client list |
| 3 · Preserve | Collect 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. | DFIR | Binary + SHA-256, $TASK/$FILE contents, task XML, resolver log slice |
| 4 · Eradicate | Unregister 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 / Endpoint | Removal log; list of modules recovered |
| 5 · Credential response | Rotate 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. | IAM | Rotation ticket; list of accounts |
| 6 · Block | Import 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 / SecOps | IOC import confirmation; firewall change record |
| 7 · Recover | Reimage. 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. | Endpoint | Reimage record; post-rebuild Q4/Q8 clean run |
| 8 · Close the gap | Whatever 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. | Network | Q9 re-run showing the host now resolves only through approved forwarders |
Detection Coverage Map
| Technique | Behaviour | CQL | IOA / control | Coverage |
|---|---|---|---|---|
| T1071.004 | DNS-tunnel C2 to the published apex | Q1, Q10 | Domain + hash IOC (prevent) | Good |
| T1071.004 / T1132.001 | Hex-label tunnel encoding, apex-independent | Q2, Q8 | Scheduled Search | Good |
| T1572 | Sustained high-volume long-name DNS | Q3 | Resolver anomaly reporting (§12 strategic) | Partial — depends on DnsRequest retention covering the window |
| T1053.005 | Task named autoupdate # + digits, 5-min repeat | Q4, Q5, Q8 | Custom IOA (block after baseline) | Good |
| T1036 | Task name imitating a software updater | Q4 | Custom IOA | Good |
| T1218.011 | rundll32 + Control_RunDLL module execution | Q6 | Custom IOA (detect) | Good |
| T1055.012 / T1055.013 | Process hollowing and doppelgänging of downloaded modules | Q7 (indirect) | ASR + Falcon behavioural preventions | Partial — no discrete injection event is queried here; relies on the sensor's own prevention layer |
| T1564.004 | Config in NTFS ADS $TASK / $FILE | — | §7.3 and §13.5 native sweep | GAP — Falcon does not publish ADS stream names |
| T1106 | Raw shellcode executed inside the bot's own process (command 11) | — | Falcon memory scanning; ASR | GAP — no child process, no file write, no discrete event to query |
| T1059.001 / T1059.003 | Shell and PowerShell via anonymous pipes to cmd.exe | Q7 (parent-based) | Script block logging (§12 near term) | Partial — piped execution produces no command line on the child |
| T1027 | Runtime string decryption, API hashing, layered packing | Q10 | ASR prevalence rule | Partial — hash-based only, defeated by a repack |
| T1105 | Module transfer over the tunnel | Q2, Q3 | Resolver rate limiting | Partial — 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.
Hunt Summary Ticket
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
Changelog
References
| Tier | Source | Used for | Accessed |
|---|---|---|---|
| 1 | Fortinet FortiGuard Labs — Inside a TrickBot Variant Using DNS Tunneling for C2 | Tunnel protocol, encoding, persistence scheme, command table, all atomic indicators | 2026-07-25 |
| 2 | Infosecurity Magazine — TrickBot Ditches HTTP for DNS Tunneling in Latest Variant | Packet types, chunking, throughput, 12-command set, task naming | 2026-07-25 |
| 3 | HackRead — New TrickBot Variant Spotted Using DNS to Control Infected Windows PCs | C2 apex, resolver egress, ADS storage, vendor detection names, defender guidance | 2026-07-25 |
| 3 | Cyber Security News — TrickBot Turns Ordinary DNS Traffic Into a Hidden Channel | Response-side IPv4 encoding and index-bit reassembly, ADS stream names | 2026-07-25 |
| ref | MITRE ATT&CK T1071.004 — Application Layer Protocol: DNS | Technique mapping and mitigation IDs | 2026-07-25 |
| ref | MITRE ATT&CK T1572 — Protocol Tunneling | Technique mapping | 2026-07-25 |
| ref | MITRE ATT&CK T1053.005 — Scheduled Task | Persistence mapping, M1028 / M1018 / M1047 | 2026-07-25 |
| ref | MITRE ATT&CK T1564.004 — NTFS File Attributes | ADS config-storage mapping | 2026-07-25 |
| ref | Microsoft — Attack Surface Reduction rules reference | ASR rule GUIDs in §13.3 | 2026-07-25 |
| ref | CIS Benchmarks | Windows / Edge / Chrome hardening citations in §12 | 2026-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.