Blind SSRF to LQL Injection to File Deletion to Authentication Bypass in Checkmk

We discussed this vulnerability during Episode 167 on 14 November 2022

A long chain of issues going from blind SSRF to new-line injection to a blind Livestatus Query Language (LQL) injection to arbitrary file deletion and finally a race condition leading to authentication bypass.

Blind Server-Side Request Forgery - The SSRF existed on the agent-receiver which is basically a thin-client, exposed on port 8000 and most endpoints just pass things along to the Checkmk REST API running on port 80. The /register_with_hostname endpoint takes a host_name parameter and uses it in crafting the path on the REST API (objects/host_config_internal/{host_name}). With no sanitization its possible to hit other endpoints on the REST API, specifically those endpoints that are unauthenticated but only for requests originating from localhost.

New Line Injection in ajax_graph_images.py - The ajax graph images endpoint is meant to generate an image showing the performance data for a particular host. It does this by crafting a LQL query. Part of this query is an AuthUser: header which is used to restrict the results to what that particular user could see. An attacker controlled this value has it comes from the force_authuser URL parameter. This value is reflected into the query without any sanitization, allowing an attacker to inject new-lines and arbitrary content.

LQL Injection - The new-line is particularly important here as much of the query is in the form of Key: value\n. Injecting a new-line allows an attacker to craft the rest of the query. Or, by injecting a KeepAlive: on\n\n the connection won’t be closed after the first empty line, and instead the attacker can inject a completely arbitrary query.

Arbitrary File Deletion - The LQL could be exploited to extract monitoring data in a way similar to a time-based boolean blind SQL injection, but another ability is to execute COMMAND requests. Despite the name, the command interface is fairly restrictive and doesn’t enable shell command injection. What it does ovver is a PROCESS_FILE command taking two parameters a filename, and a boolean indicating if the file should be deleted once processed. Processing a file doesn’t open any new attack surface, its the same commands available elsewhere, but the deletion happens even if parsing the file, or any of the commands fail. So an attacker can use this to delete any file.

NagVis Authentication Bypass - To understand the bypass first lets touch on the authentication system itself. the login.py file on the Checkmk GUI validates a session cookie. This session cookie is made up of three items: <username>:<session_id>:<hash> and the <hash> is a SHA256 hash made from four items: <username><session_id><serial><secret>. The Username and session ID are attacker controlled, the serial is an incrementing value that increments every time the password is changed or account is locked. The <secret> is read from auth.secret,

One obvious attack would be to delete auth.secret and hope to get an empty value used. However the file will be regenerated if it does not exist so that will not work. There is an alternative login route though, the NagVis integration will parse the same session cookie and does the same validation of the hash. The key difference is that if the file does not exist it will error out.

The final attack is to abuse a race window. For a short window of time, on the Checkmk GUI when regenerating the auth.secret after it was deleted it will create a new file. That new file with briefly be empty, if at that same moment the NagVis auth system attempted to read auth.secret it would get an empty value, allowing a valid session cookie to be crafted by an attacker.