Wednesday, January 28, 2009

Exploitable Userland NULL Pointer Dereference

Today I released a security advisory (TKADV2009-004) that describes the details of a very interesting vulnerability I found in FFmpeg. FFmpeg is a software solution to record, convert and stream audio and video. The FFmpeg libraries are used by a lot of popular software projects like VLC, Mplayer, Perian and Xine.

As I said, this vulnerability is quite interesting as it is another example for an exploitable NULL pointer dereference in an userland application.

The vulnerability

The initial vulnerability is a type conversion bug while converting an user controlled unsigned int value to a signed int.

..
fourxm->track_count = 0;
..
current_track = AV_RL32(&header[i + 8]);
..
The AV_RL32() macro in the above code snippet reads an unsigned int value from the media file and stores the value in the signed int variable "current_track".

Later on "current_track" is checked against another value. If "current_track" is greater than this value a heap buffer pointed to by "fourxm->tracks" gets (re)allocated.
..
fourxm->tracks = NULL;  <-- [4]
..
if (current_track + 1 > fourxm->track_count) {
  fourxm->track_count = current_track + 1;  <-- [1]
  if((unsigned)fourxm->track_count >= UINT_MAX /  <-- [3]
        sizeof(AudioTrack))
    return -1;
  fourxm->tracks = av_realloc(fourxm->tracks,
  fourxm->track_count * sizeof(AudioTrack));  <-- [2]
  if (!fourxm->tracks) {
    av_free(header);
    return AVERROR(ENOMEM);
  }
}
..
If "current_track" is greater than "fourxm->track_count" the user controlled value of "current_track" is stored into "fourxm->track_count" (see [1]). The value of "fourxm->track_count" is then used to calculate the size of the heap buffer (see [2]). To avoid an integer overflow, the size of "fourxm->track_count" is checked before the allocation takes place (see [3]).

Now what happens if we supply a value >= 0x80000000 for "current_track"? Well, as there is an unchecked type conversion between unsigned and signed, "current_track" will become negative. If "current_track" is negative, the if statement shown above will always return false and the heap buffer will never be allocated. This results in "fourxm->tracks" still pointing to NULL (see [4]).

Directly after the if statement the following write operations are performed:
..
fourxm->tracks[current_track].adpcm = AV_RL32(&header[i + 12]);
fourxm->tracks[current_track].channels = AV_RL32(&header[i + 36]);
fourxm->tracks[current_track].sample_rate = AV_RL32(&header[i+40]);
fourxm->tracks[current_track].bits = AV_RL32(&header[i + 44]);
..
As "fourxm->tracks" is pointing to NULL these write operations are leading to four classical NULL pointer dereferences. But as NULL is dereferenced by the user controlled value of "current_track" it is possible to write user controlled data to a wide range of memory locations.

Result:
NULL[current_track].offset = user_controlled_data;

As there are four write operations, four memory locations can be overwritten with arbitrary data.
Short summary:
  1. "fourxm->tracks" is initialized with NULL 
  2. The type conversion bug allows us to avoid the allocation of "fourxm->tracks" 
  3. "fourxm->tracks" still points to NULL 
  4. The resulting NULL pointer is then dereferenced by the user controlled value of "current_track" and four 32bit values of user controlled data are assigned to the dereferenced location(s). 
  5. It is therefore possible to overwrite four user controlled memory locations with four user controlled data bytes each.
As I said, that's a beautiful bug.

Exploitability

In my advisory I wrote: "A malicious party may exploit this issue to execute arbitrary code by overwriting a sensitive memory location (such as a GOT/IAT entry, a return address, buffer length or boolean variable)". To see if this nice bug is indeed exploitable I chose the following two test cases:
  • VLC 0.9.8a under Windows XP SP3
  • Mplayer under openSUSE 11.1
As VLC and Mplayer are both using the FFmpeg libraries they are vulnerable to this bug.

Result: Reliable EIP control confirmed under both test cases

Here is the result for VLC under Windows XP SP3:



Here is the result for Mplayer under openSUSE 11.1:
tk@linux-0skt:~/Documents/ffmpeg-checkout-2009-01-02> gdb mplayer
GNU gdb (GDB; openSUSE 11.1) 6.8.50.20081120-cvs
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i586-suse-linux".
For bug reporting instructions, please see:
<http://bugs.opensuse.org/>...
(no debugging symbols found)

(gdb) run ex_eip_control_mplayer.avi
Starting program: /usr/bin/mplayer ex_eip_control_mplayer.avi
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
[Thread debugging using libthread_db enabled]
(no debugging symbols found)
[...]
MPlayer dev-SVN-r27637-4.3-openSUSE Linux 11.1 (i686)-Packman (C) 2000-2008 MPlayer Team
CPU: Intel(R) Core(TM)2 Duo CPU     T7500  @ 2.20GHz (Family: 6, Model: 15, Stepping: 11)
CPUflags:  MMX: 1 MMX2: 1 3DNow: 0 3DNow2: 0 SSE: 1 SSE2: 1
Compiled with runtime CPU detection.
Can't open joystick device /dev/input/js0: No such file or directory
Can't init input joystick
mplayer: could not connect to socket
mplayer: No such file or directory
Failed to open LIRC support. You will not be able to use your remote control.

Playing ex_eip_control_mplayer.avi.
libavformat file format detected.

Program received signal SIGSEGV, Segmentation fault.
0x55555555 in ?? ()

(gdb) bt
#0  0x55555555 in ?? ()
#1  0x77777777 in ?? ()
Cannot access memory at address 0x6666666a

(gdb) i r
eax            0x0      0
ecx            0x8d4cce0        148163808
edx            0x160    352
ebx            0x806e13f6       -2140269578
esp            0xbfffcfbc       0xbfffcfbc
ebp            0x8998f38        0x8998f38
esi            0x160    352
edi            0x8d59db0        148217264
eip            0x55555555       0x55555555
eflags         0x10297  [ CF PF AF SF IF RF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     5
As a bonus I developed a full working exploit (executes calc.exe) using VLC in version 0.9.8a as an injection vector.

Here is the result under Windows XP SP3.
Roundup:
  1. This is a very nice bug ;) 
  2. Reliable EIP control confirmed while using Mplayer as injection vector under openSUSE 11.1 and VLC 0.9.8a under Windows XP SP3 
  3. Reliable code execution confirmed while using VLC 0.9.8a as injection vector under Windows XP SP3

Thursday, January 22, 2009

GStreamer bugs

I just released a security advisory (TKADV2009-003) describing the details of some heap buffer overflows and an array index out of bounds vulnerability I found in the GStreamer multimedia framework.

The following screenshot shows the result of a poc for the array index out of bounds vulnerability that can be exploited to write the value 0x00000001 to (nearly) any location in memory. I used Songbird as an injection vector as this music player (like many others) is using the GStreamer framework.

Note: EAX holds the user controlled value

Wednesday, January 14, 2009

Commercial usage of ScoopyNG

Atempo Time Navigator is now officially using the VMware detection tricks of ScoopyNG. Thanks to Atempo for asking for permission instead of just using it without confirmation. I'm wondering what other software products are using ScoopyNG. Let me know if you know one.

Sunday, January 11, 2009

Some statistics

In my experience, open source projects are much faster in fixing security bugs than commercial vendors.

Current example:

Commercial product: Sun Solaris TKADV2009-001
Patch development time 115 days

Open source project:
Amarok TKADV2009-002
Patch development time 7 days

The fact itself is not surprising as open source projects are normally not as tightly bound to business processes like commercial vendors. Nevertheless, the time difference is quite impressive.

Since a while I keep record of the "patch development time" in each of my security advisories. This is the time a vendor or open source project needed to provide a fix or patch for the vulnerability.

Here are some patch development time statistics of the vulnerabilities I reported so far:

Average patch development time of open source software projects:
(How long does it take open source projects to patch vulnerabilities?)

Average patch development time: 5.1 days
Total number of vulnerabilities: 8

Average patch development time of commercial software vendors:

(How long does it take commercial software vendors to patch vulnerabilities?)

Average patch development time: 169.9 days
Total number of vulnerabilities: 12

Well, I think these numbers are self-explanatory. I will keep these statistics updated under http://www.trapkit.de/advisories/pdts.php.

vmem_xalloc(): size == 0

The Solaris kernel vulnerability described in TKADV2009-001 can be trivially exploited to crash a Solaris system (all Zones) as an unprivileged user, even if the vulnerability is triggered in a restricted non-global zone.

$ id
uid=101(tk) gid=1(other)

$ zonename
unpriv_zone

$ ppriv -S $$
1157:   -bash
flags = <none>
E: basic
I: basic
P: basic
L: zone

$ ./poc

System crash because of a kernel panic. Debugging information:
> ::msgbuf
[...]
panic[cpu0]/thread=d4764de0:
vmem_xalloc(): size == 0


d418cd94 genunix:vmem_xalloc+2d8 (fec66738, 0, 1000, )
d418cdd0 genunix:vmem_alloc+135 (fec66738, 0, 1)
d418cdfc unix:segkmem_xalloc+2d (fec66738, 0, 0, 1, )
d418ce28 unix:segkmem_alloc_vn+b7 (fec66738, 0, 1, fec)
d418ce40 unix:segkmem_alloc+16 (fec66738, 0, 1)
d418ce8c genunix:vmem_xalloc+3b4 (da004690, fffffffc,)
d418cec8 genunix:vmem_alloc+135 (da004690, fffffffc,)
d418cee4 genunix:kmem_alloc+32 (fffffffc, 1)
d418cf30 kaio:aiosuspend+a6 (0, 3fffffff, 0, 0, )
d418cf64 kaio:kaio+162 (d418cf8c, d418cf78)
d418cf84 genunix:syscall_ap+4d (8, 0, 3fffffff, 0, )

Well, is it indeed necessary to panic the whole system if a memory size of 0 is requested?

Monday, January 05, 2009

Don't trust (media) file extensions

In reaction to the vulnerabilities I recently found in various popular media players I get a lot of mails with a lot of questions. There're two questions that keep repeating themselves:

"The vulnerability is completely theoretical. I have never heard of the [place your favorite name here] media format before, so why should I open such an obscure media file?"
"Am I secure if I don't open [place your favorite name here] media files anymore?"
Both questions are answered quite easily: How would you find out the media file format? By the file extension? DON'T trust media file extensions!

Let's take for example the VLC vulnerability that occurs while processing TiVo media files. I'm sure most of you (including me ;) have never heard of this format before or even used it. The regular file extension for TiVo files is ".ty". But what hinders an evil attacker from renaming the file from "fun.ty" to "fun.avi" or "fun.mov" or "fun.mkv" or whatever he likes? The file will still be opened and processed as a TiVo file by the media player as file extensions are *not* used to recognize the media format.

This is true for all media players under all platforms (that I know).

(Purpose of this blog entry: From now on I can simply provide a link to this entry as an answer for the above questions ;)