Constructing ELF Metadata DEFCON 20 July 28,2012 Rebecca Shapiro and Sergey Bratus Dartmouth College here? DARTMOUTH i H DARTMOUTH GRADUATE STUDIES 1 8 8 5- This Talk in One/Minute ■ "Deep magic" before a program can run ■ ELF segments, loading, relocation, ■ "Deeper magic" to support dynamic linking ■ Dynamic symbols, loading of libraries ■ Many pieces of code - enough to program anything (Turing-complete) ■ In perfectly valid ELF metadata entries alone ■ Runs before most memory protections are set for the rest of runtime ■ Runs with access to symbols (ASLR? what ASLR?) Image: "Clock" symbol by Brandon Hopkins, from thenounproject.com The Weird Kinds of Programming Exploit is a program running on the target ■ encoded as crafted data ■ reliably executed by target's intended and unindented primitives ■ Resembles assembly with calls to library functions and system calls - very weird assembly ■ aa4bmo [Phrack 61 :6, jp] ■ %n in format strings Virtual Machine vs "Weird Machine" ■ VM bytecode programs are data in memory ■ Pieces of native code implement effects and actions of bytecodes ■ "Data (bytecode) acts on the state of the VM" ■ Exploit payload is (crafted) data in memory ■ Pieces of native code produce unexpected effects on system state ■ Crafted data is executed as bytecode on a "weird" VM inside target Exploitation is Programming Weird Machines ■ Exploit programs use dormant/latent state and/or transitions not present in the target's programming model but actually present in the target ■ Memory corruptions, escaping errors, in-band signalling effects, ... ■ Memory buffers become "stored programs" (hallo von Neumann) ■ "Exploitation is setting up, instantiating, and programming a weird machine" ■ T. Dullien, Infiltrate 201 1 Where Do We See Weird ~~ Machines? Heap metadata executed on heap manager Format strings act on pring's internals TCP/IP packet acts on the stack Executable file metadata acts on loader/RTLD Exploit Techniques & Weird Machines Normal Odd Weird Crafting DWARF SQL injection roP Stack smashing Crafting ELF XSS Modern heap smashing The Quest ELF background Prior work with abusing ELF Everything you need to know about ELF metadata for this talk Branfuck to ELF compiler Relocation entry backdoor , ^ ■ Demo exploit ELF ~ Executable and Linking Format How gcc toolchain components communicate ■ Assembler (*.c —*■ *.oo) ■ Static linker (*.o — > executable) ■ Runtime linker/loader (RTLD) (exec, *.so) ■ Dynamic linker/loader (*.so) preprocessed code foo.i assembled code foo.s ^preprocessor I (cpp) i — — i i 1 compiler assembler (ccl) (as) ELF relocatable? (object files) foo,o ELF executable running process V bar.c preprocessor {tppl bar.i compiler (ccl) bar.s bar.o (Id) assembler (as) ELF Shared objects (*.so) Runtime LD (Id.so) 1 ELF File Contents Architecture/version information Symbols ■ Symbol names (string table) Interpreter location (usually Id.so) Relocation Entries Debugging information Co n stru cto rs/d eco n stru cto rs Dynamic linking information ■ ■ ■ ■ Static/initialized data Code ■ Entrypoint ELF Sections All data/code is contained in ELF sections ■ Except ELF, section, and segment headers ■ Section = contiguous chunk of bytes 1 section < — > 1 section header ■ Header contains: size, file offset, memor ^ offset, etc, for linker/loader Most sections contain one of: ■ Table of structs (.ssymtab, .rela.dyn) ■ Null terminated strings (.strtab) ■ Mixed data (ints, long, etc) (.data) ■ Code (.text) Interesting ELF Sections Symbol table (.dynsym) Relocation tables (.rela.dyn, .rela.plt) Global offset table (.got) Procedure linkage table (.got.plt) Dynamic table (.dynamic) Interesting ELF Sections - Symbol table (.dynsym) ■ Relocation tables (.rela.dyn, .rela.plt) ■ Global offset table (.got) ■ Procedure linkage table (.got.plt) ■ Dynamic table (.dynamic) Symbol Tables Info to (re)locate symbolic definitions and references ■ For variables/functions imported/exported Example symbols in libc: Num: Value Size Type Bind Vis Ndx Name 7407: 0000000000376d98 8 OBJECT GLOBAL DEFAULT 31 stdin 7408: 00000000000525c0 42 FUNC GLOBAL DEFAULT 12 putc Symbol definition for 64-bit architectures: typedef struct { uint32_t st_name; unsigned char st_info; unsigned char st_other; uint16_t st_shndx; Elf64_Addr st_value; uint64_t st_size; } Elf64_Sym; Image: "Table" Sofie Hauge Katan, from The Noun Project Interesting ELF Sections ■ Symbol table (.dynsym) ■ Relocation tables (.rela.dyn, .rela.plt) ■ Global offset table (.got) ■ Procedure linkage table (.got.plt) ■ Dynamic table (.dynamic) Relocation Tables ■ .rela.dyn ■ Relocation information for RTLD ■ Processed at load time ■ .rela.plt ■ Relocation information for dynamic linker ■ Processed as needed at runtime Image: "Commercial Loading Zone" Kirk Lohry, from The Noun Project Relocation Table Entries ■ Where to write what value at load/link time ■ Foramd64: ■ ■ r_inf(3: (.rela.dyn, .rela.plt) ■ Relocation entry type - #define ELF64_R_TYPE(i) ((i) & Oxffffffff) ■ Associated symbol table entry index - #define ELF64_R_SYM(i) ((i) » 32) ■ amd64 ABI defines 37 relocation types ■ gcc toolchain uses 13 types (1 not in ABI) } Elf64_Rela; typedef struct { Elf64_Addr r_offset; uint64_t r_info; int64_t r_addend; Image: "Door" Tak Imoto, from The Noun Projecl Interesting ELF Sections ■ Symbol table (.dynsym) ■ Relocation tables (.rela.dyn, .rela.plt) - Global offset table (.got) - Procedure linkage table (.got.plt) ■ Dynamic table (.dynamic) GOT and PLT Global Offset Table and Procedure Linkage Table ■ Entry in each for dynamically-linked functions ■ GOT is a table of addresses ■ GOT[1] = object's link_map struct ■ ELF object metadata used by RTLD/linker ■ GOT[2] = &_dl_fixup (dynamic linker function) ■ GOT entry for linked function is &function or & ■ PLT contains instructions that work with GOT [ to invoke _dl_fixup and linked function Interesting ELF Sections Symbol table (.dynsym) Relocation tables (.rela.dyn, .rela.plt) Global offset table (.got) Procedure linkage table (.got.plt) Dynamic table (.dynamic) Dynamic Table Table of metadata used by runtime loader typedef struct { Elf64_Sxword d_tag; union { Elf64_Xword d_val; Elf64_Addr d_ptr; } d_un; } Elf64_Dyn; Types of interest ■ DT_RELA, DT_RELASZ - DT RELACOUNT - DT_SYM ■ DT JMPREL, DT PLTRELSZ Useful dynamic section entries DT_RELA, DT_RELASZ, ■ Start and size of .rela.dyn table DT_SYM ■ Location of symbol table (.dynsym) DT_PLTGOT ■ Location of GOT Among others needed for clean execution md Loading KEITH XP Source: http://keithsrockin.blogdrive.eom/archive/5.html Loading and Linking The story of exec() exec(ping) ping After ex€ exec (in kernel) pin< ^ntrypointfc). () finishes ping address space executable Id. so stack After Id. so finishes loadi Id. so heap dynamic library dynamic library n libc.so Id. so stack Memory layout of ping (partial) ■ 00400000-00408000 r-xp ping ■ 00607000-00608000 r-p ping ■ 00608000-00609000 rw-p ping ■ 00609000-0061 cOOO rw-p ■ 02165000-02186000 rw-p [heap] ■ 7fc2224d2000-7fc2224de000 r-xp libnss_files-2.13.so ■ 7fc2226dd000-7fc2226de000 r-p libnss_files-2.13.so ■ 7fc2226de000-7fc2226df000 rw-p libnss_files-2.13.so ■ 7fc2226df000-7fc222876000 r-xp libc-2.13.so ■ 7fc222a75000-7fc222a79000 r-p libc-2.13.so ■ 7fc222a79000-7fc222a7a000 rw-p libc-2.13.so ■ 7fc222a7a000-7fc222a80000 rw-p ■ 7fc222a80000-7fc222aa1 000 r-xp ld-2.1 3.so ■ 7fc222c77000-7fc222c7a000 rw-p ■ 7fc222c9d000-7fc222ca0000 rw-p ■ 7fc222ca0000-7fc222ca1000 r-p ld-2.1 3.so ■ 7fc222ca1000-7fc222ca3000 rw-p ld-2.1 3.so ■ 7fff01379000-7fff0139a000 rw-p [stack] Memory Layout of a Process executable heap dynamic library ■ dynamic library n libc.so Id. so (linker/loader) stack Memory Layout of a Process dynamic library dynamic library n libc.so Id. so (linker/loader) stack Layout of Executable in Memory 0x400000 read/execute- 0x408000 0x607000 string table (.dynstr) symbol table (.dynsym) relocation tables (.rela.dyn, .rela.plt) code (.pit, . i n it, .text, .fini) read only. 0x608000 dynamic table (.dyn) global offset table (.got, .got. pit) data (.data, .bss) read/write 0x609000 00400000-00408000 r-xp 00000000 08:06 261244 00607000-00608000 r--p 00007000 08:06 261244 00608000-00609000 rw-p 00008000 08:06 261244 /bin/ping /bin/ping /bin/ping Memory Layout executable libc... interesting code dwells here linker/loader Our Perspective Id.so's data and heap metadata to process loaded ELFobjects Memory Layout executable libc... interesting code dwells here linker/loader Our Perspective ldjo^s_data_aDd heap metadata to process loaded ELFobjects Id.so's linkjnap structures (Addr) l_addr; _^-fM3ase address shared object is loaded at. */ struct link_map *l_next, *l_prev; /* Chain of loaded objects. */ ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM union { const Elf32_Word *l_gnu_chain_zero; const Elf_Symndx *l_buckets; }; unsigned int l_direct_opencount; /* Reference count for dlopen/dlclose. */ enum { /* Where this object came from. */ It executable, /* The main executable program. */ Itlibrary, /* Library needed by main executable. */ Itjoaded /* Extra run-time loaded shared object. */ } l_type:2; unsigned int l_relocated:l; /* Nonzero if object^Telocations done? size t I relro size; J; exec J^- } — Id. so Fun Ways to Craft Metadata ■ Change entrypoint to point to injected code ■ Inject object files (mayhem, phrack 61 :8) ■ Intercept library calls to run injected code ■ Injected in executable ■ Cesare PLT redirection (Phrack 56:7) ■ Mayhem ALTPLT (Phrack 61 :8) ■ Resident in attacker-built library - LD_PRELOAD (example: Jynx-Kit rootkit) ■ DT_NEEDED (Phrack 61:8) ■ Loaded at runtime (Cheating the ELF, the grugq) ■ Injected in library - LOCREATE (Skape, Uniformed 2007) ' ■ Unpack binaries using relocation entries More fun with relocation entries 3L Warning. The following you are about to see is architecture and libc implementation dependant. Please try this at home, but there are no guarantees it will work with your architecture/gcc toolchain combination. (Ours is Ubuntu 11.10's eglibc-2.13 on amd64) Not all Brainfuck instructions work in presence of ASLR This is proof of concept, after all. Image: "Caution" Sam Ahmed, from The Noun Project Injecting Relocation/Symbol tables Use eresi toolkit Injects into executable's data segment executable Inject metadata here libc... interesting code dwells here linker/loader symbol table relocation entries pit code Id.so's data and heap metadata to process loaded ELFobjects Relocation Entry Type Primer typedef struct { Elf64_Addr r_offset; uint64_t r_info; // contains type and symbol number int64_t r_addend; } Elf64_Rela; ■ Let r be our Elf64_Rela, s be the corresponding Elf64_Sym (if applicable) ■ R_X86_64_COPY ■ memcpy(r.r_offset, s.st_value, s.st_size) ■ R_X86_64_64 ■ *(base+r.r_offset) = s.st_value +r.r_addend+base ■ R_X86_64_32 | ■ Same as _64, but only writes 4 bytes \ R_X86_64_RELATIVE ■ *(base+r.r_offset = r.r_addend+base) Relocation & STTJFUNC symbol ■ Symbols of type STTJFUNC are special ■ st_value treated as a function pointer ■ Trivial example of indirect functions: #include int foo (void) attribute ((ifunc ("foo_ifunc"))); static int global = 1; static int f1 (void) { return 0; } static int f2 (void){ return 1 ; } void *foo_ifunc (void) { return global == 1 ? f1 : f2; } int main () { printf ("%d\n", foo()); } ■ Corresponding symbol table entries: 43:0000000000400524 11 FUNC LOCAL DEFAULT 1 3 f 1 44: 000000000040052f 11 FUNC LOCAL DEFAULT 13f2 57: 000000000040053a 29 FUNC GLOBAL DEFAULT 13 foo_ifunc 62:000000000040053a 29 IFUNC GLOBAL DEFAULT 13 foo Musical llnterlude: I'm My Own Grandpa (Why Reloc Entries are so Powerful) Source: Ray Stevens on https://www.youtube.com/watch?v=eYIJH81dSiw Brainfuck Primer 8 instructions: 1) > Increment the pointer. 2) < Decrement the pointer. 3) + Increment the byte at the pointer. 4) - Decrement the byte at the pointer. 5) [ Jump forward past the matching ] if the byte at the pointer is zero. 6) ] Jump backward to the matching [ unless the byte at the pointer is zero. 7) . Output the byte at the pointer. 8) , Input a byte and stor in byte at the pointer. Source: http://www.muppetlabs.com/~breadbox/bf/ Brainfuck Primer Tape (array of bytes) 8 6 instructions: Tape pointer 1) > Increment the pointer. 2) < Decrement the pointer. 3) + Increment the byte at the pointer. 4) - Decrement the byte at the pointer. 5) [ Jump forward past the matching ] if the byte at the pointer is zero. 6) ] Jump backward to the matching [ unless the byte at the pointer is zero. 7) . Ou t put the, by t a a t tha po i n t er. 8) , I nput q byto and otor i n byto at tho po i ntor. Source: http://www.muppetlabs.com/~breadbox/bf/ 0x00 0x00 0x00 0x00 0x00 0x00 Brainfuck Primer Tape (array of bytes) ■ 6 instructions: Tape pointer 1) > Increment the pointer. 2) < Decrement the pointer. <0 ° 3) + Increment the byte at the pointer. I 4) - Decrement the byte at the pointer. 5) [ Jump forward past the matching ] if the byte at the pointer is zero. 6) ] Jump backward to the matching [ unless the byte at the pointer is zero. I Example: + ^ - Source: http://www.muppetlabs.com/~breadbox/bf/ 0x00 0x00 0x00 0x00 0x00 0x00 Brainfuck Primer Tape (array of bytes) ■ 6 instructions: Tape pointer 1) > Increment the pointer. 2) < Decrement the pointer. <0 ° 3) + Increment the byte at the pointer. I 4) - Decrement the byte at the pointer. 5) [ Jump forward past the matching ] if the byte at the pointer is zero. 6) ] Jump backward to the matching [ unless the byte at the pointer is zero. Example: t > - Source: http://www.muppetlabs.com/~breadbox/bf/ 0x01 0x00 0x00 0x00 0x00 0x00 Brainfuck Primer Tape (array of bytes) ■ 6 instructions: Tape pointer 1) > Increment the pointer. 2) < Decrement the pointer. <0 ° 3) + Increment the byte at the pointer. I 4) - Decrement the byte at the pointer. 5) [ Jump forward past the matching ] if the byte at the pointer is zero. 6) ] Jump backward to the matching [ unless the byte at the pointer is zero. Example: + ^ - Source: http://www.muppetlabs.com/~breadbox/bf/ 0x01 0x00 0x00 0x00 0x00 0x00 Brainfuck Primer Tape (array of bytes) ■ 6 instructions: Tape pointer 1) > Increment the pointer. 2) < Decrement the pointer. <0 ° 3) + Increment the byte at the pointer. I 4) - Decrement the byte at the pointer. 5) [ Jump forward past the matching ] if the byte at the pointer is zero. 6) ] Jump backward to the matching [ unless the byte at the pointer is zero. Example: + > ■ Source: http://www.muppetlabs.com/~breadbox/bf/ 0x01 OxFF 0x00 0x00 0x00 0x00 Brainfuck Primer Hello, World // Hello World in brainfuck // Creds to Speedy [<++++>-]<+.+++++++..+++.[-] >++++++++[<++++>-] <. [<++++++++>_]<_. +++ . . .[-]>++++++++[<++++>- ]<+.[-] ++++++++++ Source: www.helloworld.org Compiling Brainfuck to ELF ELF executable brainfuck sou brainfuck-enhanced ELF executable running process ► elf -> bf compiler \ exec() Runtime LD (Id. so) ► ► j configurat ELF Shared objects i^Tso) ELF Brainfuck Setup Data needed at compile time * ■ Address of executable's link_map 'IP ■ Can be determined at runtime ^ * ■ Address of gadget that returns IMI ■ ROP-style, found at compile time ■ Stack location Jj^ ^ ■ Location in memory of executable's: Ijjl DT_RELA " DT_RELASZ DT_SYM DT_JMPREL DT_PLTRELSZ Collected at compile time Image: Hand" Ugur Akdemir, from The Noun Project ELF Brai dynsym table (empty) Original dynsym Original dynsym 1 ... Original dynsym n Address tape head is pointing at Copy of tape head's value Address of previous sym's value IFUNC of gadget that returns fuck Setup .rela.dyn table Brainfuck instruction Brainfuck instruction n Instructions that clean up link_map data Instructions to force branch to next rel entry Instructions to finish cleaning linkjnap data Original .rela.dyn entry Original .rela.dyn entry m ELF Brainfuck Address tape head is pointing at 0xb33f0000 Copy of tape head's value f 0xb33f0000 oxoo 0xb33f0001 0x01 0xb33f0002 0x00 0xb33f0003 Oxoo 0xb33f0004 0x00 Pointer ■ Relocation/symbol entries must be in writable memory ■ Tape must be in writable memory ELF Brainfuck Tape Pointer Address tape head is pointing at t_ptr Copy of tape head's value t_val 0xb33f0000 0xb33f0001 0x01 0xb33f0002 0x00 0xb33f0003 0x00 0xb33f0004 0x00 ELF Brainfuck Tape Pointer mv_ptr = {offset=&(t_ptr. value), type = 64, sym=t_ptr, addend=n} copy_val = {offset=&(t_val.value), type = COPY, sym=t_ptr} ■ t_ptr 0xb33f0000 t_val 0xb33f0000 0xb33f0001 | 0xb33f0002 0xb33f0003 " 0xb33f0004 0x00 0x01 0x00 0x00 0x00 ELF Brainfuck Tape Pointer n=1 mvptr = {offset=&(t_ptr. value), type = 64, sym=t_ptr, addend=1} copy_val = {offset=&(t_val.value), type = COPY, sym=t_ptr} t_ptr 0xb33f0001 t_val Zl 0xb33f0000 0xb33f0001 | 0xb33f0002 0xb33f0003 " 0xb33f0004 0x00 0x01 0x00 0x00 0x00 ELF Brainfuck Tape Pointer n mv_ptr = {offset=&(t_ptr. value), type = 64, sym=t_ptr, addend=1} copy_val = {offset=&(t_val.value), type = COPY, sym=t_ptr} t_ptr 0xb33f0001 t_val 1 0xb33f0000 0xb33f0001 0xb33f0002 0xb33f0003 0xb33f0004 0x00 0x01 0x00 0x00 0x00 Addition/Subtraction add = {offset=&(t_ptr. value), type = 64, sym=t_val, addend=n} get_ptr = {offset=&(update.offset), type = 64, sym=t_ptr} update = {offset=????, type = COPY, sym=valptr} t_ptr 0xb33f0001 t_val 1 valptr &t_val. value 0xb33f0000 0xb33f0001 Addition/Subtraction n= add = {offset=&(t_ptr. value), type = 64, sym=t_val, addend=2} get_ptr = {offset=&(update.offset), type = 64, sym=t_ptr} update = {offset=????, type = COPY, sym=valptr} t_ptr 0xb33f0001 t_val 3 valptr &t_val. value 0xb33f0000 oxoo 0xb33f0001 0x01 Addition/Subtraction add = {offset=&(t_ptr. value), type = 64, sym=t_val, addend=2} get_ptr = {offset=&(update. offset), type = 64, sym=t_ptr} update = {offset=0xb33f0001, type = COPY, sym=valptr} I t_ptr 0xb33f0001 t_val 3 valptr &t_val. value 0xb33f0000 0xb33f0001 Addition/Subtraction add = {offset=&(t_ptr. value), type = 64, sym=t_val, addend=2} get_ptr = {offset=&(update. offset), type = 64, sym=t_ptr} update = {offset=0xb33f0001, type = COPY, sym=valptr} t_ptr 0xb33f0001 t_val 3 ^ | valptr &t_val.value ' 0xb33f0000 0xb33f0001 Unconditional Branches How relocation entries get processed do { struct libnamejist *lnp = l->l_libname->next; while ( builtin_expect (Inp != NULL, 0)) { lnp->dont_free = 1 ; Inp = lnp->next; } TODO: - set l->l_prev if (I != &GL(dl_rtld_map)) _dl_relocate_object (I, l->l_scope, GLRO(dlJazy) ? RTLD_LAZY : 0, consider_profiling); I = l->l_prev; } while (I); exec libO lib n libc Id. so Unconditional Branches How relocation entries get processed do { struct libnamejist *lnp = l->l_libname->next; while ( builtin_expect (Inp != NULL, 0)) TODO" ■ I ■ r a - set l->l prev = I lnp->dont_free = 1 ; - r Inp = lnp->next; } if (I != &GL(dl_rtld_map)) _dl_relocate_object (I, l->l_scope, GLRO(dlJazy) ? RTLD_LAZY : 0, consider_profiling); I = l->l_prev; } while (I); Unconditional Branches How relocation entries get processed void _dl_relocate_object (struct linkjmap *l, struct r_scope_elem *scope[], int reloc_mode, int consider_profiling) { TODO: if (l->l_relocated) _ set |_>|_ P rev = I return ' - fix l->l_relocated ELF_DYNAMIC_RELOCATE (I, lazy, consider_profiling); /* Mark the object so we know this work has been done. */ l->l_relocated = 1; /* In case we can protect the data now that the relocations are done, do it. */ if (l->l_relro_size != 0) _dl_protect_relro (I); Unconditional Branches How relocation entries get processed void _dl_relocate_object (struct linkjmap *l, struct r_scope_elem *scope[], int reloc_mode, int consider_profiling) { TODO: if (l->l_relocated) _ set |_>|_ prev = , •- fix l->l_relocated •- set l->l relro size return; ELF_DYNAMIC_RELOCATE (I, lazy, consider_profiling); /* Mark the object so we know this work has been done. */ l->l_relocated = 1 ; /* In case we can protect the data now that the relocations are done, do it. */ if (l->l_relro_size != 0) _dl_protect_relro (I); = } Unconditional Branches How relocation entries get processed do { str^jt libnamejist *lnp = l->l_libname->next; fe (_builtin_expect (Inp != NULL, 0)) lnp->dont_free = 1; Inp = lnp->next; } TODO: - set l->l_prev = I - fix I ->l_re located •- set l->l relro size = if (I != &GL(dl_rtld_map)) _dl_relocate_object (I, l->l_scope, GLRO(dlJazy) ? RTLD_LAZY : 0, consider_profiling); I = l->l_prev; } while (I); Unconditional Branching: Todo-List Fix l->l_relocated Set l->l_prev = I Set l->l_relro_size = Set l->l_info[DT_RELA] = &next rel to process Fix l->l_info[DT_RELASZ] Unconditional Branching: Todo-List Fix l->l_relocated ■ {offset =&(l->l_buckets), type = RELATIVE, addend=0} ■ {offset =&(l->l_direct_opencount), type = RELATIVE, || addend=0} I ■ {offset =&(l->l_libname->next), type = RELATIVE, I addend=&(l->l relocated) + 4*sizeof(int)} Set l->l_prev = I I ■ {offset =&(l->l_prev), type = RELATIVE, addend=&l} 1 ■ Set l->l_relro_size = I ■ (etc) || ■ Set l->l_info[DT_RELA] = &next rel to process Fix l->l_info[DT_RELASZ] Unconditional Branching: Skiping remaining relocation entries for(; r < end; ++r) { ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff; elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], &map->l_versions[ndx], (void *) (l_addr + r->r_offset)); } end is stored on stack, set end to for branch {offset =&end, type = RELATIVE, addend=0} Conditional Branches Perform all branch bookkeeping IFUNC symbol only processed as function if st shndx != typedef struct { uint32_t st_name; unsigned char stjnfo; unsigned char st_other; uint16_t st_shndx; Elf64_Addr st_value; uint64_t st_size; } Elf64_Sym; .dynsym table Original dynsym Original dynsym 1 Original dynsym n Address tape head is pointing at Copy of tape head's value Arlrlrass n f nm\/inii£ sviy|'s x/ahiP IFUNC of gadget that returns Image: "Tree" Hernan D. Schlosman, from The Noun Project Conditional Branches setifunc = {offset=&(ifunc.shndx), type = COPY, sym=valptr} update = {offset=&end, type = 64, sym=ifunc} t_val valptr ifunc shndx 1 &t_val. value 1 4>returnQ 0xb33f0000 0x01 end xxxxxxxx Conditional Branches setifunc = {offset=&(ifunc.shndx) J type = COPY, sym=valptr} update = {offset=&end, type = 64, sym=ifunc} shndx valptr &t_val. value ifunc 1 &returnO 0xb33f0000 0x01 returnOQ called! end 3. If (shndx == 0) then end = SreturnO ELF Brainfuck T Ifor (; r< end; ++r) 1 ElfW(Half) ndx = vei elf_machine_rel (ms &map->l \ (void *) (T : (The easier of the two) "Jump backward to the matching [ unless the byte at the pointer is 0" Prepare for branch, set branch location to & of relocation entry after '[' ■ Set DT RELA (dynamic table) If tapevalue == 0, then end = &return0 ■ continues processing (&return > &rela entries) If tapevalue != zero, then end = ■ Stops processing relocaiton entries, branch executes (0 < &rela etries) ELF Brainfuck '[' ■ (Saved as an exercise for the reader) - (RTFC: elf-bf-tools on github) ■ Used eresi toolchain to inject/edit metadata ■ Injects metadata into r/w section ■ More bookkeeping is necessary to ensure executable works (not mentioned in talk) ■ Again, RTFC - elf-bf-tools repository on github ■ elf-bf-tools repository on github ■ https://github.com/bx/elf-bf-tools ■ https://github.com/bx/elf-bf-tools ■ https://github.com/bx/elf-bf-tools And Now For Something a Little More Practical... Look up library locations during runtime Address library stored in own link_map If we know where one link_map is.... ■ We know where they all are! exec lib [jibji_H libc < ► Id. so Flashback to the beginning of the talk: / 1 lb d IdUIW Ul dUUItJbbWb ■ GOT[1] = object's linkjmap struct 1—1 "~ ■ ' ■ ■!■ ii r^-i-i r ■ DT_PLTGOT ■ Location of GOT I Traversing linkjnap Structures I -To get linkmap->l_next->l_addr: I ■ Store &GOT+8 in a symbol Symbols: T symgot = {value:&G0T+8, size: 8, ...} I ■ Use the following relocation entries with that symbol 1 Relocation entries: get_exec_linkmap = {offset=&(symgot.value), type = COPY, sym=0} get_l_next = {offset=&(symgot.value), type = 64, sym=0, addend=0x18} deref_l_next = {offset=&(symgot.value), type = COPY, sym=0} get_l_addr = {offset=&(symgot.value), type = COPY, sym=0} Traversing linkjnap Structures?^ J) symgot = {value:&got_0x8, size: 8, ...} get_exec_linkmap = {offset=&(symgot.value), type = COPY, sym=0} get_l_next = {offset=&(symgot.value), type = 64, sym=0, addend=0x18} deref_l_next = {offset=&(symgot.value), type = COPY, sym=0} get_l_addr = {offset=&(symgot.value), type = COPY, sym=0} ■ get_exec_linkmap * &got+0x8 I Traversing link_map Structures symgot = {value :&got_0x8, size: 8, ...} get_exec_linkmap = {offset=&(symgot. value), type = COPY, sym=0 get_l_next = {offset=&(symgot.value), type = 64, sym=0, addend=0x18} deref_l_next = {offset=&(symgot.value), type = COPY, sym=0} get_l_addr = {offset=&(symgot.value), type = COPY, sym=0} get_exec_l i n kmap Traversing link_map Structures^ symgot = {value :&got_0x8, size: 8, ...} ^ I get_exec_linkmap = {offset=&(symgot. value), type = COPY, sym=0} get_l_next = {offset=&(symgot.value), type = 64, sym=0, addend=0x18} deref_l_next = {offset=&(symgot.value), type = COPY, sym=0} bet_l_addr = {offset=&(symgot.value), type = COPY, sym=0} II write 1 get_exec_linkmap &linkmap Traversing link_map Structures symgot = {value:&got_0x8, size: 8, ...} get_exec_linkmap = {offset=&(symgot.value) ) type = COPY, sym=0} [ get_l_next={offset=&(symgot.value),type = 64,sym=0, addend=0x18} deref_l_next = {offset=&(symgot.value), type = COPY, sym=0} m get_l_addr = {offset=&(symgot.value), type = COPY, sym=0} W get_l_next &linkmap calculate Traversing link_map Structures^ symgot = {value:&got_0x8, size: 8, ...} get_exec_linkmap = {offset=&(symgot.value) ) type = COPY, sym=0} ' get_l_next={offset=&(symgot.value),type = 64,sym=0, addend=0x18} deref_l_next = {offset=&(symgot.value), type = COPY, sym=0} m get_l_addr = {offset=&(symgot.value), type = COPY, sym=0} V 1 write get_l_next &linkmap->l_next Traversing link_map Structures symgot = {value:&got_0x8, size: 8, ...} get_exec_linkmap = {offset=&(symgot.value) J type = COPY, sym=oy\, get_l_next = {offset=&(symgot.value), type = 64, sym=0, addend=0x18} deref_l_next = {offset=&(symgot. value), type = COPY, sym=0} get_l_addr = {offset=&(symgot.value), type = COPY, sym=0} j deref I next calculate &l next Traversing link_map Structures symgot = {value:&got_0x8, size: 8, ...} get_exec_linkmap = {offset=&(symgot. value), type = COPY, sym=~^ get_l_next = {offset=&(symgot.value), type = 64, sym=0, addend=0x18} deref_l_next = {offset=&(symgot. value), type = COPY, sym=0} I get_l_addr = {offset=&(symgot.value), type = COPY, sym=0} 1 I write deref I next I next Traversing link_map Structures symgot = {value:&got_0x8, size: 8, ...} get_exec_linkmap = {offset=&(symgot.value) J type = COPY, sym^L get_l_next = {offset=&(symgot.value), type = 64, sym=0, addend=tl>4^ deref_l_next = {offset=&(symgot. value), type = COPY, sym=0} get_l_addr = {offset=&(symgot.value), type = COPY, sym=0} II write deref I next I next Traversing link_map Structures symgot = {value:&got_0x8, size: 8, ...} get_exec_linkmap = {offset=&(symgot.value) J type = COPY, sym^ get_l_next = {offset=&(symgot.value), type = 64, sym=0, adden deref_l_next = {offset=&(symgot.value), type = COPY, sym=0} get_l_addr = {offset=&(symgot. value), type = COPY, sym=0} get_l_addr I next calculate I Traversing link_map Structures symgot = {value:&got_0x8, size: 8, ...} get_exec_linkmap = {offset=&(symgot.value) J type = COPY, sym=0} get_l_next = {offset=&(symgot.value), type = 64, sym=0, addend=0 deref_l_next = {offset=&(symgot.value), type = COPY, sym=0} get_l_addr = {offset=&(symgot. value), type = COPY, sym=0} write symgot's value is now l->l_next->l_addr — base address of where ELF object is loaded 8> Demo Exploit Built backdoor into Ubuntu's inetutils v1 .8 ping Ping runs suid as root Given "-t " ■ Usage: -t, -type=TYPE send TYPE packets ■ Code: if(strcasecmp (, "echo") == 0) ... Goals: ■ Redirect call to strcasecmp to execl ■ Prevent call to setuid that drops root privledges ■ Work in presence of library randomization (ASLR) Image: "Remote Control" by Simon Child and "Television" by Piero borgo from The Noun Project ■ •Mr- INI Demo Exploit Goals: ■ Redirect call to strcasecmp to execl ■ Set strcasecmp's GOT entry to &execl ■ Prevent privlege drop ■ Set setuid's GOT entry to & retq instructios Lookup offset to execl and a retq instruction in glibc during metadata crafting time OFind base address of glibc @ runtime ■ Use link_map traversal trick! The rest is easy peasy Demo Exploit's Crafted Metadata Symbol table '.sym.p' contains 90 entries: Num: Value Size Type Bind Vis Ndx Name 0: 000000000060dff0 8 FUNC LOCAL DEFAULT UND Relocation section '.rela.p' at offset 0xf3a8 contains 14 entries: Offset Info Type Sym. Value Sym. Name + Addend m 00000060dfe0 00000060e9e0 00000060e9f0 00000060e9f8 00000060ea00 00000060eb40 00000060eb40 00000060eb40 00000060eb40 00000060eb40 00000060eb40 00000060eb40 00000060e028 00000060e218 002d00000006 R_X86_ 004e00000005 R_X86 004b00000005 R_X86_ 005100000005 R_X86_ 005600000005 R_X86 000000000005 R_X86 000000000001 R_X86 000000000005 R_X86 000000000001 R_X86 000000000005 R_X86 000000000005 R_X86 000000000001 R_X86 000000000001 R_X86 000000000008 R X86 64_GLOB_DAT 0000000000000000 _gmon_start_ + 64_COPY 000000000060e9e0 _progname + 000000000060e9f0 stdout + 000000000060e9f8 _progname_full + 64_COPY 64_COPY 64_COPY 64_COPY 64_64 64_COPY 64_64 64_COPY 64_COPY 64_64 64_64 64 RELATIVE 000000000060ea00 stderr + 0000000000000000 0000000000000018 0000000000000000 0000000000000018 0000000000000000 0000000000000000 00000000000be6e0 0000000000000000 0000000000401 dc2 Image: "Knitting Needles" by Connor Cesa and "Yarn" by Marie Coons and "Sweater" by Maurizio Fusillo from The Noun Project (demo) this slide intentionaly left blank) Thanks! Sergey Bratus Sean Smith 000 Inspirations: The grugq ERESI and Elfsh folks Mayhem I Skape Also: thanks to the N4>un Project Ipr many of the excellent graphics Questions?