
RTSPServer Codeausführungs-Schwachstelle CVE-2018-4013
CVE-2018-4013
In der HTTP-Paketparsing-Funktion der LIVE555-RTSP-Serverbibliothek existiert eine ausnutzbare Schwachstelle zur Codeausführung. Ein speziell präpariertes Paket kann einen stackbasierten Pufferüberlauf verursachen, der zur Codeausführung führt. Ein Angreifer kann ein Paket senden, um diese Schwachstelle auszulösen.
Die LIVE555 Media Libraries sind eine leichtgewichtige Sammlung von Multimedia-Streaming-Bibliotheken für RTSP/RTCP/RTSP/SIP mit Code-Unterstützung sowohl für Server als auch für Clients. Sie werden von beliebten Mediaplayern wie VLC und MPlayer sowie von zahlreichen eingebetteten Geräten (hauptsächlich Kameras) verwendet. Diese Schwachstelle befindet sich in der Serverkomponente, die mit diesen Mediaplayern interagiert, wirkt sich jedoch nicht auf die Mediaplayer selbst aus.
Eine der Funktionen, die LIVE555 für seinen Standard-RTSP-Server bereitstellt, ist die Möglichkeit, RTSP über HTTP zu tunneln; dieser Tunnel wird über einen anderen Port bedient, den der Server bindet – typischerweise TCP 80, 8000 oder 8080, je nachdem, welche Ports auf dem Host-Rechner verfügbar sind. Dieser Port kann normales RTSP unterstützen, aber in bestimmten Fällen kann der HTTP-Client den RTSP-over-HTTP-Tunnel aushandeln. Der Code, der diese Funktion behandelt, ist:
// 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]
Wie oben bei [3] gezeigt, sind die HTTP-Header „Accept“ und „x-sessioncookie“ entscheidend dafür, ob es sich um einen RTSP-over-HTTP-Tunnel handelt oder nicht. Die Parameter werden also aus den Eingabebytes in die Stack-Puffer sessionCookie [1] und acceptStr [2] eingelesen (beide mit einer Größe von 200) und anschließend weiter unten geparst.
Der Codepfad führt in die Funktion 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]
Die einzigen wirklich wichtigen Punkte sind, dass die char-Arrays aus der übergeordneten Funktion direkt bei [1] (sessionCookie) und [2] (acceptStr) erneut an eine neue Funktion übergeben werden. Dies führt in die Funktion 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]
}
}
}
}
}
Die äußerste Schleife iteriert über unsere Eingabebytes, bis der headerName gefunden wird. Im Fall dieses Programms sucht sie bei [1] kontinuierlich mit strncmp nach „Accept:“ und „x-sessioncookie:“. Wie der Kommentar vermerkt, überspringt eine weitere Schleife jegliche gefundenen Leerzeichen und beginnt dann, bei [2] nach den erwarteten Zeilenumbruchzeichen ‚\r\n‘ zu suchen. Danach begrenzt das Programm die Größe der Kopie korrekt auf resultMaxSize, das bei beiden Aufrufen dieser Funktion korrekt auf 0xc8 (200) gesetzt ist.
Nach der Kopie wird das break bei [3] erreicht, das jedoch erst bei [4] tatsächlich aus der Schleife ausbricht, wodurch der Code zurück zur anfänglichen, oben erwähnten strncmp-Schleife springt. Wenn sich also ein weiterer „Accept:“- oder „x-sessioncookie“-String im Puffer befindet, findet erneut eine Kopie statt. Betrachtet man die eigentliche Kopiermethode bei [5], erkennt man, dass unser anfänglicher Zeiger (der auf eine Adresse im Stack-Frame der Funktion handleRequestBytes zeigt) kontinuierlich inkrementiert wird. Während die Länge einer einzelnen Kopie auf die Puffergröße begrenzt ist, kann ein stackbasierter Pufferüberlauf leicht ausgelöst werden, wenn die Anzahl der Kopien auf eine ständig wachsende Zieladresse unbegrenzt ist.
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