Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book
Warsztat – Jak zrealizować OS Command Execution na D-Link 626L…nie posiadając samego urządzenia?
Wstęp
Urządzenia takie jak routery czy kamery IP dają ogromną satysfakcję, kiedy odkrywamy, jak działają. Jeszcze większą radość sprawia odkrywanie, jak możemy się do nich dostać w, no właśnie…, nieprzewidziany przez twórców oprogramowania sposób. :)
Oto jak można znaleźć furtkę w firmware D-Linka DIR 626L (DIR626LA1_FW103B01.bin), która wpuści nas „do środka”.
Monitorując doniesienia o bezpieczeństwie urządzeń, możemy zauważyć, że część wykrytych podatności zostało znalezionych w usługach szerzej nam znanych typu: serwery http, ftp itp. Nie możemy jednak zapominać o innych usługach, które mogą działać tylko dla pewnej gamy urządzeń, bądź też pojedynczego routera lub które z jakiegoś innego powodu są zaimplementowane w badane przez nas oprogramowanie.
D-Link 626L kryje za sobą pewne niespodzianki. Jedną z nich chciałbym przedstawić w tym artykule.
Jeszcze chwilka
Przed rozpoczęciem przygody musimy zapoznać się z „warsztatem”, jaki potrzebny będzie do testów. Znajomość różnych technik analizy aplikacji pozwala zbadać ją bardziej dogłębnie pod kątem działania, a także daje nam więcej radości z takich poszukiwań. Poniżej składniki niezbędnego warsztatu:
- qemu,
- system operacyjny dla architektury, którą wykorzystuje badana aplikacja; w naszym przypadku będzie to debian dla architektury mipsel,
- gdb,
- trochę Pythona też się przyda oraz doświadczenie z programowaniem i sieciami,
- firmware-mod-kit – zbiór skryptów do rozpakowywania wszelkiego rodzaju firmware’ów,
- Binwalk – nie używany w artykule, jednak umieszczam go tutaj ponieważ tak jak firmware-mod-kit jest równie przydatny (wspomnę o nim jeszcze później).
Teraz omówimy sobie każdy z wymienionych elementów.
Zacznijmy od instalacji wymaganych pakietów. Zakładając, że robimy wszystkie testy na systemie Ubuntu (mój jest 13.04), możemy to zrobić w taki sposób:
# apt-get install autoconf automake libtool zlib1g-dev libglib2.0-dev uml-utilities bridge-utils
1. Najpierw instalacja qemu. Będzie potrzebne do emulacji systemu operacyjnego, na którym uruchomimy badany soft. Emulacja systemu operacyjnego, a następnie uruchomienie na nim aplikacji do analizy, pozwoli zbliżyć się do jej realnego działania. Okej. Paczka się już napewno ściągnęła, więc rozpakujmy i skompilujmy ją sobie:
$ ./configure --static && make && sudo make install
2. Kolejnym krokiem będzie konfiguracja interfejsu sieciowego oraz grupy w taki sposób, aby emulowany system operacyjny poprzez qemu miał dostęp do naszej sieci i tym samym do Internetu. (Tu chciałbym polecić do przeczytania super post pana Cultipa, mówiący szerzej o omawianym zagadnieniu.) Tak więc konfigurujemy grupę:
# groupadd -r tuntap # usermod -a -G tuntap hawk
I wraz z grupą interfejs sieciowy. W przypadku systemu Ubuntu będzie to plik /etc/network/interfaces. Mój plik konfiguracyjny wygląda tak:
auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp # auto br0 iface br0 inet dhcp pre-up tunctl -t tap0 -g tuntap pre-up tunctl -t tap1 -g tuntap pre-up ip link set tap0 up pre-up ip link set tap1 up bridge_ports eth0 tap0 tap1 bridge_stp off bridge_maxwait 0 bridge_fd 0 post-down ip link set tap0 down post-down ip link set tap1 down post-down tunctl -d tap0 post-down tunctl -d tap1
3. Nadszedł już czas, by to wszystko wprawić w ruch. Ściągamy dwa pliki: vmlinux-2.6.32-5-4kc-malta oraz debian_squeeze_mipsel_standard.qcow2. Potrzebny będzie nam teraz skrypt uruchamiający qemu, które odpali przed chwilą ściągniętego debiana.
#!/bin/sh image="/home/hawk/routers_research/debian-mipsel/debian_squeeze_mipsel_standard.qcow2" kernel="/home/hawk/routers_research/debian-mipsel/vmlinux-2.6.32-5-4kc-malta" echo "[+] Start debian mipsel.." qemu-system-mipsel -net nic -net tap,ifname=tap1,script=no,downscript=no -nographic -M malta -m 256 -kernel $kernel -hda $image -append "root=/dev/sda1 console=tty0"
Powyższy kod zapisujemy jako skrypt. Niech jego nazwą będzie emulator.sh. Ustawiamy „most” pomiędzy naszą siecią a emulowanym debianem.
sudo ifdown eth0 sudo ifup br0
W końcu rozpoczynamy emulacje naszego debiana!
chmod +x emulator.sh ./emulator.sh
Ekstra! W ten sposób przygotowaliśmy dodatkowy system do testów.
4. Możemy się zalogować jako użytkownik (login: user, hasło: user) lub jako administrator (login: root, hasło: root). Okej. Należałoby jeszcze sprawdzić dla pewności połączenie z Internetem.
ping google.pl
5. Jeśli wszystko jest w porządku i mamy połączenie z Internetem, zostaje nam tylko doinstalować serwer sshd, aby można było logować się zdalnie. To ułatwi nam pracę.
apt-get install openssh-server
Szukamy
Teraz powróćmy do badanego firmware.
1. Spróbujemy go rozpakować – posłuży nam do tego firmware-mod-kit.
2. Dobra. Czas rozpocząć poszukiwania od sprawdzenia konfiguracji badanego firmware’a. Zobaczmy, co kryją pliki konfiguracyjne. W katalogu /etc_ro/ widać plik rcS. Co się w nim znajduje?
Plik rcS zawiera dużo użytecznych informacji przydatnych w naszych badaniach.
3. Plik pod nazwą mp.sh może być interesujący! Zobaczmy, gdzie leży i co zawiera. Przyda się do tego polecenie: find -depth -name mp.sh.
Wygląda na to, że to jakiś daemon, pewnie nasłuchujący niewiadomych dla nas poleceń.
No to go odpalmy! Ale zaraz… Sprawdźmy najpierw, co to za plik.
4. Polecenie file sbin/mpd pozwala stwierdzić, że ów program używa architektury mips do poprawnego działania (dokładniej mipsel tzn. mips w trybie little-endian). Spróbujmy więc skopiować go na przygotowaną przez nas maszynę do testów i go odpalić.
scp mpd root@192.168.1.104:~/
W moim przypadku adres 192.168.1.104 jest adresem emulowanego systemu do testów. Po skopiowaniu spróbujmy uruchomić nasz serwer w konsoli.
Tutaj mała uwaga. Jeśli nie będzie mu się chciało uruchomić z powodu błędów związanych z brakiem niektórych bibliotek, to należy wskazane biblioteki skopiować z katalogu „/lib” rozpakowanego firmware do katalogu /lib emulowanego systemu (jak to zrobiliśmy z mpd).
./mpd
5. Świetnie! Usługa mpd czymkolwiek jest, została uruchomiona. Pokazała nam nawet swój banner MP AUTOMATION daemon (ver 0.1).
6. Pozostaje nam sprawdzić, jak można się z nią komunikować. Możemy do tego użyć programu „strace” i wywołać raz jeszcze nasz deamon poleceniem: sudo strace ./mpd. Jest to jeden ze sposobów na poradzenie sobie w takiej sytuacji.
Wygląda, że coś mamy! Na samym końcu widnieje informacja dotycząca protokołu, jakiego używa badany deamon („socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) „) oraz jego portu („sin_port=htons(9034)”).
7. Żeby się przekonać, przeskanujmy emulowaną maszynę programem nmap.
Hura! Na tym etapie możemy stwierdzić, że odkryliśmy i uruchomiliśmy do testów pewien deamon, który nasłuchuje na danym porcie i czeka na bliżej nam nieznane instrukcje.
8. Uruchomimy go ponownie, ale już z pomocą debuggera. W naszym przykładzie posłużymy się debugerem, wcześniej już wspomnianym: gdb. Ale jak?
Krótki manual pokazuje, jak to zrobić. Otwórzmy dwie konsole i zalogujmy się na emulowany system. W pierwszej zostanie odpalony gdbserver (zalogowany na emulowanego debiana – pierwsza konsola):
gdbserver localhost:4444 mpd
Czeka on teraz na połączenie z debugger-klientem (zalogowany na emulowanego debiana – druga konsola). Oto on:
gdb program # odpalamy gdb (gdb) target remote localhost:4444 # łączymy się ze zdalnie uruchimionym gdbserver ... (gdb) break main # ustawiamy punkt zatrzymania programu na funkcji main
Teraz zastanówmy się przez chwilę i odpowiedzmy sobie na pytanie, dlaczego właśnie w taki sposób debugujemy program?
Jest to dobry sposób na aplikacje, które po uruchomieniu „oczekują” na jakieś zdarzenia, uniemożliwiając testerowi np. na ustawienie breakpointa czy odczytanie wartości z rejestru.
A może oprócz tej metody, którą przedstawiłem, są jeszcze jakieś inne? Lepsze? Prostsze? Bardzo zachęcam do komentowania i dzielenia się informacjami. :)
9. Pozostało tylko napisać skrypt w Pythonie, który pozwoli wysyłać pakiety z danymi do mpd i analizować, co się dalej dzieje.
from socket import * import sys import select import sys, getopt address = ('192.168.1.104', 9034) client_socket = socket(AF_INET, SOCK_DGRAM) # chwilowo pusta buffer = "" client_socket.sendto(buffer, address) recv_data, addr = client_socket.recvfrom(2048) print "Received data: ", recv_data
Zmienną „buffer” na razie pozostawiamy pustą, aby później aktualizować ją o tajemnicze, znalezione podczas debugu instrukcje.
Analizujemy
1. Nadszedł czas na statyczną analizę, którą zaczniemy od wyżej wspomnianej funkcji main. Po otrzymaniu pakietu jednym z zadań daemona jest sprawdzenie, czy zapytanie, które zostało przesłane do mpd, zawiera frazę flash set. Pójdźmy tym tropem i zacznijmy modelować taki pakiet do wysłania. Obrazek poniżej pokazuje, że adres a1 faktycznie zawiera wartość flash set, która jest sprawdzana w przysłanych danych. Wzbogaćmy zmienną buffer o znalezioną wartość:
buffer="flash set"
2. Zapis i uruchomienie skryptu (python skrypt.py) pozwoli nam uzyskać informacje od debuggera klienta przedstawione na poniższym obrazku.
3. Badając poniższy kod, natykamy się na funkcję sprawdzającą, czy dane odebrane przez serwer zawierają oprócz flash set jakąś inną wartość. Jeśli więc kolejną wartością będzie HW_NIC1_ADDR, to algorytm zacznie poszukiwania dalszych danych, zmierzając w interesującym dla nas kierunku.
Czy oprócz „flash set” serwer „mpd” odebrał jeszcze jakieś polecenie?
4. Okej. To teraz, wyprzedzając trochę fakty oraz analizę dalszego kodu, dodajmy do naszego pakietu jeszcze jeden, taki nietypowy czwarty parametr: test ; touch mamy_cie.txt i zobaczmy, co się stanie.
Po przesłaniu pakietu skryptem w pythonie funkcja ta spróbuje uruchomić skrypt eth.sh z parametrami…, a po średniku nasze polecenie. :)5. Analizując dalej kod, rzeczywiście dochodzimy do funkcji „system”, która faktycznie jest wykonywana w kolejnych etapach algorytmu, a jej parametrem mieszczącym się w rejestrze $a0 jest eth.sh sn -w test ; touch mamy_cie.txt!
Super! Wygląda na to, że się wkradliśmy! :)
Wchodzimy
Odkrycia tego typu mobilizują poszukiwacza do dogłębnej analizy nie tylko pewnych standardowych działających usług, jak httpd, ftpd itp., o czym wspomniałem na wstępie, ale także do poszukiwania innych celów ataku, którymi mogą stać się niewinnie działające usługi, na pierwszy rzut oka niezauważalne.
Zaglądanie pod kamień kryjący wszelkiego rodzaju konfiguracje (a to jest bardzo istotny wektor badań!) nie zawsze, ale czasami prowadzi właśnie do opisanych wyżej wyników.
Od producenta
Nie mogę tego jednak potwierdzić, gdyż nie posiadam fizycznego dostępu do badanego urządzenia.
1. Co się stanie, jeśli wyślemy do mpd string default?
2. W katalogu /etc_ro/ znajduje się intrygujący plik mini_httpd.pem. Do czego on może służyć?
Myślę, że w analizowanym firmware (także w deamonie mpd) istnieje jeszcze parę innych, nieodkrytych luk.
Zachęcam do poszukiwań, własnych badań oraz do spojrzenia na wcześniejszy research, który był po raz pierwszy publikowany na sekuraku:
—Mateusz Wójcik
Elegancko, wreszcie ktoś ładnie opisał warsztat!
Naprawde dobrze napisane!
dzieki i najlepszego na nowy rok.
Super. Co prawda rozumiałem co drugie zdanie (z mojej własnej winy ;) ), nie mniej jednak doceniam efekt. Dobra robota!
Brawo, i gratuluję to się nazywa fach!!!!. Pozdrawiam
Świetne wprowadzanie w temat. Dzięki.
Super że się podoba :) To jeszcze podeślijcie tekst znajomym ;)
Chyba wizualizacja 3D, nie wirtualizacja :P
@all
Dzięki za odbiór, chciałbym w przyszłości napisać coś jeszcze o routerach :)
@GDR!
Thx za uważne czytanie tekstu ;] Oczywiście, chodziło o „wizualizację 3D”. Fajny ficzer :P
Świetny artykuł, gratulacje dla autora!
Sam ostatnio dużo grzebię w fw DIR-600, a ten artykuł daje dużo ciekawych informacji do sprawdzenia na moim routerze :)
Aż chce się odpalić jakiś debugger i pobawić się.. :)
@sekurak
Jak tam postęp w robieniu Q&A ?
Wszystko fajnie wytlumaczone i opisane zrozumialem metodologie ale nie wiem czy zostalo to pominiete czy przeoczylem ale dlaczego breakpointy akurat w tych miejscach a nie innych?
Co do debugowania z gdbserverem – komentarz ;-) jeżeli podczas działania programu (daemon’a) w „zwykłym” gdb naciśniesz crtl+c – uzyskasz dostęp do debugera i będziesz mógł sobie ustawić breakpointy gdzie Ci się podoba. Można też skorzystać z parametru –pid i dołączyć do gdb już działający proces (zostanie na chwile „wstrzymany”, odpalasz normalnie komendą 'continue’). To samo tyczy się zresztą strace. :) Chyba, że chciałeś osiągnąć coś zupełnie innego, a ja nie zrozumiałem – to przepraszam.
Poza tym – zajebista robota, fajny bug, keep researchin’ człowieku :)
@holo
Thx za komentarz i uważne czytanie :) Trafne spostrzeżenie. Chciałem jedynie zwrócić uwagę na istotne punkty w badanej binarce. Stwierdziłem (może błędnie), że gdybym pisał jak dochodziłem do tych punktów, na których zatrzymałem „gdb”, to art by się niemiłosiernie wydłużył :) ALE racja. Przydałby się jakiś tekst mówiący głębiej jak się zabierać do reverse engineeringu podobnych binarek.
@ZoczuS
Ekstra! Muszę to sprawdzić ale wydaje mi się, że chciałem osiągnąć to samo co Ty czyli uzyskać dostęp do debugera po odpaleniu daemona, który zaczyna nasłuchiwać, tylko że jak to czasem bywa, robiłem to „na około” ;) Bardzo mi miło, dziękuję :)
Dobrą alternatywą dla klienta gdb jest debuger IDA. Kod jest tam ładnie „graficznie” przedstawiony. Jedynym minusem jest to, że IDA w wersji bezpłatnej (z tego co wiem) nie obsługuje między innymi architektury MIPS.
@Mateusz
Dzieki za odpowiedz. Probowalem kilka razy podchodzic do tematu inzynierii wstecznej ale jednak mnie to przeroslo a jest to mysle naprawde bardzo ciekawy temat ale do najlatwiejszych niestety nie nalezy. Oprocz breakpointow tak samo nie jest dla mnie jasne dlaczego sprawdziles akurat ten rejestr a nie inny, skad wiedziales, ze zawartosc tego rejestru to akurat wskaznik do stringa.. – pewnie wynikalo to z kodu ale jak do tego dojsc? Mam nadzieje, ze taki artykul / cykl artykulow sie pojawi i bede czekal na niego z niecerpliwoscia.
Ekstra wpis!
Mateuszu – inny plus skorzystania z takiego rozwiązania – PEDA ;-) z gdbserver’em mi niestety nie zadziałała, a niesamowicie ułatwia prace. Spróbuj sam!
http://ropshell.com/peda/
Polecam. :)
Fajny artykuł, ale zrobiłeś błąd w „pod kontem działania”
Poprawione :)