
Vulnerabilidad de ejecución de código en RTSPServer CVE-2018-4013
CVE-2018-4013
Existe una vulnerabilidad de ejecución de código explotable en la funcionalidad de análisis de paquetes HTTP de la biblioteca del servidor RTSP de LIVE555. Un paquete especialmente manipulado puede provocar un desbordamiento de búfer basado en la pila, lo que resulta en la ejecución de código. Un atacante puede enviar un paquete para desencadenar esta vulnerabilidad.
Las Live Networks LIVE555 Media Libraries son un conjunto ligero de bibliotecas de transmisión multimedia para RTSP/RTCP/RTSP/SIP, con soporte de código tanto para servidores como para clientes. Son utilizadas por reproductores multimedia populares como VLC y MPlayer, así como por multitud de dispositivos integrados (principalmente cámaras). Esta vulnerabilidad se encuentra en el componente del servidor que interactúa con estos reproductores multimedia, pero no afecta a los reproductores.
Una de las funcionalidades que ofrece LIVE555 para su servidor RTSP estándar es la capacidad de tunelizar RTSP sobre HTTP, que es atendida por un puerto diferente enlazado por el servidor, normalmente TCP 80, 8000 u 8080, dependiendo de los puertos disponibles en la máquina anfitriona. Este puerto puede soportar RTSP normal, pero en ciertos casos, el cliente HTTP puede negociar el túnel RTSP sobre HTTP. El código que maneja esta característica es:
// 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]
Como se muestra arriba en [3], los encabezados HTTP “Accept” y “x-sessioncookie” son los que determinan si se trata de un túnel RTSP sobre HTTP o no. Por lo tanto, los parámetros se leen de los bytes de entrada en los búferes sessionCookie [1] y acceptStr [2] en la pila (ambos de tamaño 200), y luego se analizan más adelante.
La ruta del código conduce a la función 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]
Lo único realmente importante a tener en cuenta es que los arreglos de caracteres de la función padre se pasan nuevamente a una nueva función directamente en [1] (sessionCookie) y [2] (acceptStr). Esto conduce a la función 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]
}
}
}
}
}
El bucle más externo itera sobre nuestros bytes de entrada hasta que se encuentra headerName. En el caso de este programa, busca continuamente “Accept:” y “x-sessioncookie:” con strncmp en [1]. Como indica el comentario, otro bucle omite cualquier espacio en blanco encontrado, y luego comienza a buscar los caracteres de nueva línea esperados ‘\r\n’ en [2]. Después de esto, el programa limita correctamente el tamaño de la copia a resultMaxSize, que está correctamente establecido en 0xc8 (200) en ambas llamadas a esta función.
Después de la copia, se alcanza el break en [3], que en realidad solo sale del bucle en [4], haciendo que el código salte de nuevo al bucle strncmp inicial mencionado anteriormente. Por lo tanto, si hay otra cadena “Accept:” o “x-sessioncookie” dentro del búfer, la copia se realiza nuevamente, y si examinamos el método real de copia en [5], podemos ver que nuestro puntero inicial (que apunta a una dirección en el marco de pila de la función handleRequestBytes) continúa incrementándose, y si bien la longitud de cada copia está limitada al tamaño del búfer, cuando no hay límite en la cantidad de copias que pueden ocurrir en una dirección de destino cada vez mayor, se puede desencadenar fácilmente un desbordamiento de búfer basado en la pila.
Salida del fallo
==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 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
Descubierto por Lilith ¯_(ツ)_/¯ de Cisco Talos.