O jednej luce w...

O jednej luce w...

Rok temu, 21 marca 2019 r., w program nagród za błędy Mail.Ru bardzo dobry trafił do HackerOne Zgłoszenie błędu od maxarr. Po wprowadzeniu bajtu zerowego (ASCII 0) do parametru POST jednego z żądań API poczty internetowej, które zwróciło przekierowanie HTTP, w danych przekierowania widoczne były fragmenty niezainicjowanej pamięci, w których znajdowały się fragmenty parametrów GET i nagłówki innych żądań do ten sam serwer.

Jest to luka krytyczna, ponieważ... żądania zawierają również sesyjne pliki cookie. Kilka godzin później wprowadzono tymczasową poprawkę filtrującą bajt zerowy (jak się później okazało, to nie wystarczyło, ponieważ nadal istniała możliwość wstrzyknięcia CRLF/ASCII 13, 10, co pozwala manipulować nagłówkami i dane odpowiedzi HTTP, jest to mniej krytyczne, ale nadal nieprzyjemne). Jednocześnie problem został przekazany analitykom bezpieczeństwa i programistom w celu znalezienia i wyeliminowania przyczyn błędu.

Poczta Mail.ru jest bardzo złożoną aplikacją; do generowania odpowiedzi może być zaangażowana duża liczba różnych komponentów front-end/backend, zarówno typu open source (wielkie podziękowania dla wszystkich twórców bezpłatnego oprogramowania), jak i opracowanych wewnętrznie. Udało nam się wykluczyć wszystkie komponenty z wyjątkiem nginx i openresty oraz zlokalizować problem przed telefonem ngx.req.set_uri() w skrypcie OpenResty, który nie zachował się zgodnie z oczekiwaniami (wstawienie bajtu zerowego lub nowego wiersza poprzez parametry GET z przepisaniem w ngx_http_rewrite_module, który zgodnie z dokumentacją jest używany i wydaje się, że powinien działać dokładnie w ten sam sposób, będzie nie działa). Wyeliminowano możliwe konsekwencje, dodano możliwie najściślejsze filtrowanie i zweryfikowano filtrowanie w celu wyeliminowania wszystkich możliwych wektorów. Jednak mechanizm, który doprowadził do wycieku zawartości pamięci, pozostał tajemnicą. Miesiąc później zgłoszenie błędu zamknięto jako rozwiązane, a analizę przyczyn błędu odłożono na lepsze czasy.

OpenResty to bardzo popularna wtyczka, która pozwala pisać skrypty Lua w Nginx i jest używana w kilku projektach Mail.ru, więc problem nie został uznany za rozwiązany. I po pewnym czasie w końcu do tego wrócili, aby zrozumieć prawdziwe przyczyny, możliwe konsekwencje i przedstawić zalecenia dla programistów. Brał udział w wykopywaniu kodu źródłowego Denis Denisov и Nikołaj Ermishkin. Okazało się że:

  • W nginx, gdy używasz przepisywania danych użytkownika, w niektórych konfiguracjach istnieje możliwość przeglądania katalogów (i prawdopodobnie SSRF), ale jest to znany fakt i powinien zostać wykryty przez statyczne analizatory konfiguracji w Wzmocnienie Nginx и gixy od Yandex (tak, też tego używamy, dzięki). Podczas korzystania z OpenResty tę funkcję łatwo przeoczyć, ale nie miało to wpływu na naszą konfigurację.

    przykład konfiguracji:

    location ~ /rewrite {
        rewrite ^.*$ $arg_x;
    }
    
    location / {
        root html;
        index index.html index.htm;
    }

    wynik

    curl localhost:8337/rewrite?x=/../../../../../../../etc/passwd
    root:x:0:0:root:/root:/bin/bash
    daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
    bin:x:2:2:bin:/bin:/usr/sbin/nologin
    ...

  • Nginx ma błąd, który powoduje wyciek pamięci, jeśli linia przepisywania zawiera bajt zerowy. Po wydaniu przekierowania nginx przydziela nowy bufor pamięci odpowiadający pełnej długości linii, ale kopiuje tam linię poprzez funkcję liniową, w której bajt zerowy jest terminatorem linii, więc linia jest kopiowana tylko do zera bajt; reszta bufora zawiera niezainicjowane dane. Można znaleźć szczegółową analizę tutaj.

    przykład konfiguracji (^@ bajt zerowy)

    
    location ~ /memleak {
        rewrite ^.*$ "^@asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdasdf";
    }
    
    location / {
        root html;
        index index.html index.htm;
    }

    wynik
    curl localhost:8337/secret -vv
    ...
    curl localhost:8337/memleak -vv
    ...
    Location: http://localhost:8337/secret
    ...

  • Nginx chroni parametry GET przed wstrzyknięciem znaków serwisowych i umożliwia używanie podczas przepisywania wyłącznie parametrów GET. Dlatego nie jest możliwe wykorzystanie wtrysku poprzez parametry kontrolowane przez użytkownika w Nginx. Parametry POST nie są chronione. OpenResty umożliwia pracę zarówno z parametrami GET, jak i POST, więc podczas korzystania z parametrów POST poprzez OpenResty możliwe staje się wstrzykiwanie znaków specjalnych.

    przykład konfiguracji:

    location ~ /memleak {
        rewrite_by_lua_block {
            ngx.req.read_body();
            local args, err = ngx.req.get_post_args();
            ngx.req.set_uri( args["url"], true );
        }
    }
    
    location / {
        root html;
        index index.html index.htm;
    }
    

    wynik:

    curl localhost:8337 -d "url=secret" -vv
    ...
    curl localhost:8337 -d "url=%00asdfasdfasdfasdfasdfasdfasdfasdf" -vv
    ...
    Location: http://localhost:8337/{...может содержать secret...}
    ...

Dalsza reakcja

Problem został zgłoszony twórcom Nginx i OpenResty, programiści nie uważają tego problemu za błąd bezpieczeństwa w Nginx, ponieważ w samym nginxie nie ma możliwości wykorzystania błędu poprzez wstrzyknięcie znaków specjalnych, napraw ujawnienie pamięci ukazał się 16 grudnia. W ciągu 4 miesięcy od raportu nie wprowadzono żadnych zmian w OpenResty, chociaż uznano, że potrzebna jest bezpieczna wersja funkcji ngx.req.set_uri(). 18 marca 2020 opublikowaliśmy informację, 21 marca udostępniliśmy OpenResty wersja 1.15.8.3, który dodaje sprawdzanie poprawności URI.

Portswigger napisał dobry artykuł i wziąłem uwagi z OpenResty i Nginx (chociaż komentarz, że odsłonięty jest tylko mały fragment pamięci, jest niepoprawny i wprowadzający w błąd, jest to określane na podstawie długości linii następującej po bajcie zerowym i, w przypadku braku wyraźnych ograniczeń dotyczących długość, może być kontrolowana przez atakującego).

Jaki więc był błąd i co można zrobić, aby temu zapobiec?

Czy wystąpił błąd w Nginx? Tak, ponieważ wyciek zawartości pamięci jest i tak błędem.

Czy w OpenResty wystąpił błąd? Tak, przynajmniej kwestia bezpieczeństwa funkcjonalności oferowanej przez OpenResty nie została zbadana i udokumentowana.

Czy wystąpił błąd konfiguracji/użytkowania w OpenResty? Tak, ponieważ w przypadku braku jednoznacznego stwierdzenia przyjęto niezweryfikowane założenie co do bezpieczeństwa wykorzystywanej funkcjonalności.

Który z tych błędów stanowi lukę w zabezpieczeniach, za którą nagroda wynosi 10000 XNUMX dolarów? Dla nas to na ogół nie jest istotne. W każdym oprogramowaniu, zwłaszcza na skrzyżowaniu kilku komponentów, zwłaszcza tych dostarczonych przez różne projekty i programistów, nikt nigdy nie może zagwarantować, że wszystkie funkcje jego pracy są znane i udokumentowane oraz że nie ma błędów. Dlatego każda luka w zabezpieczeniach pojawia się dokładnie tam, gdzie wpływa na bezpieczeństwo.

W każdym razie dobrą praktyką jest normalizowanie lub ograniczanie/filtrowanie w jak największym stopniu danych wejściowych, które trafiają do dowolnego modułu zewnętrznego/API, chyba że istnieją wyraźne instrukcje i jasne zrozumienie, że nie jest to wymagane.

Errata

Z doświadczenia poprzedni artykuł, w trosce o zachowanie czystości języka:

nagroda za błąd — konkurs polowania na robaki
Zgłoszenie błędu - powiadomienie o błędzie
przeadresować - przekierowanie
otwarte źródło - otwarte źródło
errata - pracować nad błędami

Źródło: www.habr.com

Dodaj komentarz