Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book
Obraz w głowie programisty, czyli CVE-2021-40866
Jedną z metod, które używam do znalezienia błędów bezpieczeństwa jest czytanie dokumentacji. A konkretniej, szukanie w niej miejsc, w których programista implementujący daną rzecz mógłby uznać, że coś na pewno będzie miało miejsce, „bo tak każe dokumentacja”. I o ile tego konkretnego błędu, który sprowokował powstanie tego artykułu, nie znalazłem w ten sposób, to mogę się założyć, że jego geneza wywodzi się właśnie z założenia, że świat dostosuje się do tego „co kazała dokumentacja”.
We wrześniu 2021 roku odkryłem błąd w firmware niektórych urządzeń sieciowych firmy NETGEAR, który, zgodnie z moim poczuciem humoru nazwałem za pomocą generatora nazw zespołów metalowych – Demon’s Cries (CVE-2021-40866). W zasadzie bardziej adekwatne byłoby stwierdzenie, że go „ponownie odkryłem”, ponieważ jak się okazało, błąd był już znany od 2012 roku, choć dotyczył innych modeli urządzeń sieciowych. Co zabawne, podatność była traktowana jako ficzer, który przydawał się adminom do resetowania haseł w razie, gdyby ich zapomnieli.
Demon’s Cries dotyczył serwisu Smart Control Center, oraz obsługiwanego przez niego protokołu NETGEAR Switch Discovery Protocol (NSDP). Serwis ten de facto udostępniał informacje o konfiguracji switcha oraz pozwalał – jeśli się znało hasło administratora – zmienić konfigurację wedle uznania. Przyznaję, że jestem fanem analizowania „chałupniczych” protokołów tego typu, tj. takich stworzonych na szybko, przez inżynierów, bez zbędnych rozważań na temat bezpieczeństwa – zawsze znajdzie się w nich coś ciekawego.
Sam NSDP jest binarnym protokołem używającym datagramów UDP do transportu pakietów i składa się z nagłówka, po którym następuje seria „poleceń” w postaci TLV (type/length/value) – całość Big Endian.
Nagłówek:
Offset Typ Opis
0 byte wersja (1)
1 byte polecenie (1 lub 3)
2 word status
4 word kod błędu
6 word reserved
8 byte[6] adres MAC menedżera
0xe byte[6] adres MAC urządzenia
0x14 dword numer sekwencyjny
0x18 string magic "NSDP"
0x1C dword reserved
TLV:
Offset Typ Opis
0 word typ
2 word długość danych
4 byte[] dane
- Operowanie na bitach i bajtach – ciekawostki, triki, PRAKTYKA (18-19.09.2023)
https://sklep.securitum.pl/operowanie-na-bitach-i-bajtach-ciekawostki-tricki-praktyka - Wstęp do plików binarnych i hexedytorów – ciekawostki, triki, PRAKTYKA (25-26.09.2023)
https://sklep.securitum.pl/wstep-do-plikow-bineranych-i-hexedytorow - Programowanie bezpiecznych parserów – ciekawostki, triki, PRAKTYKA (2-3.10.2023)
https://sklep.securitum.pl/programowanie-bezpiecznych-parserow - Inżynieria wsteczna nieznanych formatów – ciekawostki, triki, PRAKTYKA (9-10.10.2023)
https://sklep.securitum.pl/inzynieria-wsteczna-nieznanych-formatow
Wszystkie 4 szkolenia razem stanowią ścieżkę, która przeprowadzi uczestnika od „nie wiem nic o danych binarnych” do „umiem sam rozgryźć proste pliki binarne i zaimplementować do nich bezpieczny parser„.
Całość to ponad 28 godzin konkretnej i praktycznej wiedzy.
Pole „polecenie” w nagłówku przyjmuje wartość 1 lub 3, w zależności od tego czy chcemy odczytać informacje o konfiguracji (1), czy zmienić ustawienia urządzenia (3).
I teraz dochodzimy do mojej hipotezy z początku artykułu. Tj. coś mi mówi, że gdzieś w dokumentacji dostępnej programiście implementującemu protokół NDSP był jakiś paragraf tego typu:
W przypadku zmiany konfiguracji (polecenie=3) pierwszym TLV w serii jest TLV uwierzytelniający (typ=10). Podczas uwierzytelniania, w przypadku podania błędnego hasła, przetwarzanie reszty pakietu MUSI zostać przerwane/zakończone.
Co za tym idzie, w pakiecie UDP po nagłówku powinien być TLV z hasłem administratora, a za nim powinna być seria kolejnych TLV mówiących o tym, co przekonfigurować:
[nagłówek(polecenie=3)]
[TLV(10→uwierzytelnienie): hasło "alamakota"]
[TLV(3→zmiana nazwy urządzenia): nowa nazwa "switch 17"]
[TLV(7→zmiana maski): nowa maska 255.255.255.0]
[TLV(0xffff): koniec TLV]
Całość ma oczywiście sens – ponieważ pierwszym TLV musi być polecenie uwierzytelnienia, to już na tym etapie możemy – w przypadku nieprawidłowego hasła – odrzucić resztę pakietu UDP. A jeśli hasło było dobre, to możemy dalej przetworzyć resztę pakietu i przekonfigurować urządzenie wedle życzeń administratora.
Problem polega na tym, że niestety konkluzją z powyższych rozważań jest również: skoro TLV uwierzytelniający był pierwszym poleceniem w serii, to przy obsłudze kolejnych TLV wiemy, że administrator jest już uwierzytelniony. Bo w końcu gdyby „administrator” podał złe hasło, tj. uwierzytelnienie by się nie powiodło, to reszta pakietu byłaby odrzucona, a wykonanie nigdy by nie dotarło do obsługi kolejnych TLV w serii.
I na to wszystko przychodzi hacker, który zapomniał przeczytać dokumentację (lub gorzej! nie uszanował tego, co tam było napisane!), i wysyła pakiet w ogóle bez uwierzytelniającego TLV – a więc nigdy w ogóle nie dojdzie do wykonania kodu, który miałby szansę sprawdzić hasło i odrzucić resztę pakietu:
[nagłówek(polecenie=3)]
[TLV(10→uwierzytelnienie): hasło "alamakota"]
[TLV(9→zmiana hasła): nowe hasło "thanksNDSP!"]
[TLV(0xffff): koniec TLV]
Błąd można sprowadzić do prostego „programista zapomniał o autoryzacji”, ale to jest zbyt niskopoziomowy wniosek. Domniemany błąd faktyczny był raczej w dokumentacji, która sprawiała, że czytająca ją osoba miała w głowie jedynie prawidłową maszynę stanów, a nie maszynę stanów, która w praktyce była możliwa:
Wnioski na koniec są dwa:
Po pierwsze, tworząc dokumentację musimy pamiętać zarówno o kwestiach związanych z bezpieczeństwem, jak i o tym jaki obraz rysujemy w głowie czytającego.
Po drugie, podczas badań bezpieczeństwa zawsze warto wejść głęboko w detale implementacyjne protokołów – bardzo często znajdzie się tam coś ciekawego.
~Gynvael Coldwind
Zagadka: które Security Principle nie zostało w tym przypadku spełnione?
hxxps://owasp.org/www-project-developer-guide/draft/04-foundations/03-security-principles
Complete Mediation
Bez przesady. Dokumentacja opisana w artykule może nie opisuje scenariusza,gdy pominięto. Tlv uwierzytelniające,ale mówi , że pierwszym jest pakiet uwierzytelniający. Konkluzja zatem jest, że sprawdzenie uwierzytelniania i dalsze przetwarzanie następuje, gdy ten tlv jest obecny. Czyli zapomnienie programisty
Zgadzam się. Tak napisana dokumentacja obliguje do wywalenia błędu jeśli pierwszy tlv nie zawiera autoryzacji. Błąd implementacji i słabe review.
Erwin, mam wrażenie, że patrzysz na to przez pryzmat pojedynczego dobrego programisty. Artykuł owszem, jest o pojedynczym przypadku, ale wniosek o którym pisałem dotyczy nie pojedynczego przypadku, a statystyki.
Tj. nawet jeśli 95% programistów ogarnie to o czym napisałeś (choć przyznaje, że nie jestem aż tak optymistyczny), to 5% nadal sprawę zawali, i już mamy błędy bezpieczeństwa.
A gdyby tak od razu w dokumentacji explicite napisać „a btw, jeśli ten TLV nie będzie pierwszy, odrzuć pakiet”? Nie kosztuje to dużo więcej czasu (10 sekund?), a dostajemy szereg kolejnych poprawnych implementacji, w których albo programista to dobrze od razu zaimplementował, albo QA wyłapało porównując implementacje z dokumentacją.
To się z deka kupy nie trzyma
Koka kola Cyndrin to atencjusz i narcyz, poza tym nie jest programistą, gdyby był to by się nie zmurzdżał nad wyższością put nad printf bo w ten sposób można było programować 30 lat temu kiedy dysk twardy miał pojemność 40 GB (dziś tyle warzą instalki gier) i w ten sposób można wykorzystać może 20% możliwości języka.
Miałem na ten komentarz nie odpowiadać, ale niestety muszę.
30 lat temu dyski nie miały 40 GB, a bardziej 50-100 MB!!1one 🤷🙃
Dzięki, temat starego sprzętu sieciowego jest ciekawy, bo wynalazków tego typu jest tam całkiem sporo. Parę pytań do autora:
1. Uważasz, że jak daleko warto sięgać wstecz w przypadku urządzeń sieciowych?
2. Reakcja producentów pozostawiała kiedyś wiele do życzenia, widać to nawet po repo z exploitem do którego linkujesz – autor przestał używać sprzętu tego producenta właśnie po reakcji na zgłoszenie błędu bezpieczeństwa. Orientujesz się może jak wygląda teraz?
Podrzucisz linka do tej dokumentacji? W sumie mógłby pojawić się w samym artykule.
Ad 1.
Zależy jaki jest cel. Osobiście staram się patrzyć na to co jest dostępne na półkach sieciowych. Tak jak było w tym przypadku, tj. cały bughunt był związany z tym, że sobie takiego switcha sprawiłem – nowego – do użytku własnego.
Ale sprawa jest bardziej ciekawa, bo błąd o którym pisałem dotyczył całej serii modeli. Część z nich była już stara i poza ebayem nie da się ich kupić, a część nadal kilka lat później jest sprzedawana w sklepach. Wynika to oczywiście z tego, że jest to po prostu jedna rodzina firmware’u, który jest rozwijany wraz z nowymi modelami. Co za tym idzie, przypuszczam, że są nowe switche, dostępne teraz od ręki, które bazują na tym samym firmware (przy odrobinie szczęście na gałęzi z poprawionymi błędami).
Z drugiej strony można też patrzyć na to co jest używane faktycznie – wtedy sporo starszych modeli jest też warte zainteresowania, bo stoją pewnie w firmach kupione 5-10 lat temu i nie będą wymieniane przez najbliższe kilka lat.
Ad 2.
Nadal bywa różnie, ale jest generalnie dużo lepiej niż było 10 czy 15 lat temu :)
90-day policy FTW.
Co do dokumentacji, to – jak zresztą sugerowałem w artykule – rozważałem hipotetyczną dokumentację. Faktycznej dokumentacji niestety nie mam, a przyznaje, że jestem ciekaw, czy moja postawiona hipoteza była w tym wypadku trafna ;)
Ad. 1 Jeśli chodzi o cel, to dobre pytanie. Z jednej strony chodzi o pobawienie się. Z drugiej o znalezienie bugów. Jedno i drugie da się zrobić na piętnastoletnim sprzęcie. Jednak w praktyce na wolności takie zabytki niemal nie występują, a producent nie wspiera sprzętu i bugów nie załata, więc byłaby to zupełna sztuka dla sztuki. A jednak byłoby miło, jakby jakiś pozytywny, realny wpływ z tego wyniknął.
Z kupnem nowego to niestety mnie zmartwiłeś. Dość drogie hobby, nawet gdyby sprzedawać po zabawie. Z drugiej strony sprzęt SOHO ludzie potrafią wymieniać po 2-4 latach, a wtedy używany można sprzedać około ceny zakupu.
Co do dokumentacji, zmylił mnie hacker, który nie uszanował tego, co jest napisane. ;-)