Upgrade to Pro — share decks privately, control downloads, hide ads and more …

blinkroot - HITCON 2015 writeup

Dhaval Kapil
October 12, 2017

blinkroot - HITCON 2015 writeup

Dhaval Kapil

October 12, 2017
Tweet

More Decks by Dhaval Kapil

Other Decks in Education

Transcript

  1. Challenge overview read(0, &global_arr, 0x400); // char global_arr[0x400]; close(0); close(1);

    close(2); int index = *(int64_t *)global_arr; global_arr[index] = *(int64_t *)(global_arr + 8); puts(global_arr[16]);
  2. ELF Symbols typedef struct { uint32_t st_name; /* Offset into

    the corresponding table's string table */ unsigned char st_info; /* Symbol type and binding attributes */ unsigned char st_other; /* Symbol visibility */ uint16_t st_shndx; /* Each symbol is defined for a particular section */ Elf64_Addr st_value; /* Either an address or offset */ uint64_t st_size; /* Size of the actual reference */ } Elf64_Sym; /* A single entry in any of the symbol table */
  3. ELF Symbols • .symtab - Used mainly for debugging and

    linking. Not needed for program execution, can be stripped. Each entry’s st_name is an offset in .strtab • .dynsym - Contains references to external sources (shared libraries). Used by the dynamic linker to resolve references at run time. Each entry’s st_name is an offset into .dynstr
  4. ELF Relocations • Relocation entries help in patching binaries during

    dynamic linking typedef struct { Elf64_Addr r_offset; // The address that requires to be relocated uint64_t r_info; // Index into symbol table + type of relocation } Elf64_Rel;
  5. Dynamic Section • Dynamic section entries list out the various

    dynamic sections entries present in ELF. typedef struct { Elf32_Sword d_tag; // What kind of section is this? STRTAB, SYMTAB, etc. union { Elf32_Word d_val; Elf32_Addr d_ptr; } d_un; } Elf32_Dyn;
  6. Dynamic linking process • Dynamic linker is loaded • It

    handles it’s own relocation • Loads needed shared libraries (recursively) • For every ELF file loaded, it creates an array of ‘link_map’ structures. (The first entry being of the executable binary itself)
  7. link_map struct struct link_map { ElfW(Addr) l_addr; /* Base 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 + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM]; /* Holds pointers to dynamic section entries of string table(l_info[DT_STRTAB], symbol table (l_info[DT_SYMTAB]), relocation table (l_info[DT_JMPREL]), etc */ ... };
  8. Global Offset Table (Each entry is 8 byte) GOT[0] -

    points to the ‘dymanic section’ of the executable GOT[1] - points to the first element of link_map structure array GOT[2] - points to _dl_runtime_resolve() GOT[3] .. onwards relocation entries for external functions
  9. Dynamic linking of external function ‘func’ • Corresponding ‘func@plt’ is

    called • [func@plt] ‘func@plt’ jumps to GOT entry for func • [func@plt] Initially GOT entry points to next instruction in ‘func@plt’ • [func@plt] Pushes an offset (index of ‘func’ in .rel.plt) on stack • [func@plt] Jump to a common stub in ‘plt’ section • [common@plt] Pushes GOT[1] (ptr to link_map) on stack • [common@plt] Jump to GOT[2] (__dl_runtime_resolve()) Stack has the relocation entry offset and address of link_map structure array. __dl_runtime_resolve() calls _dl_fixup() with these two as parameters
  10. _dl_fixup() • Retrieves symbol table, string table and relocation table

    from the first link map’s l->l_info. • Retrieves the particular relocation entry in the table using the relocation offset • Relocation entry gives symbol table entry, which gives offset into the string table for the name of the symbol • Computes the address to be patched as: l->l_addr (the load address for the ELF) + reloc->r_offset (the address within the ELF to be patched) • Search for the symbol by name in shared libraries and patch the corresponding address calculated. • Also return the address that has been resolved
  11. Trick • Craft symbol entry such that flow goes into

    ‘else’ • The target value written is l->l_addr + sym->st_value • Make symbol entry point to some address already resolved by libc, say ‘read’ address in GOT (such that sym->st_value = address of read) • Make relocation entry point to appropriate index into symbol table containing ‘read’ • Setup l_addr with (offset_system - offset_read) so that instead of read’s address, system’s address is returned. • puts will be resolved to system!