
Un anno fa, il 21 marzo 2019, in è arrivato un ottimo di . Durante l'implementazione di un byte nullo (ASCII 0) in un parametro POST di una delle richieste API della webmail, che restituiva un reindirizzamento HTTP, nei dati di reindirizzamento erano visibili frammenti di memoria non inizializzata, in cui spesso comparivano frammenti dai parametri GET e dagli header di altre richieste allo stesso server.
Questa è una vulnerabilità critica, poiché le richieste contengono anche cookie di sessione. Dopo poche ore è stata fatta una correzione temporanea che filtrava il byte nullo (come si è poi scoperto, non era sufficiente, poiché restava la possibilità di iniezione CRLF / ASCII 13, 10, il che permette di manipolare gli header e i dati della risposta HTTP; è meno critico, ma comunque fastidioso). Contemporaneamente, il problema è stato passato agli analisti della sicurezza e agli sviluppatori per indagare e risolvere le cause dell'emergere del bug.
La posta Mail.ru è un'applicazione piuttosto complessa, nella formazione della risposta possono essere coinvolti un gran numero di diversi componenti frontend/backend, sia open source (un grande grazie a tutti gli sviluppatori di software libero), sia di sviluppo proprietario. È stato possibile escludere tutti i componenti tranne nginx e openresty e localizzare il problema fino alla chiamata nello script OpenResty, che si comportava in modo diverso da quanto previsto (inserire un byte nullo o una nuova linea attraverso i parametri GET con rewrite nel ngx_http_rewrite_module, che, secondo la documentazione, viene utilizzato e, a quanto pare, dovrebbe funzionare esattamente allo stesso modo, non si riesce a fare). Sono state eliminate possibili conseguenze, è stata aggiunta una filtrazione massimamente rigorosa e sono stati verificati che la filtrazione eliminasse tutti i possibili vettori. Ma il meccanismo che portava alla perdita di contenuto della memoria è rimasto un mistero. Dopo un mese, il bug report è stato chiuso come risolto, mentre l'analisi delle cause dell'emergere del bug è stata rinviata 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. E dopo un po', ci si è comunque tornati per comprendere le vere cause, le possibili conseguenze e redigere raccomandazioni per gli sviluppatori. Nelle indagini sul codice sorgente erano coinvolti e . Si è scoperto che:
- In nginx, utilizzando il rewrite con dati personalizzati, esiste la possibilità di directory traversal (e probabilmente SSRF) in alcune configurazioni, ma questo è un fatto noto e dovrebbe essere rilevato dagli analizzatori statici di configurazione in e di Yandex (sì, lo usiamo anche noi, grazie). Utilizzando OpenResty, è facile trascurare questa possibilità, ma la nostra configurazione non ne era influenzata.
esempio di configurazione:
location ~ /rewrite { rewrite ^.*$ $arg_x; } location / { root html; index index.html index.htm; }il 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 c'è un bug che porta a una perdita di contenuto della memoria, se la stringa rewrite contiene un byte nullo. Quando restituisce un redirect, nginx alloca un nuovo buffer di memoria corrispondente alla lunghezza totale della stringa, ma copia la stringa tramite una funzione di stringa, dove il byte nullo funge da terminatore di stringa. Pertanto, la stringa viene copiata solo fino al byte nullo, il resto del buffer contiene dati non inizializzati. Un'analisi dettagliata può essere trovata .
esempio di configurazione (^@ byte nullo)
location ~ /memleak { rewrite ^.*$ "^@asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdasdf"; } location / { root html; index index.html index.htm; }il risultato
curl localhost:8337/secret -vv
...
curl localhost:8337/memleak -vv
...
Location: http://localhost:8337/secret
...
- Nginx protegge i parametri GET dall'iniezione di caratteri speciali e consente di utilizzare nel rewrite solo i parametri GET. Pertanto, non è possibile sfruttare l'iniezione tramite parametri controllati dall'utente in nginx. I parametri POST, d'altra parte, non sono protetti. OpenResty consente di lavorare sia con i parametri GET che con quelli POST, quindi utilizzando i parametri POST tramite OpenResty si presenta la possibilità di iniezione di caratteri speciali.
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=asdfasdfasdfasdfasdfasdfasdfasdf" -vv
...
Posizione: http://localhost:8337/{...può contenere secret...}
...
Reazione successiva
Il problema è stato segnalato agli sviluppatori di nginx e OpenResty; gli sviluppatori non considerano il problema come un errore di sicurezza in nginx, poiché in nginx non è possibile sfruttare l'errore tramite l'iniezione di caratteri speciali. Il fix è stato pubblicato il 16 dicembre. Dopo 4 mesi dalla segnalazione, in OpenResty non sono state apportate modifiche, sebbene ci fosse la consapevolezza della necessità di una versione sicura della funzione ngx.req.set_uri(). Il 18 marzo 2020 abbiamo pubblicato informazioni, il 21 marzo OpenResty ha rilasciato , che aggiunge il controllo dell'URI.
Portswigger un buon articolo e ha raccolto commenti da OpenResty e Nginx (in effetti, il commento secondo cui viene rivelato solo un piccolo frammento della memoria è errato e fuorviante, poiché dipende dalla lunghezza della stringa che segue il byte nullo e, in assenza di espliciti limiti sulla lunghezza, può essere controllato dall'attaccante).
Qual è stato l'errore e cosa fare per prevenirlo?
C'è stato un errore in nginx? Sì, c'è stato, perché la perdita di contenuto della memoria è comunque un errore.
Ci sono stati errori in OpenResty? Sì, almeno la questione della sicurezza delle funzionalità fornite da OpenResty non è stata esplorata e documentata.
C'è stato un errore di configurazione / utilizzo di OpenResty? Sì, perché in mancanza di esplicite indicazioni, è stata fatta un'assunzione non verificata sulla sicurezza delle funzionalità in uso.
Quale di questi errori è una vulnerabilità di sicurezza con un bounty di $10000? Per noi, in realtà, non è importante. In qualsiasi software, specialmente all'intersezione di più componenti forniti da progetti e sviluppatori diversi, nessuno può mai garantire che tutte le peculiarità del loro funzionamento siano conosciute e documentate e che non ci siano errori. Pertanto, qualsiasi vulnerabilità di sicurezza si presenta proprio dove influisce sulla sicurezza.
In ogni caso, sarà una buona prassi normalizzare o limitare/filtrare al massimo i dati in ingresso che vengono inviati a qualsiasi modulo/API esterno, se non ci sono indicazioni chiare e una comprensione univoca che ciò non sia necessario.
Errata
Dall'esperienza , per mantenere la purezza della lingua:
bug bounty — concorso per la caccia agli errori
bug report — segnalazione di un errore
redirect — reindirizzamento
open source — con codice aperto
errata — lavoro sugli errori
Fonte: habr.com
