summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/catcho.c204
-rw-r--r--tools/sploit/sploit/__init__.py10
-rw-r--r--tools/sploit/sploit/arch.py8
-rw-r--r--tools/sploit/sploit/comm.py57
-rw-r--r--tools/sploit/sploit/log.py36
-rw-r--r--tools/sploit/sploit/main.py79
-rw-r--r--tools/sploit/sploit/payload.py64
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: ')