Rapid7 Analysis: Unauthenticated File Upload to RCE in BeyondTrust Remote Support (CVE-2026-102764)

Overview

An origin-validation flaw in BeyondTrust Remote Support lets an unauthenticated attacker write files on the appliance and execute code as the site's operating-system user. The entry point is the public customer connection used to start a support session. No account credentials or user interaction are required.

The flaw sits between the internal message router, brain, and an appliance-side customer worker, thin-scc. An attacker can put a reserved local router ID into a message's source address. The router forwards the message, and the worker accepts that address as proof that the request came from a local component. This gives the attacker access to an internal file-transfer service, including its native filesystem provider.

We first confirmed the file write then used the primitive to upload a PHP file to the site's public directory. Requesting that file gave us code execution as the site user. A Metasploit module now implements the chain with PHP and Unix Command targets.

We tested Remote Support 24.1.2, build 1524046, revision 4208-ddcf90715106aa0c0e19c103f8d2c412af4beff3. The test appliance had public issue-menu session starts and thin chat enabled (default). Other affected releases and fixed builds have not been established. The VM's base appliance version of Secure Remote Access, 7.1.0, is separate from the Remote Support application version. 

Impact

The attacker controls both the contents of an uploaded file and its destination path, subject to the worker's filesystem permissions. The same origin forgery also reaches the file-send operation. We used it to retrieve /proc/self/environ to determine the base installation path of the Remote Support before dropping the payload

On the tested appliance, the native file service could write into the PHP document root. The Metasploit module returns code execution in the context of msfuser, UID 1000, which establishes unauthenticated remote code execution as the site user.

Technical Analysis

We will demonstrate how an attacker can start an anonymous session and then forge a message that the appliance will accept as local. Then outline how we can reach the native file transfer service, choose the uploaded file’s contents and destination and turn the file write into PHP execution.

Addresses use Ghidra image base 0x100000. Subtract that value for the raw ELF virtual address. Function names beginning with FUN_ are Ghidra labels; descriptive names are taken from diagnostic strings where available.

Starting an anonymous customer session

We begin by obtaining two customer keys from POST /api/start_session. These are fresh public guest sessions. The keys let the client use the customer transport, but do not identify a logged-in account. Each guest connects through the /nw WebSocket endpoint, using either the native or thin customer protocol.

source/app/write_app_chooser_config:

echo "ingredi support desk customer:|$BG_app_root/data/tmp/brain.sock" # [A1]
echo "ingredi support desk customer thin:$BG_app_root/app/thin-scc-wrapper" # [A2]

app/write_app_chooser_config shows where the two customer protocols go. The native protocol reaches the brain socket at [A1]; the thin protocol launches a wrapper at [A2].

source/app/thin-scc-wrapper:

# ... omitted: protocol parsing and the alternative debug-site branch ...

quoted=$(export PHPRC="$BG_app_root/config/php-cli.ini"; echo $gskey | $ingrediRoot/app/dbquote)
if [[ $(echo "SELECT COUNT(1) FROM gw_sessions WHERE session_key = $quoted AND session_type = 'sdcust' AND (expiration IS NULL OR expiration>NOW())" | $db) != "1" ]]; then # [A3]
    echo "1 failure" >&0
    exit 0
fi

# ... omitted: reconnect throttling, customer-name setup, and settings generation ...

app/thin-scc-wrapper checks that the thin customer's key belongs to an unexpired sdcust session at [A3]. A key obtained through the public session-start endpoint passes this check.

app/thin-scc-wrapper, after the session check:

echo "0 success" >&0
workDir=`handleThinMint "$thinMint"` || exit

if [[ ! -f "$workDir/thin-scc" ]]; then
    pushd "$workDir"
    cp $ingrediRoot/app/thin-scc .
    # ... omitted: license, image, and settings.ini setup ...
else
    pushd "$workDir"
fi

export BRAIN_MELD_SOCKET_PATH=$ingrediRoot/data/tmp/brain.sock
# ... omitted: server-side environment initialization ...
./thin-scc # [A4] Starts the worker inside the appliance environment.
exitStatus=$?

The same wrapper then prepares a working directory and starts thin-scc at [A4]. This process runs on the appliance, even though it is handling an anonymous customer's thin session.

The native connection gives us a client for the internal message-routing system, referred to here as MRS. The thin connection gives us an appliance-side worker to address. Neither step requires a representative to accept the session.

A network message can claim to be local

MRS addresses have three parts: node:router:handler. Each message carries source and destination addresses in a 36-byte header made up of nine little-endian 32-bit

[…]
Content was trimmed to protect the source. Please visit the original article for the full text.

This article has been indexed from Rapid7 Cybersecurity Blog