
Vulnerabilità di esecuzione di codice in RTSPServer CVE-2018-4013
CVE-2018-4013
Esiste una vulnerabilità sfruttabile di esecuzione di codice nella funzionalità di parsing dei pacchetti HTTP della libreria del server RTSP LIVE555. Un pacchetto appositamente predisposto può causare un buffer overflow basato sullo stack, portando all'esecuzione di codice. Un attaccante può inviare un pacchetto per attivare questa vulnerabilità.
Le librerie multimediali LIVE555 sono un insieme leggero di librerie di streaming multimediale per RTSP/RTCP/RTSP/SIP, con supporto del codice sia per server che per client. Sono utilizzate da popolari lettori multimediali come VLC e MPlayer, oltre che da una moltitudine di dispositivi embedded (principalmente telecamere). Questa vulnerabilità si trova nel componente server che interagisce con questi lettori multimediali ma non ha impatto sui lettori stessi.
Una delle funzionalità abilitate da LIVE555 per il suo server RTSP standard è la capacità di incapsulare RTSP su HTTP, che viene servito su una porta diversa bindata dal server, tipicamente TCP 80, 8000 o 8080, a seconda delle porte disponibili sulla macchina host. Questa porta può supportare RTSP normale, ma in certi casi il client HTTP può negoziare il tunnel RTSP-over-HTTP. Il codice che gestisce questa funzionalità è:
// liveMedia/RTSPServer.cpp:607
void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
[...]
// The request was not (valid) RTSP, but check for a special case: HTTP commands
// (for setting up RTSP-over-HTTP tunneling):
char sessionCookie[RTSP_PARAM_STRING_MAX]; //[1]
char acceptStr[RTSP_PARAM_STRING_MAX]; //[2]
*fLastCRLF = '\0'; // temporarily, for parsing
parseSucceeded = parseHTTPRequestString(cmdName, sizeof cmdName,
urlSuffix, sizeof urlPreSuffix,
sessionCookie, sizeof sessionCookie,
acceptStr, sizeof acceptStr); //[3]
Come mostrato sopra a [3], gli header HTTP “Accept” e “x-sessioncookie” sono ciò che determina se si tratta o meno di un tunnel RTSP-over-HTTP. Pertanto, i parametri vengono letti dai byte di input nei buffer sessionCookie [1] e acceptStr [2] sullo stack (entrambi di dimensione 200), e poi analizzati più avanti.
Il percorso del codice porta alla funzione parseHTTPRequestString:
Boolean RTSPServer:: RTSPClientConnection::parseHTTPRequestString(char* resultCmdName, unsigned resultCmdNameMaxSize,
char* eurlSuffix, unsigned urlSuffixMaxSize,
char* sessionCookie, unsigned sessionCookieMaxSize,
char* acceptStr, unsigned acceptStrMaxSize) {
[...]
lookForHeader("x-sessioncookie", &reqStr[i], reqStrSize-i, sessionCookie, sessionCookieMaxSize); // [1]
lookForHeader("Accept", &reqStr[i], reqStrSize-i, acceptStr, acceptStrMaxSize); //[2]
Le uniche cose davvero importanti da notare sono che gli array di caratteri della funzione padre vengono nuovamente passati a una nuova funzione direttamente a [1] (sessionCookie) e [2] (acceptStr). Questo porta alla funzione lookForHeader:
static void lookForHeader(char const* headerName, char const* source, unsigned
sourceLen, char* resultStr, unsigned resultMaxSize) {
resultStr[0] = '\0'; // by default, return an empty string
unsigned headerNameLen = strlen(headerName);
for (int i = 0; i < (int)(sourceLen-headerNameLen); ++i) {
if (strncmp(&source[i], headerName, headerNameLen) == 0 && source[i+headerNameLen] == ':') { // [1]
// We found the header. Skip over any whitespace, then copy the rest of the line to "resultStr":
for (i += headerNameLen+1; i < (int)sourceLen && (source[i] == ' ' || source[i] == '\t'); ++i) {}
for (unsigned j = i; j < sourceLen; ++j) { // [4]
if (source[j] == '\r' || source[j] == '\n') { // [2]
// We've found the end of the line. Copy it to the result (if it will fit):
if (j-i+1 > resultMaxSize) break;
char const* resultSource = &source[i];
char const* resultSourceEnd = &source[j];
while (resultSource < resultSourceEnd) *resultStr++ = *resultSource++; // [5]
*resultStr = '\0';
break; //[3]
}
}
}
}
}
Il ciclo più esterno itera sui byte di input finché non trova headerName. Nel caso di questo programma, cerca continuamente “Accept:” e “x-sessioncookie:” con strncmp a [1]. Come nota il commento, un altro ciclo salta gli eventuali spazi bianchi, quindi inizia a cercare i caratteri di newline attesi ‘\r\n’ a [2]. Dopo questo, il programma limita correttamente la dimensione della copia a resultMaxSize, che è correttamente impostata a 0xc8 (200) in entrambe le chiamate a questa funzione.
Dopo la copia, viene raggiunto il break a [3], che in realtà esce solo dal ciclo a [4], facendo sì che il codice salti di nuovo al ciclo iniziale con strncmp menzionato sopra. Quindi, se c'è un'altra stringa “Accept:” o “x-sessioncookie” nel buffer, la copia avviene di nuovo, e se esaminiamo l'effettivo metodo di copia a [5], possiamo vedere che il nostro puntatore iniziale (che punta a un indirizzo nello stack frame della funzione handleRequestBytes) continua a incrementare, e mentre la lunghezza di ogni singola copia è limitata alla dimensione del buffer, quando non c'è limite alla quantità di copie che possono avvenire su un indirizzo di destinazione sempre crescente, un buffer overflow basato sullo stack può essere facilmente innescato.
Crash Output
==38574==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffffffd878 at pc 0x555555aad1fb bp 0x7fffffffced0 sp 0x7fffffffcec8
WRITE of size 1 at 0x7fffffffd878 thread T0
#0 0x555555aad1fa in lookForHeader /root/boop/work_work/triages/live555/live/liveMedia/RTSPServer.cpp:398
#1 0x555555aad847 in RTSPServer::RTSPClientConnection::parseHTTPRequestString(char*, unsigned int, char*, unsigned int, char*, unsigned int, char*, unsigned int) /root/boop/work_work/triages/live555/live/liveMedia/RTSPServer.cpp:479
#2 0x555555ab82ac in RTSPServer::RTSPClientConnection::handleRequestBytes(int) /root/boop/work_work/triages/live555/live/liveMedia/RTSPServer.cpp:828
#3 0x555555aa9c17 in GenericMediaServer::ClientConnection::incomingRequestHandler() /root/boop/work_work/triages/live555/live/liveMedia/GenericMediaServer.cpp:246
#4 0x555555e0063b in BasicTaskScheduler::SingleStep(unsigned int) /root/boop/work_work/triages/live555/live/BasicUsageEnvironment/BasicTaskScheduler.cpp:153
#5 0x555555e12c75 in BasicTaskScheduler0::doEventLoop(char volatile*) /root/boop/work_work/triages/live555/live/BasicUsageEnvironment/BasicTaskScheduler0.cpp:80
#6 0x555555a9452c in main /root/boop/work_work/triages/live555/live/mediaServer/live555MediaServer.cpp:89
#7 0x7ffff550b2b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#8 0x555555a978e9 in _start (/root/boop/work_work/triages/live555/live555MediaServer+0x5438e9)
Address 0x7fffffffd878 is located in stack of thread T0 at offset 2024 in frame
#0 0x555555ab6b9f in RTSPServer::RTSPClientConnection::handleRequestBytes(int) /root/boop/work_work/triages/live555/live/liveMedia/RTSPServer.cpp:607
This frame has 12 object(s):
[32, 33) 'reuseConnection'
[96, 97) 'deliverViaTCP'
[160, 164) 'contentLength'
[224, 232) 'proxyURLSuffix'
[288, 488) 'cmdName'
[544, 744) 'urlPreSuffix'
[800, 1000) 'urlSuffix'
[1056, 1256) 'cseq'
[1312, 1512) 'sessionIdStr'
[1568, 1768) 'sessionCookie'
[1824, 2024) 'acceptStr' <== Memory access at offset 2024 overflows this variable
[2080, 2480) 'urlTotalSuffix'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /root/boop/work_work/triages/live555/live/liveMedia/RTSPServer.cpp:398 in lookForHeader
Shadow bytes around the buggy address:
0x10007fff7ab0: f4 f4 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
0x10007fff7ac0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f4
0x10007fff7ad0: f4 f4 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
0x10007fff7ae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f4
0x10007fff7af0: f4 f4 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
=>0x10007fff7b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f4]
0x10007fff7b10: f4 f4 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
0x10007fff7b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007fff7b30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007fff7b40: 00 00 00 00 00 00 00 00 f4 f4 f3 f3 f3 f3 00 00
0x10007fff7b50: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00
Scoperta da Lilith ¯_(ツ)_/¯ di Cisco Talos.