Apache Superset is an open source data visualization and exploration tool. It has over 50K stars on GitHub, and there are more than 3000 instances of it exposed to the Internet. In our research, we found that a substantial portion of these servers – at least 2000 (two-thirds of all servers) – are running with a dangerous default configuration. As a result, many of these servers are effectively open to the public. Any attacker can “log in” to these servers with administrative privileges, access and modify data connected to these servers, harvest credentials, and execute remote code. In this post, we’ll dive deep into the misconfiguration, tracked as CVE-2023-27524, and provide advice for remediation as well as indicators of compromise to look for if you’re a user of Superset.
Superset is written in Python and based on the Flask web framework. A common practice for Flask-based applications is to use cryptographically signed session cookies for user state management. When a user logs in, the web application sends a session cookie that includes a user identifier back to the end user’s browser. The web application signs the cookie with a SECRET_KEY, a value that is supposed to be randomly generated and typically stored in a local configuration file. With every web request, the browser sends the signed session cookie back to the application. The application then validates the signature on the cookie to re-authenticate the user prior to processing the request.
The security of the web application depends critically on ensuring the SECRET_KEY
is actually secret. If the SECRET_KEY
is exposed, an attacker with no prior privileges could generate and sign their own cookies and access the application, masquerading as a legitimate user. The off-the-shelf flask-unsign tool automates this work: “cracking” a session cookie to discover if it was signed by a weak SECRET_KEY
, and then forging a fake but valid session cookie using a known SECRET_KEY
.
Back in October 2021, when we first started researching Superset, we noticed that the SECRET_KEY
is defaulted to the value \x02\x01thisismyscretkey\x01\x02\\e\\y\\y\\h
at install time. It’s the end user’s responsibility to modify the application configuration to set the SECRET_KEY
to a cryptographically secure random string. This is documented in the Superset configuration guide. But we were curious what percentage of users actually read the documentation. So, using Shodan, we did a basic search for Superset servers on the Internet. Simply requesting the Superset login page (without attempting to login) returns a session cookie that we then passed through flask-unsign
to determine if it was signed with the default SECRET_KEY
. To our surprise, we found that 918/1288 (> 70%) of all servers were using the default SECRET_KEY
!
So what can an attacker do knowing the SECRET_KEY
for a Superset application? Assuming the Superset server is not behind single sign-on (SSO), an attacker can login as an administrator by forging a session cookie with a user_id
or _user_id
value set to 1, using the off-the-shelf flask-unsign
toolkit. “1” corresponds to the first Superset user, who is almost always an administrator. Setting the forged session cookie in the browser’s local storage and refreshing the page allows an attacker to access the application as an administrator. For Superset servers behind SSO, more work may be required to discover a valid user_id
value – we have not tested this attack path.
Superset is designed to enable integrations with a variety of databases for exploring data and creating visualizations. Admin access gives attackers a lot of control over these databases and the ability to add and remove database connections. By default database connections are set up with read-only permissions but an attacker with admin access can enable writes and DML (data model language) statements. The powerful SQL Lab interface allows attackers to run arbitrary SQL statements against connected databases. Depending on database user privileges, attackers can query, modify, and delete any data in the database as well as execute remote code on the database server.
Administrative interfaces to web applications are often feature-rich and result in remote code execution on the application server. We found such multiple paths to remote code execution across different Superset versions in a variety of configurations. Remote code execution is possible both on databases connected to Superset and the Superset server itself. We also found a host of methods for harvesting credentials. These credentials include Superset user password hashes and database credentials, both in plaintext and in a reversible format. We are not disclosing any exploit methods at this time, though we think it’ll be straightforward for interested attackers to figure it out.
After our initial report to the Superset team back in Oct. 2021, we decided to re-check the state of Superset in Feb. 2023 to see if the situation with the default Flask key had improved.
We discovered that in January 2022 the SECRET_KEY
value was rotated to a new default CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET
, and a warning was added to the logs with this Git commit.
We were curious if this change translated to a change in user behavior. We repeated the Shodan experiment from October 2021, using both the original default SECRET_KEY
and the new one. We also included two other SECRET_KEY
s we found, one in a deployment template, thisISaSECRET_1234,
and another in the documentation YOUR_OWN_RANDOM_GENERATED_SECRET_KEY
.
A basic search for Superset instances produced 3390 results, of which 3176 appeared to be really Superset instances. And of these 3176 instances, we found that 2124 (~67%) were using one of the four default keys.
The usage of Superset over the last year has increased, but the usage of a default SECRET_KEY
hasn’t dropped much. A large number of installs are using the new default SECRET_KEY
. To get extra precise, we did a sweep of Superset instances to grab their version information, which is often visible on the landing page. From this we got a breakdown of default keys in use by version: The rotation of the SECRET_KEY
and addition of the warning in the logs happened with version 1.4.1. It can be seen from version 1.4.1 onwards, a significant proportion of instances are sill running with a default key. For instance, 71% of Superset 2.0.0 instances and 55% of Superset 2.0.1 instances and 87% of the latest Docker version 0.0.0-dev instances are running with default keys.
Alarmed by the numbers, we re-confirmed the attack paths described above and raised the issue again to the Apache security team.
The Superset team made an update with the 2.1 release to not allow the server to start up if it’s configured with a default SECRET_KEY
. With this update, many new users of Superset will no longer unintentionally shoot themselves in the foot.
This fix is not foolproof though as it’s still possible to run Superset with a default SECRET_KEY
if it’s installed through a docker-compose file or a helm template. The docker-compose file contains a new default SECRET_KEY
of TEST_NON_DEV_SECRET
that we suspect some users will unwittingly run Superset with. Some configurations also set admin/admin as the default credential for the admin user.
Among the 2000+ affected users, we found a broad mix of large corporations, small companies, government agencies, and universities. We sent out good-faith notifications to a number of organizations, some of whom remediated shortly after.
If you’re a user of Superset, you can check if your server is vulnerable with this script on Github. The script uses the flask-unsign
toolkit to check if the Superset session cookie is signed with one of the known default SECRET_KEY
s.
If the script shows your Superset instance is vulnerable, and you have a Superset instance running on the Internet, we recommend that you fix immediately or remove it from the Internet.
Fixing the issue requires generating a SECRET_KEY
securely and configuring it, following the instructions here. In addition, since sensitive information such as database passwords is also encrypted with the SECRET_KEY
, that information will need to be re-encrypted with the new SECRET_KEY
. The superset
CLI tool automates the process of rotating secrets – see here.
We have not validated exploitation against Superset installs with single sign-on (SSO) configured. SSO may make it hard to forge session cookies if the user_id
s are unpredictable GUIDs rather than auto-incrementing identifiers. At the same time, it’s possible there are other attack paths that leak user ids, or the user ids map to easily discoverable identifiers such as email addresses. We recommend remediating even if your Superset install is behind SSO.
Telling if you’ve already been compromised is not that easy because exploiting this misconfiguration allows anyone to masquerade as a legitimate user. Superset provides a detailed action log in the interface that can be used to inspect user activity. We recommend looking for unusual admin-level actions such as viewing or modifying database configuration, adding a new database,, exporting data, or unusual queries in the SQLLab query history. We also recommend looking at the application access log to check for unusual API calls such as calls to the /api/v1/database
endpoint. Of course an attacker can easily cover their tracks once they fully compromise the server.
The issue of hardcoded Flask secret keys is not new. Apache Airflow, a sister project to Superset, was affected by a similar issue, filed as CVE-2020-17526, discovered by Junghan Lee of Deliveryhero. Security researcher @iangcarroll automated discovery of vulnerable Airflow instances for bug bounty and wrote up a blog post describing the process here. The authentication bypass method he describes is exactly the same as what’s described above, as both Airflow and Superset are based off the same common framework, Flask AppBuilder.
Later on @iangcarroll found a similar vulnerability in Redash, another open source data visualization tool based on Flask. This was filed as CVE-2021-41192. The approach the Superset team took to addressing this vulnerability – refusing to start the server if it’s running with a default SECRET_KEY
– is the same approach the Redash team took.
It’s not often that data is available at scale to understand how security design choices impact user behavior. Checking for vulnerabilities and misconfigurations typically requires crossing ethical boundaries. In this case, we got lucky. Telling if a Superset server is misconfigured simply requires browsing to the login page and cracking the returned session cookie.
It’s commonly accepted that users don’t read documentation and applications should be designed to force users along a path where they have no choice but to be secure by default. The data we’ve gathered backs up this common wisdom. We all know that default credentials and default keys are bad, but how bad are they really? In the case of Superset, the impact of the insecure default Flask key extends to roughly 2/3 of all users. Again, users don’t read documentation. And users don’t read logs. The best approach is to take the choice away from users and require them to take deliberate actions to be purposefully insecure.
SECRET_KEY
and adds warning to logs with this Git commitSECRET_KEY
SECRET_KEY