Ten Advisories, One Release, Zero Version Numbers: nginx-ui’s 9 October Batch Points at Git Hashes
On 9 October 2026, ten security advisories were published against nginx-ui, the Go web management interface for nginx that carries 11,582 stars and 891 forks. One is critical, eight are high, one is moderate. They come from nine different credited reporters, cover nine distinct CWE classes, and were all published within a four-hour window.
Every one of them was already fixed. Eight of the ten fixes were committed on 28 July 2026 — most of them within a four-minute span — and shipped the next day. The remaining two were fixed on 1 and 4 September. The disclosure gap is roughly ten weeks for the July cluster, and that is the ordinary part of the story.
The disorienting part is that not one of the ten advisories names a version a human would recognise. Every first_patched_version field is a Go pseudo-version: strings like 1.9.10-0.20260728074146-a467ed652591. nginx-ui has never published a v1.9.10. The real fixed release is v2.5.0, and nothing in the advisory metadata says so.
The batch, and who found it
The critical one is CVE-2026-107806 (GHSA-p393-cf76-4jmr, CWE-94), reported by mamdouhmahfouz. An authenticated nginx-ui user calls POST /api/restore with a forged encrypted backup. The restore handler derives the manifest signing key from the attacker-supplied AES key, so the attacker signs their own archive, and restoreNginxUIConfig copies the restored app.ini over the live config — including protected nginx command settings such as TestConfigCmd that the settings API will not let you write. A follow-up POST /api/nginx/test then runs that command in the nginx-ui runtime context. GitHub scores it CVSS 4.0 9.0 in its own metadata and 9.4 in the vector NVD carries; both are secondary scores, and NVD has the record as Undergoing Analysis with no independent rating.
The other nine, by CVE:
- CVE-2026-107810 (8.1, CWE-59/CWE-61, Vincent550102) — backup restore follows crafted symlinks into the live nginx configuration path before restore flags are applied. Same fix commit as the critical.
- CVE-2026-107807 (8.8, CWE-312/CWE-598, captain99hook) — node secret exposed in a URL query parameter, where it lands in logs and referrers.
- CVE-2026-107811 (8.8, CWE-200/CWE-862, PLpaPLpa) —
/api/nodesleaks cluster node tokens, enabling cross-node impersonation asinitUser. - CVE-2026-107813 (8.8, CWE-862, arpitjain099) — explicitly an incomplete fix of a prior advisory (GHSA-5v7c-xpfp-p65m / CVE-2026-84315). The earlier patch wrapped the nginx, cert, dns, backup, site and stream mutation routers in
middleware.RequireSecureSession()and added reload/restart to the MCP sensitive-tool list — but never touched the physically separateapi/clusterpackage, which registers the same class of sensitive operations on the bare authenticated group. A stolen JWT with no fresh step-up still performs cluster node CRUD, reads and rewrites node token secrets, and triggers cluster-wide nginx reload. - CVE-2026-107809 (8.8, CWE-352, 1491342590) — the
AuthRequiredtoken cookie fallback enables CSRF against management APIs. - CVE-2026-107808 (8.1, CWE-287/CWE-305/CWE-308, Voilater) — a 2FA bypass with a precise shape:
Enabled2FA()returns true if either TOTP or a passkey is configured, butPOST /api/loginonly enforces a second factor whenEnabledOTP()is true. An account with a passkey and no TOTP displays as 2FA-protected in the UI and is admitted on password alone. The passkey is never requested. - CVE-2026-107812 (7.5, CWE-494, novice-22) — self-upgrade fetches the release tarball and its digest from the same endpoint and verifies only that they match, with no signature. Whoever controls that path substitutes both and gets code execution as the nginx-ui user, typically root. The
github_proxysetting also acceptshttp://. - CVE-2026-107805 (7.5, CWE-400, lujiefsi) — unauthenticated signed-request body staging writes an attacker-controlled body to a temp file and
fsyncs it before validating the digest and signature. - CVE-2026-107804 (5.3, CWE-346, lujiefsi) — the bundled reverse proxy does not preserve external client identity, so management requests look like loopback and can pass the loopback exception in an IP allowlist; failed logins from everywhere collapse onto one address and share a login-ban threshold.
Nine reporters, ten bugs, one day. The advisories themselves show no sign of a coordinated campaign — different CWEs, different subsystems, different disclosure paths — which reads more like a maintainer clearing a queue than like a single audit landing.
The pseudo-version problem
Go advisories identify versions by module semantics, and for a repository whose module path still declares v1.9.10 while its releases are tagged v2.x, the result is that every fixed version renders as a timestamp plus a commit hash. Here is what an operator actually gets, and what it means:
…20260728074146-a467ed652591→ commit a467ed65, “fix: harden backup restore trust boundaries”, 28 July 07:41:46Z (CVE-2026-107806, CVE-2026-107810)…20260728074433-a3999bd78a3b→ a3999bd7, “feat: add signed node authentication”, 07:44:33Z (CVE-2026-107807, CVE-2026-107809, CVE-2026-107813)…20260728074558-95cd21b70814→ 95cd21b7, “fix: enforce passkey-only login”, 07:45:58Z (CVE-2026-107808)…20260728091109-0ecbd106c37b→ 0ecbd106, “feat: automate node credential lifecycle”, 09:11:09Z (CVE-2026-107811)…20260728114330-580585516dd8→ 58058551, “fix: verify signed upgrade artifacts”, 11:43:30Z (CVE-2026-107812)…20260901043436-8c9b9a1aff21→ 8c9b9a1a, 1 September (CVE-2026-107805)…20260904075558-e30e331303fc→ e30e3313, 4 September (CVE-2026-107804)
We resolved each of those commits against the published release tags using the repository’s own compare endpoint. Eight of the ten fixes first appear in v2.5.0 (published 29 July 2026, 08:42:28Z). Two — CVE-2026-107805 and CVE-2026-107804 — first appear in v2.6.0 (14 September 2026). That mapping is the single most useful piece of information for anyone running this software, and it does not exist anywhere in the ten advisories.
Two advisory texts do carry the real answer in prose: the GHSA descriptions for CVE-2026-107805 and CVE-2026-107804 both say “Upgrade to Nginx UI 2.6.0 or later.” The other eight leave you with the hash. Automated tooling reads the structured field, not the prose.
The remediation release shipped a new bug
Here is where the two September fixes stop being a footnote. CVE-2026-107805 was introduced by v2.5.0 itself.
The advisory states the affected range as “2.5.0 through 2.5.x” and the OSV range opens at the pseudo-version …20260729084040-acb59fdd81db. We resolved that commit: it is acb59fdd, dated 29 July 2026 at 08:40:40Z, with the message “chore: prepare v2.5.0” — two minutes before the v2.5.0 release was published. The flaw lives in the signed-node-request path that a3999bd7 added on 28 July as the fix for three other advisories in this very batch. The new signature-authentication machinery staged the request body to disk before it verified the signature.
So an operator who did exactly the right thing on 29 July — upgraded promptly to the release that closed eight advisories — acquired an unauthenticated storage-exhaustion bug in the process, and had to wait until 14 September for v2.6.0 to close it. This is the same shape we documented in NetScaler’s CVE-2026-107406, where the fixed build from one bulletin sat inside the affected range of the next. Defensive patching has a cost that advisory metadata never shows, and it is not zero.
What the scanners see
The ten advisories are in OSV and in GitHub’s database, so Dependabot and OSV-backed scanners will flag a Go module that depends on nginx-ui. But nginx-ui is overwhelmingly deployed as a binary or container, not imported as a module, and that is the population at risk. Dependency scanning does not see a running nginx-ui on port 8080.
The Go vulnerability database is a step behind as well. The golang/vulndb triage issue for CVE-2026-107806 (issue 6741) was opened on 10 October 2026 at 00:03:04Z and still carries the NeedsTriage label, with no GO-ID assigned — so govulncheck, which reads vuln.go.dev, returns nothing for this advisory at the time of writing. We checked that issue directly; we did not survey triage status for the other nine.
None of the ten CVEs is in CISA’s Known Exploited Vulnerabilities catalog — we queried the published JSON feed (catalogVersion 2026.10.08, 1,739 entries) on 10 October and all ten are absent. Only CVE-2026-107806, CVE-2026-107804 and CVE-2026-107805 have NVD publication timestamps so far; the other seven are GitHub-reviewed advisories whose NVD records had not appeared when we checked.
What to do
- Upgrade to v2.6.0 at minimum; v2.8.4 is current. v2.5.0 closes eight of the ten and opens one. We confirmed by commit ancestry that the current release, v2.8.4 (7 October 2026), contains all ten fixes. If you are on any 2.5.x, you are still exposed to CVE-2026-107805 and CVE-2026-107804.
- Read the v2.5.0 release notes before you assume upgrading is sufficient. The maintainer’s own notice is explicit: if an installation ever ran 2.3.6 or earlier, or restored a backup from one, upgrading alone does not invalidate already-exposed credentials. Rotate
[node] Secretand[app] JwtSecreton every instance, confirm each cluster node showsPaired signaturefirst, and do not hand-replaceCrypto.Secret— it protects persisted encrypted data and needs a supported migration. - Check whether any account has a passkey but no TOTP. CVE-2026-107808 silently downgrades exactly those accounts to password-only while the UI reports them as 2FA-enabled. The reassuring badge is the bug.
- Audit the upgrade channel. If
github_proxyis set to a custom mirror — or to anything overhttp://— CVE-2026-107812 makes that mirror a root-code-execution path on the next self-upgrade. Pin it to a source you control or disable self-upgrade. - Do not rely on dependency scanning for this class of software. Management UIs deployed as containers need version inventory from the running fleet, not from a lockfile.
- Treat “incomplete fix” advisories as a signal about structure. CVE-2026-107813 exists because a middleware requirement was applied per-router rather than centrally. Whenever a fix enumerates call sites, ask what the eleventh call site is.
Verification note: the ten advisories, their CVE IDs, severities, CVSS vectors, CWE lists, credited reporters, affected ranges, fixed pseudo-versions and publication timestamps were read directly from the GitHub Advisory API and cross-checked against an OSV package query for github.com/0xJacky/Nginx-UI (42 total records, 10 published in October 2026). Fix-commit dates and messages, the “chore: prepare v2.5.0” introducing commit for CVE-2026-107805, the full release list and the repository metrics (11,582 stars, 891 forks) came from the GitHub REST API on 10 October 2026. The mapping from each fix commit to its first containing release was computed with the repository’s compare endpoint, not inferred from dates; v2.8.4 was confirmed to contain all ten. The NVD status for CVE-2026-107806 (Undergoing Analysis, CVSS 4.0 9.4 secondary) came from the NVD 2.0 API. The golang/vulndb triage state was read from issue 6741. KEV absence was checked against CISA’s published JSON feed (catalogVersion 2026.10.08, 1,739 entries). The parent advisory GHSA-5v7c-xpfp-p65m returns 404 from the global advisory API, so the incomplete-fix relationship is reported as the CVE-2026-107813 advisory text describes it. The reading that the batch resembles a maintainer clearing a queue rather than a single coordinated audit is our editorial assessment. We ran no exploit code and tested nothing.
Sources:
- GitHub Advisory — GHSA-p393-cf76-4jmr / CVE-2026-107806 (critical; backup restore app config overwrite RCE)
- GitHub Advisory — GHSA-h246-wpgf-vmq5 / CVE-2026-107813 (incomplete fix of CVE-2026-84315; api/cluster router)
- GitHub Advisory — GHSA-j3hg-9rp3-5hw9 / CVE-2026-107805 (affected from 2.5.0; fixed in 2.6.0)
- GitHub Advisory — GHSA-45gv-9wjv-xh7p / CVE-2026-107808 (passkey-without-TOTP 2FA bypass)
- NVD — CVE-2026-107806 (published 9 October 2026, Undergoing Analysis)
- nginx-ui — v2.5.0 release notes (29 July 2026; credential-rotation notice)
- nginx-ui — commit a467ed65, “harden backup restore trust boundaries” (28 July 2026)
- golang/vulndb — issue 6741, triage for GHSA-p393-cf76-4jmr (NeedsTriage as of 10 October 2026)
- CISA — Known Exploited Vulnerabilities catalog (2026.10.08, 1,739 entries; none of the ten listed)