HollowGraph — Microsoft 365 Calendar as Covert C2

A .NET NativeAOT DLL implant that uses a compromised M365 mailbox calendar (via Graph API) as a two-way dead drop, with a DNS AAAA-record credential-refresh channel to cloudlanecdn[.]com. High-confidence code overlap with the Cavern framework; Group-IB does not confidently attribute.
Threat
HollowGraph (Cavern variant)
Severity
TARGETED ESPIONAGE
Type
.NET DLL implant · Cloud C2 / Exfil
Access
Compromised M365 tenant / mailbox
Version
v0.1 · 2026-07-21
Author
HuntPack
Confidence
High (1× T1 + 3× T2)
01

Executive Summary

HollowGraph is a .NET NativeAOT-compiled DLL implant reported by Group-IB on 2026-07-20. Its defining trait is that it never talks to attacker infrastructure for tasking or exfil: it drives the Microsoft Graph API against a compromised Microsoft 365 mailbox calendar, treating the calendar as a two-way dead drop. Operators plant tasking as a calendar event buried at 2050-05-13 (24 years out, so the mailbox owner never scrolls to it); the implant reads it with a get command, and exfiltrates stolen files with a send command as encrypted attachments (File{n}.txt) on new far-future events. Payloads use hybrid RSA + AES-256 with separate inbound/outbound key pairs.

A second, out-of-band channel refreshes the implant's stolen OAuth credentials (tenantId / clientId / clientSecret / target mailbox) by decoding bytes smuggled inside IPv6 AAAA records answered by the attacker domain cloudlanecdn[.]com, then writing them to an on-disk config named logAzure.txt (dressed up as a routine log). Group-IB ties the implant to the Cavern framework with high confidence (matching command grammar, the 003 debug self-command) and notes only a low-confidence overlap with Lyceum (an OilRig subgroup); it does not attribute the campaign. Roughly 12 machines were seen infected, about 3 actively communicating, with victim traffic 2026-06-03 to 2026-07-09.

Highest-value defensive angle: because the C2 rides trusted Microsoft cloud services, endpoint network detection alone is weak. The strongest signals are (1) the on-disk artifact logAzure.txt, (2) DNS AAAA lookups to cloudlanecdn[.]com and its *.p/*.q tunnel labels, and (3) M365/Entra audit evidence of an application identity writing far-future calendar events with GUID / "Event ID:" subjects and File{n}.txt attachments.

Defender priority: Hunt the M365 audit trail and the credential channel, not just the box. Enumerate application registrations holding Calendars.ReadWrite / Mail.Read application permissions, hunt calendar items dated 2050 across all mailboxes, and rotate any tenant/client secrets found in logAzure.txt. The endpoint DNS + config-file hunts here are the tripwire; the M365 audit hunts are the confirmation.

02

Source Review & Web-Hunter Notes

Four sources fetched and saved verbatim to HollowGraph-M365Calendar-Hunt-sources/ at research time. Every atomic indicator in this pack traces to snapshot 01 (Group-IB primary) and is corroborated by the T2 press. The Register carries the narrative but not the atomic IOC appendix.

#SourceTierContributed
01Group-IB — HOLLOWGRAPH blog (primary)T1Domain, 3× SHA256, logAzure.txt, Graph endpoints, get/send/003 grammar, DNS AAAA tunnel format, RSA/AES scheme, Cavern link, Lyceum pivot hash, victim stats
02The Hacker NewsT2Corroborates domain, logAzure.txt, File{n}.txt, 2050 dating, get/send, RSA+AES, Cavern Manticore note, 12/3 victims, timeline
03BleepingComputerT2Corroborates domain, logAzure.txt, AAAA 14-byte payload detail, 22:00–23:00 UTC retrieval window, Event ID:/Boss ID subject split
04The RegisterT2Narrative: 2050 dating, Graph calendar C2, 12/3 victims, Israeli targeting, Cavern (high) / Lyceum (low)

Provenance note: the SHA256 samples and the Lyceum pivot hash are carried from the Group-IB appendix (snapshot 01). No public IPs were published; the C2 is domain-only. Where a campaign artifact is a pattern rather than a literal (File{n}.txt, GUID subjects), it is shipped as a behavioral signature, not an atomic block.

03

Hunt Brief & Attack Chain

Hypothesis: If HollowGraph is active, we should see a .NET DLL host process on an endpoint that (a) drops/reads logAzure.txt, (b) issues bursts of AAAA DNS lookups to cloudlanecdn[.]com subdomains, and — in the M365 tenant — an application identity creating/reading calendar events dated 2050 with GUID / "Event ID:" subjects and File{n}.txt attachments.

#StageBehaviorTelemetry
1Foothold.NET NativeAOT DLL loaded by a host process (rundll32/regsvr32/custom)ProcessRollup2
2Config stagingReads/writes logAzure.txt holding tenantId/clientId/clientSecret + RSA keysFile-write / ProcessRollup2 CommandLine
3Credential refreshAAAA lookups to *.p/*.q.cloudlanecdn[.]com; decodes 14 bytes per IPv6 answerDnsRequest (AAAA)
4Tasking (get)Graph calendarView query for the planted 2050-05-13 event; downloads + RSA-decrypts attachmentM365 audit / Graph sign-in
5Collection + Exfil (send)Creates far-future events, uploads AES-256 encrypted File{n}.txt attachments; PATCH subject to GUID/Boss IDM365 audit (Exchange item create/update)
6EvasionAll C2 rides Graph over trusted Microsoft infra; on-disk name mimics a log; events buried at 2050Cross-source correlation
04

Consolidated IOC Table

TypeValueConfActionContext
Domaincloudlanecdn[.]comhighdetectDNS AAAA C2 / credential-refresh tunnel
SHA25675e51774b8f79e5f256eaae639635f911b3e744d4774fd6068dd980255621509highdetectHollowGraph .NET DLL implant
SHA256f3f3006f8304788251b153d53b305322b8acab0c66ec816b8d9f101bcc851da3highdetectHollowGraph .NET DLL implant
SHA256b3d0f6e4e3be395fd7cf9e8101c89963d77216578cbb117a6ac9bc3564485effhighdetectHollowGraph .NET DLL implant
FilenamelogAzure.txthighhuntOn-disk config storing creds + RSA keys
AttachmentFile{n}.txtmediumhuntEncrypted calendar-event attachments (behavioral)
Calendar date2050-05-13highhuntPlanted far-future tasking / exfil events
Event subjectEvent ID: {taskID} / Boss{..}ID{..} / bare GUIDmediumhuntget / send / operator-rename subject patterns
DNS labels*.p.cloudlanecdn[.]com / *.q.cloudlanecdn[.]comhighhuntLength (p) + data (q) tunnel query labels
Command grammarget / send / 003 (debug)mediumenrichCavern-style tasking verbs
SHA256315bdba98c6fe863d39f6afccc727e17d5aea63bf21259444fa988cae56d61c1lowpivotRelated Lyceum 2025 .NET backdoor — attribution pivot only, do NOT block
05

Affected Surface & Telemetry Matrix

SurfaceExposureTelemetryPriorityGap
Windows endpoint (.NET runtime)DLL implant host, logAzure.txt, DNS channelFalcon ProcessRollup2 / DnsRequestCriticalManaged-assembly load has limited telemetry — hunt host process
Microsoft 365 / Exchange OnlineCalendar dead drop, exfil attachmentsM365 Unified Audit Log (Exchange item ops)CriticalRequires UAL ingest; RecordType/field names parser-specific
Entra ID (Azure AD)Stolen client-credential OAuth app driving GraphEntra sign-in (service principal) + audit logsHighNon-interactive token use easy to miss without SPN sign-in ingest
Egress DNSAAAA tunnel to cloudlanecdn[.]comFalcon DnsRequest / resolver logsHighSplit-horizon / on-box resolvers may hide the loader process
06

MITRE ATT&CK Mapping

TacticTechniqueBehaviorQuery / Control
Initial Access / PersistenceT1078.004 Valid Accounts: Cloud AccountsUses stolen M365 client-credential app to reach GraphS7-B, S12
Execution / Defense EvasionT1218.011 Rundll32 / T1218.010 Regsvr32.NET DLL hosted by a signed system binaryQ7
Command & ControlT1102.002 Web Service: BidirectionalM365 calendar as two-way dead drop via GraphS7-A
Command & ControlT1071.004 Application Layer Protocol: DNSAAAA-record channel to cloudlanecdn[.]comQ3, Q4
Command & ControlT1573.001/.002 Encrypted Channel (AES / RSA)Hybrid RSA + AES-256, split in/out keysQ7 (host), S7
Defense EvasionT1036.005 Masquerading: Match Legit NamelogAzure.txt log-lookalike; cloudlanecdn "CDN" nameQ1, Q2
Credential AccessT1552.001 Unsecured Credentials in Filestenant/client secret stored in logAzure.txtQ1, Q2, S12
Collection / ExfiltrationT1567 Exfiltration Over Web ServiceStolen files as encrypted File{n}.txt calendar attachmentsS7-A
C2 / IngressT1132 Data EncodingConfig bytes encoded in IPv6 AAAA answers (14B/record)Q4
07

Native Audit-Log Hunts (M365 / Entra / Graph)

The calendar C2 lives in the Microsoft cloud, not on the wire, so the confirming evidence is in the M365 Unified Audit Log and Entra sign-in/audit logs. These run in Microsoft 365 / Purview / Sentinel (KQL over ingested logs), not Falcon CQL. Field names and RecordType values are tenant/parser specific — validate against a sample row before deploying.

S7-A · M365 Unified Audit Log — far-future calendar events by an app identity

// Microsoft 365 Unified Audit Log (Purview) / Sentinel — Graph-driven calendar dead drop
// Anomalous Exchange calendar item create/update by a client-credentials application,
// with far-future (2050) dates or HollowGraph subject/attachment patterns.
OfficeActivity
| where Operation in ("Create", "Update", "Send")
| where RecordType startswith "Exchange"          // calendar items are Exchange records
| where isnotempty(ClientAppId)                    // application-driven (non-interactive) write
| where Item has "2050"                            // planted 2050-05-13 tasking / exfil events
    or Subject matches regex @"^Event ID:|^Boss.*ID|^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-"
    or Item has "File" and Item has ".txt"         // File{n}.txt attachment naming
| project TimeGenerated, UserId, ClientAppId, ClientIP, Operation, Subject, Item
| sort by TimeGenerated desc

S7-B · Entra ID — client-credential app grant + non-interactive Graph token use

// (a) An app registration granted broad calendar/mail APPLICATION permissions or a fresh secret
AuditLogs
| where OperationName in ("Add app role assignment to service principal",
                          "Add delegated permission grant",
                          "Consent to application",
                          "Add service principal credentials")
| where Result == "success"
| project TimeGenerated, OperationName, InitiatedBy, TargetResources

// (b) Service-principal (client-credentials) Graph sign-ins spiking from one IP/ASN
AADServicePrincipalSignInLogs
| where isnotempty(AppId)
| where ResourceDisplayName == "Microsoft Graph"
| summarize calls = count() by AppId, IPAddress, bin(TimeGenerated, 1h)
| where calls >= 50                               // tune per tenant baseline

Pair these with the endpoint hunts in section 8: a host firing Q1/Q3 plus an app identity firing S7-A on the same day is a high-confidence HollowGraph confirmation.

08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.
Q1 · logAzure.txt referenced by a process
CONF MEDFP MEDCOST LOW

Looks for: any process whose command line references the on-disk config logAzure.txt. FP: legitimate tooling rarely uses this exact basename; the discriminator is logAzure.txt, not the substring "azure".

// HUNT: HollowGraph on-disk config/log basename logAzure.txt referenced by a process
// CONF: medium  FP: medium  COST: low | REQUIRES: ProcessRollup2 CommandLine
// MITRE: T1552.001 Unsecured Credentials In Files; T1036.005 Masquerading (log-lookalike)
// TUNING: exclude backup/log agents that legitimately touch files named *Azure*; require the exact basename and correlate the same aid with Q3/Q4 DNS to cloudlanecdn
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| CommandLine=/logAzure\.txt/i
| groupBy([aid, ComputerName, UserName, FileName, ParentBaseFileName], function=[count(as=Hits), collect([CommandLine], limit=5)], limit=200)
| sort(Hits, order=desc, limit=100)
Q2 · logAzure.txt written to disk (basename match)
CONF MEDFP MEDCOST LOW

Looks for: the config file being created on disk, matched by basename via FileName so there is no path slash inside the regex. FP: exclude AV quarantine / backup copy paths seen in TargetFileName.

// HUNT: HollowGraph config file logAzure.txt created on disk (BASENAME match, no path slash in regex)
// CONF: medium  FP: medium  COST: low | REQUIRES: file-write telemetry — validate event availability in tenant
// MITRE: T1552.001 Unsecured Credentials In Files
// TUNING: match by BASENAME via FileName (^logAzure\.txt$) to avoid a slash inside /.../; exclude backup/quarantine dirs in TargetFileName
#event_simpleName=/NewExecutableWritten|PeFileWritten|NewScriptWritten/
| FileName=/^logAzure\.txt$/i
| table([@timestamp, aid, ComputerName, UserName, FileName, TargetFileName], limit=200)
| sort(@timestamp, order=desc, limit=100)
Q3 · DNS to cloudlanecdn[.]com C2 domain
CONF HIGHFP LOWCOST LOW

Looks for: any DNS resolution for the attacker C2 domain or any subdomain. Atomic IOC — a hit is high confidence.

// HUNT: DNS resolution to HollowGraph C2 domain cloudlanecdn[.]com (and subdomains)
// CONF: high  FP: low  COST: low | REQUIRES: DnsRequest
// MITRE: T1071.004 Application Layer Protocol: DNS; T1568 Dynamic Resolution
#event_simpleName=DnsRequest
| DomainName=/(^|\.)cloudlanecdn\.com$/i
| groupBy([aid, ComputerName, DomainName], function=[count(as=Queries), collect([ContextBaseFileName], limit=5)], limit=500)
| sort(Queries, order=desc, limit=100)
Q4 · DNS AAAA tunnel — *.p / *.q.cloudlanecdn labels
CONF HIGHFP LOWCOST MED

Looks for: the credential-refresh tunnel — bursts of AAAA lookups to the *.p (length) and *.q (data) labels under the C2 domain. Volume threshold surfaces the beaconing cadence.

// HUNT: HollowGraph DNS-tunnel credential refresh — high-volume AAAA lookups to *.p/*.q.cloudlanecdn[.]com
// CONF: high  FP: low  COST: medium | REQUIRES: DnsRequest with RequestType (AAAA=28)
// MITRE: T1071.004 DNS; T1132 Data Encoding
#event_simpleName=DnsRequest
| DomainName=/\.(p|q)\.cloudlanecdn\.com$/i
| RequestType=28
| groupBy([aid, ComputerName], function=[count(as=AAAAQueries), count(DomainName, distinct=true, as=UniqueLabels), collect([ContextBaseFileName], limit=5)], limit=500)
| AAAAQueries >= 20
| sort(AAAAQueries, order=desc, limit=100)
Q5 · Loader process tied to cloudlanecdn DNS
CONF MEDFP MEDCOST MED

Looks for: correlates the process making the C2 DNS lookups with its process record, exposing the .NET DLL host. FP: enterprise forwarders/proxies can appear as the resolving process — exclude by ContextBaseFileName.

// HUNT: process making DNS lookups to HollowGraph C2 (ties the loading process to cloudlanecdn traffic)
// CONF: medium  FP: medium  COST: medium | REQUIRES: ProcessRollup2 + DnsRequest correlation
// MITRE: T1071.004 DNS; T1218.011 Rundll32 (DLL host)
// TUNING: exclude enterprise resolvers/proxies as the process; expect rundll32.exe/regsvr32.exe/dotnet.exe or an unusual signed host as the resolver
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2|DnsRequest/
| falconPID:=ContextProcessId
| falconPID:=TargetProcessId
| selfJoinFilter(field=[aid, falconPID], where=[{#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/}, {#event_simpleName=DnsRequest DomainName=/cloudlanecdn\.com$/i}])
| groupBy([aid, ComputerName, falconPID], function=collect([FileName, ImageFileName, CommandLine, DomainName], limit=8), limit=300)
Q6 · HollowGraph implant by SHA256
CONF HIGHFP LOWCOST LOW

Looks for: execution/load of any of the three reported HollowGraph DLL hashes. Atomic — any hit is a confirmed sample.

// HUNT: HollowGraph .NET DLL implant by SHA256 (execution / load)
// CONF: high  FP: low  COST: low | REQUIRES: ProcessRollup2 / ImageHash
// MITRE: T1204 User Execution; T1218 Signed/System Binary Proxy (DLL host)
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| in(SHA256HashData, values=["75e51774b8f79e5f256eaae639635f911b3e744d4774fd6068dd980255621509", "f3f3006f8304788251b153d53b305322b8acab0c66ec816b8d9f101bcc851da3", "b3d0f6e4e3be395fd7cf9e8101c89963d77216578cbb117a6ac9bc3564485eff"])
| table([@timestamp, aid, ComputerName, UserName, ImageFileName, SHA256HashData, CommandLine], limit=200)
Q7 · Suspicious .NET DLL host resolving external DNS
CONF MEDFP HIGHCOST MED

Looks for: a signed DLL-host binary loading a DLL from a user-writable path — the NativeAOT loader shape. FP: rundll32/regsvr32 are heavily used legitimately; gate on user-writable path and correlate with Q3/Q4 on the same host.

// HUNT: suspicious .NET DLL host (rundll32/regsvr32/dllhost/dotnet) loading a DLL from a user-writable path
// CONF: medium  FP: high  COST: medium | REQUIRES: ProcessRollup2; managed-assembly-load telemetry is limited — hunt the host process
// MITRE: T1218.010 Regsvr32; T1218.011 Rundll32; T1573 Encrypted Channel (RSA/AES)
// TUNING: require the DLL path to be user-writable (Users/ProgramData/AppData/Temp/Public) AND the same aid firing Q3/Q4; exclude System32 and Program Files signed hosts
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| in(FileName, values=["rundll32.exe", "regsvr32.exe", "dllhost.exe", "dotnet.exe"], ignoreCase=true)
| CommandLine=/\.dll/i
| ImageFileName=/\\(Users|ProgramData|AppData|Temp|Public)\\/i
| groupBy([aid, ComputerName, UserName, FileName, ImageFileName], function=[count(as=Hits), collect([CommandLine], limit=5)], limit=300)
| sort(Hits, order=desc, limit=100)
09

Custom IOA Recommendations

IOA-1 · DLL host resolving the C2 domain

Rule group: Covert C2 · Type: Network Connection / DNS Request · Action: Detect (promote to Block only after FP baseline) · Severity: High · MITRE: T1071.004.

  • Domain (PCRE): (^|\.)cloudlanecdn\.com$
  • Exclusions: none needed — atomic C2 domain.
  • Rationale: real-time detect of any host touching the DNS channel; complements Q3/Q4 retro hunts.

IOA-2 · rundll32/regsvr32 loading a DLL from a user-writable path

Rule group: LOLBIN Abuse · Type: Process Creation · Action: Detect · Severity: Medium · MITRE: T1218.010/.011.

  • Image Filename (PCRE): .*\\(rundll32|regsvr32)\.exe$
  • Command Line (PCRE): .*\\(Users|ProgramData|AppData|Temp|Public)\\.*\.dll.*
  • FP tuning: heavy legitimate use — keep in Detect; pilot on a canary group before any Block promotion.

Do not promote either IOA to Block until it has run in Detect for 14+ days with a measured FP rate under 5%. IOA-2 in particular has a high legitimate-use baseline.

10

Machine-Readable IOC Appendix

Quick-copy blocks for import and hunting. Atomic indicators (domain + hashes) trace to the saved Group-IB snapshot; pattern indicators ship as behavioral signatures, not blocks.

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
domain,cloudlanecdn[.]com,detect,high,2027-01-21,HollowGraph DNS C2 and credential-refresh tunnel,campaign:HollowGraph
sha256,75e51774b8f79e5f256eaae639635f911b3e744d4774fd6068dd980255621509,prevent,critical,2027-07-21,HollowGraph .NET NativeAOT DLL implant,campaign:HollowGraph
sha256,f3f3006f8304788251b153d53b305322b8acab0c66ec816b8d9f101bcc851da3,prevent,critical,2027-07-21,HollowGraph .NET NativeAOT DLL implant,campaign:HollowGraph
sha256,b3d0f6e4e3be395fd7cf9e8101c89963d77216578cbb117a6ac9bc3564485eff,prevent,critical,2027-07-21,HollowGraph .NET NativeAOT DLL implant,campaign:HollowGraph
# attribution pivot only (do NOT block) - related Lyceum 2025 .NET backdoor:
# 315bdba98c6fe863d39f6afccc727e17d5aea63bf21259444fa988cae56d61c1
Behavioral Signatureshunt patterns
logAzure.txt            on-disk config storing tenantId/clientId/clientSecret + RSA keys
File{n}.txt             encrypted calendar-event attachment naming (File0.txt, File1.txt, ...)
2050-05-13              far-future calendar event date for planted tasking / exfil events
Subject Event ID: {7-char taskID}   GET / tasking event subject pattern
Subject Boss{..}ID{..}              SEND / exfil event subject pattern
Subject bare GUID                   operator-recognition rename pattern
*.p.cloudlanecdn[.]com  DNS-tunnel length-query labels (AAAA)
*.q.cloudlanecdn[.]com  DNS-tunnel data-query labels (AAAA, 14 bytes/record)
command grammar: get / send / 003(debug)   Cavern-style tasking verbs
Named Malware / Toolingattribution
HollowGraph       .NET NativeAOT DLL implant (this campaign)
Cavern            modular espionage framework - HIGH-confidence code overlap
Cavern Manticore  Iranian MOIS-linked cluster associated with Cavern (per THN)
Lyceum / OilRig   Iranian-nexus subgroup - LOW-confidence overlap only, NOT attributed
M365 / Entra Audit Checklistcloud hunt
- Enumerate app registrations with Calendars.ReadWrite / Mail.Read / Mail.ReadWrite APPLICATION permissions
- Review client-secret creation (Add service principal credentials) in the last 90 days
- Hunt calendar items dated 2050 across all mailboxes (Graph: /users/{id}/calendarView)
- Revoke + rotate any tenantId/clientId/clientSecret found in logAzure.txt on affected hosts
- Conditional Access: block non-interactive client-credential flows from unmanaged IPs
- Alert on app-only Graph calendar writes (Calendars.ReadWrite used by a daemon app)
11

Detection Validation Gates

GateCheckPass criteria
Telemetry readyDnsRequest + ProcessRollup2 flowing; M365 UAL + Entra SPN sign-in ingestedNon-empty samples in a 1h window for each source
Benign baselineRun Q5/Q7 read-only for 48h; classify hitsLegit DLL-host resolvers enumerated and excluded
Positive testOn an isolated host, nslookup a benign *.p.cloudlanecdn.com-shaped label against a lab resolver; write a benign file named logAzure.txtQ1/Q2 and Q3/Q4 return the test host within 2 min
PromotionIOA FP rate measured on canary groupUnder 5% for 14 days before any Block promotion

Validate with benign payloads on isolated/canary endpoints only — never real malware, never production.

12

Hardening — Tiered

Immediate (this week)
  • Audit and lock down application permissions: revoke any app registration holding Calendars.ReadWrite / Mail.Read(Write) application scopes that isn't explicitly required. MITRE M1018; CIS M365 Foundations (app registration governance).
  • Rotate any tenant/client secrets that may be exposed; enforce short client-secret lifetimes and prefer certificate credentials. MITRE M1027.
  • Block resolution of cloudlanecdn[.]com at the resolver/proxy and add the 3 SHA256 to Falcon prevent. MITRE M1031.
Near term (1–4 weeks)
  • Conditional Access: block or challenge non-interactive client-credential Graph flows from unmanaged / unexpected IP ranges. MITRE M1032 / M1037.
  • Enable and ingest M365 Unified Audit Log + Entra service-principal sign-in logs into the SIEM if not already. MITRE M1047.
  • App-consent governance: require admin approval for new application permission grants. MITRE M1018.
Strategic (1–3 months)
  • Constrain DNS egress to approved resolvers; alert on high-volume AAAA to low-prevalence domains. MITRE M1037.
  • Application control (WDAC / AppLocker) to constrain rundll32/regsvr32 loading DLLs from user-writable paths. MITRE M1038.
  • Least-privilege review of all daemon/app-only identities touching Exchange Online. MITRE M1018.
13

Deployable Playbooks

P1 · Enumerate risky Graph application permissions (Microsoft Graph PowerShell)

Connect-MgGraph -Scopes "Application.Read.All","AuditLog.Read.All"
$risky = "Calendars.ReadWrite","Mail.Read","Mail.ReadWrite"
Get-MgServicePrincipal -All | ForEach-Object {
  $sp = $_
  Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id |
    Where-Object { $risky -contains $_.AppRoleId } |
    Select-Object @{n='App';e={$sp.DisplayName}}, AppRoleId, ResourceDisplayName
}

P2 · Hunt 2050-dated calendar events across mailboxes (Graph)

# For each in-scope mailbox, pull events in the 2050 window and flag suspicious subjects
# GET /users/{id}/calendarView?startDateTime=2050-01-01T00:00:00Z&endDateTime=2050-12-31T23:59:59Z
# Review Subject for: GUID-only, "Event ID:", "Boss..ID..", and any File{n}.txt attachment

P3 · Sinkhole / block the DNS channel

# Resolver RPZ / proxy block
# cloudlanecdn.com  CNAME  sinkhole.internal.   ; block + log all *.cloudlanecdn.com
# Alert on any client that attempted resolution prior to the block taking effect

Deploying via your RMM: the snippets above are raw PowerShell / config. Hand them to your RMM deployment tooling for admin checks, error handling, and custom-field writeback for compliance tracking.

14

Containment Runbook

PhaseActionsOwnerEvidence
IdentifyRun Q1–Q7 + S7 hunts; scope affected hosts and the compromised mailbox/app identitySOCaid list, app IDs, calendar event IDs
ContainNetwork-isolate implicated hosts; disable the abused app registration; block cloudlanecdn[.]comIR + IdentityIsolation + app-disable timestamps
EradicateRemove implant DLL + logAzure.txt; rotate tenant/client secrets; delete planted 2050 eventsIRFile hashes, rotated-secret log
RecoverRestore least-privilege app scopes; re-enable hosts after clean scanITClean-scan reports
LessonsApp-consent governance + AAAA DNS monitoring gaps closedSecurity EngConfig change tickets
15

Detection Coverage Map

TechniqueBehaviorCQLIOACoverage
T1552.001logAzure.txt config/creds on diskQ1, Q2Good
T1071.004DNS AAAA C2 to cloudlanecdnQ3, Q4IOA-1Good
T1218.010/.011.NET DLL host / loaderQ5, Q7IOA-2Partial
Known samplesSHA256 execution/loadQ6Good
T1102.002M365 calendar dead drop (Graph)S7-A (audit)Partial
T1078.004Client-credential app abuseS7-B (audit)Partial
T1573.001/.002RSA/AES managed crypto loadQ7 (proxy)GAP

Coverage: 6 of 7 behaviors have at least partial detection. The residual GAP is direct observation of managed RSA/AES assembly loading — Falcon has limited managed-assembly-load telemetry, so it is hunted indirectly via the DLL-host process (Q7). Validation gates (section 11) must pass before these are relied on operationally.

16

Hunt Summary Ticket

HUNT-TICKET · HollowGraph M365 Calendar C2
TITLE:      HollowGraph - .NET DLL implant using M365 calendar dead drop + DNS AAAA C2
SEVERITY:   Critical (targeted espionage; ~12 hosts, ~3 active)
SCOPE:      Windows endpoints (.NET) + M365/Entra tenant; egress DNS
HYPOTHESIS: Implant drops logAzure.txt, beacons AAAA to cloudlanecdn[.]com, and
            drives Graph calendar events dated 2050 with File{n}.txt attachments.
QUERIES:    Q1-Q2 logAzure.txt | Q3-Q4 cloudlanecdn DNS/AAAA | Q5 loader tie
            Q6 SHA256 | Q7 DLL host | S7-A/B M365 + Entra audit
DO FIRST:   Run Q3 (DNS) fleet-wide and S7-A (2050 calendar events) in the tenant.
            A host on both = high-confidence confirmation.
FINDINGS:   (analyst completes - queries must be run in Falcon/M365; not auto-run)
GAPS:       Direct RSA/AES assembly-load telemetry (hunted via Q7 host proxy)
ACTIONS:    Block domain + 3 SHA256; audit Graph app permissions; rotate secrets
OWNER:      SOC / IR / Identity
VERSION:    v0.1 - 2026-07-21
17

Changelog

v0.12026-07-21Initial HuntPack. 7 CQL queries + 2 M365/Entra native audit hunts + 2 Custom IOA recs, built from Group-IB primary and 3 corroborating T2 sources. All atomic IOCs provenance-traced to saved snapshots.
18

References

TierSourceUsed forAccessed
T1Group-IB — HOLLOWGRAPHPrimary IOCs, TTPs, attribution2026-07-21
T2The Hacker NewsCorroboration, Cavern Manticore note2026-07-21
T2BleepingComputerAAAA payload detail, subject split2026-07-21
T2The RegisterNarrative corroboration2026-07-21