Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book
Groźne zdalne wykonywanie kodu w MyBB – omówienie łańcucha podatności (+ bonus).
Niedawno znaleziono groźny łańcuch dwóch podatności (CVE-2021-27889 oraz CVE-2021-27890) w popularnym, otwartoźródłowym silniku forów dyskusyjnych MyBB. Podatności załatano w najnowszej obecnie wersji, 1.8.26.
Połączenie powyższych podatności w jeden prosty łańcuch stanowi efektywny sposób osiągnięcia pełnego RCE.
Nazywam się Patryk, jestem badaczem bezpieczeństwa webaplikacji (w Internecie znanym pod nickiem SivertPL), a jednocześnie autorem pierwszych publicznych exploitów wykorzystujących wyżej wymienione luki, a tutaj omówię nie tylko zasadę ich działania, ale także mój plan na ich skuteczną exploitację.
Zacznijmy więc.
CVE-2021-27889 – czyli prawdopodobnie najgroźniejszy XSS w historii forów internetowych, a zarazem pierwsze ogniwo łańcucha
Cross-site Scripting (XSS) vulnerability in MyBB before 1.8.26 via Nested Auto URL when parsing messages. (https://nvd.nist.gov/vuln/detail/CVE-2021-27889).
Pierwszą podatnością, kluczową w kontekście przeprowadzenia skutecznego ataku, jest jeden z najpiękniejszych XSS-ów, jakie widziałem. I wiem, co mówię. Dlaczego?
Ponieważ podatny jest parser MyCode (taka wymyślna nazwa na typowe BBCode). Dla każdego, kto udzielał się kiedykolwiek na “klasycznych” forach internetowych, funkcjonalność ta jest na pewno znana. A co za tym idzie, podatne jest każde miejsce, które zezwala na wprowadzanie znaczników BBCode.
Payload może być więc dostarczony przez prywatną wiadomość do administratora, przez odpowiedź na dany temat, przez pluginy od shoutboxów, przez treść samego tematu i tak dalej.
Podatna jest funkcjonalność autourl, czyli autodetekcji (po wyrażeniu regularnym) czegoś, co wygląda na link, a potem pakowania go w znacznik <a>. W założeniu miało to ułatwić korzystanie z linków, redukując potrzebę specjalnego dodawania tagów, aby można było w taki link po prostu kliknąć.
Fakt ten umożliwia obejście standardowych zabezpieczeń przed XSS-em w tagach MyCode, w tym przypadku tagu [img]. Z bardzo prostego powodu – podczas analizy tagu [img] parser dokonuje następującej korekty:
[img]http://google.com[/img]
na:
<img src=”http://google.com„>
Zaznaczony boldem URL wygląda jakby był odpowiednio sanityzowany, aby zapobiec wydostaniu się z cudzysłowów poprzez nieodpowiednią zawartość tagu [img]. Usuwane są spacje (!) oraz przeprowadzany jest HTML-encoding nieodpowiednich znaków. Nic nowego w kontekście obrony przed XSS-ami. Ot, zwyczajne eliminowanie potencjalnych zagrożeń.
Jednak regex sanityzujący powyższy URL zapomniał o nawiasach, które nie są kodowane przez HTML, a w tym przypadku umożliwią ominięcie wyżej wymienionych zabezpieczeń poprzez sprowokowanie do wywołania odrębnej funkcji MyBB, która zrobi to za nas.
Pierwsza faza ataku polega na dodaniu drugiego, odseparowanego linku, do wnętrza tagu [img], w którego miejsce, w późniejszej fazie, wstawiony zostanie drugi tag HTML – wcześniej wspominany <a href …
[img]http://google.com/x?)http://google.com/onerror=alert(666);//[/img]
Po pierwszym przejściu parsera MyCode, kod wyglądał będzie tak:
<img src=”http://google.com/x?)http://google.com/onerror=alert(666);//”>
Warto w tym miejscu wspomnieć, iż w pierwszej kolejności odbywa się parsing samego MyCode, a dopiero później ma miejsce automatyczna detekcja URL-i i ich zamiana na HTML-owe linki, w które można kliknąć.
Co oznacza, że funkcja autourl doszuka się w powyższym kodzie prawidłowego URL-a.
<img src=”http://google.com/x?)http://google.com/onerror=alert(666);//”>
…i co zrobi? Zamieni go na znacznik <a href…
<img src=”http://google.com/x?)<a href=” http:=”” google.com=”” onerror=”alert(666);//"” target=”_blank” rel=”noopener” class=”mycode_url”>
(treść powyższego już po korekcyjnym parsingu przeglądarki).
… i tym samym wstrzyknie nam cudzysłów w treść atrybutu src, opuszczając to pole i wstrzykując pozostały wprowadzony przez nas kod jako dodatkowe atrybuty HTML znacznika img.
Wykorzystaliśmy atrybut onerror do wprowadzenia żywego JavaScriptu, który naturalnie wykona się od razu, gdyż parametr src nie jest prawidłowym linkiem do pliku graficznego.
Pierwsze i najważniejsze ogniwo łańcucha podatności zostało ustanowione, ale to dopiero początek…
CVE-2021-27890 – SQL Injection w silniku template’ów, prowadzące w unikalny sposób do authenticated RCE.
Podatność sama w sobie, w mojej opinii, nie jest niczym niesamowitym, ponieważ wymaga dostępu do panelu ACP (skrót ten oznacza Admin Control Panel, jest to wydzielona sekcja forum dostępna wyłącznie dla administratorów, pozwalająca na zarządzanie całym silnikiem) i dopiero stamtąd pozwala na wykonanie żywego kodu PHP na serwerze. Mimo to okazuje się być bardzo wartościowym elementem całego łańcucha.
Motywy MyBB są plikami XML zawierającymi odnogi, w których znajduje się kod HTML pozwalający na częściową interpolację zmiennych PHP poprzez funkcję eval (czyli wstawiania dynamicznych danych w formie odwołań do zmiennych PHP w celu ominięcia konieczności używania placeholderów).
Zabronione jest jednak bezpośrednie wykonanie kodu PHP, a dozwolone jest jedynie odwoływanie się do zmiennych, ze względu na bardzo dokładną sanityzację.
Podczas importowania motywów importowany plik XML jest parsowany, a jego zmienne zapisywane są w bazie danych SQL.
Jeden z parametrów, a ściślej parametr templateset, jest podatny na SQL Injection.
Poniższy snippet wziąłem bezpośrednio z mojego exploita, w którym dodatkowo użyłem kodowania base64, żeby przekazać duży payload w sposób bezpieczny pod względem niechcianych znaków, do wstrzykniętego zapytania.
Wstrzykując następujący kod do parametru templateset, naszego sztucznego template’u…
’) AND 1=0 UNION SELECT title, '${passthru(base64_decode(\\'” + btoa(SHELL_PAYLOAD) + „\\’))}’ from mybb_templates — „;
… omijamy wyżej wymienioną sanityzację i, za sprawą zapytania SQL, wstrzykujemy kod PHP bezpośrednio do eval, gdzie zostanie wykonany przez interpreter PHP.
Połączenie ataków, czyli od XSS przez SQLi po RCE
W moim exploicie połączyłem obie podatności w kompletny i spójny łańcuch.
Exploit dostępny jest tutaj: https://www.exploit-db.com/exploits/49696 (exploit-db zepsuło JavaScript przy kopiowaniu z e-maila, i nie chcą go naprawić, mimo próśb o zamianę pliku, ale może to i dobrze?)
W pierwszej fazie ataku konstruuję payload XSS oraz koduję go w taki sposób, aby ominąć konieczność używania w payloadzie cudzysłowów, które mogłyby zepsuć podatność. Najlepszy sposób na to, to stare, dobre String.fromCharCode, które przekaże dosłownie wszystko w sposób bezpieczny, wprost do funkcji eval().
Jednak ładunek zakodowany z użyciem funkcji String.fromCharCode zajmuje dość dużo miejsca, co w pewnym momencie przestaje być wygodne i eleganckie, zatem w samym payloadzie załączam jedynie stager, czyli payload, który pobiera drugi, większy skrypt i egzekwuje go wprost z Internetu, zachowując jednocześnie swoją małą wielkość.
Aby uniknąć nadzwyczaj ścisłego trzymania się Same Origin Policy przez Firefoxa, dodałem skrypt tworzący zwykły tag <script> w drzewie DOM, który załącza drugi skrypt z zewnętrznego źródła, gdyż zewnętrzny import() nie działa na wszystkich przeglądarkach.
Tak przygotowany ładunek ostrożnie umieszczam w samym wektorze ataku, a następnie wysyłam do ofiary.
Druga faza ataku to wykonanie skryptu z samego exploita – czyli tego, który pobierany jest przez stager.
Skrypt ten ukrywa przed ofiarą fakt wykorzystania podatności poprzez zamianę widocznego w sposób oczywisty kodu w treści posta/wiadomości, na coś nie wzbudzającego większych podejrzeń, a następnie z poziomu samego JavaScriptu konstruuje odpowiedni template, pobiera odpowiednie tokeny anti-CSRF oraz konstruuje i wysyła requesty w kierunku ACP (za pomocą staromodnego XMLHttpRequest), które załadują i aktywują template wykorzystujący dalsze podatności.
Całość, po kliknięciu / wyrenderowaniu XSS-a, zajmuje mniej niż kilka sekund, co widać na filmiku, który nagrałem.
(muzyka: Madeon – Pop Culture).
Na filmiku uskuteczniam podatność jako admin, aby zaoszczędzić czasu, ale to samo by się stało, gdyby atakującym był użytkownik, bowiem liczy się tylko to, kto otworzy i wykona kod znajdujący się w XSS-ie.
Bonus – polska odpowiedź i kolejna możliwa droga ataku z poziomu XSS.
Podatność CVE-2021-27890, choć niewątpliwie groźna, wymaga ofiary z uprawnieniami administratora, który na dodatek musi mieć aktywną sesję ACP (w MyBB logowanie do ACP wymaga ponownego podania hasła), co nieco utrudnia skuteczny atak, gdyż nie zawsze administrator podczas wizyty na forum wchodzi w ów panel, a i czas sesji również jest ograniczony.
Z pomocą przychodzi więc kolejna podatność, tym razem CVE-2021-27946, znaleziona przez Polaka, Tomasza Młyńskiego ‘aka devilshakerz’ (jednego z głównych contributorów MyBB), na którą również napisałem pierwszy (i jedyny) publiczny exploit.
Exploit: https://www.exploit-db.com/exploits/49699
Podatność ta wykorzystuje dość prosty second order SQL Injection w parametrze votes[] podczas edycji ankiet w tematach. Aby ją wywołać, należy posiadać uprawnienia do edycji ankiet, które najczęściej posiada co najmniej moderator.
Dokładne omówienie podatności znajduje się w exploicie, ale dla leniwych TL:DR.
TL:DR: SQL Injection drugiego rzędu w edycji ankiety, w połączeniu z CVE-2021-27889, pozwala na wykonanie bardzo podobnego ataku do opisanego na początku, lecz na ofiarę o mniejszych uprawnieniach i bez konieczności posiadania przezeń aktywnej sesji ACP. Na skutek takiego ataku, atakujący może otrzymać dowolną daną z bazy SQL (najczęściej będzie to hash oraz salt hasła administratora), a w niektórych przypadkach przejąć kompletnie serwer poprzez wykorzystanie funkcjonalności stacked queries, w przypadkach, gdy forum używa PostgreSQL bądź MS-SQL jako silnika baz danych.
W exploicie użyta została całkiem monotonna i wręcz katorżnicza metoda na ekstrakcję danych, gdyż wówczas nie byłem świadomy istnienia drugiego sposobu na łatwiejszą i przyjemniejszą ekstrakcję, ale niech już nie będzie tak łatwo. Polecam pokombinować z zapytaniem INSERT INTO … VALUES oraz funkcjonalnością pozwalającą stworzyć kilka rekordów naraz z jednego takiego zapytania, by w konsekwencji wstrzyknąć rezultat zagnieżdżonego zapytania SELECT do parametru tekstowego (VARCHAR), w odróżnieniu od czasochłonnej metody wyciągania znaku po znaku za pomocą polecenia substring oraz liczbowej reprezentacji znaków ASCII (taki pseudo blind), pokazanej w publicznej wersji exploita.
Podziękowanie
Chciałbym jeszcze podziękować tym, bez których moje poszukiwania nie byłby możliwe: przede wszystkim badaczom, Simonowi Scannellowi oraz Carlowi Smithowi, za znalezienie świetnych podatności, Tomaszowi Młyńskiemu, znanemu jako ‘devilshakerz’, za wskazanie ciekawego błędu SQLi, o którym mowa w bonusie, portalowi exploit-db (Offensive Security), ekipie sekuraka za możliwość publikacji tego opracowania, a także mojemu przyjacielowi Kornelowi, bez którego wsparcia porzuciłbym branżę security lata temu…
Dziękuję i pozdrawiam!
Patryk
Fajne, w pewnym stopniu kończy idiotyczne dyskusje o xssach i tym czy są jeszcze istotne czy nie
Brawo! Świetnie się czytało i ukłon za wiedzę.