Skip to content
Popping Root On Unifi Os Server Unauthenticated Rce Chain Detection Analysis

Popping Root On Unifi Os Server Unauthenticated Rce Chain Detection Analysis

bishopfox.com • June 8, 2026

UniFi OS Server is Ubiquiti’s software distribution of UniFi OS, the management platform that fronts the UniFi family of applications (Network, Protect, and the identity and update services that ship with it). It runs as a collection of localhost-bound backend services behind a single Nginx front end that terminates TLS, authenticates requests, and proxies them to the right internal services. This proxy function is the part that matters here: the entire security model depends on Nginx correctly deciding which requests are public and which require authentication.

On May 21, 2026, Ubiquiti published Security Advisory Bulletin 064 ( SAB-064 ), covering five vulnerabilities across the UniFi OS device family. The three that matter most to an unauthenticated attacker are an improper access control flaw and path traversal flaw in the authentication gateway (CVE-2026-34908 and CVE-2026-34909, both rated CVSS 10.0 Critical, vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H ) and an improper input validation (command injection) flaw in the package-update service (CVE-2026-34910, also CVSS 10.0 with the same vector). Chained together, they give unauthenticated command execution. The advisory credits the access control flaw to Duc Anh Nguyen (@heckintosh_), the path traversal to Abdulaziz Almadhi of Catchify Security, and the command injection to John Carroll.

The vendor’s own 10.0 rating is worth a second look because the vector tells the same story this post does: a network-reachable attacker with no privileges ( AV:N , PR:N ) and no user interaction ( UI:N ) can cause high confidentiality, integrity, and availability impacts ( C:H/I:H/A:H ) that cross a scope boundary ( S:C ). The changed scope is the vendor acknowledging that the impact escapes the vulnerable component, which is exactly the story we tell below.

The advisory bundles two more CVEs worth noting briefly. CVE-2026-33000 (CVSS 9.1, credited to V3rlust) is the same command injection sink reached with a valid view:identity:update token, so it is the authenticated sibling of CVE-2026-34910; the gateway bypass is what removes the token requirement and turns it into the unauthenticated CVE-2026-34910. CVE-2026-34911 (CVSS 7.7, credited to Hakai Security) is a separate low-privilege path traversal file read that we did not pursue.

For this post, we focus on the unauthenticated chain (CVE-2026-34908 and CVE-2026-34909 into CVE-2026-34910), because it is the one an attacker can drive from the outside with no credentials. We’ll concentrate on four things: how defenders can detect exposure safely, the preconditions that decide whether a given UniFi OS Server is actually reachable and exploitable, how an attacker drives the chain to root, and what root on this appliance actually puts at risk, which is considerably more than the host itself.

Our detection tool is available at .

Bishop Fox worked from the vendor advisory and a diff of the extracted 5.0.6 and 5.0.8 root filesystems. The interesting code lived in three places that a package-level diff alone could not surface:

Renaming the decompiled identifiers to readable forms is what made the attack chain legible, and the snippets later in this post use those readable names. Armed with these (now comprehensible) diffs, we confirmed the root cause across all three layers, validated the full unauthenticated chain against a live UniFi OS Server 5.0.6 target, escalated from the command injection to a root shell on that target, and confirmed the fix on a patched 5.0.8 target.

One scope note before the details: The advisory lists the bypass and command-injection CVEs as affecting the broad UniFi OS hardware lineup as well (Dream Machines, Cloud Gateways, the UNVR and UNAS lines, UniFi Express, and more), each with its own fixed version. Our analysis and validation targeted the UniFi OS Server software distribution specifically, so the hardware appliances are in scope per the vendor advisory even though we did not validate the chain on them directly.

The chain has three parts: getting past the authentication gateway, reaching the command-injection sink behind it, and escalating from the service account that sink runs as to root.

Nginx fronts everything and enforces authentication with an auth_request subrequest to the unifi-core Node service. That handler decides whether a request is public or must be authenticated. In 5.0.6 it makes that decision from the request URI:

The root cause is a divergence between two different views of the same URI. The public-exemption check is a prefix match on the raw x-original-uri (Nginx’s $request_uri , exactly as the client sent it, still percent-encoded). But Nginx selects the upstream location and, therefore, which backend the request is proxied to, using the normalized $uri (percent-decoded, so %2f becomes / , and with ../ segments collapsed). When those two views disagree, the gate and the router are reading different requests.

That is the whole bug – why this was registered as two separate CVEs is unclear to us. A request can be crafted, so its raw form begins with the auth-exempt /api/auth/validate-sso/ prefix (so the gateway returns 200 and waves it through), while its normalized form resolves to an authenticated internal /proxy/ / route (so Nginx proxies it to a backend that assumes authentication already happened).

We confirmed the bypass against a live 5.0.6 virtual machine. Requests built this way reached internal backends that are supposed to require authentication: the identity ( ulp-go ) backend answered with its own application-level JSON, and the Network application answered with its own login-required error, in both cases the backend’s response rather than Nginx’s 401 . Control requests pinned down the cause: the identical traversal behind a non-exempt prefix returned 401 , and the untraversed /proxy/ / path returned 401. Only the validate-sso exemption produced the bypass, which is exactly what the root-cause analysis predicted.

Behind the gateway sits a package-update route in the shared identity and update backend (present in both the ulp-go application and the identity-update application). The handler retrieves the “latest package” for a named package and, when asked to do so, resolves that version by shelling out to a local command.

The data flow from the request to the shell is short. The handler takes a caller-supplied package name and an option that forces the command code path and passes the package name down through a small chain of helpers to a sink that builds a command string and executes it. The sink builds the string with fmt.Sprintf against a format like:

where %v is the caller’s package name, interpolated verbatim. In 5.0.6 there is no validation of that name, and the resulting string is handed to a helper that runs it through a sh -c shell wrapper. Because the package name reaches a shell with no sanitization, shell metacharacters in it are interpreted rather than treated as data.

This same route and sink is the basis for two CVEs. Reached with a valid view:identity:update token, it is CVE-2026-33000 (a privileged-access command injection). Reached without any token by way of the gateway bypass described above, it is CVE-2026-34910, the unauthenticated case which is the focus of this post.

We validated the unauthenticated path against our live 5.0.6 test target using a benign, time-based oracle: a request whose injected portion simply causes the server to pause for a fixed interval before responding. On 5.0.6, the response was delayed by the injected interval (and returned HTTP 200); on patched 5.0.8, the same request returned HTTP 400 with no delay. That differential, slow 200 on 5.0.6 vs. fast 400 on 5.0.8, is what confirmed both the vulnerability and the fix.

The command injection does not execute as root directly. It runs as the service account behind the package-update runnable, ucs-update on the live latest_package path. That account is not sandboxed, though. Its sudoers entitlements include passwordless sudo on /usr/bin/dpkg , /bin/chmod , /bin/systemctl , and /usr/bin/uos , and each one provides an independent, one-step path to root. Passwordless sudo dpkg is the simplest, allowing installation of a package whose maintainer script runs as root.

We took that step end-to-end on our 5.0.6 test target. We built a throwaway .deb package whose post-install script reads /etc/shadow , installed it with sudo -n dpkg -i , and recovered the file’s contents, confirming the script had run as root. This proved the chain does not stop at code execution inside a constrained service account; it lands a full root shell, reached from a single unauthenticated request, and privilege escalation here is a formality rather than a hurdle.

A production-safe detection here must give a definitive answer without harming the target or pulling anything sensitive into scan output. Our tool does that by reaching the vulnerable code path without running an injected command.

The check sends the gateway-bypass request to the package-update endpoint without the parameters a real injection would carry. On a vulnerable target, the request reaches the package-update handler but is rejected because of the missing parameters. That handler-specific error, returned with an HTTP 200, is proof that the bypass reached the sink, with nothing executed. A patched target rejects the request at the gateway with an HTTP 400 instead. Our detection tool also sends the same request without the bypass as a control, which should return 401, to guard against false positives on an open or misconfigured host.

This is safe in the ways that matter: no command runs on the target, no file or secret is read, and the target’s state is unchanged. When the probe does not produce a clean vulnerable-or-patched result, the tool fetches the root page and looks for UniFi OS portal markers, which lets it separate a host that is not a UniFi OS Server at all from one that is but blocked the probe. Each target lands in one of four verdicts: vulnerable, patched, unaffected (not a UniFi OS Server), or inconclusive (a UniFi OS host that needs manual review).

The exploitation signature is clean and worth alerting on, and it is the single highest-value control here because it fires at the entry, before privilege escalation, secret theft, or log tampering can happen. That ordering matters. Once the attacker has root, they can clear the on-host logs, and one of the runnable service accounts is already configured such that its sudo actions are not logged at all. Any detection that depends on post-foothold artifacts is racing against an attacker who can erase them. The inbound request cannot be erased if you are watching for it or shipping logs off the box.

The bypass leaves a recognizable request shape: a request URI that contains the /api/auth/validate-sso/ exemption prefix together with encoded path-traversal sequences ( ..%2f , ..%2e , %2e%2e , and similar). A legitimate validate-sso request never carries encoded traversal, so any such request is suspect regardless of response code. Behind the bypass, watch the package-update route ( .../ucs/update/latest_package ) for parameters carrying shell metacharacters, and watch the host for unexpected child processes (and anomalous sudo dpkg , chmod , or systemctl calls) under the ucs-update service account.

The three parts above land a root shell on the appliance from one unauthenticated request. What decides severity is what that root shell sits on top of, and that is the rest of this section. Each step below is grounded in what we observed on the live 5.0.6 target. We note where a capability is something root simply confers versus something we exercised directly, and we are describing impact to scope it, not publishing a turnkey recipe.

A UniFi OS Server is not a generic Linux box; it is the management plane for an organization’s network, including, where those devices are deployed, its physical-access doors, surveillance cameras, and the identities tied to them. Root on the appliance is administrative control over everything the console governs. The rest of this section follows that idea outward, from the secrets on disk to the network, to the physical world, to persistence that outlives the patch.

As root, every secret the appliance keeps is readable with a plain file read or database dump, no further bug required. On our test target, that set included:

Compromised data from the credential server, in particular, deserves a defender’s attention beyond “rotate it.” The RADIUS, WiFi, and VPN secrets are keys to the protected network, not just the management VLAN, so they are lateral-movement enablers. Biometric and NFC data cannot be rotated at all; once disclosed, they stay disclosed, which makes them the most consequential and least recoverable items in the set.

The signing key turns a one-time remote code execution into durable, credential-backed access, and this is the one post-exploitation step we did prove out. Using the key recovered from the appliance, we built a session token asserting the Owner identity and authenticated to the console API with no password and no second factor. It returned the owner’s own profile, role: owner with the super-admin flags set. The owner record carries the full scope set (network management, the door and camera capabilities below, cloud access), so the forged token is a working administrative login, not a partial one.

The mechanism is what makes this durable rather than incidental. The console signs and verifies its session tokens with a symmetric HS256 secret, and the verification does not pin the algorithm, issuer, or audience. The only server-side check is a per-user revision value that is itself read from an on-disk cache, so a valid token is derivable entirely offline once the key is in hand. Crucially, that verification design is unchanged in 5.0.8. We confirmed the forged token against both a vulnerable 5.0.6 and a patched 5.0.8 console. The patch closes the remote access vector that yields a valid key, but it does not alter the token model. So any disclosure of the signing key (by this chain on an unpatched box, or by any other means) yields admin tokens that work against patched consoles too.

This is the reason patching alone is not remediation. Upgrading to 5.0.8 does nothing to a signing key an attacker already copied, and until that key is rotated, the forged admin tokens keep working across reboots, password resets, and the upgrade itself. Worse, a forged owner token can drive the console’s own SSH-enable and root-password-set workflow, which establishes an interactive root login that survives even a rotation of the JWT key. That is the step that turns “rotate the key and you are clear” into “rebuild the console,” and it is covered in the defender actions above.

From an administrative position on the console, whether by forged token or by direct database and API access, the attacker inherits the controller’s actual job. On the network side, that means pushing device configurations, rewriting firewall, routing, and DNS rules, enabling VPN ingress, creating rogue admin accounts, and re-adopting or de-authorizing managed gateways, switches, and access points. The compromise pivots from the management plane to the data plane, and the managed fleet should be treated as downstream-compromised.

Two more directions are worth stating briefly. If the console is cloud-connected, the cloud-access tokens from the secret store can pivot to the Ubiquiti cloud account and its remote-management surface, and in a multi-site organization that can reach other sites under the same account from the internet, turning a single-site LAN compromise into an organization-wide, internet-facing one. Containment that stops at the local box misses that blast radius. And because the attacker holds root, the destructive option is always on the table: wipe configurations and databases, delete backups, factory-reset, push bricking firmware, or hold the environment for ransom. That availability impact is the A:H in the vendor’s CVSS vector, and it means an attacker who is not interested in stealth can simply take the network, the doors, and the cameras offline.

UniFi OS Server 5.0.8 closes the chain with three independent changes, one addressing each part: the gateway bypass, the command injection, and the privilege escalation:

We confirmed each change by diffing the 5.0.6 and 5.0.8 artifacts, and we confirmed behaviorally that the patched build returns HTTP 400 (with no command execution and no delay) for the request that executed on 5.0.6.

One limit of the patch is worth stating plainly, because it changes incident response. The fix closes the way in, but it does not reach back and undo what an attacker already did with root on an instance that was exposed beforehand. In particular, it does not invalidate a session-token signing key that was already exfiltrated, so forged admin tokens minted from that key keep working against a fully patched console until the key is rotated. Patching is the first step, not the last.

This chain earns its 10.0 severity score from how little it asks and how much it returns: with no credentials and no user interaction, a single request to a reachable UniFi OS Server becomes a root shell, which we validated end to end on version 5.0.6 and confirmed closed on 5.0.8.

What makes that critical is what the appliance is. As the management plane for the network and (where physical security devices are deployed) the doors and cameras behind it, root access means administrative control of everything the console governs, from the secrets on disk to the physical world.

Patch to 5.0.8, or the fixed version for your hardware model, but treat that as the first move rather than the whole response. It closes the entry without undoing what an attacker with root already did. Any instance exposed pre-patch should be treated as compromised, its secrets rotated and its network, door, and camera activity reviewed.

Cosmos customers were notified our research into this vulnerability shortly after the vendor advisory. If you are interested in learning more managed services delivered through our Cosmos platform, visit bishopfox.com/services/cosmos .

Our detection tool is available on GitHub: . For more vulnerability intelligence insights, visit the Bishop Fox Blog .

Be first to learn latest tools, advisories, and findings.

Extracted Entities

Domains (1)