Threats tagged 'ghsa-33rr-wq23-g6gg'
1 threat
Most recently published first
| Severity | CVSS | Signals | Threat | Source | Published | Score and star |
|---|---|---|---|---|---|---|
| High | 8.8 | EPSS 0.2% | CVE-2026-107809nginx-ui · Cross-Site Request Forgery (CSRF) High8.8EPSS 0.2%CVE Database V5CVE-2026-107809 ## Summary Nginx-UI v2.4.3 stores the API JWT in a browser cookie named `token` and the `AuthRequired` middleware accepts that cookie as an authentication source. Because management endpoints are protected by `AuthRequired` and do not enforce a universal CSRF token or Origin/Referer check, a remote attacker can induce a logged-in administrator to submit cross-site state-changing requests. The attacker does not need to read the cross-origin response; the browser-sent cookie is enough to authenticate the request. ## Details The root cause is a trust-boundary failure between browser-managed cookies and API bearer-token authentication. In `app/src/pinia/moudule/user.ts:25-35`, the front end writes the JWT to a `token` cookie when the token changes. In `internal/middleware/middleware.go:16-36`, `getToken` first checks the `Authorization` header and `token` query parameter, then falls back to `c.Cookie("token")`. That fallback causes browser-initiated cross-site requests to carry a valid API credential automatically. The route group in `router/routers.go:87-110` applies `middleware.AuthRequired()` to many management modules, including `config.InitRouter`, `nginx.InitRouter`, `settings.InitRouter`, and backup-related handlers. For accounts without OTP/Passkey, `RequireSecureSession` does not provide a universal second factor for all state-changing operations. A representative sink is `api/config/add.go:68-78`, where an authenticated request writes Nginx configuration with `os.WriteFile` and then calls `nginx.Control(nginx.Reload)`. CORS does not mitigate this issue because preventing cross-origin response reads does not prevent cross-site form or no-cors requests from being sent with cookies. Core vulnerable code path: ```typescript // app/src/pinia/moudule/user.ts:25-35 watch(token, v => { if (v) { cookies.set('token', v, getCookieOptions(86400)) if (!shortToken.value) { void fetchShortToken() } } else { cookies.remove('token', { path: '/' }) shortToken.value = '' } }) ``` The front end stores the authentication token in a `token` cookie. The cookie is created client-side and is not shown here as HttpOnly; it becomes the credential that the back end later accepts through cookie fallback. ```go // internal/middleware/middleware.go:16-36 // getToken from header, cookie or query func getToken(c *gin.Context) (token string) { if token = c.GetHeader("Authorization"); token != "" { return } if token = c.Query("token"); token != "" { if len(token) > 16 { // Long token (base64 encoded JWT) tokenBytes, _ := base64.StdEncoding.DecodeString(token) return string(tokenBytes) } // Short token (16 characters) return token } if token, _ = c.Cookie("token"); token != "" { return token } return "" } ``` `getToken` accepts the `token` cookie as an authentication credential after checking the header and query parameter. That behavior lets a browser-sent cookie authenticate state-changing API requests triggered from another origin. ```go // router/routers.go:87-110 // Authorization required and not websocket request g := root.Group("/", middleware.AuthRequired(), middleware.Proxy()) { debug.InitRouter(g) user.InitUserRouter(g) analytic.InitRouter(g) user.InitManageUserRouter(g) nginx.InitRouter(g) sites.InitRouter(g) streams.InitRouter(g) config.InitRouter(g) template.InitRouter(g) certificate.InitCertificateRouter(g) certificate.InitDNSCredentialRouter(g) certificate.InitAcmeUserRouter(g) dnsapi.InitRouter(g) system.InitPrivateRouter(g) settings.InitRouter(g) llm.InitRouter(g) cluster.InitRouter(g) notification.InitRouter(g) external_notify.InitRouter(g) backup.InitAutoBackupRouter(g) nginxLog.InitRouter(g) upstream.InitHTTPRouter(g) ``` Many management modules, including Nginx, configuration, settings, backup, and user management, are placed behind `AuthRequired`. The cookie fallback therefore protects high-impact state-changing routes, not only read-only endpoints. ```go // api/config/add.go:68-78 err = os.WriteFile(path, []byte(content), 0644) if err != nil { cosy.ErrHandler(c, err) return } res := nginx.Control(nginx.Reload) if res.IsError() { res.RespError(c) return } ``` This representative sink shows why the CSRF is high impact: once authenticated through the cookie fallback, a request can write Nginx configuration content and trigger a reload. ## POC Prerequisites: an administrator is logged in to Nginx-UI and has a browser `token` cookie; the administrator account has not enabled OTP/Passkey, or the chosen target endpoint does not require secure-session proof; the attacker knows the Nginx-UI management URL and can induce the administrator to visit an attacker-controlled page. Reproduction: (1) Host an attacker page at `GET /csrf.html` that automatically submits a cross-site request to the victim instance. (2) The page submits `POST /api/configs` | CVE Database V5 |
Showing 1–1 of 1 result