Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2024-39935

CVE-2024-39935: Nginx Proxy Manager RCE Vulnerability

CVE-2024-39935 is a command injection flaw in Nginx Proxy Manager that allows authenticated users with certificate privileges to execute OS commands. This post explains its technical details, affected versions, and mitigation steps.

Published:

CVE-2024-39935 Overview

CVE-2024-39935 is an OS command injection vulnerability in jc21 NGINX Proxy Manager versions before 2.11.3. The flaw resides in backend/internal/certificate.js, where untrusted input passed to the DNS provider configuration is concatenated into a shell command. An authenticated user with certificate management privileges can inject arbitrary operating system commands that execute in the context of the NGINX Proxy Manager process. The issue is tracked under CWE-78: OS Command Injection. Note that this project is not part of any NGINX software shipped by F5.

Critical Impact

Authenticated attackers with certificate management rights can achieve remote code execution on the host running NGINX Proxy Manager, leading to full compromise of the reverse proxy and any secrets it manages.

Affected Products

  • jc21 NGINX Proxy Manager versions prior to 2.11.3
  • Deployments using DNS challenge providers for Let's Encrypt certificate issuance
  • Docker and bare-metal installations exposing the administrative UI or API

Discovery Timeline

  • 2024-07-04 - CVE-2024-39935 published to NVD
  • 2026-06-17 - Last updated in NVD database

Technical Details for CVE-2024-39935

Vulnerability Analysis

The vulnerability is triggered when NGINX Proxy Manager writes DNS provider credentials to disk during Let's Encrypt certificate issuance. The backend built a shell command string by concatenating user-supplied credential content directly into a bash pipeline invoked via child_process. Although the original code attempted to escape single quotes and backslashes, the escaping was incomplete and could be bypassed with specially crafted credential values containing shell metacharacters. Successful exploitation yields command execution as the user running the NGINX Proxy Manager backend, typically root inside the container. From that position, an attacker can read TLS private keys, modify proxy routes, pivot to internal upstreams, and persist backdoors.

Root Cause

The root cause is unsafe construction of a shell command from user-controlled input. The dns_provider_credentials field, editable through the certificate management UI, was interpolated into a string executed by a shell. Any control character or unescaped quoting construct broke the single-quote sandbox and allowed attacker-controlled tokens to be interpreted by the shell.

Attack Vector

An authenticated user with certificate management privileges creates or edits a certificate using a DNS challenge. The attacker submits crafted content in the DNS provider credentials field. When the backend attempts to write the credentials file, the shell interprets the injected payload and executes attacker commands on the host.

javascript
// Fixed code from backend/internal/certificate.js (v2.11.3)
logger.info(`Requesting Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);

const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
// Vulnerable code removed:
// const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
// const credentialsCmd     = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';

// Patched code uses Node fs APIs, avoiding the shell entirely:
fs.mkdirSync('/etc/letsencrypt/credentials', { recursive: true });
fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, {mode: 0o600});

// Whether the plugin has a --<name>-credentials argument
const hasConfigArg = certificate.meta.dns_provider !== 'route53';

Source: NginxProxyManager commit 99cce7e. The patch replaces the shell pipeline with fs.mkdirSync and fs.writeFileSync, eliminating the injection sink.

Detection Methods for CVE-2024-39935

Indicators of Compromise

  • Unexpected child processes spawned by the NGINX Proxy Manager Node.js backend, particularly sh, bash, curl, or wget invocations that do not correlate with certificate renewal activity.
  • New or modified files under /etc/letsencrypt/credentials/ containing shell metacharacters or non-credential content.
  • Outbound network connections from the proxy container to unfamiliar hosts shortly after a certificate create or edit action.
  • Audit log entries in the NPM database showing certificate updates by non-administrative users with unusually large or binary dns_provider_credentials values.

Detection Strategies

  • Monitor process lineage: any process descending from the NPM Node.js backend other than certbot and its documented helpers should be treated as suspicious.
  • Inspect application logs for Requesting Let'sEncrypt certificates entries followed by shell errors or unexpected command output.
  • Baseline the DNS provider plugins normally used in your environment and alert on additions or configuration changes.

Monitoring Recommendations

  • Enable container runtime auditing (for example auditd or eBPF-based process monitoring) on hosts running NGINX Proxy Manager.
  • Forward NPM access and error logs to a central logging system and correlate certificate management API calls with process execution events.
  • Alert on writes to /etc/letsencrypt/credentials/ performed outside of scheduled renewal windows.

How to Mitigate CVE-2024-39935

Immediate Actions Required

  • Upgrade jc21 NGINX Proxy Manager to version 2.11.3 or later, which replaces the shell-based credential writer with safe Node.js file APIs.
  • Audit all user accounts with certificate management privileges and remove access for accounts that do not require it.
  • Review the certificate list and DNS provider credential entries for anomalous content that may indicate prior exploitation.
  • Rotate any secrets, API tokens, and TLS private keys stored on the host, as they must be considered exposed if compromise is suspected.

Patch Information

The fix is delivered in NGINX Proxy Manager 2.11.3. See the upstream fix in the security commit and the full change set in the v2.11.2 to v2.11.3 comparison. Additional context is available in the upstream issue discussion.

Workarounds

  • Restrict access to the NGINX Proxy Manager administrative interface to trusted networks using firewall rules or a VPN.
  • Limit certificate management privileges to a small number of trusted administrators until the patch is applied.
  • Avoid configuring DNS challenge providers with user-editable credentials on unpatched instances; prefer HTTP-01 challenges where feasible.
  • Run NPM in a hardened container with a read-only filesystem and dropped capabilities to constrain the blast radius of any command execution.
bash
# Upgrade the Docker deployment to a patched version
docker pull jc21/nginx-proxy-manager:2.11.3
docker stop nginx-proxy-manager
docker rm nginx-proxy-manager
docker run -d \
  --name nginx-proxy-manager \
  -p 80:80 -p 81:81 -p 443:443 \
  -v /path/to/data:/data \
  -v /path/to/letsencrypt:/etc/letsencrypt \
  jc21/nginx-proxy-manager:2.11.3

# Verify the running version
docker exec nginx-proxy-manager cat /app/package.json | grep version

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.