Feb 16, 2026·6 min read·8 visits
Fleet forgot to check if you were an admin before letting you access the Go profiler. Any user with a login could dump the server's heap or spike the CPU.
A critical authorization bypass in Fleet, the open-source osquery manager, allowed any authenticated user—regardless of privilege level—to access the sensitive `debug/pprof` endpoints. This exposure grants low-level observers the ability to inspect the Go runtime's memory, goroutines, and CPU usage, leading to significant information disclosure and potential Denial of Service (DoS) attacks.
If you work with Go (Golang), you know about pprof. It is the built-in profiling tool that serves as a window into the soul of your running application. It tells you exactly how much memory is being used, which functions are hogging the CPU, what every single goroutine is doing, and what the thread stacks look like.
In a development environment, this is a godsend. In production, it is a loaded gun. Exposing pprof endpoints to the public internet—or even to untrusted internal users—is a classic "don't do that" scenario. It essentially hands an attacker a blueprint of your application's internal state and a button to set the CPU on fire.
Fleet, the popular device management platform, inadvertently left this window unlocked. While they didn't expose it to the anonymous internet (thankfully), they made a critical error in their permission model: they assumed that if you could log in, you could be trusted with the keys to the runtime. This vulnerability isn't about memory corruption or buffer overflows; it's about a fundamental misunderstanding of the question "Who is allowed to see this?"
The vulnerability lies in a middleware function designed to protect the debug routes. In the world of web security, there is a massive difference between Authentication (proving who you are) and Authorization (proving you have permission to do something).
Fleet's debugAuthenticationMiddleware confused the two. The code checked if the user was "active" and capable of performing actions, but it failed to check if the user actually held the Global Admin role required to view sensitive diagnostics.
This meant that a user with the "Observer" role—intended to be a read-only, low-privilege account for auditing basic fleet stats—could access /debug/pprof/heap. Imagine giving the janitor the master key to the CEO's safe just because they have a valid employee ID badge. The door was locked to strangers, but wide open to anyone on the payroll.
Let's look at the code in server/service/debug_handler.go. This is where the logic failure lived. The middleware simply called v.CanPerformActions(), which returns true for almost any valid, logged-in user context.
Vulnerable Code (Before):
// debugAuthenticationMiddleware checks if the user is authenticated
func debugAuthenticationMiddleware(v *viewer.Viewer) error {
// weak check: acts like "isLoggedIn()"
if !v.CanPerformActions() {
return authError
}
return nil
}The fix, introduced in commit 5c030e32a3a9bc512355b5e1bf19636e4e6d0317, adds the necessary authorization check. It explicitly looks at the GlobalRole of the user ensuring they are a full Administrator.
Patched Code (After):
func debugAuthenticationMiddleware(v *viewer.Viewer) error {
// strong check: "isLoggedIn() AND isAdmin()"
if !v.CanPerformActions() ||
v.User.GlobalRole == nil ||
*v.User.GlobalRole != fleet.RoleAdmin {
return authError
}
return nil
}This simple || condition closes the gap entirely. It illustrates a common developer pitfall: relying on helper methods like CanPerformActions() without verifying exactly what that method guarantees. In this case, it guaranteed liveness, not privilege.
Exploiting this is trivially easy if you have a valid account. You don't need to craft malicious packets or overflow a buffer. You just need curl.
Step 1: Authenticate First, log in as a low-privilege user (e.g., an Observer) to get your session token.
Step 2: Access the Endpoint Send a request to the heap endpoint. This triggers the Go runtime to dump a snapshot of memory usage.
curl -H "Authorization: Bearer <TOKEN>" \
https://fleet-server.example.com/debug/pprof/heap > heap.pb.gzStep 3: Analyze Open the dump locally using the Go toolchain:
go tool pprof -http=:8080 heap.pb.gzAt this point, you can browse the memory allocation graph. While Go heap dumps don't typically contain raw string data of variables (unlike a core dump), they reveal the application architecture, the density of certain objects, and potentially side-channel information about what the server is processing.
The DoS Angle A more destructive attack involves the CPU profile endpoint:
curl https://fleet-server.example.com/debug/pprof/profile?seconds=30This forces the server to sample stack traces 100 times per second for 30 seconds. If a few users do this simultaneously, they can significantly degrade the performance of the Fleet server, causing a Denial of Service for legitimate management traffic.
The impact here is two-fold: Information Disclosure and Availability.
Confidentiality: Access to pprof allows an attacker to map out the internal structure of the Fleet server. They can see which code paths are active, view goroutine stack traces (which sometimes contain function arguments or sensitive context), and identify libraries in use. In rare cases, environment variables or command-line arguments might be visible depending on how the application was started.
Availability: The more immediate threat is DoS. Generating a heap profile requires the garbage collector to pause or work harder to traverse the object graph. Generating a CPU profile adds overhead. An authenticated attacker (perhaps a disgruntled employee or a compromised low-level account) could weaponize this to blind the security team by taking the management server offline during an active incident.
The primary fix is to upgrade Fleet. The patch effectively locks the door by checking for the admin role.
Patched Versions:
Defense in Depth Workarounds:
If you cannot upgrade immediately, you should block these paths at the network level. No external user (even an admin) usually needs to access pprof from the public internet.
Nginx Configuration Example:
location /debug/pprof/ {
deny all;
return 403;
}By implementing this block at the load balancer or reverse proxy, you render the application-level vulnerability moot. This is a good practice for any Go application running in production: never expose /debug routes to the world, regardless of how secure you think your middleware is.
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:U| Product | Affected Versions | Fixed Version |
|---|---|---|
Fleet Fleet Device Management | < 4.53.3 | 4.53.3 |
Fleet Fleet Device Management | >= 4.75.0, < 4.75.2 | 4.75.2 |
Fleet Fleet Device Management | >= 4.76.0, < 4.76.2 | 4.76.2 |
Fleet Fleet Device Management | >= 4.77.0, < 4.77.1 | 4.77.1 |
Fleet Fleet Device Management | >= 4.78.0, < 4.78.3 | 4.78.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-284 |
| Attack Vector | Network |
| CVSS Score | 6.3 (Medium) |
| EPSS Score | 0.0004 |
| Privileges Required | Low |
| Impact | Info Leak / DoS |