Nginx-UI is Vulnerable to Unauthenticated Remote Code Execution via Backup Restore
Product: nginx-ui
Repository: 0xJacky/nginx-ui (branch: dev)
Vulnerability Class: Authentication Bypass → Arbitrary File Write → OS Command Injection
Affected Component: POST /api/restore
nginx-ui exposes a backup restore endpoint (POST /api/restore) that is completely unauthenticated during the first 10 minutes after process startup on any fresh installation. An unauthenticated remote attacker can upload a crafted backup archive that overwrites the application's configuration file (app.ini) and SQLite database. Because the attacker controls the restored app.ini, they can inject an arbitrary OS command into the TestConfigCmd setting. After the application automatically restarts to apply the restored config, a single follow-up request triggers that command as the user running nginx-ui — typically root in Docker deployments.
The 10-minute unauthenticated window resets on every process restart, making this exploitable not only on initial deployments but on any restart event (container restart, upgrade, health-check-triggered restart).
backup.InitRouter is called on the root group, which carries only IPWhiteList() middleware — no AuthRequired(): [1](#2-0)
The route definition: [2](#2-1)
authIfInstalled Guard Has a Time-Bounded BypassThe only authentication guard on the restore route is authIfInstalled: [3](#2-2)
It calls AuthRequired() only when InstallLockStatus() || IsInstallTimeoutExceeded() is true. Both conditions are false on a fresh install within the first 10 minutes: [4](#2-3)
InstallLockStatus() returns false because JwtSecret is "" on a fresh install and SkipInstallation defaults to false.IsInstallTimeoutExceeded() returns false for the first 10 minutes after startupTime is set in init().When both are false, authIfInstalled calls ctx.Next() with zero authentication.
EncryptedForm Middleware Is Not a Security BarrierThe EncryptedForm() middleware between authIfInstalled and RestoreBackup is optional — it only activates if the request includes an encrypted_params field. If that field is absent, it calls c.Next() immediately: [5](#2-4)
An attacker sends a plain multipart/form-data request without encrypted_params and the middleware is a no-op.
The restore handler accepts the AES key and IV directly from the attacker via the security_token form field: [6](#2-5)
The manifest integrity check derives its HMAC signing key from the attacker-supplied AES key: [7](#2-6)
Since the attacker crafts the backup and supplies the key, they can produce a valid HMAC signature for any manifest content they choose. The integrity check is self-referential and provides no security against a crafted backup.
app.ini and the SQLite Database UnconditionallyWhen restore_nginx_ui=true, restoreNginxUIConfig directly copies files from the backup onto disk with no content validation: [8](#2-7)
TestConfigCmd Is Executed as a Shell CommandAfter restore, risefront.Restart() is called, reloading app.ini: [9](#2-8)
On the next call to TestConfig(), the value of TestConfigCmd from the restored app.ini is passed verbatim to /bin/sh -c: [10](#2-9) [11](#2-10)
| Requirement | Notes |
|---|---|
| Network access to nginx-ui port | Default: 9000/tcp |
| Target is a fresh install | JwtSecret is empty in app.ini |
| Within 10 minutes of last process start | Window resets on every restart |
| IP not blocked by IPWhiteList | Default config has no IP whitelist |
The 10-minute window is not a meaningful mitigation in practice. Docker containers restart frequently due to health checks, upgrades, and orchestrator rescheduling. Any restart resets startupTime via init(), reopening the window.
```http
GET /api/install HTTP/1.1
Host: target:9000
```
Expected response confirming vulnerability:
```json
{"lock": false, "timeout": false}
```
The backup format (derived from internal/backup/backup.go) is:
```
backup-TIMESTAMP.zip ← outer ZIP (unencrypted)
├── manifest.json ← JSON manifest
├── manifest.sig ← HMAC-SHA256 of manifest.json
├── nginx-ui.zip ← AES-CBC encrypted inner ZIP
└── nginx.zip ← AES-CBC encrypted inner ZIP
```
2a. Generate a random 32-byte AES key and 16-byte IV.
2b. Create the malicious app.ini to place inside nginx-ui.zip:
```ini
[app]
JwtSecret = attacker_chosen_jwt_secret_32chars
[node]
S
This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. No user interaction is required. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality high, integrity high, availability high.
The score comes from this vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVE-2026-42238 is classified as CWE-94: Code Injection. Input is incorporated into code that the runtime evaluates, so an attacker can have their own code executed.
CVE-2026-42238 is recorded against 2 packages.
Published on 25 June 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.
github.com (Advisory)
nvd.nist.gov (Advisory)
github.com (Web)
github.com/0xjacky/nginx-ui has other advisories on record. If you are patching this one, these are worth checking on the same host:
These advisories are the same class of weakness (CWE-94: Code Injection) in other software:
Details
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Affected Packages
| Software | From version | Fixed in |
|---|---|---|
| github.com/0xjacky/nginx-ui | — | — |
| nginx-ui | — | 2.3.8 |
References
Similar Threats
Exploit Protection
CVE-2026-42238 carries CVSS 9.8 Critical rating. BotEraser checks your installation against this and other known CVE records, and blocks IPs associated with exploit activity.
Check My Site For CVE-2026-42238 →No credit card required · Results in minutes
ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.
Stay up to date with the latest from Boteraser.
We use cookies to improve your experience on our site. By using our site, you consent to cookies.
Manage your cookie preferences below:
Essential cookies enable basic functions and are necessary for the proper function of the website.
CloudFlare provides web performance and security solutions, enhancing site speed and protecting against threats.
Service URL: developers.cloudflare.com (opens in a new window)
These cookies are needed for adding comments on this website.
These cookies are used for managing login functionality on this website.
Statistics cookies collect information anonymously. This information helps us understand how visitors use our website.
Google Analytics is a powerful tool that tracks and analyzes website traffic for informed marketing decisions.
Service URL: policies.google.com (opens in a new window)
You can find more information in our Cookie Policy and Privacy Policy.