Bypassed PUBG’s Rate Limit with small trick

0 Comments

Once upon a time, I stumbled upon something interesting while poking around apps listing on hackerone. It wasn’t an RCE, it wasn’t a SQLi but just a small tweak to bypass rate limit.

Image

The Setup

I was on PUBG’s account portal — specifically the password reset page at:

https://accounts.pubg.com/profile/password/forgot

Like most sites, PUBG had a rate limit to stop people from brute-forcing the reset form. After a few attempts, it would hit you with the dreaded:

HTTP/1.1 429 Too Many Requests {"message":"error.forgot-rate-limit"}

Image

if the backend trusts the X-Forwarded-For header without validating it, you can trick it into thinking you’re a completely different IP address.

So I sent the exact same password reset request — but with a little twist:

  • X-Forwarded-For: 127.0.0.1

Image

Image

Image

HTTP/1.1 200 OK {"redirect":"/email-sent"}

Why This Worked

PUBG’s backend was using the X-Forwarded-For header to track the client’s IP for rate limiting. But here’s the problem — that header is completely user-controlled.

they trusted whatever value the client provided. This meant I could rotate that header with any fake IP and completely bypass the limit.

This same behavior existed on the /location endpoint: GET /location HTTP/1.1 X-Forwarded-For: 24.113.42.169

Image

Image

ChatGPT to reproducing the Issue Locally

ChatGPT did the whole reproduction — I had no idea what I was doing from this section!!!!

Sample flask app from my boss

Image

This app runs only on localhost (127.0.0.1:8000) — meaning any outside requests must go through Nginx.

The Nginx Reverse Proxy

Image

At this point, any request to the public server will be proxied to Flask, with X-Forwarded-For coming from Nginx.

The Test Without Spoofing:

When I made a normal request without adding any extra headers, Flask showed my real public IP: 49.156.x.x

Image

Then, I sent the exact same request — but this time I manually set the X-Forwarded-For header:

Image

*** Just to be clear, this is only a reproduction ChatGPT helped me with. No clue if PUBG’s real setup looked anything like this.

From there, the bypass was straightforward: change the header, reset the counter, keep sending requests.

and turns out, PUBG has been vibecoding this way the whole time.