Żądny wiedzy? Wbijaj na Mega Sekurak Hacking Party w maju! -30% z kodem: majearly

Błąd weryfikacji sum kontrolnych w OpenWRT – możliwość wykonania własnego kodu po stronie urządzenia! (CVE-2020-7982)

25 marca 2020, 15:00 | W biegu | komentarze 3
Tagi: , ,

OpenWRT to popularne otwartoźródłowe oprogramowanie, używane często w miejsce oryginalnego oprogramowania na routerach różnych producentów. Wczoraj Guido Vranken opublikował opis błędu, który zidentyfikował w procesie instalacji oprogramowania, który może skutkować możliwością wykonania dowolnego kodu po stronie urządzenia (CVE-2020-7982). Praktyczny atak będzie jednak wymagał wcześniejszego przeprowadzenia Man-in-the-middle co nieco zmniejsza krytyczność tego błędu.

Ale po kolei. Z poziomu interfejsu OpenWRT istnieje możliwość instalacji nowych pakietów za pomocą polecenia:

opkg install <nazwa_pakietu>

Zanim będzie można zainstalować jakąś paczkę, wcześniej trzeba pobrać ich listę, co z kolei robi się poleceniem:

opkg update

Proces wygląda zatem bardzo podobnie, jak w przypadku apt znanego z systemów z rodziny Debian/Ubuntu. Podobnie jak każdy szanujący się menadżer pakietów, tak i opkg weryfikuje podpis cyfrowy danych pobranych za pomocą opkg update. Wśród tych danych znajduje się lista wszystkich paczek wraz z ich sumą SHA256, która pozwala potwierdzić z kolei integralność pakietów pobieranych za pomocą opkg install. Przykładowa informacja o pakiecie wygląda następująco:

Package: 464xlat
Version: 12
Depends: libc, kmod-nat46, ip
License: GPL-2.0
Section: net
Architecture: x86_64
Installed-Size: 4123
Filename: 464xlat_12_x86_64.ipk
Size: 4926
SHA256sum: 968d57b7f99e3d64a7ea7912f9b1dc4a961c8f00526e6c395d59ae178b17359f
Description:  464xlat provides support to deploy limited IPv4 access services to mobile
 and wireline IPv6-only edge networks without encapsulation (RFC6877)

Okazuje się, że błąd bezpieczeństwa występował właśnie w metodzie odpowiedzialnej za przeparsowanie powyższej informacji o pakiecie i wydobyciu z niej sumy SHA256.

W uproszczeniu owa metoda działała w następujący sposób:

  • Jeśli linia zaczyna się od SHA256sum: to biorę wszystko, co znajduje się bezpośrednio za stringiem SHA256sum:
  • Pomijam wszystkie spacje w tym co właśnie wyciągnąłem,
  • Zamieniam hex-string na bajty (czyli hex2bin).

Zobaczmy odpowiedni fragment kodu wydobyty bezpośrednio ze źródeł OpenWRT:

char *checksum_hex2bin(const char *src, size_t *len)
{
        size_t slen;
        unsigned char *p;
        const unsigned char *s = (unsigned char *)src;
        static unsigned char buf[32];

        if (!src) {
                *len = 0;
                return NULL;
        }

        while (isspace(*src))
                src++;

        slen = strlen(src);

        if (slen > 64) {
                *len = 0;
                return NULL;
        }

        for (p = buf, *len = 0;
             slen > 0 && isxdigit(s[0]) && isxdigit(s[1]);
             slen--, s += 2, (*len)++)
                *p++ = hex2bin(s[0]) * 16 + hex2bin(s[1]);

        return (char *)buf;
}

W linii 238 tworzony jest wskaźnik s, która początkowo przypisana jest do samego miejsca co src. W liniach 246-247 pomijane są wszystkie spacje, w taki sposób, że src wskazuje na miejsce bezpośrednio za spacjami. Jednakże w pętli for zaczynającej się od 256 linii popełniono błąd – iterowana jest zmienna s, zamiast src. A zmienna s wskazuje na oryginalny string znajdujący się bezpośrednio za SHA256sum:. Za tym ciągiem znaków zawsze znajdowała się spacja, a co za tym idzie isxdigit(s[0]) zawsze zwróci false, więc pętla for nigdy się nie wykona! Zmienna wskazywana przez *len przyjmie zatem wartość 0, okaże się więc, że hash sha256 dla każdego pakietu ma zerową długość.

W innej części kodu znajduje się z kolei następujący fragment:

/* Check for sha256 value */
pkg_sha256 = pkg_get_sha256(pkg);
if (pkg_sha256) {
 /* verify sha256... */
 }

Tutaj nigdy nie wejdziemy w if z linii 1417, ponieważ hash sha256 zawsze jest zerowy.

Reasumując, wskutek opisanego powyżej błędu, opkg w ogóle nie sprawdzał poprawności sum SHA256. Jeśli więc napastnik dysponował możliwością przeprowadzenia ataku man-in-the-middle w danej sieci, mógł podmienić paczkę instalowaną przez opkg i wykonać własny kod po stronie serwera. Jedyny warunek był taki, że podmieniona paczka musiała mieć taki sam rozmiar jak oryginalna. Podatność została wprowadzona do kodu w commicie z lutego 2017 roku, błąd występował więc przez ok. trzy lata.

Jeśli macie OpenWRT w wersjach: 18.06.0 do 18.06.6 lub 19.07.0 lub LEDE 17.01.0 do 17.01.7, czym prędzej zróbcie aktualizację.

— Michał Bentkowski (@SecurityMB)

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



Komentarze

  1. Kamil

    Ja nie zrozumiem nigdy ludzi piszących w C/C++. Nazywanie zmiennych „p”, „s”, „c”, „a”, „i”, itd. to proszenie się o problemy bo kto po kilkudziesięciu liniach kodu (Clean Code w większości kodu C/C++ który czytałem chyba nie istnieje, za to if ifa goni w 300 liniowych methodach) wie co czego była jaka zmienna. Kod ma być czytelny dla ludzi. Gdyby ktoś normalnie nazwał zmienne to pewnie było by dziwne, że iteruje po czymś po czym nie powinien. A tak „s” mogło dla kogoś oznaczać „slen”, „src” bo zaczyna się na „s”…
    Czy ktoś ich kara za pisanie czytelnego kodu czy to próba ominięcia jednej z dwóch najcięższych rzeczy w pisaniu softu (nazywania rzeczy)?

    Odpowiedz
    • Sebo

      Tutaj wina człowieka jest taka jak zawsze. Czyli może się trafić. Prawdziwy problem jak się okazuje leży w testach które nie pokrywały przypadku złej sumy. W zasadzie zaryzykuje że nie pokrywały bo ich wcale nie ma. OpenWRT to typowy projekt robiony przez bedroom-hackerów uwalonych pizza i colą. Oni nie testują. Im od razu wychodzi dobrze. Testowanie jest przereklamowane, jak widać.

      Odpowiedz
      • Monter

        > „OpenWRT to typowy projekt robiony przez bedroom-hackerów uwalonych pizza i colą.”

        Testerami są zwykle użytkownicy z wcześniejszym dostępem. Gdyby nie ci „hakerzy” to do dziś interfejs wielu urządzeń z braku tego typu inicjatyw wyglądałby i działałby jak w D-LINK DI-604.

        Odpowiedz

Odpowiedz na Monter