Sep 21, 2026·6 min read·6 visits
Unauthenticated remote attackers can create unauthorized administrative accounts on fresh Nginx Ignition installations by exploiting a race condition in the initial setup API.
Nginx Ignition prior to version 2.41.1 contains a Time-of-Check to Time-of-Use (TOCTOU) race condition in its unauthenticated onboarding API endpoint. This flaw allows remote, unauthenticated attackers to register an administrative account by sending concurrent HTTP requests during the initial system configuration phase, bypassing the check meant to restrict onboarding to a single initial administrator.
CVE-2026-61628 is a critical security flaw identified in Nginx Ignition, an open-source administration panel and user interface developed by Lucas Dillmann for managing the NGINX web server. The flaw is located in the application's initial setup API endpoint /api/users/onboarding/finish, which is designed to configure the initial administrator account on clean installations.
This endpoint is unauthenticated by design to allow the initial deployment configuration. However, the API handler fails to perform atomic checks when evaluating if the system has already been configured. The underlying weakness is classified as a Time-of-Check to Time-of-Use (TOCTOU) race condition, mapped to CWE-362.
If the application is deployed in a multi-threaded or concurrent processing environment, the vulnerability exposes a severe initial access vector. An attacker can exploit this condition to bypass setup barriers and register their own administrative accounts alongside the legitimate administrator.
The root cause of CVE-2026-61628 lies in the separation of the validation check and the insertion operations in the onboarding handler logic. In affected versions prior to 2.41.1, the handler inside api/user/onboarding_finish_handler.go uses a non-atomic two-phase execution flow.
During the check phase, the handler queries the database using OnboardingCompleted(ctx) to determine if any accounts are already present. If the database returns zero users, the application assumes setup is incomplete. In the subsequent write phase, the handler hashes the password, constructs the administrative domain object, and calls the repository's Save() function to write the new user record.
Because Nginx Ignition's Go backend processes concurrent HTTP requests inside separate goroutines, multiple requests can execute the check phase simultaneously. If multiple requests reach the check phase before any single write is committed, they all read a count of zero users. As a result, all concurrent threads proceed to the write phase and successfully write their database records without generating a unique constraint conflict or transactional rollback.
The original handler performed the check and database insert sequentially without wrapping the operations in an exclusive database lock or unified transaction. The security patch submitted in Pull Request #131 addresses this by deprecating the two-step execution and delegating validation to FinishOnboarding.
// api/user/onboarding_finish_handler.go (PATCHED)
if err = h.commands.FinishOnboarding(ctx.Request.Context(), domainModel); err != nil {
if errors.Is(err, user.ErrOnboardingAlreadyCompleted) {
ctx.Status(http.StatusForbidden)
return
}
panic(err)
}The FinishOnboarding command calls the repository method TryCreateInitialUser, which begins a transaction and implements explicit, dialect-specific table locks. This architecture blocks concurrent check attempts.
// database/user/repository.go (PATCHED)
func (r *repository) TryCreateInitialUser(ctx context.Context, u *user.User) (bool, error) {
transaction, err := r.database.Begin()
if err != nil {
return false, err
}
defer transaction.Rollback()
if err = lockUserTable(ctx, transaction); err != nil {
return false, err
}
exists, err := transaction.NewSelect().Model((*userModel)(nil)).Exists(ctx)
if err != nil {
return false, err
}
if exists {
return false, nil
}
model := toModel(u)
_, err = transaction.NewInsert().Model(&model).Exec(ctx)
if err != nil {
return false, err
}
return true, transaction.Commit()
}The locking function lockUserTable dynamically maps locks to the target database engine. For PostgreSQL, it executes LOCK TABLE "user" IN EXCLUSIVE MODE, forcing consecutive transactions to block and queue. For SQLite, it triggers ROLLBACK; BEGIN IMMEDIATE; to enforce exclusive read-write locks at the database connection layer. This resolves the TOCTOU gap comprehensively across both supported databases.
Exploitation of CVE-2026-61628 depends on temporal precision and system state. The target must be a fresh installation that has not yet completed its onboarding process, or an instance where the user database has been purged. Once onboarding is complete, the API path is blocked and the race window is permanently closed.
To exploit the flaw, the attacker generates multiple parallel connection threads and targets the /api/users/onboarding/finish endpoint with HTTP POST requests. These requests contain JSON payloads containing the desired attacker-controlled administrator credentials and authorization parameters.
If the threads execute concurrently within the processing window, both requests execute successfully. When the database transaction commits, two distinct users are registered with administrative privileges. The attacker can then navigate to the standard web administration interface and authenticate as a administrator.
A successful attack results in full administrative control over the Nginx Ignition console and the managed NGINX environment. Attackers obtain administrative permissions (ReadWrite), which authorize full reading and writing capabilities across all server settings.
With this access level, attackers can alter proxy hosts, modify routing files, and access downstream network systems. The configuration console also manages sensitive data, such as private SSL/TLS certificates and system logs. Attackers can export active cryptographic keys or configure malicious proxy rules to capture passing client credentials.
This vulnerability has been assigned a CVSS v3.1 base score of 8.1. The attack requires no prior authentication and demands no victim interaction. Although the complexity is high due to the strict timing window, the impact on confidentiality, integrity, and availability is critical.
To address CVE-2026-61628, administrators must update Nginx Ignition to version 2.41.1 or higher. This update introduces the necessary table locking and immediate transaction controls to prevent parallel setup routines.
If an upgrade cannot be applied immediately, administrators should implement network-level access controls to limit access to the onboarding endpoint. Restricting communication to the setup handler to trusted IPs prevents external access during configuration.
# Example NGINX configuration to restrict onboarding access
location /api/users/onboarding/finish {
allow 192.168.1.50; # Administrator Workstation
deny all;
proxy_pass http://localhost:8080;
}Security teams should also audit active database tables to identify potential indicator files. Running a query against the user table to check for multiple accounts with identical or near-identical creation timestamps is a reliable method to identify exploitation attempts.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
nginx-ignition lucasdillmann | < 2.41.1 | 2.41.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-362 |
| Attack Vector | Network |
| CVSS v3.1 | 8.1 |
| Exploit Status | poc |
| KEV Status | Not Listed |
| Impact | Administrative Access |
The software performs multiple concurrent operations on a shared resource, but does not properly synchronize these operations, creating a race condition.
A vulnerability exists in the i18n middleware of nginx-ignition, enabling CPU amplification attacks. By transmitting a crafted Accept-Language header containing malformed tags separated by underscores, an unauthenticated remote attacker can bypass the length-guard threshold of the underlying Go parsing library. Normalization of underscores to hyphens occurs after the initial validation checks, forcing the parser into expensive quadratic-time loops that consume 100% of available CPU resources. This leads to a complete denial of service for the administrative API and potentially degrades the availability of the hosting system. This vulnerability has been resolved in version 2.40.1.
A logic error in Hatchet's OAuth state validation mechanism allows unauthenticated remote attackers to bypass state parameter verification. By submitting an empty state parameter, attackers can exploit an equality collision with cleared session keys, facilitating Login Cross-Site Request Forgery (Login CSRF) or Session Fixation.
A high-severity access control vulnerability in ToolHive CLI before v0.30.1 and ToolHive Studio before v0.38.0 allows local containerized MCP servers to bypass network isolation. This enables malicious workloads to establish TCP/IP connections to administrative and control plane endpoints exposed on the host loopback interface.
AnyCable is a real-time communication server. Prior to version 1.6.15, its Pusher-compatible REST API suffered from an authentication bypass vulnerability because it failed to verify that the request body matched the signature-validated body_md5 parameter. This allows attackers to perform replay attacks with modified body contents.
A denial-of-service vulnerability exists in AnyIO prior to version 4.14.2. Standard error streams of process-pool workers are connected to an operating system pipe that is never drained by the parent process. This allows a worker to fill the pipe buffer and deadlock indefinitely.
CVE-2026-63349 is a critical privilege-dropping bypass vulnerability in the AnyIO asynchronous framework (versions 4.14.0 and 4.14.1) on POSIX platforms. Due to a variable assignment typo, supplementary groups specified by the developer are not correctly propagated to the execution backend, resulting in subprocesses retaining the parent process's elevated supplementary group permissions.