
Pred rokom, 21. marca 2019, v na HackerOne prišiel veľmi dobrý od . Pri zavedení nulového bytu (ASCII 0) do POST parametra jedného z API požiadaviek webovej pošty, ktorý vracal HTTP presmerovanie, sa v dátach presmerovania objavili útržky neinitializovanej pamäte, v ktorých sa najčastejšie objavovali fragmenty z GET parametrov a hlavičiek iných požiadaviek na ten istý server.
Toto je kritická zraniteľnosť, pretože požiadavky obsahujú aj session cookies. Po niekoľkých hodinách bol vyvinutý dočasný fix, ktorý filtroval nulový byt (ako sa neskôr ukázalo, to bolo nedostatočné, pretože zostávala možnosť injekcie CRLF /ASCII 13, 10, čo umožňuje manipulovať s hlavičkami a údajmi HTTP odpovede, to je menej kritické, ale stále nepríjemné). Zároveň bola problém odovzdaný analytikom bezpečnosti a vývojárom na hľadanie a odstránenie príčin vzniku chyby.
Mail.ru je veľmi zložitá aplikácia, pri formovaní odpovede môže byť zapojených veľa rôznych frontend/back-end komponentov, ako open-source (veľká vďaka všetkým vývojárom voľného softvéru), tak aj vlastnej výroby. Podarilo sa vylúčiť všetky komponenty okrem nginx a openresty a lokalizovať problém na volanie v OpenResty skripte, ktorý sa správal inak, ako sa očakávalo (vpichnúť nulový byt alebo nový riadok prostredníctvom GET parametrov s rewrite v ngx_http_rewrite_module, ktorý, podľa dokumentácie sa používa a mal by fungovať úplne analogicky, sa nepodarilo). Boli odstránené možné následky, bola pridaná maximálne prísna filtrácia a bolo skontrolované, že filtrácia odstraňuje všetky možné vektory. No mechanizmus, ktorý viedol k úniku obsahu pamäte, zostal záhadou. Po mesiaci bol bug report uzavretý ako vyriešený, a analýza príčin vzniku chyby bola odložená na lepšie časy.
OpenResty je veľmi populárny plugin, ktorý umožňuje písať Lua skripty v rámci nginx, a používa sa v niekoľkých projektoch Mail.ru, preto problém nebol považovaný za vyriešený. A o niekoľko času sa k nemu konečne vrátili, aby pochopili skutočné príčiny, možné dôsledky a pripravili odporúčania pre vývojárov. Pri preskúmavaní zdrojového kódu sa zúčastnili a . Zistilo sa, že:
- V nginx pri použití prepisovania s používateľskými údajmi je možnosť directory traversal (a pravdepodobne aj SSRF) v niektorých konfiguráciách, avšak to je známy fakt a mal by byť odhalený statickými analyzátormi konfigurácií v a od Yandexu (áno, aj my ho používame, ďakujeme). Pri použití OpenResty je takéto riziko ľahko prehliadnuteľné, no naša konfigurácia sa to netýkala.
príklad konfigurácie:
location ~ /rewrite { rewrite ^.*$ $arg_x; } location / { root html; index index.html index.htm; }výsledok
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
... - V nginx je chyba, ktorá vedie k úniku obsahu pamäte, ak má riadok prepisovania nulový bajt. Pri odoslaní presmerovania nginx alokuje nový pamäťový buffer, zodpovedajúci plnej dĺžke riadku, ale kopíruje tam riadok cez riadkovú funkciu, v ktorej je nulový bajt terminátorom riadku, takže sa riadok skopíruje iba do nulového bajtu, zvyšok bufferu obsahuje neinicializované údaje. Podrobnú analýzu nájdete .
príklad konfigurácie (^@ nulový bajt)
location ~ /memleak { rewrite ^.*$ "^@asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdasdf"; } location / { root html; index index.html index.htm; }výsledok
curl localhost:8337/secret -vv
...
curl localhost:8337/memleak -vv
...
Location: http://localhost:8337/secret
...
- Nginx chráni GET parametre pred injekciou špeciálnych znakov a umožňuje v prepisovaní používať iba GET parametre. Preto nie je možné využiť injekciu cez kontrolované používateľské parametre v nginx. POST parametre pritom nie sú chránené. OpenResty umožňuje pracovať aj s GET a POST parametrami, takže pri použití POST parametrov cez OpenResty sa objavuje možnosť injekcie špeciálnych znakov.
príklad konfigurácie:
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; }výsledok:
curl localhost:8337 -d "url=secret" -vv
...
curl localhost:8337 -d "url=asdfasdfasdfasdfasdfasdfasdfasdf" -vv
...
Location: http://localhost:8337/{...môže obsahovať secret...}
...
Ďalšia reakcia
O probléme boli informovaní vývojári nginx a OpenResty, vývojári problém nepovažujú za bezpečnostnú chybu v nginx, pretože v samotnom nginx nie je možnosť využiť chybu cez injekciu špeciálnych znakov, oprava bola zverejnená 16. decembra. Za 4 mesiace od nahlásenia v OpenResty taktiež neboli vykonané žiadne zmeny, aj keď sa vedelo, že je potrebná bezpečná verzia funkcie ngx.req.set_uri(). 18. marca 2020 sme zverejnili informácie, 21. marca OpenResty vydala , ktorá pridáva kontrolu URI.
Portswigger dobrý článok a vzal komentáre od OpenResty a Nginxu (pravda, poznámka o tom, že sa odhaľuje len malý fragment pamäte, je nesprávna a zavádzajúca, určuje sa dĺžkou reťazca nasledujúceho po nulovom bajte a pri absencii jasných obmedzení na dĺžku môže byť kontrolovaná útočníkom).
Aká bola chyba a čo robiť, aby sa tomu zabránilo?
Bola chyba v nginx? Áno, bola, pretože únik obsahu pamäte je v každom prípade chybou.
Bola chyba v OpenResty? Áno, minimálne otázka bezpečnosti ponúkanej funkčnosti OpenResty nebola preskúmaná a zdokumentovaná.
Bola chybou konfigurácia / použitie OpenResty? Áno, pretože pri absencii jasného vyjadrenia sa predpokladalo, že použitá funkčnosť je bezpečná.
Ktorá z týchto chýb je zraniteľnosťou s odmenou za $10000? Pre nás to vlastne nie je dôležité. V akomkoľvek softvéri, obzvlášť na rozhraní viacerých komponentov, predovšetkým poskytovaných rôznymi projektmi a vývojármi, nikto a nikdy nemôže zaručiť, že všetky vlastnosti ich fungovania sú známe a zdokumentované a že neexistujú chyby. Preto akákoľvek zraniteľnosť v oblasti bezpečnosti vzniká presne tam, kde ovplyvňuje bezpečnosť.
V každom prípade bude dobrou praxou normalizovať alebo maximálne obmedzovať / filtrovať vstupné údaje, ktoré idú do akéhokoľvek externého modulu / API, ak neexistujú jasné pokyny a jednoznačné pochopenie toho, že to nie je potrebné.
Errata
Z skúsenosti , kvôli zachovaniu čistoty jazyka:
bug bounty — súťaž o lov chýb
bug report — upozornenie na chybu
redirect — presmerovanie
open-source — s otvoreným kódom
errata — práca na chybách
Zdroj: habr.com
