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

CVE-2026-107808 — Nginx-UI

HIGHFix: 0xJacky/nginx-ui@95cd21b

CVE-2026-107808 is a high-severity (CVSS 8.1) Improper Authentication 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: Authentication bypass: password login does not enforce a passkey-only second factor (2FA bypass)

Also known asGHSA-45gv-9wjv-xh7pGO-2026-6704
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-107808.

EPSS Exploitation Probability

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

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

How urgent is this, really

CVE-2026-107808 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

nginx-ui supports two second-factor methods — TOTP (OTP) and WebAuthn passkeys — and reports an account as 2FA-enabled when either is configured. However, the password login endpoint (POST /api/login) only enforces a second factor when a TOTP secret is present. An account that has registered a passkey but no TOTP is logged in after password verification alone — the passkey is never requested. This silently downgrades a passkey-protected account to single-factor (password-only) authentication.

Details

The account's 2FA policy treats passkeys as a valid factor (model/user.go):

func (u *User) EnabledOTP() bool     { return len(u.OTPSecret) != 0 }
func (u *User) EnabledPasskey() bool { /* true if a passkey row exists */ }
func (u *User) Enabled2FA() bool     { return u.EnabledOTP() || u.EnabledPasskey() }

func (u *User) AfterFind(_ *gorm.DB) error {
    u.EnabledTwoFA = u.Enabled2FA() // exposed to the UI as `enabled_2fa`
    return nil
}

But the login handler only checks EnabledOTP() (api/user/auth.go, Login):

u, err := user.Login(json.Name, json.Password)
...
if u.EnabledOTP() {                       // <-- only TOTP is enforced
    if json.OTP == "" && json.RecoveryCode == "" {
        c.JSON(http.StatusOK, LoginResponse{Message: "The user has enabled 2FA", Code: Enabled2FA}) // 199
        user.BanIP(clientIP)
        return
    }
    if _, err = user.VerifyOTP(u, json.OTP, json.RecoveryCode); err != nil { /* ... */ }
    secureSessionID = user.SetSecureSessionID(u.ID)
}

// Passkey-only accounts fall through to here and receive a full session token:
accessToken, err := user.GenerateJWT(u)

No branch requires a WebAuthn assertion during password login when EnabledPasskey() is true. The root cause is the mismatch between the policy definition (Enabled2FA() = OTP or passkey) and the enforcement check (EnabledOTP() only).

The same EnabledOTP()-only gating in RequireSecureSession() (internal/middleware/secure_session.go) means passkey-only users are also exempted from step-up on sensitive actions.

Proof of Concept

Tested against nginx-ui built from source (go build -tags unembed) on 127.0.0.1:9000, with WebAuthn configured and a victim account that has a registered passkey and no TOTP. The vulnerable code is identical on the production main branch (api/user/auth.go Login gates on u.EnabledOTP() only; verified at commit 6c86e5a, 2026-05-17).

Account state advertised by GET /api/2fa_status:

{"enabled":true,"otp_status":false,"passkey_status":true, ...}

Attacker logs in with password only (POST /api/login, encrypted params as the client normally sends):

HTTP 200
{"message":"ok","code":200,"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...."}

code: 200 + a valid JWT = an authenticated session, with the passkey never used. For comparison, the identical account with a TOTP secret instead correctly returns the second-factor challenge:

HTTP 200
{"message":"The user has enabled 2FA","code":199}
Account statePOST /api/login (password only)
Passkey registered, no TOTPcode 200 + JWT — 2FA NOT enforced
TOTP registeredcode 199 — 2FA challenge enforced

This isolates the defect: the login path enforces OTP but ignores passkeys.

Impact

  • Any account protected only by a passkey is reduced to password-only authentication. An attacker who obtains the password (phishing, reuse, leak) gains full access despite the registered security key.
  • In nginx-ui all authenticated users are effectively administrators and the terminal feature grants a host shell, so account takeover leads to full control of the managed nginx instance / host.
  • Users are given a false sense of security: the UI shows the account as 2FA-enabled while the second factor is not enforced at login.

Remediation

  • Gate the second-factor decision on u.Enabled2FA() (not u.EnabledOTP()) in Login, in both SSO callbacks, and in RequireSecureSession().
  • For a passkey-only user logging in with a password, return a "passkey assertion required" challenge and complete login only after a successful WebAuthn assertion (the begin_passkey_login / finish_passkey_login flow already exists and should be required as the second step).
  • Centralize session-token issuance so no login entry point can skip the 2FA decision.

Affected components

  • api/user/auth.go — Login
  • model/user.go — EnabledOTP, EnabledPasskey, Enabled2FA, AfterFind
  • api/user/2fa.go — get2FAStatus
  • internal/middleware/secure_session.go — RequireSecureSession

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/0xJacky/Nginx-UI≥ 1.9.10-0.20250517140552-daee3ac7ade1&&< 1.9.10-0.20260728074558-95cd21b708141.9.10-0.20260728074558-95cd21b70814go get github.com/0xJacky/Nginx-UI@v1.9.10-0.20260728074558-95cd21b70814

Affected Products

1 product · 1 configurations
Application
nginx uinginxui
≥ 2.0.0 && < 2.5.0
range

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.20260728074558-95cd21b70814 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-107808 is resolved across your whole dependency graph.

  3. Workarounds

    Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.

Frequently Asked Questions

## Summary nginx-ui supports two second-factor methods — TOTP (OTP) and WebAuthn passkeys — and reports an account as 2FA-enabled when **either** is configured. However, the password login endpoint (`POST /api/login`) only enforces a second factor when a **TOTP secret** is present. An account that has registered a **passkey but no TOTP** is logged in after password verification alone — the passkey is never requested. This silently downgrades a passkey-protected account to single-factor (password-only) authentication. ## Details The account's 2FA policy treats passkeys as a valid factor (`mo
O3 Security · Impact-Aware SCA

Is CVE-2026-107808 in your dependencies?

Find it across Go, including transitive dependencies.

CVE-2026-107808: Nginx-UI — Fix Available