Malware Incident Report - December 6, 2025

1. Incident Overview

Domain: redstonerdev.io (new.redstonerdev.io) Server: remote (mail.redstonerdev.io) Incident Type: Cryptomining malware via npm package injection Detection Date: December 6, 2025 Resolution Date: December 6, 2025 Status: RESOLVED - System Clean

Timeline

2. Technical Analysis

Attack Vector

Primary Infection Method: Compromised npm packages installed as "extraneous" dependencies

Malicious Packages Identified:

@emnapi/core @emnapi/runtime @emnapi/wasi-threads @napi-rs/wasm-runtime @tybys/wasm-util

These packages were NOT listed in /opt/nextjs/package.json but were present in node_modules/, indicating they were injected as dependencies of legitimate packages or installed through a compromised build process.

Malware Characteristics

Type: Cryptominer (CPU-based cryptocurrency mining) Behavior:

Files Created:

/tmp/runnv/runnv - Main cryptominer binary /tmp/runnv/config.json - Mining pool configuration (7249 bytes) /tmp/dockerd - ELF 64-bit executable (3.6 MB) /tmp/bot.1 - ELF 64-bit executable (3.6 MB) /tmp/bot - HTML redirect file (later manifestation)

Process Characteristics:

USER PID CPU COMMAND www-data 3061630 233% /tmp/runnv/runnv

Impact Assessment

Service Availability:

System Impact:

Attack Success Factors:

  1. npm package dependency confusion
  2. Automated build/deployment process
  3. Lack of real-time monitoring
  4. No package integrity verification

3. Detection Methods

Initial Symptoms

  1. Website not loading (HTTP 502)
  2. High CPU usage on server
  3. Suspicious process spawning from next-server

Verification Commands Used

# Check website status curl -I https://new.redstonerdev.io/ # Identify malware processes ps aux | grep -E 'runnv|/tmp/dockerd|/tmp/bot' # Check process tree pgrep -f 'next-server' | xargs -I {} pstree -p {} # Verify npm package integrity npm list 2>&1 | grep extraneous # Check for malware files find /tmp -type f -executable -mmin -60 ls -la /tmp/ | grep -E 'bot|dockerd|runnv'

Red Flags Identified

  1. Extraneous npm packages - Packages not in package.json
  2. Unexpected child processes - runnv spawned by next-server
  3. High CPU usage - Sustained >80% on legitimate service PID
  4. /tmp executables - ELF binaries with suspicious names
  5. HTTP 502 errors - Website service degraded despite running

4. Remediation Steps

Immediate Actions (During Active Infection)

Step 1: Kill Malware Processes

# Identify and kill malware pkill -f runnv pkill -f '/tmp/dockerd' pkill -f '/tmp/bot'

Step 2: Remove Malware Files

# Clean /tmp directory rm -rf /tmp/runnv rm -f /tmp/bot /tmp/dockerd /tmp/bot.* /tmp/nginxs /tmp/fghgf

Step 3: Remove Malicious npm Packages

cd /opt/nextjs # Manual removal of extraneous packages rm -rf node_modules/@emnapi rm -rf node_modules/@napi-rs rm -rf node_modules/@tybys

Step 4: Restart Website Service

systemctl restart website

Step 5: Verify Cleanup

# Check for malware processes ps aux | grep -E 'runnv|dockerd|bot' | grep -v grep # Verify website loading curl -I https://new.redstonerdev.io/ # Check process tree is clean pgrep -f 'next-server' | xargs -I {} pstree -p {} # Verify no extraneous packages npm list 2>&1 | grep extraneous # Verify no extraneous packages find /tmp -type f -executable -mmin -60

Why Standard Rebuild Failed

Initial Approach Attempted:

# This DID NOT work: rm -rf .next node_modules npm install npm run build

Why It Failed: The extraneous packages were being re-installed during npm install because:

  1. They were dependencies of other packages
  2. npm's dependency resolution was pulling them in
  3. package-lock.json may have cached them

Successful Approach: Manual removal of specific extraneous packages from node_modules/ followed by service restart WITHOUT full rebuild.

5. Root Cause Analysis

How Infection Occurred

Most Likely Scenario:

  1. A legitimate npm package in package.json had a compromised dependency
  2. During npm install, the dependency chain included malicious packages
  3. These packages had post-install scripts that dropped binaries
  4. The malware spawned when next-server executed

Alternative Scenarios:

Vulnerability Chain

Compromised npm Package ↓ Dependency Resolution (npm install) ↓ Post-install Script Execution ↓ Binary Drop to /tmp ↓ Execution via Node.js child_process ↓ Cryptominer Active

6. Prevention Measures Implemented

Automated Monitoring System

Created: /usr/local/bin/malware-monitor.sh

Features:

Discord Webhook: https://discord.com/api/webhooks/1446854037671510119/...

Monitoring Script Key Functions:

check_malware_processes() { local malware=$(ps aux | grep -E 'runnv|/tmp/dockerd|/tmp/bot|/tmp/nginxs|/tmp/fghgf|gfxnick|emerald.us' | grep -v grep) # ... sends Discord alert if found } check_high_cpu() { local high_cpu=$(ps aux | awk -v threshold=$CPU_THRESHOLD '$3 > threshold && $11 !~ /ps$/ && $11 !~ /awk$/ && $11 !~ /malware-monitor/ {print $1,$2,$3,$11}') # ... sends Discord alert if found } check_extraneous_packages() { local extraneous=$(npm list 2>&1 | grep extraneous | head -5) # ... sends Discord alert if found }

Cron Schedule:

*/5 * * * * /usr/local/bin/malware-monitor.sh

Security Hardening Recommendations

Package Management:

  1. ✅ Monitor for extraneous packages
  2. ⚠️ TODO: Implement npm audit in CI/CD
  3. ⚠️ TODO: Use package lock file verification
  4. ⚠️ TODO: Implement dependency scanning (Snyk, npm audit)
  5. ⚠️ TODO: Use private npm registry with scanning

Runtime Protection:

  1. ✅ Real-time monitoring with Discord alerts
  2. ⚠️ TODO: Restrict /tmp with noexec mount option
  3. ⚠️ TODO: Implement AppArmor/SELinux profiles
  4. ⚠️ TODO: Container isolation for Node.js processes
  5. ⚠️ TODO: Resource limits (CPU cgroups)

Access Control:

  1. ✅ SSH key-only authentication (local server)
  2. ⚠️ TODO: Implement SSH key-only on remote server
  3. ⚠️ TODO: Fail2ban for brute force prevention
  4. ⚠️ TODO: Firewall rules (restrict outbound connections)
  5. ⚠️ TODO: Network segmentation

7. Indicators of Compromise (IoCs)

File-Based IoCs

Malware Binaries:

/tmp/runnv/runnv /tmp/runnv/config.json /tmp/dockerd (SHA1: 37953022a68bdfc2341819ef31f501fab0d2d1c7) /tmp/bot.1 (SHA1: 37953022a68bdfc2341819ef31f501fab0d2d1c7) /tmp/nginxs /tmp/fghgf

npm Packages:

@emnapi/core @emnapi/runtime @emnapi/wasi-threads @napi-rs/wasm-runtime @tybys/wasm-util

Process-Based IoCs

Process Names:

Behavioral IoCs:

Network IoCs

Mining Pool Domains:

8. Verification and Testing

Post-Remediation Checks

System Health:

# CPU Usage top -bn1 | head -20 # Result: 85.7% idle - HEALTHY # Website Status curl -I https://new.redstonerdev.io/ # Result: HTTP 200 - HEALTHY # Process Tree pgrep -f 'next-server' | xargs -I {} pstree -p {} # Result: Only legitimate next-server threads - CLEAN # Malware Check ps aux | grep -E 'runnv|dockerd|bot' | grep -v grep # Result: No processes found - CLEAN # Extraneous Packages npm list 2>&1 | grep extraneous # Result: No extraneous packages - CLEAN # /tmp Directory find /tmp -type f -executable -mmin -60 # Result: Only 1 legitimate executable - CLEAN

Monitoring System:

# Check monitoring log tail -f /var/log/malware-monitor.log # Result: "No threats detected" every 5 minutes # Verify cron job crontab -l | grep malware # Result: */5 * * * * /usr/local/bin/malware-monitor.sh - ACTIVE

False Positive Resolution

Issue: Monitoring script initially triggered false positives on its own ps command

Fix Applied:

# Modified check_high_cpu() function to exclude monitoring processes local high_cpu=$(ps aux | awk -v threshold=$CPU_THRESHOLD '$3 > threshold && $11 !~ /ps$/ && $11 !~ /awk$/ && $11 !~ /malware-monitor/ {print $1,$2,$3,$11}')

9. Local Server Status

Server: ssh local Status: CLEAN - No infection detected Security: SSH key-only authentication Uptime: 34 days Services: Nginx, Ollama, Minecraft servers, ProcessManager

Key Differences from Remote:

10. Documentation Created

Email Server Backup Documentation

In preparation for potential system wipe, created comprehensive email server documentation:

Files Created:

  1. EMAIL-SETUP-DOCUMENTATION.md (13 KB)

  2. setup-email-server.sh (13 KB)

  3. EMAIL-SETUP-README.md (8 KB)

  4. Backed Up to /root/email-backup/ on remote server:

11. Lessons Learned

What Worked Well

  1. ✅ Quick identification of extraneous packages as root cause
  2. ✅ Process tree analysis revealed malware spawning
  3. ✅ Manual package removal bypassed reinfection cycle
  4. ✅ Automated monitoring provides ongoing protection
  5. ✅ Discord integration ensures immediate notification

What Could Be Improved

  1. ⚠️ Earlier detection (real-time vs. reactive)
  2. ⚠️ Package integrity verification before installation
  3. ⚠️ Runtime sandboxing for Node.js processes
  4. ⚠️ Automated remediation capabilities
  5. ⚠️ Better audit trail of package installations

Critical Success Factors

12. Recovery Verification Checklist

Use this checklist if malware reappears:

Detection Phase

Analysis Phase

Remediation Phase

Verification Phase

13. Emergency Contacts and Resources

Monitoring:

Key File Locations:

Malware Signatures:

# Quick check command ps aux | grep -E 'runnv|/tmp/dockerd|/tmp/bot|/tmp/nginxs|/tmp/fghgf|gfxnick|emerald.us' | grep -v grep

14. Appendix

Complete Malware Monitoring Script

Location: /usr/local/bin/malware-monitor.sh

#!/bin/bash # Malware Detection and Discord Alert Script # Monitors for high CPU usage and suspicious processes WEBHOOK_URL="https://discord.com/api/webhooks/1446854037671510119/jDJzc-Sdi-c16H_sRnYT8KWTEW0fN2E2LHzqYbGhxrr0V7l222ik1ymrXDH7MOb3xE_X" CPU_THRESHOLD=80 LOG_FILE="/var/log/malware-monitor.log" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" } send_discord_alert() { local title="$1" local description="$2" local color="$3" # decimal color (e.g., 16711680 for red, 16776960 for yellow) # Escape JSON special characters in description description=$(echo "$description" | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g') curl -H "Content-Type: application/json" \ -X POST \ -d "{ \"content\": \"@everyone\", \"embeds\": [{ \"title\": \"$title\", \"description\": \"$description\", \"color\": $color, \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\", \"footer\": { \"text\": \"Malware Monitor - $(hostname)\" } }] }" \ "$WEBHOOK_URL" 2>&1 | grep -v "^$" } # Check for high CPU usage (processes using >80% CPU) check_high_cpu() { local high_cpu=$(ps aux | awk -v threshold=$CPU_THRESHOLD '$3 > threshold && $11 !~ /ps$/ && $11 !~ /awk$/ && $11 !~ /malware-monitor/ {print $1,$2,$3,$11}') if [ -n "$high_cpu" ]; then log "ALERT: High CPU usage detected" log "$high_cpu" local details="**High CPU processes detected:**\n\`\`\`" details+="\n$high_cpu" details+="\n\`\`\`" send_discord_alert "🚨 High CPU Usage Detected" "$details" 16711680 return 1 fi return 0 } # Check for known malware processes check_malware_processes() { local malware=$(ps aux | grep -E 'runnv|/tmp/dockerd|/tmp/bot|/tmp/nginxs|/tmp/fghgf|gfxnick|emerald.us' | grep -v grep) if [ -n "$malware" ]; then log "ALERT: Malware processes detected" log "$malware" local details="**⚠️ MALWARE PROCESSES DETECTED ⚠️**\n\`\`\`" details+="\n$malware" details+="\n\`\`\`\n**Action Required:** Kill processes and clean /tmp immediately!" send_discord_alert "🔴 MALWARE DETECTED - IMMEDIATE ACTION REQUIRED" "$details" 16711680 return 1 fi return 0 } # Check for suspicious executables in /tmp check_tmp_executables() { local suspicious=$(find /tmp -type f -executable -newer /tmp -mmin -15 2>/dev/null | grep -v -E '\.socket$|^/tmp/systemd') if [ -n "$suspicious" ]; then log "ALERT: New executable files in /tmp" log "$suspicious" local details="**New executable files found in /tmp:**\n\`\`\`" details+="\n$suspicious" details+="\n\`\`\`" send_discord_alert "⚠️ Suspicious Files in /tmp" "$details" 16776960 return 1 fi return 0 } # Check for suspicious packages in node_modules check_extraneous_packages() { if [ -d "/opt/nextjs/node_modules" ]; then cd /opt/nextjs local extraneous=$(npm list 2>&1 | grep extraneous | head -5) if [ -n "$extraneous" ]; then log "WARNING: Extraneous npm packages detected" log "$extraneous" local details="**Extraneous npm packages detected:**\n\`\`\`" details+="\n$extraneous" details+="\n\`\`\`\n**Note:** These could be malicious packages" send_discord_alert "⚠️ Extraneous NPM Packages Detected" "$details" 16776960 return 1 fi fi return 0 } # Check website service status check_website_status() { if ! systemctl is-active --quiet website; then log "ALERT: Website service is not running" local status=$(systemctl status website 2>&1 | head -10) local details="**Website service is DOWN!**\n\`\`\`" details+="\n$status" details+="\n\`\`\`" send_discord_alert "🔴 Website Service Down" "$details" 16711680 return 1 fi # Check if website service has suspicious child processes local website_pid=$(pgrep -f "next-server") if [ -n "$website_pid" ]; then local children=$(pstree -p $website_pid 2>/dev/null | grep -E 'runnv|dockerd|bot') if [ -n "$children" ]; then log "ALERT: Website service has malware child processes" local details="**Website service infected with malware!**\n\`\`\`" details+="\n$children" details+="\n\`\`\`" send_discord_alert "🔴 WEBSITE SERVICE INFECTED" "$details" 16711680 return 1 fi fi return 0 } # Main monitoring logic main() { log "Starting malware scan..." local alerts=0 # Run all checks check_malware_processes || ((alerts++)) check_high_cpu || ((alerts++)) check_tmp_executables || ((alerts++)) check_extraneous_packages || ((alerts++)) check_website_status || ((alerts++)) if [ $alerts -eq 0 ]; then log "Scan complete - No threats detected" else log "Scan complete - $alerts alert(s) sent" fi } # Run main function main

System Information at Time of Incident

Server: mail.redstonerdev.io (remote) OS: Debian 12 Uptime: 27 days, 10:07 Memory: 24GB total, 2.8GB used CPU: 32 cores Website: Next.js 16.0.0 Node.js: (version from website service) Malware: Cryptominer via npm package injection

Report Prepared: December 6, 2025 Report Version: 1.0 Status: Incident Resolved - System Clean - Monitoring Active