Aproximativ o vulnerabilitate în...

Aproximativ o vulnerabilitate în...

Acum un an, 21 martie 2019, în programul de recompense pentru bug Mail.Ru una foarte bună a venit la HackerOne raport de eroare din maxarr. La introducerea unui octet zero (ASCII 0) în parametrul POST al uneia dintre solicitările API de webmail care a returnat o redirecționare HTTP, bucăți de memorie neinițializată erau vizibile în datele de redirecționare, în care fragmente din parametrii GET și anteturile altor solicitări către acelasi server.

Aceasta este o vulnerabilitate critică deoarece... cererile conțin și cookie-uri de sesiune. Câteva ore mai târziu, a fost făcută o remediere temporară care a filtrat octetul zero (după cum s-a dovedit mai târziu, acest lucru nu a fost suficient, deoarece mai exista posibilitatea de a injecta CRLF / ASCII 13, 10, care vă permite să manipulați anteturile și datele răspunsului HTTP, acest lucru este mai puțin critic, dar totuși neplăcut). În același timp, problema a fost transferată analiștilor și dezvoltatorilor de securitate pentru a găsi și elimina cauzele erorii.

Mail.ru mail este o aplicație foarte complexă; în generarea răspunsului pot fi implicate un număr mare de componente front-end/back-end diferite, atât open source (multe mulțumiri tuturor dezvoltatorilor de software gratuit), cât și dezvoltate in-house. Am reușit să excludem toate componentele cu excepția nginx și openresty și să localizăm problema înainte de a apela ngx.req.set_uri() într-un script OpenResty care nu s-a comportat conform așteptărilor (inserarea unui octet nul sau a unui avans de linie prin intermediul parametrilor GET cu rescriere în ngx_http_rewrite_module, care, conform documentației, este utilizat și, se pare, ar trebui să funcționeze exact în același mod, va nu functioneaza). Posibilele consecințe au fost eliminate, filtrarea a fost adăugată cât mai strict posibil și filtrarea a fost verificată pentru a elimina toți vectorii posibili. Dar mecanismul care a dus la scurgerea conținutului memoriei a rămas un mister. O lună mai târziu, raportul de eroare a fost închis ca fiind rezolvat, iar analiza cauzelor erorii a fost amânată până la vremuri mai bune.

OpenResty este un plugin foarte popular care vă permite să scrieți scripturi Lua în interiorul nginx și este folosit în mai multe proiecte Mail.ru, așa că problema nu a fost considerată rezolvată. Și după ceva timp, au revenit în cele din urmă la el pentru a înțelege adevăratele motive, posibilele consecințe și pentru a face recomandări pentru dezvoltatori. A participat la excavarea codului sursă Denis Denisov и Nikolay Ermishkin. S-a dovedit ca:

  • În nginx, atunci când se utilizează rescrie cu datele utilizatorului, există posibilitatea traversării directoarelor (și probabil SSRF) în unele configurații, dar acesta este un fapt cunoscut și ar trebui detectat de analizoarele de configurație statică în Nginx Amplify и gixy de la Yandex (da, folosim și asta, mulțumesc). Când utilizați OpenResty, această caracteristică este ușor de ratat, dar acest lucru nu a afectat configurația noastră.

    exemplu de configurare:

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

    rezultat

    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 are o eroare care determină scurgerea memoriei dacă linia de rescriere conține un octet nul. Când este emisă o redirecționare, nginx alocă un nou buffer de memorie corespunzător lungimii întregi a liniei, dar copiază linia acolo printr-o funcție de linie în care octetul zero este un terminator de linie, astfel încât linia este copiată numai până la zero. octet; restul bufferului conține date neinițializate. O analiză detaliată poate fi găsită aici.

    exemplu de configurare (^@ zero octet)

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

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

  • Nginx protejează parametrii GET de injectarea de caractere de serviciu și face posibilă utilizarea numai a parametrilor GET în rescriere. Prin urmare, nu este posibilă exploatarea injecției prin parametri controlați de utilizator în nginx. Parametrii POST nu sunt protejați. OpenResty vă permite să lucrați atât cu parametrii GET, cât și cu POST, astfel încât atunci când utilizați parametrii POST prin OpenResty, devine posibil să injectați caractere speciale.

    exemplu de configurare:

    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;
    }
    

    rezultat:

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

Reacție în continuare

Problema a fost raportată dezvoltatorilor nginx și OpenResty, dezvoltatorii nu consideră problema ca fiind o eroare de securitate în nginx, deoarece în nginx în sine nu există nicio modalitate de a exploata eroarea prin injectarea de caractere speciale, remediați dezvăluirea memoriei a fost publicat pe 16 decembrie. În cele 4 luni de la raport, nu s-au făcut modificări la OpenResty, deși s-a înțeles că era necesară o versiune sigură a funcției ngx.req.set_uri(). Pe 18 martie 2020 am publicat informații, pe 21 martie a fost lansat OpenResty versiunea 1.15.8.3, care adaugă validarea URI.

Portswigger a scris bun articol și a primit comentarii de la OpenResty și Nginx (deși comentariul că doar un mic fragment de memorie este expus este incorect și înșelător, acesta este determinat de lungimea liniei care urmează octetul nul și, în absența restricțiilor explicite privind lungime, poate fi controlat de atacator).

Deci, care a fost greșeala și ce se poate face pentru a o preveni?

A existat o eroare în nginx? Da, a fost, deoarece scurgerea conținutului memoriei este o eroare în orice caz.

A existat o eroare în OpenResty? Da, cel puțin problema securității funcționalității oferite de OpenResty nu a fost investigată și documentată.

A existat o eroare de configurare/utilizare cu OpenResty? Da, deoarece în absența unei declarații explicite, s-a făcut o presupunere neverificată cu privire la securitatea funcționalității utilizate.

Care dintre aceste erori este o vulnerabilitate de securitate cu o recompensă de 10000 USD? Pentru noi, în general, acest lucru nu este important. În orice software, mai ales la intersecția mai multor componente, în special cele furnizate de diferite proiecte și dezvoltatori, nimeni nu poate garanta vreodată că toate caracteristicile muncii lor sunt cunoscute și documentate și că nu există erori. Prin urmare, orice vulnerabilitate de securitate apare exact acolo unde afectează securitatea.

În orice caz, este o bună practică să normalizați sau să limitați/filtrați cât mai mult posibil datele de intrare care intră în orice modul/API extern, cu excepția cazului în care există instrucțiuni explicite și o înțelegere clară a faptului că acest lucru nu este necesar.

Erată

Prin experiență articolul anterior, de dragul păstrării purității limbii:

recompensă pentru bug-uri — concurs de vânătoare de insecte
raport de eroare - notificare de eroare
redirecţiona - redirecționare
sursa deschisa - sursa deschisa
erată - lucrați la greșeli

Sursa: www.habr.com

Adauga un comentariu