shadowbrokers-exploits/windows/fuzzbunch/iohandler.py
2017-04-14 11:45:07 +02:00

860 lines
27 KiB
Python

"""
IO Handling classes
The IO Handler works as a wrapper around all user input and output.
Theoretically, to change the way Fuzzbunch looks should only require changing
this file. Specialized print functions provide a template-like facility.
"""
import sys
import exception
from util import variable_replace
MAX_OUTPUT_ROWS = 5
MAX_PROMPT_ECHO_LEN = 50
try:
try:
import readline
except ImportError:
import pyreadline as readline
HAVE_READLINE = True
except:
HAVE_READLINE = False
mswindows = (sys.platform == "win32")
__all__ = ["IOhandler"]
"""
Valid Attributes : none, bold,
faint, italic,
underline, blink,
fast, reverse,
concealed
Valid Colors : grey red
green yellow
blue magenta
cyan white
"""
COLORIZEMAP = {"[-]" : {"fg" : "red", "attr" : "bold"},
"[+]" : {"fg" : "green", "attr" : "bold"},
"[!]" : {"fg" : "red", "attr" : "bold"},
"[*]" : {"fg" : "green", "attr" : "bold"},
"[?]" : {"fg" : "blue", "attr" : "bold"}}
VMAP = {"[-]" : {"fg" : "magenta", "attr" : "bold"},
"[+]" : {"fg" : "magenta", "attr" : "bold"},
"[!]" : {"fg" : "magenta", "attr" : "bold"},
"[*]" : {"fg" : "magenta", "attr" : "bold"},
"[?]" : {"fg" : "magenta", "attr" : "bold"}}
class DevNull:
def write(self, *ignore):
pass
def close(self):
pass
def flush(self):
pass
def truncate(string, length=MAX_PROMPT_ECHO_LEN):
return string if (len(string) <= length) else ("%s... (plus %d characters)" % (string[:length], len(string) - length))
class IOhandler:
"""Handle I/O for fuzzbunch commands"""
def __init__(self, stdin=None,
stdout=None,
use_raw=1,
noprompt=False,
completekey='tab',
enablecolor=True,
history=4096):
"""
@param stdin
@param stdout
@param use_raw
@param noprompt Do we want to prompt for values upon plugin execution?
@param completekey Command completion
"""
import sys
if stdin is not None:
self.stdin = stdin
else:
self.stdin = sys.stdin
if stdout is not None:
self.stdout = stdout
else:
self.stdout = sys.stdout
self.stderr = self.stdout
self.logout = DevNull()
self.noprompt = noprompt
self.raw_input = use_raw
self.completekey = completekey
self.havecolor = False # Are we in color mode?
self.enablecolor = enablecolor # Do we want to permanently enable color. Change to False to disable
self.colormode = "ansi"
self.colormap = COLORIZEMAP
self.historysize = history
def setlogfile(self, logfile):
self.logout.close()
try:
self.logout = open(logfile, "w")
except:
self.logout = DevNull()
def setcolormode(self, isEnabled):
"""Switch to enable or disable color output"""
self.enablecolor=isEnabled
def switch(self):
if self.colormap == COLORIZEMAP:
self.colormap = VMAP
else:
self.colormap = COLORIZEMAP
def pre_input(self, completefn):
if self.raw_input:
if HAVE_READLINE:
import atexit
self.old_completer = readline.get_completer()
# Fix Bug #3129: Limit the history size to consume less memory
readline.set_history_length(self.historysize)
readline.set_completer(completefn)
readline.parse_and_bind(self.completekey+": complete")
try:
readline.read_history_file()
except IOError:
pass
atexit.register(readline.write_history_file)
self.havecolor = True
if mswindows and self.enablecolor:
self.cwrite = readline.GetOutputFile().write_color
else:
self.cwrite = self.stdout.write
def post_input(self):
if self.raw_input:
if HAVE_READLINE:
readline.set_completer(self.old_completer)
self.havecolor = False
self.cwrite = None
"""
User input handling
"""
def get_input_lines(self, prompt):
done = False
lines = []
while not done:
line = self.get_input(prompt)
if line == 'EOF':
done = True
else:
lines.append(line)
return ''.join(lines)
def get_input_line(self, prompt):
line = self.get_input(prompt)
if line == 'EOF':
return ''
else:
return line.strip()
def get_input(self, prompt):
if self.raw_input:
try:
line = raw_input(prompt)
except (EOFError, KeyboardInterrupt):
line = 'EOF'
else:
self.write(prompt)
self.flush()
line = self.stdin.readline()
if not len(line):
line = 'EOF'
else:
line = line[:-1]
return line
def prompt_user(self, msg, default=None, params=None, gvars=None):
if self.noprompt:
return variable_replace(default, gvars) # Fix a small bug in 3.2.0
if default is not None:
# If we pass a default, interepret any variables (marked with '$')
interpreted_default = variable_replace(default, gvars)
p = "[?] %s [%s] : " % (msg, truncate(interpreted_default))
else:
# No default, so empty string
p = "[?] %s : " % msg
interpreted_default = ""
if self.havecolor and self.enablecolor:
p = self.colorize(p)
line = self.get_input(p)
# Check the user input
if line.upper() in ("EOF",):
self.newline()
if line.upper() in ("EOF", "Q", "QUIT"):
raise exception.PromptErr, "Aborted by user"
if line.upper() in ("?", "HELP"):
raise exception.PromptHelp, "No help available"
# Retrieve the line, and replace any '$' vars with their values
line = variable_replace(line, gvars)
if not len(line.strip()):
# If line is empty, just use the default
line = interpreted_default
# If it's a choice, figure out which value they chose
if params:
try:
index = int(line)
line = params[index][0]
except (IndexError, ValueError):
raise exception.CmdErr, "Invalid input"
return line
def prompt_yn(self, msg, default="Yes", params=None, gvars=None):
if default.lower() not in ["yes", 'y', 'no', 'n']:
default = 'No'
yn = self.prompt_user(msg, default, params=params, gvars=gvars).lower()
if yn == 'yes' or yn == 'y':
return True
return False
def prompt_continue(self):
line = self.prompt_user("Execute Plugin?", "Yes")
if line.lower() not in ("yes", "y"):
raise exception.CmdErr, "Execution Aborted"
return
def prompt_confirm_redir(self):
line = self.prompt_user("Press Any Key To Continue")
del line
return
def prompt_runsubmode(self, text):
line = self.prompt_user(text, "Yes")
if line.lower() not in ("yes", "y"):
return False
else:
return True
"""
Basic user output handling
"""
def flush(self):
self.logout.flush()
self.stdout.flush()
def log(self, line):
line += "\n"
self.logout.write(line)
def write(self, line):
self.log(line)
line += "\n"
# Fix bug #2910
if self.havecolor and self.enablecolor:
try:
self.cwrite(self.colorize(line))
except LookupError:
# We failed to print in color. This is a problem looking up the encoding
# Permanently disable color and continue
self.havecolor = False
self.enablecolor = False
self.stdout.write(line)
else:
self.stdout.write(line)
self.flush()
def newline(self):
self.write("")
def print_error(self, line):
self.write("[-] " + line)
def print_success(self, line):
self.write("[+] " + line)
def print_warning(self, line):
self.write("[!] " + line)
def print_msg(self, line):
self.write("[*] " + line)
"""
Output formatting help
"""
def get_single_col_max_width(self, data, colnum):
# Min len = 6
maxlen = 6
for row in data:
for i,col in enumerate(row):
if i == colnum:
maxlen = max(maxlen, len(col))
return maxlen
def get_column_max_width(self, data):
maxlens = list(0 for i in range(0, len(data[0])))
for row in data:
for i,col in enumerate(row):
maxlens[i] = max(maxlens[i], len(col), 1)
return maxlens
def print_headingline(self, msg):
self.newline()
self.write(msg)
self.write("=" * len(msg))
self.newline()
def print_vheading(self, vector):
self.newline()
for v in vector:
self.write(v)
self.newline()
def vprint(self, fmt, vector, max_str_len=None):
for v in vector:
if max_str_len:
if isinstance(v, tuple):
v = tuple(truncate(field, max_str_len) for field in v)
else:
v = truncate(field, max_str_len)
self.write(fmt % v)
self.newline()
def makeplural(self, name, count):
if count < 2:
return name
if name.endswith("h"):
return name + "es"
elif name.endswith("s"):
return name
else:
return name + "s"
"""
Color support
"""
def colorize(self, line):
for pattern, attrs in self.colormap.items():
plen = len(pattern)
index = line.find(pattern)
if self.colormap == VMAP:
pattern = "[TF]"
if index != -1:
r = index + plen
line = (line[:index] +
self.color(**attrs) +
pattern +
self.color() +
line[r:])
return line
return line
def color(self, fg=None, bg=None, attr=None):
if self.colormode != "ansi":
return ""
attrs = 'none bold faint italic underline blink fast reverse concealed'
colors = 'grey red green yellow blue magenta cyan white'
attrs = dict((s,i) for i,s in enumerate(attrs.split()))
colors = dict((s,i) for i,s in enumerate(colors.split()))
fgoffset, bgoffset = 30,40
cmd = ["0"]
if fg in colors:
cmd.append("%s" % (colors[fg] + fgoffset))
if bg in colors:
cmd.append("%s" % (colors[bg] + bgoffset))
if attr:
for a in attr.split():
if a in attrs:
cmd.append("%s" % attrs[a])
return "\033[" + ";".join(cmd) + "m"
"""
Specialized output routines
"""
# OK because it's readline
def print_history(self, args):
self.newline()
for index,item in args['items']:
self.write("%4d %s" % (index, item))
self.newline()
def print_cmd_list(self, args):
self.print_headingline(args['title'])
cmds = args['commands']
cmds.insert(0, ("Command", "Description"))
cmds.insert(1, ("-------", "-----------"))
self.vprint(" %-15s %s", cmds)
def print_banner(self, args):
self.write(args['banner'])
self.write("--[ Version %s" % args['version'])
for count, type in args['stats']:
self.write(" * %d %s" % (count, self.makeplural(type, count)))
self.newline()
def print_opensessions(self, args):
self.newline()
for session in enumerate(args['sessions']):
self.print_warning("Session item %d (%s) has open contract" % session)
self.newline()
def print_usage(self, arg):
self.write("Usage: %s\n" % arg[0])
self.write("\n".join(arg[1:]))
def print_module_lists(self, args):
category = args['module']
modules = args['plugins']
self.print_headingline("Plugin Category: %s" % category)
modules.sort()
modules.insert(0, ("Name", "Version"))
modules.insert(1, ("----", "-------"))
widths = self.get_column_max_width(modules)
self.vprint(" %%-%ds %%s" % (widths[0] + 4), modules)
def print_module_types(self, args):
self.print_headingline("Plugin Categories")
modules = args['modules']
modules.sort()
modules.insert(0, ("Category", "Active Plugin"))
modules.insert(1, ("--------", "-------------"))
widths = self.get_column_max_width(modules)
self.vprint(" %%-%ds %%s" % (widths[0] + 4), modules)
def print_session_item(self, args):
self.print_vheading(("Name: %s" % args['name'],
"Status: %s" % args['status']))
for type,info in args['info']:
self.write("%s:\n" % type)
if info:
params = [x[:2] for x in dict(info.get_paramlist()).values()]
params.insert(0, ("Name", "Value"))
params.insert(1, ("----", "-----"))
widths = self.get_column_max_width(params)
self.vprint(" %%-%ds %%s" % (widths[0] + 4), params, MAX_PROMPT_ECHO_LEN)
def print_session_items(self, args):
items = args['items']
self.print_headingline("Session History")
if not items:
self.write(" *Empty*\n")
self.newline()
else:
_list = [("Index", "Name", "Status"),
("-----", "----", "------")]
for index,item in enumerate(items):
_list.append((str(index), item[0], item[1]))
widths = self.get_column_max_width(_list)
fmt = "%%7s %%-%ds %%s" % (widths[1] + 4)
self.vprint(fmt, _list, MAX_PROMPT_ECHO_LEN)
# Move to table
def is_table_row_empty(self, v):
ncols = len(v)
for i in range(0, ncols):
if len(v[i]) > 0 and len(v[i][0]) > 0:
return False
return True
# Move to table
def get_table_row(self, vector):
ncols = len(vector)
while not self.is_table_row_empty(vector):
line = []
for i in range(0, ncols):
try:
val = vector[i].pop(0)
except IndexError:
val = ""
line.append(val)
yield line
# Move to Table
def print_row(self, widths, data):
fmt = "%%-%ds " * len(widths) % tuple(widths)
vectors = []
for i,width in enumerate(widths):
vector = []
col = data[i]
printed_rows = 0
while col:
if printed_rows > MAX_OUTPUT_ROWS:
vector.append("... (plus %d more lines)" % (len(col) / widths[i]))
break
vector.append(col[:widths[i]])
col = col[widths[i]:]
printed_rows += 1
vectors.append(vector)
for line in self.get_table_row(vectors):
self.write(fmt % tuple(line))
# Move to Table
def print_table(self, widths, heading, params):
sep = tuple(['-' * len(word) for word in heading[0]])
heading.append(sep)
heading.extend(params)
for d in heading:
self.print_row(widths, d)
self.newline()
def print_set_names(self, args):
self.print_headingline("Module: %s" % args['title'])
params = args['vars']
if not params:
self.write(" *Empty")
self.newline()
else:
widths = [self.get_single_col_max_width(params, 0), 50]
self.print_table(widths, [("Name", "Value")], params)
def print_exe_set_names(self, args):
if not args['session']:
return self.print_set_names(args)
self.print_headingline("Module: %s" % args['title'])
params = args['vars']
session = args['session']['params']
print_params = []
for param in params:
if session.get(param.name):
realval = truncate(session.get(param.name))
redirval = truncate(param.value)
else:
realval = truncate(param.value)
redirval = ""
print_params.append((param.name, realval, redirval))
widths = self.get_column_max_width(print_params)
widths[1] = max(widths[1], len("Set Value"))
widths[2] = max(widths[2], len("Redirected Value"))
self.print_table(widths, [("Name", "Set Value", "Redirected Value")], print_params)
def print_set_attributes(self, title, param, attribs):
self.print_vheading(("Module: %s" % title,
"Parameter: %s" % param))
attribs.insert(0, ("Parameter Attribute", "Value"))
attribs.insert(1, ("-------------------", "-----"))
widths = self.get_column_max_width(attribs)
fmt = " %%-%ds %%s" % (widths[0] + 4)
self.vprint(fmt, attribs)
def print_set_choices(self, choices):
choices.insert(0, ("Parameter Options", "Description"))
choices.insert(1, ("-----------------", "-----------"))
widths = self.get_column_max_width(choices)
fmt = " %%-%ds %%s" % (widths[0] + 4)
self.vprint(fmt, choices)
def print_sorted_vals(self, sorted_vals, vals):
i = 0
for cat in ("Contract", "History", "Other"):
self.write("\n---[ %s\n" % cat)
for index in sorted_vals[cat]:
(val, name, label, info) = vals[index]
self.write(" %d) %s (%s)" % (i, val, name))
i += 1
if len(sorted_vals["Contract"]):
default = 0
else:
default = i - 1
return i,default
def print_param_list(self, param_list):
self.write("")
widths = self.get_column_max_width(param_list)
widths[0] = max(widths[0], len('Name'))
widths[1] = max(widths[1], len('Value'))
widths[2] = 50
self.print_table(widths, [('Name', 'Value', 'Description')], param_list)
def print_touch_info(self, args):
touchlist = args['touchlist']
self.print_headingline("Touch List")
if not touchlist:
self.write(" *Empty*\n")
self.newline()
else:
hdrlist = []
hdrlist.append(("Index", "Name", "Description"))
hdrlist.append(("-----", "----", "-----------"))
widths = self.get_column_max_width(touchlist)
fmt = "%%7s %%-%ds %%s" % (widths[0] + 4)
self.vprint(fmt, hdrlist)
fmt = "%%7d %%-%ds %%s (%%s)" % (widths[0] + 4)
for i,touch in enumerate(touchlist):
self.write(fmt % ((i,) + touch))
self.newline()
"""
Apply command
"""
def print_apply_prompt_list(self, args):
if args['default']:
default = args['default']
else:
default = '[NOT SET]'
vals = args['vals']
vals = [(str(i), args['contract'], v) for i,v in enumerate(args['vals'])]
vals.append((str(i+1), "Current Value", default))
widths = self.get_column_max_width(vals)
widths[0] = len("Index")
widths[2] = 50
self.newline()
self.print_msg("%s :: Deconflict" % args['variable'])
self.newline()
self.print_table(widths, [("Index", "Session ID", "Value")], vals)
def print_apply_prompt(self, args):
if args['default']:
default = args['default']
else:
default = '[NOT SET]'
vals = [(str(i), contract.get_item_info(), param.value.value)
for i,(param,contract) in enumerate(args['vals'])]
vals.append((str(i+1), "Current Value", default))
widths = self.get_column_max_width(vals)
widths[0] = len("Index")
widths[2] = 50
self.newline()
self.print_msg("%s :: Deconflict" % args['variable'])
self.newline()
self.print_table(widths, [("Index", "Session ID", "Value")], vals)
"""
Prompt Command
"""
def print_prompt_param(self, args, default):
self.newline()
self.print_msg(" %s :: %s" % (args['name'], args['description']))
fmt = []
if args['attribs']:
fmt.append("")
widths = self.get_column_max_width(args['attribs'])
choice_fmt = " %%s%%d) %%-%ds %%s" % (widths[0] + 4)
for i,(attr,val) in enumerate(args['attribs']):
if default and i == int(default):
markdef = "*"
else:
markdef = " "
fmt.append(choice_fmt % (markdef,i,attr,val))
self.vprint("%s", fmt)
def print_prompt_param_help(self, args):
self.newline()
fmt = [ " Name : %s" % args['name'],
" Desc : %s" % args['description'],
" Type : %s" % args['type'],
" Required : %s" % args['required'],
" Is Valid : %s" % args['valid']]
if args['type'] != "Choice":
fmt.append(" Value : %s" % args['value'])
self.vprint("%s", fmt)
"""
Redirection
"""
def print_localiplist(self, iplist):
self.print_headingline("Local IP Addresses")
for ip in iplist:
self.write(" * %s " % ip)
self.newline()
def print_redir_info(self, redir, paramList):
#params = iDict(paramList)
try:
self.print_headingline("Local")
if not redir or not redir['local']:
self.write(" *Empty* ")
else:
for l in redir['local']:
self.write("Name: %s" % l.name)
self.write("Protocol: %s" % l.protocol)
self.write("Listen Address: %s" % l.listenaddr)
self.write("Listen Port: %s" % l.listenport)
self.write("Destination Address: %s" % l.destaddr)
self.write("Destination Port: %s" % l.destport)
self.write("Source Port : %s" % l.srcport)
self.write("")
self.print_headingline("Remote")
if not redir or not redir['remote']:
self.write(" *Empty* ")
else:
for r in redir['remote']:
self.write("Name: %s" % r.name)
self.write("Protocol: %s" % r.protocol)
self.write("Listen Address: %s" % r.listenaddr)
self.write("Listen Port: %s" % r.listenport)
self.write("Destination Address: %s" % r.destaddr)
self.write("Destination Port: %s" % r.destport)
except:
# XXX - ESKE. Due to group visibility some of the redir params
# being looked up might not be in the params list
pass
self.write("")
def print_local_tunnels(self, tunnels):
data = [("Proto", "Listen IP", "Source IP", "Destination IP")]
for tunnel in tunnels:
if tunnel.srcport is None:
srcport = 'ANY'
else:
srcport = '0'
data.append((tunnel.protocol,
tunnel.listenaddr + ":" + tunnel.listenport,
"Redirector" + ":" + srcport,
tunnel.destaddr + ":" + tunnel.destport))
maxlens = self.get_column_max_width(data)
data.insert(1, tuple(["-" * length for length in maxlens]))
fmt = "%%-%ds %%-%ds %%-%ds %%-%ds" % tuple(maxlens)
for line in data:
self.write(fmt % line)
def print_remote_tunnels(self, tunnels):
data = [("Proto", "Destination IP", "Listen IP", "Target")]
for tunnel in tunnels:
data.append((tunnel.protocol,
tunnel.destaddr + ":" + tunnel.destport,
tunnel.listenaddr + ":" + tunnel.listenport,
"TARGET"))
maxlens = self.get_column_max_width(data)
data.insert(1, tuple(["-" * length for length in maxlens]))
fmt = "%%-%ds %%-%ds %%-%ds %%-%ds" % tuple(maxlens)
for line in data:
self.write(fmt % line)
def print_global_redir(self, tunnels):
self.print_headingline("Local")
if not tunnels['local']:
self.write(" *empty* ")
else:
self.print_local_tunnels(tunnels['local'])
self.print_headingline("Remote")
if not tunnels['remote']:
self.write(" *empty* ")
else:
self.print_remote_tunnels(tunnels['remote'])
self.write("")
"""
Autorun
"""
def print_autoruncmds(self, status, auto):
if status:
self.print_msg("Autorun ON")
for cat, cmds in auto.items():
self.print_headingline(cat + " Autorun List")
for i,cmd in enumerate(cmds):
self.write(" %d) %s" % (i,cmd[0]))
self.newline()
else:
self.print_msg("Autorun OFF")
self.newline()
"""
General Help
"""
def print_standardop(self):
standardop = """
Fuzzbunch2 Standard OP Usage Help
---------------------------------
=== Summary ===
Run the following commands. Answer questions along the way.
Abort on any failures.
use PcConfig
use Explodingcan
use Pclauncher
=== Detail ===
use PcConfig will run the Peddlecheap configuration plugin and will
generate a configured Peddlecheap DLL.
use Explodingcan will run the Explodingcan exploit. It will first run
through the Explodingcan touch plugin then try to run the exploit. This
plugin will generate an open socket connection that MUST be consumed by the
Pclauncher plugin before exiting.
use Pclauncher will upload the configured Peddlecheap DLL to target over
the open connection from Explodingcan and run it from memory. A new window
will be opened for the LP to communicate with target.
"""
self.write(standardop)