Not so unique snowflakes

When faced with the problem of identifying entities, most people reach for incremental IDs. Since this requires a central actor to avoid duplicates and can be easily guessed, many solutions depend on UUIDs or GUIDs (universally / globally unique identifiers). However, although being unique solves the first problem, it doesn’t necessarily cover the second. We’ll present our new solution for detecting such issues in web projects in the form of an extension for Burp Suite Pro below.

Most people in IT know UUIDs or at least recognize the its most common representation of 128-bits displayed as hexadecimal numbers in 5 groups, below is an example generated by the built-in function of MySQL.

mysql> SELECT UUID();
| UUID()                               |
| 5caca4bb-f521-11e6-aa0a-00127955f71a |
1 row in set (0.00 sec)

Not everyone knows however, what these numbers represent. UUIDs come in different flavors, and depending on the version number, it might be easy or really difficult to guess the output of a UUID generator.

To start with a good example, version 4 UUIDs contain 122 random bits, so depending on the quality of the PRNG (Pseudo Random Number Generator), there’s a good chance that such identifiers are hard to predict. This is what you get when you use the static randomUUID method of the class java.util.UUID in Java, or the function uuid4 of the module uuid in Python. You can already see a big difference in how standard library designers treat this subject, as former offers a self-explanatory name, while latter depends on people knowing which version number 4 might refer to.

On the other end of the spectrum is version 1, which contains a node identifier (typically the MAC address of the network card) and a timestamp. While MAC addresses are a form of information leak themselves (see the case of Microsoft Word w.r.t privacy), if the attacker already has a valid ID with the node identifier, it’s a lot easier to guess future UUIDs.

A great example of this is an account recovery form, where a unique link is sent to the user’s email address, and clicking that link proves that whoever is trying to recover the account has access to the mailbox. Version 1 UUIDs might sound like a good idea for some to generate the link. However, if an attacker is performing such an operation, she already has a few advantages.

  • Sending such an email to her own account reveals the node identifier and the delay between sending the HTTP request to start the recovery process and the timestamp within the UUID.
  • When starting the recovery on the victim account, she know precisely when she sends the request to the server.

With the above information in her possession, the attacker can now try to narrow down the set of possible valid identifiers to a size that’s feasible for a brute force attack. Similar attacks can be performed for session management and insecure direct object references.

For pentesters, the first task is to analyze the version of the UUID, this is easy to do, below is an example using the Python REPL with the above UUID generated by MySQL.

>>> import uuid
>>> uuid.UUID('5caca4bb-f521-11e6-aa0a-00127955f71a').version

As it can be seen, MySQL generates version 1 UUIDs by default, even though it’s not that apparent just by looking at the string itself. Since UUIDs are used by many applications as references to various objects (business objects, sessions, security tokens), we developed a small and simple plugin for Burp Suite Pro.

This plugin performs a quick analysis on every request sent as part of passive scanning, so no additional traffic is generated. It uses the built-in regular expression and UUID classes of the Java Runtime Environment, so the source code is short and the overhead is negligible. A pre-compiled JAR file can be downloaded from the releases page of our GitHub repository, which can be added to the Burp Extender directly.

The plugin has no UI of its own, and aside from adding it to Burp, there’s no additional configuration necessary, it just works. In case of UUIDs above version 2, it just adds an information-level issue to let you know that the application under test uses UUIDs and you might want to check its entropy and make informed decisions as the human that’s actually doing the pentest. As it can be seen below, the location of the UUID within the request is marked, which can be helpful in the typical multi-kilobyte requests of enterprise monsters.

However in case of version 1 and 2, a medium severity issue is added, along with the information that can be extracted from the UUID itself. Below is an example using our beloved UUID generated by MySQL at the beginning of this blog post; it’s interesting that the node is only 5 bytes long, and has no correlation with the MAC address of any of the network cards within the testing PC.

We hope that this plugin will help pentesters working on web applications do a better job and have one less thing to tackle manually. The source code is available in our GitHub repository under MIT license, feel free to share and improve its functionality. Pull requests welcome!