CVE-2026-102764 (BT26-05, CVSS 9.3): improper origin validation / authentication bypass in Remote Support (shared PRA code), exploited as a zero-day since late August 2026 by China-nexus espionage actor ARMORY CASTLE (UNC5221 / Warp Panda). Patched October 15; KEV-listed October 16 with a federal remediation deadline of October 19.
Verdict: for internet-facing appliances, this is assume-compromise, not patch-and-done. Six-plus weeks of pre-patch zero-day exploitation means patching only closes the door — the hunt reaches back to late August, and because the actor dumped the appliance credential vault (bt-secrets), a confirmed hit means rotating everything the appliance held: stored credentials, Jump Item targets, injected accounts. Remote-access appliances are where espionage actors live; Mandiant's hunt guidance is unusually concrete — use it.
Post-exploitation chain: SAWFLY PHP webshell (mangoapi.php, staged as app_84304470%2Ephp) → root via a SUID copy of /bin/sh planted at /usr/bin/dbus-authd → vault dump with bt-secrets → persistence via a post_boot.d script (zz-root-persist) editing the sudoers template to survive reboot and patching → Jump Item roles raised to Administrator → BARECLAW SOCKS5/WebSocket tunneler behind a modified haproxy.conf (path /uidchanarge) → logs deleted. Scope note: sources dispute whether one or two vendor-confirmed instances were compromised; treat your own exposure as independent of that count.
| Category | Source | Status |
|---|---|---|
| Vendor | BeyondTrust | Advisory BT26-05 (Critical, 9.3, public Oct 15) + blog "ARMORY CASTLE and BT26-05: What Security Teams Need to Know"; patches for supported versions (24.3.1+) |
| Threat intel | Mandiant / Google | Full hunt package — role-change and syslog-gap detections, proxy-path hunting, YARA G_Hunting_Webshell_SAWFLY_1 (the published queries render live below) |
| Government | CISA | KEV Oct 16 + dedicated alert with hunt guidance (unrecognized privileged sessions, script files, log gaps, account/policy changes, new MFA registrations) |
| VM scanners | Tenable | Nessus plugin published Oct 20 — "RS ≤ 26.2.3 Improper Origin Validation (BT26-05)" |
| Qualys / Rapid7 | Nothing found — Rapid7 covered it as news only; verify QID / InsightVM check in console | |
| SOC content | Prophet Security | Detection & investigation write-up published |
.php POSTs and WebSocket paths at the reverse proxy; user_changed events raising Jump Item/team roles; file-transfer sessions by accounts whose roles changedpost_boot.d additions, sudoers-template edits, haproxy.conf ACL changes/login success from residential-broadband or commercial-VPN space; off-hours admin loginsMandiant's published queries (live above) come first. Generic translations for environments without appliance syslog in the SIEM:
# reverse proxy / WAF in front of the appliance request.path not in (known Remote Support path set) AND (path endswith .php OR websocket upgrade) AND src not in (support staff + managed endpoint ranges)
# appliance syslog continuity (tamper canary) no events from appliance for > N minutes, where the gap starts and ends inside one admin session window → review that session's account and source
Splunk (index=*) / Elastic ES|QL (FROM logs-*) — field names follow the Splunk CIM (Web / Endpoint / Change) and ECS respectively; tune to your add-on or integration:
# reverse proxy / WAF: unknown .php and WebSocket paths, since late August
index=* earliest=-90d@d (uri_path="*.php" OR uri_path="/uidchanarge*" OR http_user_agent="*websocket*" OR "Upgrade: websocket")
| where NOT cidrmatch("<support-staff and managed-endpoint ranges>", src)
| stats count min(_time) as first max(_time) as last values(uri_path) as paths by src
# host artifacts: SUID dropper, boot persistence, vault dump, sudoers edit
index=* earliest=-90d@d (file_path="/usr/bin/dbus-authd" OR file_path="*/post_boot.d/*" OR file_path="*zz-root-persist*"
OR file_path="*/bt-secrets.log" OR file_path="/etc/sudoers*" OR file_path="*/haproxy.conf")
| stats count values(file_path) as paths values(action) as actions min(_time) as first by host
# syslog continuity canary: 5-minute gaps from the appliance
index=* "<appliance log signature>" earliest=-30d@d
| timechart span=5m count | where count=0
→ overlay each gap on /login sessions; a gap bounded by one admin session is tampering evidence
// reverse proxy / WAF: unknown .php and WebSocket paths, since late August
FROM logs-*
| WHERE @timestamp > NOW() - 90 days
| WHERE url.path LIKE "*.php" OR url.path LIKE "/uidchanarge*" OR user_agent.original LIKE "*websocket*" OR message LIKE "*Upgrade: websocket*"
| WHERE NOT CIDR_MATCH(source.ip, "<support-staff and managed-endpoint ranges>")
| STATS count = COUNT(*), first = MIN(@timestamp), last = MAX(@timestamp), paths = VALUES(url.path) BY source.ip
// host artifacts: SUID dropper, boot persistence, vault dump, sudoers edit
FROM logs-*
| WHERE @timestamp > NOW() - 90 days
| WHERE file.path == "/usr/bin/dbus-authd" OR file.path LIKE "*/post_boot.d/*" OR file.path LIKE "*zz-root-persist*"
OR file.path LIKE "*/bt-secrets.log" OR file.path LIKE "/etc/sudoers*" OR file.path LIKE "*/haproxy.conf"
| STATS count = COUNT(*), paths = VALUES(file.path), actions = VALUES(event.action), first = MIN(@timestamp) BY host.name
// syslog continuity canary: 5-minute gaps from the appliance
// (ES|QL emits no row for an empty bucket: a missing 5-minute step in this series is the gap)
FROM logs-*
| WHERE @timestamp > NOW() - 30 days AND message LIKE "*<appliance log signature>*"
| STATS count = COUNT(*) BY t = BUCKET(@timestamp, 5 minute)
| SORT t
// overlay each gap on /login sessions; a gap bounded by one admin session is tampering evidence
BeyondTrust security advisories (vendor) · BeyondTrust — BT26-05 blog · Mandiant — ARMORY CASTLE · CISA alert · BleepingComputer · The Hacker News