TL;DR: I discovered a Race Condition vulnerability on zt.zerodaytest.com that allowed a single authenticated user to bypass a "one review per organization" restriction and create unlimited duplicate reviews simultaneously — by sending parallel HTTP requests in the exact same millisecond using Burp Suite's Single Packet Attack technique.

🎯 Target

  • Platform: zt.zerodaytest.com (Byte Capsule IT)
  • Program Type: Vulnerability Disclosure Program (VDP)
  • Vulnerability Type: Race Condition — Limit Overrun (TOCTOU)
  • Severity: 🟡 Low
  • Status: ✅ Resolved & Rewarded
  • Affected Endpoint: POST /api/reviews

🧠 What is a Race Condition?

Before diving in, let me quickly explain the concept.

A Race Condition happens when a system's behavior depends on the timing of multiple operations — and those operations happen so close together that the system doesn't have time to process the first one before the second one arrives.

The specific type here is called TOCTOU — Time of Check to Time of Use:

Time of CHECK → "Has this user already submitted a review?"
                       ↓ (database says NO — review not committed yet)
Time of USE   → "OK, allow the submission" ← all threads pass this
                       ↓
              Multiple reviews created simultaneously 💥

The gap between the check and the actual database write is the attack window — and it only needs to be milliseconds wide.

🔍 Discovery

While testing the review submission feature on zt.zerodaytest.com, I noticed the platform enforced a strict one-review-per-organization rule. Submitting a second review sequentially was correctly blocked:

{"error": "You have already reviewed this organization"}

That sequential check works fine. But what if we don't submit them sequentially?

I asked myself: "What happens if multiple requests arrive at the server at exactly the same time?"

🧪 Proof of Concept

Step 1 — Intercept the Request

Log in, navigate to an organization's review page, and intercept the POST /api/reviews request in Burp Suite:

POST /api/reviews HTTP/2
Host: zt.zerodaytest.com
Cookie: [REDACTED]
Content-Type: application/json
{"content":"race","rating":5}

Step 2 — Set Up the Single Packet Attack

In Burp Suite:

  1. Send the request to Repeater
  2. Duplicate it 5–10 times
  3. Add all tabs to a single Tab Group (e.g., named "Race")
  4. Change the send mode to "Send group in parallel (single packet attack)"

💡 Why Single Packet Attack? HTTP/2 allows multiple requests to be multiplexed over a single TCP connection. Burp Suite's Single Packet Attack bundles all requests into one TCP packet — meaning they all arrive at the server simultaneously, eliminating any network jitter that would otherwise separate them in time.

Step 3 — Fire

Click Send.

Step 4 — Observe the Responses

All requests return 201 Created simultaneously:

None
HTTP/2 201 Created
{"message":"Review submitted for approval","review":{
  "_id":"69fd7af1d11ddcf5ea653474",
  "organizationId":{"name":"bakingsector"},
  "submittedBy":{"email":"namineru@bdm.ovh"},
  "content":"race","rating":5,"status":"pending"
}}
HTTP/2 201 Created
{"message":"Review submitted for approval","review":{
  "_id":"69fd7af1d11ddcf5ea653476",  ← Different _id, same user!
  "organizationId":{"name":"bakingsector"},
  "submittedBy":{"email":"namineru@bdm.ovh"},
  "content":"race","rating":5,"status":"pending"
}}

Notice the different _id values — these are distinct database records created by the same user for the same organization. The duplicate check was bypassed entirely.

🧠 Root Cause Analysis

Normal Sequential Flow (Secure):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Request 1 → Check DB → No review found → Write → ✅ Created
Request 2 → Check DB → Review found   → Reject → ❌ Blocked
Race Condition Flow (Vulnerable):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Request 1 → Check DB → No review found ─┐
Request 2 → Check DB → No review found ─┤ All checks happen
Request 3 → Check DB → No review found ─┤ before any write
Request 4 → Check DB → No review found ─┘ completes
     ↓
All 4 pass validation → All 4 written to DB 💥

The backend read the database state before any of the concurrent writes had committed. Since none of the parallel requests saw an existing review, all of them passed the duplicate check.

💣 Impact

1. Rating Integrity Manipulation

An attacker can flood an organization with dozens of 5-star or 1-star reviews from a single account, artificially inflating or destroying their public reputation score.

2. Admin Queue Flooding

By scripting this attack, hundreds of pending reviews can be injected into the approval queue — making the moderation process unusable and burying legitimate reviews under a mountain of duplicates.

3. Database Integrity Violation

The system stores unauthorized, redundant records — violating the intended schema constraint that each (userId, organizationId) pair should be unique.

Why Low Severity?

The team correctly assessed this as Low because all reviews require admin approval before becoming publicly visible. A diligent admin could catch and reject duplicate submissions from the same user. There's no direct, immediate public-facing impact without admin action — which is a meaningful mitigating factor.

That said, the underlying flaw is real, reproducible, and worth fixing.

🛡️ Remediation

Fix 1 — Database-Level Unique Constraint (Recommended)

-- Add a composite unique index on the reviews table
CREATE UNIQUE INDEX unique_user_org_review
ON reviews (organizationId, submittedBy);

This is the most reliable fix. The database engine itself will reject duplicate inserts — regardless of race conditions at the application layer. No amount of parallel requests can bypass a DB-level constraint.

Fix 2 — Pessimistic Locking

// Acquire a lock before checking + writing
await db.transaction(async (trx) => {
  const existing = await trx('reviews')
    .where({ organizationId, submittedBy: userId })
    .forUpdate()  // ← locks the row for this transaction
    .first();
  if (existing) throw new Error('Already reviewed');
  await trx('reviews').insert({ organizationId, submittedBy: userId, ... });
});

Fix 3 — Atomic Upsert / Insert-or-Fail

// Use INSERT ... ON CONFLICT DO NOTHING
await db.raw(`
  INSERT INTO reviews (organizationId, submittedBy, content, rating)
  VALUES (?, ?, ?, ?)
  ON CONFLICT (organizationId, submittedBy) DO NOTHING
`, [orgId, userId, content, rating]);

📊 Vulnerability Summary

None

📅 Disclosure Timeline

None
None

📝 Key Takeaways

1. Sequential testing isn't enough. The duplicate check worked perfectly in normal usage. Race conditions only reveal themselves under concurrent load — always test business-logic restrictions with parallel requests.

2. Single Packet Attack is a game changer. Burp Suite's HTTP/2 parallel request feature removes network timing as a variable. If your target supports HTTP/2, this technique is incredibly reliable.

3. Application-layer checks are fragile. Any check-then-act pattern at the code level is potentially vulnerable to TOCTOU. The only truly reliable defense is enforcement at the database layer via unique constraints or atomic transactions.

4. Low severity ≠ not worth reporting. Even with admin moderation as a mitigation, the underlying data integrity flaw was real and the fix was necessary. Always report what you find — let the program assess the impact.

💬 If this write-up was helpful, leave a clap and follow for more real-world bug bounty findings. Happy hunting — stay ethical, stay curious.