diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/catcho.c | 204 | ||||
-rw-r--r-- | tools/sploit/sploit/__init__.py | 10 | ||||
-rw-r--r-- | tools/sploit/sploit/arch.py | 8 | ||||
-rw-r--r-- | tools/sploit/sploit/comm.py | 57 | ||||
-rw-r--r-- | tools/sploit/sploit/log.py | 36 | ||||
-rw-r--r-- | tools/sploit/sploit/main.py | 79 | ||||
-rw-r--r-- | tools/sploit/sploit/payload.py | 64 |
7 files changed, 181 insertions, 277 deletions
diff --git a/tools/catcho.c b/tools/catcho.c deleted file mode 100644 index 3426e50..0000000 --- a/tools/catcho.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * catcho - outputing text to the standard output - * - * Usage - * ----- - * catcho [options] - * - * Options - * ------- - * -m --mode MODE Set the initial text mode (Default: passthrough) - * -v --version Display program information and exit - * -V --verbose Duplicate stdout on stderr - * - * Modes - * ----- - * passthru / passthrough / normal / n - Input is copied to output verbatim. - * null / z - Input is dropped. - * hb - Input (interpreted as hex) is compressed to bytes on the output. - * bh - The opposite of hb. - * ib - Input (interpreted as bit-bytes) is compressed to bytes on the output. - * bi - The opposite of ib. - * - * Append 'f' to any mode (eg: bhf): Input is interpreted as '\n'-delimited - * filenames. File contents are filtered based on mode and sent to output. - * - * Switching modes - * --------------- - * When catcho receives signal SIGINT (ctrl-C from the terminal, for example), - * any pending input/output is flushed and catcho then interprets the input - * as a mode name terminated by a '\n'. Once the input provides a string\n, - * it switches the desired mode and input processing resumes. - * - * Exiting catcho - * -------------- - * Since SIGINT is used for mode switching, the 'normal' way to terminate - * catcho is by sending it an End-Of-File byte on its input. (ctrl-D on your - * terminal) - */ - -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <getopt.h> -#include <signal.h> - -#define LAST_STR_CHAR(s) (s[strlen(s)-1]) - -/* modes */ -enum mode -{ - N, - Z, - HB, - BH, - IB, - BI -}; - -/* global state */ -static enum mode current_mode = N; -static int current_mode_f = 0; -static int current_verbose = 0; - -static size_t ccfwrite(const void *ptr, size_t size, size_t nmemb) -{ - size_t ret; - ret = fwrite(ptr, size, nmemb, stdout); - - if (current_verbose) - fwrite(ptr, size, nmemb, stderr); - - return ret; -} - -static void sigint(int sig) -{ - (void)sig; - ccfwrite("in signal handler\n", 18, 1); -} - -static int set_mode(const char *mode) -{ - if (!mode) - return 0; - - if (!strcmp(mode, "passthru") || // these don't support 'f' suffix - !strcmp(mode, "passthrough") || - !strcmp(mode, "normal")) - { - current_mode = N; - current_mode_f = 0; - } - else if (!strcmp(mode, "null")) // this doesn't support 'f' suffix - { - current_mode = Z; - current_mode_f = 0; - } - else if (!strncmp(mode, "n", 1)) - { - current_mode = N; - current_mode_f = (LAST_STR_CHAR(mode) == 'f'); - } - else if (!strncmp(mode, "z", 1)) - { - current_mode = Z; - current_mode_f = (LAST_STR_CHAR(mode) == 'f'); - } - else if (!strncmp(mode, "hb", 2)) - { - current_mode = HB; - current_mode_f = (LAST_STR_CHAR(mode) == 'f'); - } - else if (!strncmp(mode, "bh", 2)) - { - current_mode = BH; - current_mode_f = (LAST_STR_CHAR(mode) == 'f'); - } - else if (!strncmp(mode, "ib", 2)) - { - current_mode = IB; - current_mode_f = (LAST_STR_CHAR(mode) == 'f'); - } - else if (!strncmp(mode, "bi", 2)) - { - current_mode = BI; - current_mode_f = (LAST_STR_CHAR(mode) == 'f'); - } - else - { - fprintf(stderr, "Mode string '%s' not recognized\n", mode); - return 0; - } - - return 1; -} - -void show_version(const char *name) -{ - printf("%s version 1.0\n", name); -} - -int main(int argc, char **argv) -{ - /* - * handling command-line arguments: - * i'm going to leave opterr alone (defaults to non-zero) - * \this causes getopt to print its own error messages to - * stderr if things go wrong, which is fine. - */ - - const char *shortopts = "m:vV"; - - const struct option longopts[] = { - { "mode", required_argument, NULL, 'm' }, - { "version", no_argument, NULL, 'v' }, - { "verbose", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 } - }; - - int opt; - - while ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) - { - switch (opt) - { - case '?': - return 1; - - case 'm': - if (!set_mode(optarg)) - return 1; - break; - - case 'v': - show_version(argv[0]); - return 0; - - case 'V': - current_verbose = 1; - break; - } - } - - /* setup signals (mode switch) */ - signal(SIGINT, &sigint); - - - char memes[4]; - fread(memes, 4, 1, stdin); - switch (current_mode) - { - case N: printf("NORMAL\n"); break; - case Z: printf("NULL\n"); break; - case HB: printf("hex to bytes\n"); break; - case BH: printf("bytes to hex\n"); break; - case IB: printf("bits to bytes\n"); break; - case BI: printf("bytes to bits\n"); break; - } - - printf("file mode: %i\n", current_mode_f); - printf("verbose mode: %i\n", current_verbose); - - return 0; -} diff --git a/tools/sploit/sploit/__init__.py b/tools/sploit/sploit/__init__.py index 5082cfa..9e637df 100644 --- a/tools/sploit/sploit/__init__.py +++ b/tools/sploit/sploit/__init__.py @@ -1,2 +1,8 @@ -__all__ = ["log","comm","until","arch","mem"] -from sploit import log, comm, until, arch, mem +from sploit import ( + arch, + comm, + log, + mem, + payload, + until, +) diff --git a/tools/sploit/sploit/arch.py b/tools/sploit/sploit/arch.py index d75bbda..ce88111 100644 --- a/tools/sploit/sploit/arch.py +++ b/tools/sploit/sploit/arch.py @@ -5,24 +5,24 @@ def itob(i, signed=False): return i.to_bytes(arch.wordsize, arch.endianness, signed=signed) class Arch: - def __init__(self, wordsize, endianness, alignment, nop): + def __init__(self, wordsize, endianness, alignment, nopcode): self.wordsize = wordsize self.endianness = endianness self.alignment = alignment - self.nop = nop + self.nopcode = nopcode archx86 = Arch( wordsize = 4, endianness = "little", alignment = 16, - nop = b'\x90' + nopcode = b'\x90' ) archx86_64 = Arch( wordsize = 8, endianness = "little", alignment = 16, - nop = b'\x90' + nopcode = b'\x90' ) arch = archx86_64 diff --git a/tools/sploit/sploit/comm.py b/tools/sploit/sploit/comm.py index 7d6cd8c..604045c 100644 --- a/tools/sploit/sploit/comm.py +++ b/tools/sploit/sploit/comm.py @@ -4,35 +4,42 @@ import os import sys import select -from sploit.log import log +from sploit.log import * from sploit.until import bind class Comm: logonread = True + logonwrite = False flushonwrite = True def __init__(self, backend): self.back = backend + def shutdown(self): + self.back.stdout.close() + def read(self, size): data = os.read(self.back.stdin.fileno(), size) if(data == b''): raise BrokenPipeError('Tried to read on broken pipe') - if self.logonread : log(data) + if self.logonread : ilog(data, file=sys.stdout, color=NORMAL) return data def readline(self): data = self.back.stdin.readline() if(data == b''): raise BrokenPipeError('Tried to read on broken pipe') - if self.logonread : log(data) + if self.logonread : ilog(data, file=sys.stdout, color=NORMAL) return data def readall(self): data = b'' - for line in self.back.stdin: - log(line) - data += line + try: + for line in self.back.stdin: + if self.logonread : ilog(line, file=sys.stdout, color=NORMAL) + data += line + except KeyboardInterrupt: + pass return data def readuntil(self, pred, /, *args, **kwargs): @@ -40,12 +47,14 @@ class Comm: pred = bind(pred, *args, **kwargs) l = self.logonread self.logonread = False - while(True): - data += self.read(1) - if(pred(data)): - break - self.logonread = l - if self.logonread : log(data) + try: + while(True): + data += self.read(1) + if(pred(data)): + break + finally: + self.logonread = l + if self.logonread : ilog(data, file=sys.stdout, color=NORMAL) return data def readlineuntil(self, pred, /, *args, **kwargs): @@ -60,12 +69,13 @@ class Comm: def write(self, data): self.back.stdout.write(data) if self.flushonwrite : self.back.stdout.flush() + if self.logonwrite : ilog(data, file=sys.stdout, color=ALT) def writeline(self, data): self.write(data + b'\n') def interact(self): - print("<--Interact Mode-->") + ilog("<--Interact Mode-->") stdin = sys.stdin.buffer os.set_blocking(self.back.stdin.fileno(), False) os.set_blocking(stdin.fileno(), False) @@ -79,9 +89,11 @@ class Comm: if(data == b''): break write(data) + def writeinput(write): + ilog(write, file=sys.stdout, color=NORMAL) readtable = { stdin.fileno() : lambda : readall(stdin.readline, self.write), - self.back.stdin.fileno() : lambda : readall(self.back.stdin.readline, log) + self.back.stdin.fileno() : lambda : readall(self.back.stdin.readline, writeinput) } readtable[self.back.stdin.fileno()]() while(not brk): @@ -97,17 +109,17 @@ class Comm: break os.set_blocking(self.back.stdin.fileno(), True) os.set_blocking(stdin.fileno(), True) - print("<--Interact Mode Done-->") + ilog("<--Interact Mode Done-->") class Process: def __init__(self, args): - print(f"Running: {' '.join(args)}") + ilog(f"Running: {' '.join(args)}") self.proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, preexec_fn=lambda : os.setpgrp()) - print(f"PID: {self.proc.pid}") + ilog(f"PID: {self.proc.pid}") self.stdin = self.proc.stdout self.stdout = self.proc.stdin @@ -116,8 +128,8 @@ class Process: if(self.proc.poll() != None): return try: - print("Waiting on Target Program to End...") - print("Press Ctrl+C to Forcefully Kill It...") + ilog("Waiting on Target Program to End...") + ilog("Press Ctrl+C to Forcefully Kill It...") self.proc.wait() except KeyboardInterrupt: self.proc.kill() @@ -135,11 +147,11 @@ class Pipes: 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) + ilog("Waiting on Target to Connect...", file=sys.stdout) + ilog(f"<{self.pathin} >{self.pathout}", file=sys.stdout) self.stdout = open(self.pathin, "wb") self.stdin = open(self.pathout, "rb") - print("Connected!") + ilog("Connected!") def __del__(self): try: @@ -149,4 +161,3 @@ class Pipes: 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) - diff --git a/tools/sploit/sploit/log.py b/tools/sploit/sploit/log.py index cd9c3be..823b252 100644 --- a/tools/sploit/sploit/log.py +++ b/tools/sploit/sploit/log.py @@ -1,6 +1,32 @@ -ENCODING = '' -def log(s): - if ENCODING != '': - s = s.decode(ENCODING) - print(s) +import codecs +import sys +# https://docs.python.org/3/library/codecs.html#standard-encodings +ENCODING = None + +ERROR = 31 +WARNING = 33 +STATUS = 32 +NORMAL = 0 +ALT = 90 + +def enc_value(value, enc): + if type(value) is bytes: + if enc is not None: + value = codecs.encode(value, enc) + elif ENCODING is not None: + value = codecs.encode(value, ENCODING) + value = str(value)[2:-1] # strip b'' + return str(value) + +def generic_log(*values, sep, end, file, flush, enc, color): + string = sep.join([ enc_value(x, enc) for x in values ]) + print(f'\033[{color}m{string}\033[0m', end=end, file=file, flush=flush) + +# For library internal use +def ilog(*values, sep=' ', end='\n', file=sys.stderr, flush=True, enc=None, color=STATUS): + generic_log(*values, sep=sep, end=end, file=file, flush=flush, enc=enc, color=color) + +# For external use in user script (via print = elog) +def elog(*values, sep=' ', end='\n', file=sys.stdout, flush=True, enc=None, color=ALT): + generic_log(*values, sep=sep, end=end, file=file, flush=flush, enc=enc, color=color) diff --git a/tools/sploit/sploit/main.py b/tools/sploit/sploit/main.py index 6404786..b0fe3eb 100644 --- a/tools/sploit/sploit/main.py +++ b/tools/sploit/sploit/main.py @@ -1,59 +1,60 @@ -import argparse +from argparse import ArgumentParser, REMAINDER +import gc import tempfile import traceback from sploit.comm import * +from sploit.log import * + +def print_banner(color, line1='', line2='', line3=''): + ilog() + ilog(' ░▒█▀▀▀█░▒█▀▀█░▒█░░░░▒█▀▀▀█░▀█▀░▀▀█▀▀ ', end='', color=ALT) + ilog(line1, color=ALT) + ilog(' ░░▀▀▀▄▄░▒█▄▄█░▒█░░░░▒█░░▒█░▒█░░░▒█░░ ', end='', color=color) + ilog(line2, color=ALT) + ilog(' ░▒█▄▄▄█░▒█░░░░▒█▄▄█░▒█▄▄▄█░▄█▄░░▒█░░ ', end='', color=ALT) + ilog(line3, color=ALT) + ilog() 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') + parser = ArgumentParser(description='Execute Sploit script against target') + parser.add_argument('script', help='Exploit script to run') + parser.add_argument('target', nargs=REMAINDER, help='Target program to exploit') args = parser.parse_args() - try: - 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) - except KeyboardInterrupt: - pass + if(len(args.target)>0): + target(args.script, args.target) + else: + pipe(args.script) -def daemon(script): - print("Running in Pipe Daemon Mode...") +def pipe(script): + print_banner(ERROR, line3='Pipe Mode') with tempfile.TemporaryDirectory() as tmpdir: while(True): try: p = Pipes(tmpdir) except KeyboardInterrupt: break - try: - runscript(script, Comm(p)); - except KeyboardInterrupt: - pass - except: - traceback.print_exc() + runscript(script, Comm(p)) del p -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))); + print_banner(STATUS, line3='Subprocess Mode') + runscript(script, Comm(Process(target))) def runscript(script, comm): - print("Running Script...") - code = compile(open(script).read(), script, 'exec') - exec(code, {'io': comm}) - print("Script Finished!") - comm.readall() + try: + ilog("Running Script...") + code = compile(open(script).read(), script, 'exec') + exec(code, {'io': comm, 'print': elog}) + ilog("Script Finished!") + comm.shutdown() + comm.readall() + return + except KeyboardInterrupt: + pass + except: + ilog(traceback.format_exc(), end='', color=ERROR) + finally: + gc.collect() + ilog("Script Ended Early!", color=WARNING) diff --git a/tools/sploit/sploit/payload.py b/tools/sploit/sploit/payload.py new file mode 100644 index 0000000..49e0c04 --- /dev/null +++ b/tools/sploit/sploit/payload.py @@ -0,0 +1,64 @@ +from sploit.arch import arch, itob +from sploit.mem import Symtbl + +# Users can set this to the (absolute) address of a 'ret' ROP gadget. Some +# features may require it. +RETGADGET : int = None + +class Placeholder(bytearray): + def __init__(self, text='_unnamed_'): + self += bytearray(itob(0)) + self.text = text + +class Payload: + def __init__(self, size=0, base=0, **kwargs): + self.payload = b'' + self.size = size + self.alignstart = None + self.tab = Symtbl(base=base, **kwargs) + + def __len__(self): + return len(self.payload) + + def __getattr__(self, sym): + return getattr(self.tab, sym) + + def data(self, x, sym='_'): + off = len(self) + self.payload += x + setattr(self.tab, sym, off) + return getattr(self.tab, sym) + + def value(self, x, sym='_', signed=False): + return self.data(itob(x, signed=signed), sym=sym) + + def ret(self, x, sym='_'): + self.align() + return self.value(x, sym=sym) + + def stuff(self, x, size, sym='_', *, explain=''): + if size >= 0: + if (size := size / len(x)) == int(size): + if size == 0 or not isinstance(x, Placeholder): + return self.data(x * int(size), sym=sym) + + raise Exception(explain+"Can not stuff payload: " + f"Placeholder for {x.text} detected") + raise Exception(explain+"Can not stuff payload: " + "Element does not divide the space evenly") + raise Exception(explain+"Can not stuff payload: " + "Available space is negative") + + def pad(self, x=None, sym='_'): + size = self.size - len(self) + return self.stuff((x or arch.nopcode), size, sym=sym, + explain='Error padding payload: ') + + def align(self, x=None, sym='_'): + if self.alignstart is None: + self.alignstart = len(self) + + retgad = (itob(RETGADGET) if RETGADGET else Placeholder('ret gadget')) + size = (self.alignstart - len(self)) % arch.alignment + return self.stuff((x or retgad), size, sym=sym, + explain='Error aligning payload: ') |