Overview
BRICKSTORM is a sophisticated, Go-based backdoor attributed to People's Republic of China (PRC) state-sponsored threat actors (tracked as UNC5221 / Warp Panda) that specifically targets VMware vSphere infrastructure — the vCenter Server Appliance (VCSA) and ESXi hypervisors. The malware is designed to operate entirely beneath the guest OS layer, exploiting the fundamental visibility gap created by the absence of EDR support on virtualization control planes.
Key Risks
Complete Virtualisation-Layer Compromise
Control of vCenter grants power-off, deletion, and reconfiguration of every managed VM. Domain controllers and PAM vaults are directly reachable, bypassing OS-layer controls entirely.
Silent Credential & Data Exfiltration
BRICKSTEAL scrapes Tomcat memory for SSO tokens and credentials. VM cloning allows offline NTDS.dit extraction without triggering host-based controls.
KQL Detections
The following queries target Microsoft Sentinel ingesting vSphere syslog. Each maps to the relevant MITRE ATT&CK technique.
1. SSH Enablement via VAMI (Initial Access)
Detects POST/PUT requests to the vSphere Appliance Management Interface on port 5480 enabling SSH — a common first step in BRICKSTORM deployments. Filter to exclude known privileged access workstation IPs.
Syslog
| where ProcessName has_any ("vami-httpd", "vmware-vami")
| where SyslogMessage has "5480"
| where SyslogMessage has_any ("POST", "PUT")
| where SyslogMessage has_any ("ssh", "enableSSH", "sshd")
| extend SourceIP = extract(@"(\d+\.\d+\.\d+\.\d+)", 1, SyslogMessage)
| where SourceIP !in (trusted_mgmt_ips) // Replace with your PAW IP list
| project TimeGenerated, HostName, SourceIP, SyslogMessage
| order by TimeGenerated desc
2. Transient SSO Account Lifecycle (Persistence)
Identifies accounts that are created and deleted within a 30-minute window — a hallmark of BRICKSTORM's ghost-account persistence technique used to avoid leaving permanent artefacts in vCenter SSO.
Syslog
| where ProcessName has_any ("vsphere-ui", "vmware-sso")
| where SyslogMessage has "PrincipalManagement"
| extend EventType = extract(@"(UserAdded|UserDeleted|GroupMemberAdded)", 1, SyslogMessage)
| extend AccountName = extract(@"principal[=\s]+([^\s,]+)", 1, SyslogMessage)
| summarize
Created = countif(EventType == "UserAdded"),
Deleted = countif(EventType == "UserDeleted"),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by AccountName, bin(TimeGenerated, 30m)
| where Created >= 1 and Deleted >= 1
| extend DwellMinutes = datetime_diff('minute', LastSeen, FirstSeen)
| where DwellMinutes <= 30
| project FirstSeen, AccountName, DwellMinutes, Created, Deleted
| order by FirstSeen desc
3. auditd — Suspicious Binary Execution (Execution)
Leverages Linux auditd telemetry forwarded from the VCSA to catch known BRICKSTORM binary names and executions from suspicious paths such as /tmp/ and /dev/shm/.
Syslog
| where SyslogMessage has "msg=audit"
| where SyslogMessage has_any ('key="execpriv"', 'key="privileged"')
| extend
AuditUser = extract(@'auid=(\d+)', 1, SyslogMessage),
Command = extract(@'exe="([^"]+)"', 1, SyslogMessage),
CmdArgs = extract(@'proctitle=([^\s]+)', 1, SyslogMessage)
| where Command has_any ("pg_update","vmp","vmsrc","BRICKSTORM","/tmp/","/dev/shm/")
or AuditUser == "0" // Root executions of unexpected binaries
| project TimeGenerated, HostName, AuditUser, Command, CmdArgs, SyslogMessage
| order by TimeGenerated desc
4. Startup Script Injection via sed (Persistence)
BRICKSTORM achieves persistence by patching VCSA startup scripts. This query watches auditd for writes to known init paths and is most effective when AIDE or equivalent file integrity monitoring is also in place.
Syslog
| where SyslogMessage has "msg=audit"
| where SyslogMessage has_any ('key="startup_scripts"', 'key="perm_mod"')
| extend
FilePath = extract(@'name="([^"]+)"', 1, SyslogMessage),
Syscall = extract(@'syscall=([^\s]+)', 1, SyslogMessage)
| where FilePath has_any (
"/etc/rc.local",
"/etc/rc.local.d/",
"/etc/sysconfig/init",
"/opt/vmware/etc/init.d/",
"/etc/init.d/"
)
| project TimeGenerated, HostName, FilePath, Syscall, SyslogMessage
| order by TimeGenerated desc
5. VM Clone / Export of Domain Controller (Exfiltration)
VM cloning is a preferred BRICKSTORM technique for extracting NTDS.dit offline without triggering Windows-layer controls. This query alerts on clone or export events targeting VMs with names matching domain controller and privileged infrastructure patterns.
Syslog
| where ProcessName has_any ("vpxd", "vmware-vpxd")
| where SyslogMessage has_any (
"VmClonedEvent",
"VmBeingClonedEvent",
"VmDiskHotPlugEvent",
"VmExportedEvent"
)
| extend
VMName = extract(@'vm\.name=([^\s,]+)', 1, SyslogMessage),
AdminIP = extract(@'(\d+\.\d+\.\d+\.\d+)', 1, SyslogMessage),
EventType = extract(@'(VmCloned|VmBeingCloned|VmDiskHotPlug|VmExported)Event', 1, SyslogMessage)
| where VMName has_any ("DC","DomainControl","CA","cert","PAM","vault","ADFS")
or VMName matches regex @"[Dd][Cc]\d+"
| project TimeGenerated, VMName, EventType, AdminIP, SyslogMessage
| order by TimeGenerated desc
6. Ghost NIC Addition to VM (Lateral Movement)
Adding a hidden network adapter to a VM allows BRICKSTORM to bridge isolated network segments. This query fires on NIC addition events targeting management, vSAN, or restricted port groups.
Syslog
| where ProcessName has_any ("vpxd","vmware-vpxd")
| where SyslogMessage has_any (
"VmNetworkAdapterAddedEvent",
"VmReconfiguredEvent"
)
| extend
VMName = extract(@'vm\.name=([^\s,]+)', 1, SyslogMessage),
NetworkPG = extract(@'network[=\s]+([^\s,]+)', 1, SyslogMessage),
AdminIP = extract(@'(\d+\.\d+\.\d+\.\d+)', 1, SyslogMessage)
| where NetworkPG has_any ("mgmt","management","infra","restricted","vsan","vmotion")
or SyslogMessage has "VmNetworkAdapterAddedEvent"
| project TimeGenerated, VMName, NetworkPG, AdminIP, SyslogMessage
| order by TimeGenerated desc
7. VCSA Outbound C2 Blocked (C2 Detection)
Requires a zero-trust egress policy on VCSA. Repeated blocked outbound connections from VCSA IPs to common C2 and SOCKS proxy ports indicate an active implant attempting to beacon. Tune the vcsa_management_ips variable to your environment.
Syslog
| where SyslogMessage has_any (
"VCSA_FW_DROP",
"INTERNET_BLOCKED",
"ZT_OUTBOUND_DENIED"
)
| extend
SrcIP = extract(@'SRC=(\d+\.\d+\.\d+\.\d+)', 1, SyslogMessage),
DstIP = extract(@'DST=(\d+\.\d+\.\d+\.\d+)', 1, SyslogMessage),
DstPort = extract(@'DPT=(\d+)', 1, SyslogMessage),
Proto = extract(@'PROTO=(\w+)', 1, SyslogMessage)
| where SrcIP has_any (vcsa_management_ips) // Replace with your VCSA IPs
| where DstPort in ("443","80","8080","8443","1080","9050") // Common C2 / SOCKS ports
| summarize
Count = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
DestIPs = make_set(DstIP)
by SrcIP, DstPort, Proto
| where Count > 3
| order by Count desc
8. AIDE Integrity Violation Alert (Tamper Detection)
AIDE (Advanced Intrusion Detection Environment) running on the VCSA provides a critical compensating control where EDR cannot operate. This query surfaces integrity violations in high-value paths targeted by BRICKSTORM for persistence and defence evasion.
Syslog
| where SyslogMessage has "AIDE_TRAP"
| where SyslogMessage has_any (
"differences between database",
"Changed files",
"Added files",
"Removed files"
)
| extend
ChangedFile = extract(@'(?:Changed|Added|Removed) files?: ([^\n]+)', 1, SyslogMessage)
| where ChangedFile has_any (
"/lib64","/root/.ssh","/etc/audit","/etc/audisp",
"/etc/rc.local","/opt/vmware/etc/init.d","/etc/sysconfig/init"
)
| project TimeGenerated, HostName, ChangedFile, SyslogMessage
| order by TimeGenerated desc