GitHub Security Advisory: GHSA-fhh2-gg7w-gwpq
Package: github.com/0xJacky/Nginx-UI (GO)
Severity: CRITICAL (CVSS 0.0)
CWE: CWE-312: Cleartext Storage of Sensitive Information, CWE-347: Improper Verification of Cryptographic Signature, CWE-354: Improper Validation of Integrity Check Value
Vulnerable versions: <= 1.9.9
Patched in: unpatched

Summary: nginx-ui Backup Restore Allows Tampering with Encrypted Backups

Details:
## Summary
The `nginx-ui` backup restore mechanism allows attackers to tamper with encrypted backup archives and inject malicious configuration during restoration.

## Details
The backup format lacks a trusted integrity root. Although files are encrypted, the encryption key and IV are provided to the client and the integrity metadata (`hash_info.txt`) is encrypted using the same key. As a result, an attacker who can access the backup token can decrypt the archive, modify its contents, recompute integrity hashes, and re-encrypt the bundle.

Because the restore process does not enforce integrity verification and accepts backups even when hash mismatches are detected, the system restores attacker-controlled configuration even when integrity verification warnings are raised. In certain configurations this may lead to arbitrary command execution on the host.

The backup system is built around the following workflow:

1. Backup files are compressed into `nginx-ui.zip` and `nginx.zip`.
2. The files are encrypted using AES-256-CBC.
3. SHA-256 hashes of the encrypted files are stored in `hash_info.txt`.
4. The hash file is also encrypted with the same AES key and IV.
5. The AES key and IV are provided to the client as a "backup security token".

This architecture creates a circular trust model:

- The encryption key is available to the client.
- The integrity metadata is encrypted with that same key.
- The restore process trusts hashes contained within the backup itself.

Because the attacker can decrypt and re-encrypt all files using the provided token, they can also recompute valid hashes for any modified content.

### Environment
- **OS**: Kali Linux 6.17.10-1kali1 (6.17.10+kali-amd64)
- **Application Version**: nginx-ui v2.3.3 (513) e5da6dd (go1.26.0)
- **Deployment**: Docker Container default installation
- **Relevant Source Files**:
  - `backup_crypto.go`
  - `backup.go`
  - `restore.go`
  - `SystemRestoreContent.vue`


## PoC
1. Generate a backup and extract the security token (Key and IV) from the HTTP response headers or the `.key` file.
    <img width="1483" height="586" alt="image" src="https://github.com/user-attachments/assets/857a1b3f-ce66-4929-a165-2f28393df17f" />

2. Decrypt the `nginx-ui.zip` archive using the obtained token.
``` 
import base64
import os
import sys
import zipfile
from io import BytesIO
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

def decrypt_aes_cbc(encrypted_data: bytes, key_b64: str, iv_b64: str) -> bytes:
    key = base64.b64decode(key_b64)
    iv = base64.b64decode(iv_b64)
    
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted = cipher.decrypt(encrypted_data)
    return unpad(decrypted, AES.block_size)

def process_local_backup(file_path, token, output_dir):
    key_b64, iv_b64 = token.split(":")
    os.makedirs(output_dir, exist_ok=True)
    print(f"[*] File processing: {file_path}")
    
    with zipfile.ZipFile(file_path, 'r') as main_zip:
        main_zip.extractall(output_dir)