— -

## Introduction

Hey folks 👋

None

In this write-up, I want to share one of my favorite bug classes to hunt for: **race conditions in business logic**. Specifically, I'll walk you through a scenario where I was able to bypass a strict file attachment limit on a support ticketing system by sending parallel requests at just the right moment.

For privacy reasons, I won't be naming the actual target. Instead, I'll use a fictional company called **"AcmeSupport"** running a help desk at `help.acmesupport.com`. The vulnerability and methodology are real, only the target name has been changed.

Let's dive in.

The Target: AcmeSupport Help Desk

AcmeSupport allows users to open support tickets and attach files (logs, screenshots, etc.) to help the support team troubleshoot issues. According to the UI and the documentation, **a maximum of 10 attachments per ticket** is enforced.

This kind of limit makes sense for several reasons:

- It prevents storage abuse - It keeps support tickets manageable for agents - It mitigates resource exhaustion

But the question I always ask when I see such a limit is: **how is this limit enforced on the backend?**

— -

## Discovery

I started by going through the standard ticket creation flow:

1. Navigated to `https://help.acmesupport.com/hc/en-us/requests/new/` 2. Filled out the new ticket form 3. Started attaching files one by one

After uploading 10 files, the application correctly blocked any further uploads. The frontend would not even let me select an 11th file. Good UX, but I never trust client-side enforcement, so I kept digging.

I fired up Burp Suite and started inspecting the actual upload request. Each file upload was its own HTTP `POST` request to a specific endpoint, with the file in the body.

That's when the lightbulb went off 💡 — if every upload is a separate request, what happens if I send a bunch of them at the *exact same moment*?

— -

POC

None
None

— -

## Exploitation: The Race

Here's how I tested the theory:

**Step 1:** I started a new ticket form and intercepted a single file upload request in Burp.

**Step 2:** I sent that request to Burp Repeater.

**Step 3:** Inside Repeater, I duplicated the request roughly **20 times** — double the supposed limit.

**Step 4:** I selected all 20 tabs, grouped them together, and used Burp's **"Send group in parallel (single connection)"** feature. This option uses HTTP/2 single-packet attack technique, which bundles all requests into one TCP packet so they arrive at the server with sub-millisecond differences.

**Step 5:** Hit send and watch the responses.

**Result:** All 20 requests returned a `200 OK`. Every single file was accepted. Refreshing the ticket showed all 20 attachments successfully uploaded, completely bypassing the documented limit of 10.

The window of opportunity is tiny, often just a few milliseconds, but that's all you need.

— -

Why Did This Work?

The backend logic likely looked something like this in pseudocode:

```python function handleUpload(ticketId, file): count = db.getAttachmentCount(ticketId) # Step 1: Check if count >= 10: # Step 2: Decide return error("Limit reached") db.saveAttachment(ticketId, file) # Step 3: Update return success ```

When 20 requests hit this function in parallel, every single one reads `count = 0` (or some value below 10) before any of them have a chance to call `saveAttachment`. They all pass the check, and they all write to the database.

There's no atomicity, no locking, no transaction isolation. Just a beautiful, exploitable gap.

— -

Lessons Learned

A few takeaways from this hunt:

- **Don't trust client-side limits.** They exist for UX, not security. - **Whenever you see a numeric limit, ask yourself how it's enforced.** Counters are race condition magnets. - **Burp's parallel send feature is your best friend.** The HTTP/2 single-packet attack made this trivial to test. - **Race conditions are often dismissed as low-impact.** But when you connect them to storage, cost, DoS, or social engineering vectors, the impact story becomes much stronger.

— -

Final Thoughts

Race conditions are one of those bug classes that feel almost magical when they work. The application *thinks* it's enforcing a rule, the developers *swear* the limit is solid, and yet a few well-timed packets blow right past it.

They're everywhere — coupon codes, voting systems, balance transfers, file uploads, friend requests, follower counts. Anywhere there's a counter or a quota, there's likely a race waiting to be found.

Happy hunting

— -

Got questions? Drop them in the comments.*