Introduction
Identity providers (IdPs) or Identity and Access Management (IAM) solutions are essential for implementing secure and efficient user authentication and authorization in every application. By centralizing user identity management, IdPs streamline the verification of user credentials and grant access to various resources.
This post is the second part of our journey into open source IAMs. This blog post focuses on Authentik, detailing two high-severity vulnerabilities we found and our journey of exploiting them. Furthermore, we share a proof of concept (POC) demo exploiting a privilege escalation vulnerability we found.
All vulnerabilities discovered during this research were responsibly disclosed to Authentik as part of our commitment to contributing to the open source and security community.
Quick Overview
In the first part, we detail how a sensitive information leak (CVE-2024-42490) prompted us to investigate further, leading us to discover an Object Relational Mapper (ORM) leak. We share our journey and challenges in attempting to exploit this vulnerability.
In the second part, we share how we found a privilege escalation vulnerability in Authentik (CVE-2024-37905), displaying an exploit chain (and a demo) in which any authenticated user can elevate their permissions to a superuser.
Let’s get started
Table of Contents
The Research Subject: Authentik The Private Key Information Leak (CVE-2024-42490) Explained Certificates in Authentik The Quest for the Certificate UUID Privilege Escalation Vulnerability in Authentik (CVE-2024-37905) & POC Key Insights from the Authentik Security Research Wrapping Up
The Research Subject: Authentik
Authentik is an opensource identity provider (IdP), a self-hosted alternative to traditional IdPs.
It supports many authentication protocols, allowing it to integrate with other identity providers and service providers using protocols like SAML2, OAuth2, and OpenID Connect (OIDC).
According to our Shodan search, there are ~4,000 internet-facing Authentik systems.
So, with our research subject set, let’s dive into our first vulnerability.
The Private Key Information Leak (CVE-2024-42490) Explained
Our research journey starts by setting up a lab environment, navigating to the admin interface, logging in as admin (akadmin) and inspecting the application.
On the admin interface, one menu that caught our attention was the system->certificates menu.
Figure 1- Admin Console – Certificates Management Menu
In this menu (Figure 1), we can manage the certificates used by Authentik; an admin can generate self-signed certificates on Authentik directly or import certificates and private key pairs (Figure 2 below).
Figure 2 – Admin Console – Certificate “Upload” Page
Here, we wondered if we could use this ability to find file-upload vulnerabilities. We started by inspecting the relevant source code. While inspecting the source code and the database structure, we figured that the certificate (and private key) content was saved on the Authentik database (Figure 3 below).
Figure 3 – Authentik Database Certificates Table
As seen in Figure 3, the private key’s content and the certificate are saved in the certificates table; hence, the content is not stored on the server’s file system. Sadly (for us), there is no file upload vulnerability potential here.
After examining the certificates management page, we found that the certificate can be downloaded by the admin (Figure 4 below).
Figure 4 – Admin Console – Certificates Menu – Download Private Key
So far, all is good.
At this point, we speculated: Can we download the certificate as a non-admin user?
Viewing the content sent to the server while requesting the certificate private key, we used the same API URI /api/v3/crypto/certificatekeypairs/<kp_uuid>/view_private_key/, used a non-admin user’s credentials, and whoops; we found that a non-admin user could download the certificate private key.
Our next step was examining the source code responsible for this functionality.
Inspecting the code responsible for the “private key download”:
Figure 5 – Authentik view_private_key Source Code
As shown in Figure 5 above, we found no authorization checks done in the private key download (view_private_key) function; this means anyone can download the private key!
Figure 6 – Authentik API Download (Certificate) Private Key Without Authorization
As shown in Figure 6, anyone (no authentication required) can download the private certificate hosted on the Authentik server.
There is one missing piece: an attacker should know the internal uuid to retrieve the certificate and private key content. Granted, this request required an internal UUID (set by the Authentik server); It is worth noting that obtaining this UUID is not trivial and will be explored later.
In the following sections, we detail our research process for trying to leak the uuid.
Certificates in Authentik
Why Should We Care?
The certificates (stored on the Authentik database) can serve two main functions: 1) the certificate that is attached to the Authentik web server itself (used HTTPS connection) and 2) to sign OAuth2 tokens (or SAML requests and responses) when configured as IdP.
This means that leaking the webserver certificate can be used to impersonate the webserver itself and potentially trick users (or admins) into providing credentials to an attacker-controlled Authentik setup.
Furthermore, if configured for OAuth (as we assume most Authentik setups do), leaking a certificate can cause a significant integrity issue since an attacker could impersonate users’ tokens and, by doing so, grant access to any system trusting Authentik.
With our motivation set, let’s dive in.
The Quest for the Certificate UUID
The only part that separates us from getting the server’s private keys is the certificate’s uuid, which we will refer to as kp_uuid (since this is how it is named in the source code).
At first glance, we wondered—how hard it can be to leak a uuid?
This question led us to a new journey. In the following sections, we will detail some of our paths to answer this question.
If you are not interested in learning about the research process and the relatively new methods we used, you can skip to the next section.
First (Naive) Attempt—Leaking the UUID From an API Call
While examining the network traffic, we noticed that some API calls are “leaking” (returns) some internal UUIDs when working on the web UI.
Looking further for specific API calls, the following request (the web_certificate key in Figure 6 below), using admin credentials, returned the used certificate kp_uuid directly.
Figure 7 – Brands API Returns the Certificate kp_uuid- Using Admin Credentials
Let us try this API without authorization (Figure 7 below).
Figure 8 – Error Received When Requesting the API Using no Credentials
Let’s try using a regular (non-admin) user (Figure 9 below).
Figure 9 – Error Received When Requesting the API Using a User’s Credentials
As shown in the above figures (9 and 8), requesting the /brands API using a user’s credentials or no credentials does not return any values.
Let’s try to find another way.
Upon regular use of the web application, we saw an API endpoint that seems relevant to us: /api/v3/core/brands/current/.
We can invoke this API without authorization—let’s inspect its content (Figure 10).
Unfortunately, the server did not return the certificate uuid on this API request.
Second Attempt: UUID Prediction
At this point, an idea came to mind: Can we predict or estimate the generated UUID? This idea is based on the fact that it is a certificate with a timestamp, which puts a time constraint on generation time.
Let’s inspect the code declaring the relevant model (structure), CertificateKeyPair.
Figure 11 – The CertificateKeyPair Model – Declaring the kp_uuid Field as uuid4
As shown in Figure 11, the kp_uuid field is defined as uuid4, uuid4 (which uses os.urandom(16)) is considered random enough for us to decide not to continue exploring this avenue.
Oh well, we need to try harder.
So, we are looking for a way to leak the kp_uuid. In the next section, we will dive into a rabbit hole.
Third Attempt: Django ORM Leak
“authentik is at its very core a Django project. It consists of many individual django applications.” (source: Authentik developer docs)
Looking for a way to leak the kp_uuid, we encountered a great article by elttam that introduced a new class of vulnerability: ORM Leak.
The blog post discusses the “insecure use of Object Relational Mappers (ORMs) that could be exploited to dump sensitive information.”
We will not delve into the entire article, but to summarize the key sentence that addresses the root cause of the issue, it would be:
The problem arises because Django’s ORM (filter method) uses keyword parameters to build the QuerySet; when using the unpacking operator (**) from user-controlled input, they can control the queries sent to the database.
Okay, so the pattern we are looking for is using the filter method with the unpacking operator ** applied to user-supplied data, thus allowing us to leak some internal data (and hopefully our kp_uuid)
With that in mind, let’s inspect the application for this pattern and try to leak the kp_uuid. Searching the source code, we found a promising instance (Figure 12 below).
Figure 12 – Code Snippet /api/v3/events/events/per_month/ API Handler
As shown in Figure 12, in this API call (/api/v3/events/events/per_month/), the user data supplied in the query parameter is directly loaded as JSON into the query variable (line 190). The query variable is then provided to the .filter(**query) function (line 196).
Inspecting the source code reveals that this API call only requires a valid user (not only admins can query it).
OK, let us try out this new technique.
But wait, what does this API even query and return anyway?
Authentik events/events API
Let’s request the /api/v3/events/events/per_month/ API and see; we will use the filter action=login and the query parameter as an empty JSON object {} (meaning no filters); this query should return all login events.
Note: we used the admin credentials (an API key) for this request.
Figure 13 – Requesting events/per_month/ API Returns Timestamps and “Hits” (Histogram)
As seen in Figure 13, the specific request returns timestamps (from last month) and “hits” per the relevant timeframe (Histogram).
Back to the uuid Leak Attempts
Can we leak the certificate kp_uuid from this request?
Let’s inspect the event log.
Figure 14 – Admin Event Log View
Figure 14 shows a few types of logs (called action in Authenthik); the snippet shows login, logout and model created events. (For the complete list, you can query the following API /api/v3/events/events/actions/.)
Further inspecting the model created event (Figure 15 below):
Figure 15 – Admin Event Log – kp_uuid is Logged on Upload
When uploading a certificate to Authentik, a log entry is created (Figure 15), including our long-awaited kp_uuid,
Cool, so we can leak this value from the logs; let’s try (using admin credentials).
Figure 16 – Requesting the events/per_month/ API Using Regex ORM Leak Returns a Valid Response
In the above request (Figure 16), we used the __regex keyword inside the query and the pattern ^6.* This pattern will indicate that a log entry starting with our pattern ((”6” here) exists in the events log.
Then, according to the response (y_cord != 0), we know that the specific pattern exists in the log, so we use this info to update the regex search pattern, appending each optional character separately (hex).
ORMs Are Awesome: Attempting to Exploit Using Standard User
But wait, we are using admin credentials; they have full access anyway.
Let’s try the same technique using a standard (non-admin) user.
Figure 17 – A Standard User Request to the events/per_month API Returns a Valid Response With no Data
Requesting the /api/v3/events/events/per_month/ API call with non-admin user credentials did return a response, but no data was returned from this request.
Further inspecting the source code (Figure 12), we can see that the QuerySet is further filtered by using the get_objects_for_user method; this method filters the data to include the events only if the user has the relevant permission and, in our case, returns nothing. 🙁
Pushing the Limits to Leak UUIDs: Relational Filtering Attacks
Let’s see if we can find another way to bypass the get_objects_for_user limitation.
In the elttam article mentioned earlier (plORMbing your Django ORM), the author also describes a new attack vector regarding model relations: “Relational Filtering Attacks.” In this case, we can leak data from other tables with relations to the “leaked” table.
With that in mind, let’s inspect the database and the events table.
Figure 18 – The Events Database Table
Figure 19 – The Events Table Foreign Keys (N)
As seen in the above figures, there are no foreign keys in the table, and the user column is of type jsonb (JSON Types) and has no relation to other tables.
From here, we had some ideas on how to try and still exploit this issue. We tried to convert the exploit to a timing (and error-based) attack without success.
We reported the issue to the Authentik security team, and they confirmed it, assigned CVE-2024-42490 and fixed it promptly.
Here, we decided to go back and find another way. In the next section, we reveal our path, discovering another vulnerability.
Privilege Escalation Vulnerability in Authentik (CVE-2024-37905) & POC
Here, our journey starts as a non-admin user; log in to the Authentik web app.
Figure 20 – Authentik Web UI – Logged in as a User
One of the options a user has is navigating to the settings menu.
Browsing to the settings page, we found an exciting functionality (Figure 21).
Figure 21 – A Snippet of the Tokens Management Page (User Settings)
As seen in Figure 21, a user can create an API Token, which grants access to Authentik API.
Figure 22 – User Token Creation Dialog
After creating the token and navigating to the tokens management page (Figure 23 below).
Figure 23 – Users Can Edit Their Token
Here, we found an interesting option: users can edit (update) their API token.
We decided to inspect the content sent on the update request (Figure 24).
Figure 24 – Update-token Request – Returns user_obj
As seen in Figure 24, the API response includes a user and user_obj property.
A question came to mind: Can we update the token and assign it to the admin?
Explicitly setting (POST request) the user key to {user:6} (akadmin id is 6 in our case) returns an error.
Let’s try to update (PUT) the token’s user property.
Figure 25 – Updating the Token’s User Property
But, as it turns out, if we use the PUT method, we can modify the user property of the API key. This led us to upgrade the user API key (token) to an admin API key!
From here, we used the key to reset the admin’s password and have full control of the system (leaving the admin outside). Needless to say, this step can be stealthy.
See the POC video below:
Demo Video 1 – Authentik CVE-2024-37905 POC
We reported the issue to the Authentik security team, and they confirmed it, assigned CVE-2024-37905 and fixed it promptly.
Oh, by the way, we can download any private key now.
Key Insights from the Authentik Security Research
Even though, in this case, we could not further exploit the ORM leak with user privileges, this attack vector is worth looking at since it can lead to sensitive information leaks.
When auditing a system, even if some parts of an application have business logic preventing specific actions, there may be overlooked areas where the API is vulnerable, like the PUT vs POST API calls in our case.
When inspecting a system in a white-box setup, look for the ORM Leak patterns relevant to the current language/framework.
While using ORM can significantly improve developers’ development time and application security, it is crucial for them to be aware of vulnerable patterns and safely use such frameworks.
Wrapping Up
In this post, we delved into the world of the open source IdP Authentik, and our research journey revealed new attack vectors and vulnerabilities.
We detail how a sensitive information leak (CVE-2024-42490) prompted further investigation, leading to the discovery of an ORM leak. We shared our research process and the challenges of exploiting this vulnerability.
Moreover, we shed light on the security issue we found, CVE-2024-37905, an impactful privilege escalation vulnerability.
Whether you are a developer, system administrator or security enthusiast, understanding these intricacies is vital for safeguarding your systems against potential threats.
Maor Abutbul is a vulnerability researcher at CyberArk Labs.