← Verdict cards Cutsign
Hunt / IR + patch KEV — federal deadline passed Oct 19 Exploited since late August China-nexus: ARMORY CASTLE as of 2026-10-23

BeyondTrust Remote Support zero-day

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.

30-second read

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.

Exposed if

  • Self-hosted Remote Support 26.2.3 or earlier, internet-facing — exact affected range wording differs between sources; verify against the vendor advisory
  • Privileged Remote Access sharing the flawed code path
  • Versions older than 24.3.1 must upgrade before they can take the patch — still exposed meanwhile

Not exposed if

  • Patched with BT26-05 and hunted clean back to late August (patching alone does not evict the sudoers persistence)
  • Cloud-hosted instances patched vendor-side — but review session/audit logs all the same

Detection coverage — as of Oct 23

CategorySourceStatus
VendorBeyondTrustAdvisory 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 intelMandiant / GoogleFull hunt package — role-change and syslog-gap detections, proxy-path hunting, YARA G_Hunting_Webshell_SAWFLY_1 (the published queries render live below)
GovernmentCISAKEV Oct 16 + dedicated alert with hunt guidance (unrecognized privileged sessions, script files, log gaps, account/policy changes, new MFA registrations)
VM scannersTenableNessus plugin published Oct 20 — "RS ≤ 26.2.3 Improper Origin Validation (BT26-05)"
Qualys / Rapid7Nothing found — Rapid7 covered it as news only; verify QID / InsightVM check in console
SOC contentProphet SecurityDetection & investigation write-up published

Do now

  • Patch BT26-05 (upgrade to ≥24.3.1 first where needed); restrict appliance exposure
  • Hunt back to late August: unknown .php POSTs and WebSocket paths at the reverse proxy; user_changed events raising Jump Item/team roles; file-transfer sessions by accounts whose roles changed
  • Host: SUID binaries (/usr/bin/dbus-authd), post_boot.d additions, sudoers-template edits, haproxy.conf ACL changes
  • Treat syslog-forwarding gaps bounded by a single session as tampering evidence
  • Confirmed hit → rotate the vault's entire contents and every injected credential

Escalation triggers

  • Any webshell/SUID/persistence artifact → full IR, vault-wide rotation, lateral-movement scoping
  • /login success from residential-broadband or commercial-VPN space; off-hours admin logins
  • New ARMORY CASTLE tooling or victims published
  • Exploitation expanding beyond espionage targeting (commodity adoption)

Inferred detections — while you wait

Mandiant'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

Timeline

Sources

BeyondTrust security advisories (vendor) · BeyondTrust — BT26-05 blog · Mandiant — ARMORY CASTLE · CISA alert · BleepingComputer · The Hacker News

Public OSINT only — contains no internal data Marcus Liberto · source Lane and triggers per CIRT triage-flow draft Tracked story: Cutsign 10-16 (two deep reads) → 10-19 (KEV)