Universally Unique IDentifiers, also known as UUIDs or GUIDs, are 128-bit numbers used to uniquely identify information in computer systems. UUIDs can be used to refer a wide variety of elements (documents, objects, sessions, tokens, entities, and so on). They can also be used as database keys.
As the name implies, UUIDs should be for practical purposes unique and ideally hard to guess; although in certain scenarios – some of them which will be later discussed in this post – an attacker in possession of UUIDs that were previously generated by a system might be able to predict future ones.
The 16 octets of a UUID are represented as 32 hexadecimal digits and separated by hyphens into five groups, in the form 8-4-4-4-12. This results in a total of 32 alphanumeric characters and 4 hyphens. See the UUID v1 example below:
123e4567-e89b-12d3-a456-426655440000
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
In this example:
Variant | Msb0 | Msb1 | Msb2 | Msb3 | Description |
0 | 0 | X | X | X | Reserved, NCS backward compatibility. |
1 | 1 | 0 | X | X | The variant specified in this document. |
2 | 1 | 1 | 0 | X | Reserved, Microsoft Corporation backward compatibility. |
3 | 1 | 1 | 1 | X | Reserved for future definition. |
Nowadays most implementations adopt variant-1 where the Most Significant Bit (Msb0) is going to be set (1), whereas Msb1 is going to be unset (0). This leaves N with two “free” bits (Msb2 and Msb3), meaning that it can only be one of the following: 8, 9, a or b.
In the UUID 00000000-0000-0000-0000-000000000000, all bits are set to zero.
UUIDs v1, also known as host or time based UUIDs, are generated taking into consideration different components. Take for example, the following UUID v1:
e034b584–7d89-11e9–9669–1aecf481a97b
Version-2 UUIDs are similar to version 1, except the least significant 8 bits of the clock sequence are replaced by a “local domain” number, and the least significant 32 bits of the timestamp are replaced by an integer identifier meaningful within the specified local domain, however, many UUID implementations omit version 2.
Both UUID v3 and UUID v5 are generated using the hash of namespace and name. The key difference between versions three and five is that UUID v3 uses MD5 as the hashing algorithm, whereas UUID v5 uses SHA-1.
UUID = hash(NAMESPACE_IDENTIFIER + NAME)
UUIDs v4 are generated almost entirely random. Only four (4) bits are used to indicate the version and two (2) bits are used to indicate the variant (when variant-1 is adopted). The rest of the bits (122) are randomly generated. See the UUID v4 example below:
00e8da9b-9ae8-4bdd-af76-af89bed2262f
One of the disadvantages of using UUID v4 is that there is a small chance of generating duplicated UUIDs. Although, with the high number of possible combinations (2^122), this situation is highly unlikely to happen.
Consider the following scenario: A web application allows users to reset their password by means of a “Forgot Password” functionality. After following the password reset process, a new UUID v1 is generated and an email with a unique reset link is sent to the email associated with said user. The reset link may look something like this:
https://www.acme.com/reset/836d28b2-7592-11e9-8201-bb2f15014a14
Once an attacker knows the web application uses UUID v1 for generating the password reset link, they could take the approach listed below to guess the right token for an arbitrary account:
https://www.acme.com/reset/99874128-7592-11e9-8201-bb2f15014a14
https://www.acme.com/reset/998796b4-7592-11e9-8201-bb2f15014a14
https://www.acme.com/reset/99876914-7592-11e9-8201-bb2f15014a14
A possibility exists where multiple back- end servers are present behind a single application, or that load balancing is taking place. In this scenario, the attack can be significantly harder to carry out, as UUIDs are going to vary from one server to another. If this is the case, an attacker must try to find a way of hitting the same server consistently (i.e. if the domain name for the targeted application resolves to different public IPs, modify your local ‘hosts’ file so the domain always resolves to the same IP) or perform the attack multiple times until the same server is hit for all requests.
Another challenge to carrying out the attack in this example is that, if there is a significant delay between requests, the first 4 octets of the UUIDs are going to vary considerably from each other making it impractical (the hexadecimal range to brute-force would be too big).
As stated by Scott Contini in “Cautionary Note: UUIDs generally do not meet security requirements”, sometimes UUID v4 is used, but the pseudo-random number generator (PRNG) is faulty.
Take a look at the function that was used for generating ‘random’ UUIDs:
function randomUUID() { var s = [], itoh = '0123456789ABCDEF'; // Make array of random hex digits. The UUID only has 32 digits in it, but we // allocate an extra item to make room for the '-'s we'll be inserting. for (var i = 0; i < 36; i++) s[i] = Math.floor(Math.random()*0x10); // Conform to RFC-4122, section 4.4 s[14] = 4; // Set 4 high bits of time_high field to version s[19] = (s[19] & 0x3) | 0x8; // Specify 2 high bits of clock sequence // Convert to hex chars for (var i = 0; i < 36; i++) s[i] = itoh[s[i]]; // Insert '-'s s[8] = s[13] = s[18] = s[23] = '-'; return s.join(''); }
In this example, the randomUUID() function uses Math.random() which by itself is not cryptographically secure. Contini went further into actually understanding how the Math.random() was implemented in JavaScript, and developed a script to brute-force the hi value given a random UUID generated with the vulnerable function in order to predict future outputs (the script can be downloaded from here).
Examples like this show us that every time we are able to obtain the source code responsible for generating UUIDs in a web application, it is worth taking a look at how that is implemented. Sometimes developers don’t use secure functions or even develop their own, which may lead to cryptographic vulnerabilities (Contini, 2015).
There are several security considerations for each version of UUIDs to consider when building an application. The best solution is to go with the most secure version of UUIDs for the kind and nature of the application you’re building.
For automated passive testing use the UUID issues for Burp Suite extension (also available in PortSwigger repository).
VerSprite focuses on emulating cybercrime and simulating test scenarios that not only reflect current attack patterns, but also threat motives. The foundation of VerSprite’s penetration testing methodology is based on emulating realistic attacks by a malicious actor through the use of PASTA (Process for Attack Simulation and Threat Analysis). Read More →