Boolean Oracle in a Product Sort Parameter
A retail API exposes GET /api/products?category=books&sort=price. The sort value is concatenated into an ORDER BY clause. Valid values are price, name, and rating; anything else returns HTTP 500 with a generic error. The endpoint sits behind a WAF that blocks SLEEP, BENCHMARK, pg_sleep, and SQL comments. It also blocks the literal string information_schema in request bodies but not its URL-encoded form; it does not block IF, ASCII, or SUBSTRING. The response is a JSON array of 40 products, many with identical prices, so changing the secondary sort order is visible in the first few product IDs. The category parameter is not injectable and returns a stable set of 40 books. You have a Burp session and can use Repeater and Intruder. The database is MySQL 8.0. You want to confirm a boolean-based blind injection and extract the current database name without relying on time delays.
1. How do you confirm the injection with two requests, and why does the WAF not block it?
Read the worked answer
Send sort=price as the baseline. Then send sort=price,IF(1=1,price,name) and sort=price,IF(1=2,price,name). The application concatenates the sort value into ORDER BY, so the injected IF becomes a secondary sort expression. With many products sharing the same price, the true condition orders ties by price, effectively leaving the secondary order unchanged, while the false condition orders ties by name. Compare the first few product IDs in Repeater using Comparer. A stable difference between the two responses confirms that the expression is evaluated. The WAF does not block this because it targets time-delay functions and comments, not boolean expressions; IF, ASCII, and SUBSTRING are allowed. If either request returns 500, the syntax is wrong or a filter fired; adjust encoding rather than assuming injection.
2. Design an Intruder attack to extract the first character of database() using binary search. What payload and grep setting?
Read the worked answer
Use the true/false pair to establish two reference responses: record the first product ID for IF(1=1,price,name) and for IF(1=2,price,name). In Intruder, set the payload position in sort=price,IF(ASCII(SUBSTRING(database(),1,1))>§64§,price,name) with Sniper. Use a binary-search payload list: 64, 96, 112, 120, 124, 126, 127, then narrow based on the response. Configure Grep - Match on the true response's first product ID; a match means the condition is true. For example, if 96 matches and 112 does not, the character is between 97 and 112; continue with 104, 108, and so on. URL-encode commas and parentheses in Repeater or Intruder as needed. Once the ASCII value is known, convert it with Decoder. This takes about seven requests per character, far fewer than a linear character set.
3. What false-positive risks exist and how do you control them?
Read the worked answer
Tie order can be randomized by the database or pagination, and caching can return a previous response. Fix the category to books, add a cache-buster such as &cb=12345 that the API ignores, and send the true/false pair three times each. Check Cache-Control and Age headers; if a CDN is present, use a unique query value per test. Treat any 403 or 429 from the WAF as inconclusive and retry with a delay. Compare full JSON arrays in Comparer, not just the first ID, because a single product may move for unrelated reasons. Use Logger to keep the exact request order. If the true and false responses are identical across repeats, stop and reassess the injection context rather than forcing a payload.