
Un anno fa, il 21 marzo 2019, in su HackerOne è arrivato un segnalazione molto interessante da . Durante l'inserimento di un byte nullo (ASCII 0) nel parametro POST di una delle richieste API della webmail, che restituiva un reindirizzamento HTTP, nei dati del reindirizzamento sono emersi frammenti di memoria non inizializzata, in cui spesso si svelavano porzioni di parametri GET e intestazioni di altre richieste allo stesso server.
Si tratta di una vulnerabilità critica, poiché le richieste contengono anche i cookie di sessione. Dopo poche ore è stata implementata una soluzione temporanea, che filtrava il byte nullo (come si è scoperto poi, ciò non era sufficiente, in quanto rimaneva la possibilità di iniezione CRLF / ASCII 13, 10, il che consente di manipolare le intestazioni e i dati della risposta HTTP; ciò è meno critico, ma comunque sgradevole). Allo stesso tempo, la problematica è stata passata agli analisti di sicurezza e agli sviluppatori per indagare e risolvere le cause dell'errore.
Mail.ru è un'applicazione complessa, nella formazione della risposta possono intervenire molti componenti frontend/backend, sia open source (un grande grazie a tutti gli sviluppatori del software libero) che di sviluppo proprietario. Siamo riusciti a escludere tutti i componenti tranne nginx e openresty e a localizzare il problema fino alla chiamata nello script OpenResty, che si comportava in modo diverso da quanto previsto (non sarà possibile inserire un byte nullo o un a capo tramite parametri GET con rewrite nel ngx_http_rewrite_module, che secondo la documentazione è utilizzato e dovrebbe funzionare assolutamente allo stesso modo). Sono state eliminate possibili conseguenze, è stata aggiunta una filtrazione rigorosa e è stato verificato che la filtrazione eliminasse tutti i possibili vettori. Ma il meccanismo che portava alla perdita di contenuto di memoria è rimasto un mistero. Dopo un mese il bug report è stato chiuso come risolto, mentre l'analisi delle cause del bug è stata rimandata a tempi migliori.
OpenResty è un plugin molto popolare che consente di scrivere script Lua all'interno di nginx, ed è utilizzato in diversi progetti di Mail.ru, quindi il problema non era considerato risolto. Dopo un po' di tempo, ci si è tornati sopra per capire le vere cause, le possibili conseguenze e per fornire raccomandazioni agli sviluppatori. Alla ricerca nel codice sorgente hanno partecipato e . È emerso che:
- In nginx, quando si utilizza rewrite con dati personalizzati, esiste la possibilità di traversal delle directory (e probabilmente SSRF) in alcune configurazioni, ma questo è un fatto noto, che deve essere rilevato dagli analizzatori statici delle configurazioni in e di Yandex (sì, lo utilizziamo anche noi, grazie). Utilizzando OpenResty, potrebbe facilmente sfuggire questa possibilità, ma la nostra configurazione non ne era toccata.
esempio di configurazione:
location ~ /rewrite { rewrite ^.*$ $arg_x; } location / { root html; index index.html index.htm; }risultato
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
... - In nginx, there is a bug that leads to memory content leakage if the rewrite string contains a null byte. When generating a redirect, nginx allocates a new memory buffer corresponding to the full length of the string but copies the string using a string function where the null byte acts as a terminator. As a result, only the portion up to the null byte is copied, leaving the rest of the buffer with uninitialized data. A detailed analysis can be found .
configuration example (^@ null byte)
location ~ /memleak { rewrite ^.*$ "^@asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdasdf"; } location / { root html; index index.html index.htm; }risultato
curl localhost:8337/secret -vv
...
curl localhost:8337/memleak -vv
...
Location: http://localhost:8337/secret
...
- Nginx protects GET parameters from injection of control characters and only allows the use of GET parameters in rewrite. Therefore, exploiting injection through user-controlled parameters in nginx is not possible. POST parameters are not protected, however. OpenResty allows working with both GET and POST parameters, so using POST parameters through OpenResty opens up the possibility of injecting special characters.
esempio di configurazione:
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; }risultato:
curl localhost:8337 -d "url=secret" -vv
...
curl localhost:8337 -d "url=%00asdfasdfasdfasdfasdfasdfasdfasdf" -vv
...
Location: http://localhost:8337/{...può contenere secret...}
...
Reazione successiva
Il problema è stato segnalato agli sviluppatori di nginx e OpenResty, i quali non considerano la questione un errore di sicurezza in nginx, poiché non esiste una possibilità di sfruttamento dell'errore tramite l'iniezione di caratteri speciali, fix è stato pubblicato il 16 dicembre. Nei 4 mesi successivi alla segnalazione a OpenResty non sono stati apportati cambiamenti, anche se si comprendeva la necessità di una versione sicura della funzione ngx.req.set_uri(). Il 18 marzo 2020 abbiamo pubblicato informazioni e il 21 marzo OpenResty ha rilasciato , che aggiunge un controllo della URI.
Portswigger un buon articolo e ha raccolto commenti da OpenResty e Nginx (il commento che solo un piccolo frammento della memoria viene rivelato è errato e fuorviante, poiché ciò è determinato dalla lunghezza della stringa successiva al byte nullo e, in assenza di vincoli espliciti sulla lunghezza, può essere controllato dall'attaccante).
Qual era l'errore e cosa fare per prevenirlo?
C'era un errore in nginx? Sì, c'era, perché l'esposizione del contenuto della memoria è comunque un errore.
C'erano errori in OpenResty? Sì, almeno non è stata esplorata e documentata la questione della sicurezza delle funzionalità offerte da OpenResty.
È stato commesso un errore di configurazione/utilizzo di OpenResty? Sì, perché in assenza di istruzioni esplicite, è stata fatta un'assunzione non verificata sulla sicurezza delle funzionalità utilizzate.
Quale di questi errori costituisce una vulnerabilità di sicurezza con una ricompensa di $10,000? Per noi è sostanzialmente irrilevante. In qualsiasi software, specialmente nel punto di intersezione di più componenti, soprattutto forniti da diversi progetti e sviluppatori, nessuno può garantire mai che tutte le peculiarità del loro funzionamento siano note e documentate e che non ci siano errori. Pertanto, qualsiasi vulnerabilità di sicurezza emerge proprio lì dove influisce sulla sicurezza.
In ogni caso, è buona pratica normalizzare o limitare/maximizzare il filtraggio dei dati in ingresso che vengono inviati a qualsiasi modulo/API esterna, se non ci sono istruzioni chiare e una comprensione inequivocabile che ciò non sia necessario.
Errata
Secondo l'esperienza , per mantenere la pura lingua:
bug bounty — concorso di caccia agli errori
rapporto di bug — notifica di errore
reindirizzamento — reindirizzamento
open source — open source
errata — lavoro sugli errori
Fonte: habr.com
