I found a critical unauthenticated SQL injection that earned a $4,000 bounty. The WAF blocked most SQLi payloads, but I turned HTTP 500 errors into a data extraction channel.
The exploit came down to 3 things:
- SQL injection: A public API passed user input directly into BigQuery SQL.
- WAF bypass: Keyword filtering missed BigQuery scripting techniques.
- Error-based extraction: Detailed database errors exposed query results in HTTP 500 responses.
Here's how I chained all three together:
1. Find the injection
A public analytics endpoint accepted JSON input.
Adding a single `'` to a parameter returned:
`500 → Syntax error: Unclosed string literal`
The error also revealed Google BigQuery was the backend.
2. Build a boolean oracle
The WAF blocked keywords like `UNION`, `SELECT`, `AND`, and `OR`.
But BigQuery scripting constructs like `IF` and `ASSERT` still worked:
`IF TRUE THEN ASSERT FALSE; END IF;` → 500
`IF FALSE THEN ASSERT FALSE; END IF;` → 200
This let me ask the database true/false questions and read the answers from HTTP status codes.
3. Turn the 500 error into an output channel
This was the biggest breakthrough because blind SQLi normally requires guessing data one character at a time using those true/false responses.
But BigQuery supports:
`RAISE USING MESSAGE = 'hello'`
This deliberately throws a database error containing `hello`.
Because the API exposed database error details, the response included:
`500 → ... hello ...`
Now imagine replacing `hello` with a database query result.
The database includes that result in its error message, and the API returns it.
Instead of guessing characters, I could retrieve an entire value in one request.
4. Bypass the WAF using BigQuery's own features
The WAF blocked `EXECUTE IMMEDIATE`, which runs dynamically constructed SQL.
But this worked:
`EXECUTE/**/IMMEDIATE`
The SQL parser understood it, while the WAF's keyword matching didn't.
Another blocked keyword was `INFORMATION_SCHEMA`, used to enumerate tables and columns.
The bypass was:
`REVERSE('AMEHCS_NOITAMROFNI')`
BigQuery reconstructs `INFORMATION_SCHEMA` at runtime.
Using that reversed string in a dynamic query let me access database metadata without sending the blocked keyword literally.
5. What this unlocked
Using those techniques, I was able to:
- Enumerate datasets, tables, and columns
- Query tables unrelated to the original analytics endpoint
- Retrieve customer records, names, and addresses
- Access operational records containing personal identifiers and contact information
- Read sensitive fields that another API intentionally redacted
#BugBounty tip:
When a WAF blocks SQLi, don't just try random payloads. Understand the database dialect, what the WAF filters, and whether verbose errors leak data.
Sometimes a `500` isn't just an error. It may be your output channel.