
Pirms gada, 21. martā 2019, HackerOne saņēma ļoti labu no . Ieviešot nulles baitu (ASCII 0) POST parametrā vienā no tīmekļa pasta API pieprasījumiem, kas atgrieza HTTP pāradresāciju, pāradresācijas datos bija redzamas neinicializētas atmiņas daļas, kurās visbiežāk tika atklāti fragmenti no GET parametriem un citu pieprasījumu virsrakstiem uz to pašu serveri.
Tā ir kritiska ievainojamība, jo pieprasījumi ietver arī sesiju sīkdatnes. Pēc dažām stundām tika veikts pagaidu labojums, kas filtrēja nulles baitu (kā izrādījās, tas bija nepietiekami, jo palika iespēja injicēt CRLF /ASCII 13, 10, kas ļauj manipulēt ar virsrakstiem un HTTP atbildes datiem, tas ir mazāk kritiski, bet tomēr nepatīkami). Tajā pašā laikā problēma tika nodota drošības analītiķiem un izstrādātājiem, lai atrastu un novērstu kļūdas cēloņus.
Mail.ru pasts ir ļoti sarežģīta lietotne, un atbildes veidošanā var piedalīties liels skaits dažādu frontend/backend komponentu, gan atvērta koda (liels paldies visiem brīvā programmatūras izstrādātājiem), gan pašu izstrādātu. Izdevās izslēgt visus komponentus, izņemot nginx un openresty, un lokalizēt problēmu līdz izsaukumam OpenResty skriptā, kas uzvedās ne tā, kā tika gaidīts (ievietot nulles baitu vai jaunu rindu caur GET parametriem ar pārrakstīšanu ngx_http_rewrite_module, kurš, saskaņā ar dokumentāciju, tiek izmantots un, šķiet, jādarbojas pilnīgi analogi, neizdosies). Tika novērstas iespējamās sekas, pievienota maksimāli stingra filtrēšana un tika pārbaudīts, vai filtrēšana novērš visus iespējamos vektorus. Bet mehānisms, kas izraisīja atmiņas satura noplūdi, palika noslēpums. Pēc mēneša kļūdu ziņojums tika slēgts kā atrisināts, bet analīze par kļūdas cēloņiem tika atlikta uz labākiem laikiem.
OpenResty ir ļoti populārs papildinājums, kas ļauj rakstīt Lua skriptus nginx iekšienē, un tas tiek izmantots vairākos Mail.ru projektos, tāpēc problēma netika uzskatīta par atrisinātu. Un pēc kāda laika pie tās vēlreiz atgriezās, lai saprastu patiesos cēloņus, iespējamas sekas un izstrādātu ieteikumus izstrādātājiem. Koda izpētē piedalījās un . Noskaidrojās, ka:
- Nginx, izmantojot pārrakstīšanu ar lietotāju datiem, var radīt direktoriju pārejas iespējas (un, iespējams, SSRF) noteiktās konfigurācijās, tomēr tas ir zināms fakts, un tam jābūt atklātam statiskajiem konfigurāciju analīzētājiem. un no Yandex (jā, mēs to arī lietojam, paldies). Izmantojot OpenResty, šādu iespēju ir viegli palaist garām, taču mūsu konfigurācija tas neskāra.
konfigurācijas piemērs:
location ~ /rewrite { rewrite ^.*$ $arg_x; } location / { root html; index index.html index.htm; }rezultāts
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 ir kļūda, kas izraisa atmiņas satura noplūdi, ja pārrakstīšanas rinda satur nulles baitus. Atgriežot pārvirzīšanu, nginx piešķir jaunu atmiņas buferi, kas atbilst pilnai rindiņas garumam, taču kopē to rindas funkcijā, kur nulles baits ir rindiņas terminators, tādēļ rinda tiek kopēta tikai līdz nulles baitam, bet bufera atlikums satur nenorādītus datus. Sīkāku analīzi var atrast. .
konfigurācijas piemērs (^@ nulles baits)
location ~ /memleak { rewrite ^.*$ "^@asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdasdf"; } location / { root html; index index.html index.htm; }rezultāts
curl localhost:8337/secret -vv
...
curl localhost:8337/memleak -vv
...
Atrašanās vieta: http://localhost:8337/secret
...
- Nginx aizsargā GET parametrus pret speciālo simbolu injekcijām un ļauj izmantot pārrakstīšanā tikai GET parametrus. Tādējādi izmantojot lietotāja kontrolētus parametrus, injekciju nginx nav iespējams izmantot. POST parametri, savukārt, nav aizsargāti. OpenResty ļauj strādāt gan ar GET, gan ar POST parametriem, tātad, izmantojot POST parametrus caur OpenResty, rodas iespēja injekcijai ar speciālajiem simboliem.
konfigurācijas piemērs:
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; }rezultāts:
curl localhost:8337 -d "url=secret" -vv
...
curl localhost:8337 -d "url=asdfasdfasdfasdfasdfasdfasdfasdf" -vv
...
Atrašanās: http://localhost:8337/{...var varētu saturēt secret...}
...
Turpmākā reakcija
Par problēmu tika informēti nginx un OpenResty izstrādātāji, kuri šo problēmu neuzskata par drošības kļūdu nginx, jo pašā nginx nav iespējas izmantot kļūdu ar speciālo simbolu injekciju, turklāt tika publicēta 16. decembrī. Pagājuši 4 mēneši kopš ziņojuma iesniegšanas OpenResty, tomēr nav veiktas nekādas izmaiņas, lai gan bija sapratne, ka nepieciešams drošs variants funkcijai ngx.req.set_uri(). 2020. gada 18. martā mēs publicējām informāciju, 21. martā OpenResty izlaida , kas pievieno URI validāciju.
Portswigger labs rakstīja labu rakstu un iekļāva komentārus no OpenResty un Nginx (lai gan komentārs par to, ka atklājas tikai neliels atmiņas fragments, ir nepareizs un maldinošs, jo tas ir atkarīgs no virknes garuma, kas seko nulles baitam, un, ja nav skaidriem ierobežojumiem par garumu, to var kontrolēt uzbrucējs).
Kas tad bija kļūda un ko darīt, lai to novērstu?
Vai kļūda bija nginx? Jā, tā bija, jo atmiņas satura izplūde jebkurā gadījumā ir kļūda.
Vai kļūda bija OpenResty? Jā, vismaz jautājums par OpenResty piedāvātā funkcionalitātes drošību netika izpētīts un dokumentēts.
Vai tika pieļauta konfigurācijas / lietošanas kļūda OpenResty? Jā, jo, neesot skaidram norādījumam, tika izdarīts nepārbaudīts pieņēmums par izmantotās funkcionalitātes drošību.
Kura no šīm kļūdām ir drošības ievainojamība ar $10000 balvu? Mums tas būtībā nav svarīgi. Jebkurā programmatūrā, īpaši saskaroties ar vairākiem komponentiem, ko nodrošina dažādi projekti un izstrādātāji, neviens un nekad nevar garantēt, ka visi darba aspekti ir zināmi un dokumentēti un nav kļūdu. Tāpēc jebkura drošības ievainojamība rodas tieši tur, kur tā ietekmē drošību.
Jebkurā gadījumā laba prakse būs normalizēt vai maksimāli ierobežot / filtrēt ievades datus, kas tiek nosūtīti uz jebkuru ārēju moduli / API, ja nav skaidru norādījumu un nepārprotamas izpratnes, ka tas nav nepieciešams.
Errata
Saskaņā ar pieredzi , lai saglabātu valodas tīrību:
bug bounty — kļūdu medību konkurss
kļūdu ziņojumu — kļūdas paziņojums
redirect — novirzīšana
open source — ar atvērtu kodu
errata — darbs pie kļūdām
Avots: habr.com
