How a failing red-team engagement led us to find a silly zero day.

And why “insecure by default” is still an issue in 2019.





In early 2019 we conducted a red-team engagement for a security tech company that, well, knows how to secure stuff. The scope excluded social engineering or any physical attacks against the office network, so we were left with only the internet-facing attack surface.

Confident and proud, we found 2 SQL Injections and an RCE in a matter of hours. We broke into the network and gained access to the most sensitive internal DB packed with consumer data.

Actually, none of that happened. The company did a fantastic job with securing their perimeter, after all - they are a security vendor.









Exposed DB - a new hope?

The company did however have an InfluxDB cluster open to the internet. It was protected by a password and all attempts to brute force it failed. So we did the only thing desperate hackers can do: find a 0-day in InfluxDB.

InfluxDB is an open-source time-series database, used to collect mostly metric data from time-sensitive systems, such as IOT. In our case, it collected data from various application servers and DB clusters and was used for monitoring purposes, probably by the company’s NOC.

Having no other leads for breaking into the network, I started reading InfluxDB’s documentation in order to fully understand how the authentication mechanism works. It turns out that InfluxDB supports both password and “JWT” based authentication.

JWT is an authentication method that is based on a “shared secret,” which is known to both parties. If an external party knows the “secret,” it can create valid tokens and authenticate with them.





(In)secure by default

“Secure by Default” is a concept in which the software is initially designed to be secure in its default configuration. It doesn’t require the user to manually configure or turn on security features in the system. This concept makes it difficult for a user to accidentally expose its system to unwanted access. A classic example for systems that do not follow this practice, and that we hear about every now and again, is exposed MongoDB’s that don’t require authentication. (Updated versions of MongoDB do offer enhanced security controls, so feel free to go ahead and use it).

Reading InfluxDB’s source code , it was apparent that the default setup of the JWT does not create any value in the “shared-secret” key. In other words, the “secret” needed to create a valid JWT token is empty by default. Users are expected to generate a secure secret on their own, but the documentation doesn’t mention that.

















This felt quite silly to me, as this was not a sophisticated vulnerability. It had no fancy buffer overflows or command injections and was just a default insecure configuration. Nonetheless, even the most vigilant security admins can fall for this and unknowingly expose their systems to breaches.

In our red-team engagement, this issue effectively allowed us to extract sensitive internal metadata including server addresses, internal JIRA communication, and a little bit of customer data. It also allowed us to expand the attack surface and find servers and applications that we were unaware of.









Steps to reproduce

First things first, this issue was privately reported to the InfluxDB team in the beginning of the year. They responded quickly and efficiently, and, according to them, the issue should be mitigated by now (I haven’t re-tested it though).

If you are an admin with an old version of InfluxDB (1.3), I would recommend updating or setting a shared secret in the config file.

In order to test if your instance is vulnerable, please go through the following steps.

Test this ONLY against your own infrastructure and DO NOT try this without authorization from the system’s owner.

Discover a user name in the system via the following URL: https://<influx-server-address>:8086/debug/requests Create a valid JWT token with this user, an empty secret, and a valid expiry date You can use the following tool for creating the JWT: https://jwt.io/ header - {"alg": "HS256", "typ": "JWT"} payload - {"username":"<input user name here>","exp":1548669066} signature - HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),<leave this field empty>) The expiry date is in the form of epoch time. Authenticate to the server using the HTTP header: Authorization: Bearer <The generated JWT token> There you have it, now you are logged into the system. You can now query the data using InfluxQL.









Final thoughts

In these days everything is instantaneous, infrastructure is set up with the click of a button. Dev-Ops & IT folks rely on the vendors to provide secure solutions that keep hackers out by default, without the hassle of tedious configuration or setup work.

It is the responsibility of vendors (or us - those teaching the developers) to lower the risk of human error as much as possible.

In 2019, “secure by default” should be the motto of any development team regardless of the sensitivity of their product and the nature of the system’s users.