Post Memory Corruption
Memory Analysis
Jonathan Brossard
CEO – Toucan System
jonathan@
Who am I ?
(a bit of self promotion ;)
-Security Research Engineer, CEO @ Toucan System (French Company).
-Known online as endrazine (irc, twitter...)
-Met some of you on irc.
-Currently lives in Sydney (pentester for CBA).
-Speaker at several conferences : Defcon/HITB/HES/Ruxcon/h2hc...
-Organiser of the Hackito Ergo Sum conference (Paris).
I don't reverse plain text
Agenda
A few basics
Being environment aware
PMCMA Design
Extending Pmcma
•Stack desynchronization
What's pmcma ?
It's a debugger, for Linux (maybe one day *NIX) ptrace() based.
Pmcma allows to find and test exploitation scenarios.
Pmcma's output is a roadmap to exploitation, not exploit code.
Tells you if a given bug triggering an invalid memory access is a vulnerability, if it is exploitable with the state of the art, and how to exploit it.
What's pmcma ?
DEMO
Tool available on September 1st 2011 at http://www.pmcma.org
A FEW BASICS
How do applications
crash ?
*Stack corruptions
*Signal 6
*Segfault (Signal 11)
Invalid memory access
-trying to read a page not readable. often not mapped at all.
-trying to write to a page not writable. often not mapped at all.
-trying to execute a page not executable. often not mapped at all.
Why do they happen ?
Because of any kind of miscomputation, really :
-integer overflows in loop counters or destination registers when copying/initializing data, casting errors when extending registers or
-uninitialised memory, dangling pointers
-variable misuse
-heap overflows (when inadvertently overwriting a function ptr)
-missing format strings
-overflows in heap, .data, .bss, or any other writable section (including shared libraries).
-stack overflows when no stack cookies are present...
Exploiting invalid exec
Trivial, really. Eg :
call eax
with eax fully user controled
Invalid memory reads (1/2)
Eg :
cmp BYTE PTR [ebx+0x8],0x9
Invalid memory reads (2/2)
Eg :
fld QWORD PTR [eax+0x8]
Exploiting invalid memory
reads ?
-usually plain not exploitable
-won't allow us to modify the memory of the mapping directly
-in theory : we could perform a user controled read, to trigger a second (better) bug.
Invalid memory writes
Eg :
mov DWORD PTR [ebx+edx*1],eax
How to...
To exploit invalid writes, we need to find ways to transform an arbitray write into an arbitrary exec.
The most obvious targets are function
pointers.
Exploiting invalid memory
writes : scenario
-Target a known function pointer (typically : .dtors, GOT entry...).
Can be prevented at compile time :
no .dtors, static GOT...
-Target function pointers in the whole binary ?
-Overwrite a given location to trigger an other bug (eg : stack overflow)
Being environment aware
Problems to take into
account
-Kernel : ASLR ? NX ?
-Compilation/linking : RELRO (partial/full) ? no .dtors section ? SSP ?
FORTIFY_SOURCE ?
=> Pmcma needs to mesure/detect those features
ASLR
Major problem when chosing an exploitation strategy.
ASLR : not perfect
-Prelinking (default on Fedora) breaks ASLR
-All kernels don't have the same randomization strength.
-Non PIE binaries
=> Truth is : we need better tools to test it !
Testing ASLR
=> Compare mappings, deduce randomization
DEMO : being environment aware
PMCMA DESIGN
GOALS
-We want to test overwriting different memory locations inside a process and see if they have an influence over the flow of execution
-We want to scale to big applications (web browsers, network deamons...)
-We want a decent execution time
mk_fork()
The idea :
many times)
mk_fork() : benefits
Mapping looks « just like » it will when actually exploiting a binary
No ASLR/mapping replication problem
Exhaustive and hopefully fast
How to force a process to
fork ?
1)Find a +X location mapped in memory.
2)Save registers
3)Use ptrace() to inject fork() shellcode.
4)Modify registers so eip points to shellcode.
5)Execute shellcode.
6)Wait() for both original process and offspring.
7)Restore bytes in both processes.
8)Restore registers in both processes.
Forking shellcode
;forking shellcode: |
xor eax,eax |
|
00000000 |
6631C0 |
|
00000003 |
B002 |
mov al,0x2 |
00000005 |
CD80 |
int 0x80 |
mk_fork()
Original process
Executable
Writable
Executable
…
Offspring 2
Executable
Writable
|
|
|
Executable |
|
|
|
|
|
|
|
|
|
Offspring 1 |
… |
|
|
|
|
|
|
Executable |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Writable |
|
|
|
|
|
|
Executable
…
mk_fork()
Offspring 1
Executable
Writable
Executable
…
mk_fork()
Offspring 2
Executable
Executable
…
mk_fork()
Offspring n
Executable
Writable
Executable
…
mk_fork() : PROS
-allows for multiple tests out of a single process
-fast, efficient (no recording of memory snapshots)
-no need to use breakpoints
-no single stepping
mk_fork() : CONS
-Dealing with offsprings termination ? (Zombie processes)
-I/O, IPC, network sockets will be in unpredictable state
-Hence syscalls will get wrong too (!!)
Zombie reaping
-Avoid the wait() for a SIGCHILD in the parent process.
-Kill processes after a given timeout, including all of their children.