An authenticated user who can create and restore a backup can craft a valid backup archive that causes the restore staging process to write attacker-controlled files into the live Nginx configuration path even when both restore_nginx and restore_nginx_ui are set to false.
The restore flow always extracts the outer archive, verifies the manifest, decrypts nginx-ui.zip and nginx.zip, and extracts both inner archives before it decides whether RestoreNginx or RestoreNginxUI should be applied. The zip extractor explicitly allows absolute symlinks when the link target is under nginx.GetConfPath() or nginx.GetModulesPath(). Later regular-file entries are then created with os.OpenFile() on the symlinked path, which follows the symlink and writes into the live path.
Relevant code paths:
This means the restore trust boundary is broken during extraction. A restore request that explicitly opted out of restoring either Nginx or Nginx UI can still modify the live Nginx configuration tree during staging.
I verified this locally in an isolated environment with a temporary package-level harness that exercised the real Backup() and Restore() implementations.
What the executed test did:
app.ini, database file, and a temporary live Nginx config directory.Backup() implementation to obtain a valid backup archive plus AES key/IV.nginx.zip, replaced it with a crafted zip containing:
link -> <live nginx conf dir>link/poc.confmanifest.json size/hash values for the modified encrypted nginx.zip and re-signed manifest.sig with the expected signing key derived from the AES key.Restore() implementation with:
RestoreNginx: falseRestoreNginxUI: false<live nginx conf dir>/poc.conf was created anyway.Observed result from the actual local verification:
false.Any deployment that allows an authenticated user to create and restore backups is affected. A crafted restore archive can modify the live Nginx configuration path before either restore toggle is honored. This can lead to persistent configuration injection, denial of service on a later reload, or other follow-on impact depending on what files the deployment later consumes from the modified path.
{
"cwe_ids": [
"CWE-59",
"CWE-61"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-09T17:07:05Z",
"nvd_published_at": null,
"severity": "HIGH"
}