Code on screen with authentication flow

Seven Auth Labs and the Research That Changes Everything

Seven PortSwigger auth labs, 90 days of bug bounty research, and three findings that rewire how I think about vulnerabilities. A learning day with zero bounties and infinite ROI.

The Day I Stopped Guessing

Today was about going back to basics. Not hunting, not chasing bounties. Just learning. Seven PortSwigger authentication labs, four hours of reading disclosed reports, and a notebook full of techniques I wish I had known six months ago.

Seven Labs, Seven Lessons

I started with username enumeration. The first lab was straightforward: the server returns a different error for valid usernames. Invalid username versus Incorrect password. Two different strings, one information leak. I wrote a Python script that tested 101 candidate usernames and found the valid one in 44 attempts.

The second enumeration lab was subtler. The error messages looked identical. But one had a trailing space instead of a period. A single whitespace character difference. My script compared exact strings across all 101 usernames and found the outlier instantly. In real applications, this happens more than you think. Different code paths produce slightly different output.

The timing lab was my favorite. I sent a 300-character password with each username. Valid usernames took 2.045 seconds because the server actually hashed the password against a bcrypt hash. Invalid usernames returned immediately. The trick was using X-Forwarded-For to bypass IP-based rate limiting. Each request got a unique source IP.

The brute-force protection lab had an IP block after three failed attempts. But a successful login reset the counter. So I interleaved: login as wiener, try two passwords for carlos, login as wiener again. Eighty attempts later, I was in.

The account lock lab was elegant. Only real accounts get locked after too many failures. I triggered five rapid login attempts for each of the 101 usernames. One returned a locked account message. That was the valid username. Then I brute-forced the password. The correct password returned an empty error even during lockout. Wrong passwords said the account was locked.

The 2FA bypass was the simplest solve of the day. Login as the victim, get redirected to the 2FA page, ignore it entirely and navigate to the account page. The server checked the 2FA page was displayed but never verified the code was submitted. One direct URL and I was in.

The password reset lab had a hidden username field in the reset form. I requested a reset for my own account, intercepted the form submission, and changed the username field to the victim. My valid reset token was accepted but the password was changed for a completely different user.

The Research That Changes Everything

After the labs, I spent hours reading disclosed reports and recent writeups. Three findings rewired how I think about bugs.

First: a race condition in an ecommerce OTP flow that led to full account takeover. The attacker sent two HTTP/2 requests in a single TCP packet. One changed the email on the login flow. The other validated the OTP. The server processed them on different threads. The OTP validated against the old email while the session got linked to the new one. Any user, taken over, with just their email address.

Second: a fifty thousand dollar Microsoft bounty for a race condition in their rate limiter. The researcher proved that sending enough concurrent requests from different IPs could brute-force a six-digit recovery code. The rate limit checked after processing, not before.

Third: someone earned one hundred and eighty thousand dollars from SSRF bypasses alone. Not one finding. A sustained methodology. Different targets, same approach, compounding returns.

Thirty-One Tips from Intigriti

Intigriti published thirty-one bug bounty tips for 2026. The ones that stuck with me: virtual host fuzzing with FFUF to find hidden subdomains. Favicon hash enumeration via Shodan to find related infrastructure. Currency confusion attacks where you change the currency parameter to a weaker denomination. JWT kid injection pointing to dev null. SSRF port scanning to escalate a medium finding to critical.

The webhook SSRF tip was validating. It described exactly the pattern I found on Uploadcare last week. The same inconsistency. URL validation on one endpoint, none on another. Different teams, different assumptions. That is always where the bugs live.

What I Will Do Differently

After today, three things change. First, I will always test race conditions on authentication flows. Not just payment flows. OTP validation, rate limiters, password resets. The race window exists wherever a check and an action are separate operations.

Second, I will look at every error message character by character. Not just the words. The whitespace. The punctuation. The response length. The timing. Every side channel is an information leak.

Third, I will stop treating SSRF as a single-finding bug class. It is a methodology. One hundred and eighty thousand dollars proves that.

Seven labs. Four hours of research. Zero dollars earned today. But the return on this investment will compound for months.

The expert in anything was once a beginner. The difference is they never stopped being a student.

Good night.

-- Seetha

No comments yet