diff --git a/patator.py b/patator.py index 243d31b..ea4044f 100755 --- a/patator.py +++ b/patator.py @@ -135,7 +135,9 @@ ajpy | AJP | https://github.com/hypn0s/AJPy/ -------------------------------------------------------------------------------------------------- openldap | LDAP | http://www.openldap.org/ | 2.4.24 | -------------------------------------------------------------------------------------------------- -impacket | SMB | https://github.com/CoreSecurity/impacket | 0.9.12 | +impacket | SMB, MSSQL | https://github.com/CoreSecurity/impacket | 0.9.12 | +-------------------------------------------------------------------------------------------------- +pyOpenSSL | impacket | https://pyopenssl.org/ | 17.5.0 | -------------------------------------------------------------------------------------------------- cx_Oracle | Oracle | http://cx-oracle.sourceforge.net/ | 5.1.1 | -------------------------------------------------------------------------------------------------- @@ -145,7 +147,7 @@ xfreerdp | RDP (NLA) | https://github.com/FreeRDP/FreeRDP/ -------------------------------------------------------------------------------------------------- psycopg | PostgreSQL | http://initd.org/psycopg/ | 2.4.5 | -------------------------------------------------------------------------------------------------- -pycrypto | VNC | http://www.dlitz.net/software/pycrypto/ | 2.3 | +pycrypto | VNC, impacket | http://www.dlitz.net/software/pycrypto/ | 2.6.1 | -------------------------------------------------------------------------------------------------- dnspython | DNS | http://www.dnspython.org/ | 1.10.0 | -------------------------------------------------------------------------------------------------- @@ -153,7 +155,7 @@ IPy | NET keyword | https://github.com/haypo/python-ipy -------------------------------------------------------------------------------------------------- pysnmp | SNMP | http://pysnmp.sourceforge.net/ | 4.2.1 | -------------------------------------------------------------------------------------------------- -pyasn1 | SNMP | http://sourceforge.net/projects/pyasn1/ | 0.1.2 | +pyasn1 | SNMP, impacket | http://sourceforge.net/projects/pyasn1/ | 0.1.2 | -------------------------------------------------------------------------------------------------- ike-scan | IKE | http://www.nta-monitor.com/tools-resources/ | 1.9 | -------------------------------------------------------------------------------------------------- @@ -577,7 +579,7 @@ NB1. SNMPv3 requires passphrases to be at least 8 characters long. * Brute-force the ZIP file password (cracking older pkzip encryption used to be not supported in JtR). ---------- -unzip_pass zipfile=path/to/file.zip password=FILE0 0=passwords.txt -x ignore:code!=0 +unzip_pass zipfile=file.zip password=FILE0 0=passwords.txt -x ignore:code!=0 }}} @@ -676,16 +678,21 @@ class TXTFormatter(logging.Formatter): def format(self, record): if not record.msg or record.msg == 'headers': - self._fmt = self.resultfmt + fmt = self.resultfmt if not all(True if 0x20 <= ord(c) < 0x7f else False for c in record.candidate): record.candidate = repr(record.candidate) else: if record.levelno == logging.DEBUG: - self._fmt = '%(asctime)s %(name)-7s %(levelname)7s [%(pname)s] %(message)s' + fmt = '%(asctime)s %(name)-7s %(levelname)7s [%(pname)s] %(message)s' else: - self._fmt = '%(asctime)s %(name)-7s %(levelname)7s - %(message)s' + fmt = '%(asctime)s %(name)-7s %(levelname)7s - %(message)s' + + if PY3: + self._style._fmt = fmt + else: + self._fmt = fmt return logging.Formatter.format(self, record) @@ -714,8 +721,8 @@ class XMLFormatter(logging.Formatter): def format(self, record): - for k, v in record.__dict__.iteritems(): - if isinstance(v, basestring): + for k, v in record.__dict__.items(): + if isinstance(v, str): record.__dict__[k] = xmlescape(v) return super(XMLFormatter, self).format(record) @@ -793,7 +800,7 @@ def process_logs(queue, indicatorsfmt, argv, log_dir): else: # remove "..." with open(results_xml, 'r+b') as f: - offset = f.read().find('') + offset = f.read().find(b'') if offset != -1: f.seek(offset) f.truncate(f.tell()) @@ -896,6 +903,23 @@ except ImportError: from cStringIO import StringIO from sys import maxint +PY3 = sys.version_info >= (3,) +if PY3: # http://python3porting.com/problems.html + def b(x): + return x.encode('ISO-8859-1') + def B(x): + return x.decode() +else: + def b(x): + return x + def B(x): + return x + +try: + input = raw_input +except NameError: + pass + notfound = [] try: from IPy import IP @@ -904,33 +928,40 @@ except ImportError: has_ipy = False notfound.append('IPy') -import multiprocessing.forking -class _Popen(multiprocessing.forking.Popen): +try: + # Python 3.4+ + if sys.platform.startswith('win'): + import multiprocessing.popen_spawn_win32 as forking + else: + import multiprocessing.popen_fork as forking +except ImportError: + import multiprocessing.forking as forking + +if sys.platform.startswith('win'): + # First define a modified version of Popen. + class _Popen(forking.Popen): def __init__(self, *args, **kw): + if hasattr(sys, 'frozen'): + # We have to set original _MEIPASS2 value from sys._MEIPASS + # to get --onefile mode working. + os.putenv('_MEIPASS2', sys._MEIPASS) + try: + super(_Popen, self).__init__(*args, **kw) + finally: if hasattr(sys, 'frozen'): - # We have to set original _MEIPASS2 value from sys._MEIPASS - # to get --onefile mode working. - os.putenv('_MEIPASS2', sys._MEIPASS) - try: - super(_Popen, self).__init__(*args, **kw) - finally: - if hasattr(sys, 'frozen'): - # On some platforms (e.g. AIX) 'os.unsetenv()' is not - # available. In those cases we cannot delete the variable - # but only set it to the empty string. The bootloader - # can handle this case. - if hasattr(os, 'unsetenv'): - os.unsetenv('_MEIPASS2') - else: - os.putenv('_MEIPASS2', '') + # On some platforms (e.g. AIX) 'os.unsetenv()' is not + # available. In those cases we cannot delete the variable + # but only set it to the empty string. The bootloader + # can handle this case. + if hasattr(os, 'unsetenv'): + os.unsetenv('_MEIPASS2') + else: + os.putenv('_MEIPASS2', '') -class Process(multiprocessing.Process): - _Popen = _Popen + # Second override 'Popen' class with our modified version. + forking.Popen = _Popen -# So BaseManager.start() uses this new Process class -multiprocessing.Process = Process from multiprocessing.managers import SyncManager - # imports }}} # utils {{{ @@ -975,7 +1006,7 @@ def create_dir(top_path): if os.path.isdir(top_path): files = os.listdir(top_path) if files: - if raw_input("Directory '%s' is not empty, do you want to wipe it ? [Y/n]: " % top_path) != 'n': + if input("Directory '%s' is not empty, do you want to wipe it ? [Y/n]: " % top_path) != 'n': for root, dirs, files in os.walk(top_path): if dirs: print("Directory '%s' contains sub-directories, safely aborting..." % root) @@ -1400,7 +1431,7 @@ Please read the README inside for more examples and usage information. log_queue = multiprocessing.Queue() - logsvc = Process(name='LogSvc', target=process_logs, args=(log_queue, module.Response.indicatorsfmt, argv, build_logdir(opts.log_dir, opts.auto_log))) + logsvc = multiprocessing.Process(name='LogSvc', target=process_logs, args=(log_queue, module.Response.indicatorsfmt, argv, build_logdir(opts.log_dir, opts.auto_log))) logsvc.daemon = True logsvc.start() @@ -1426,6 +1457,7 @@ Please read the README inside for more examples and usage information. p = expand_path(v[1:]) with open(p) as f: v = f.read() + kargs.append((k, v)) iter_vals = [v for k, v in sorted(wlists.items())] @@ -1619,14 +1651,14 @@ Please read the README inside for more examples and usage information. # consumers for num in range(self.num_threads): report_queue = multiprocessing.Queue(maxsize=1000) - t = Process(name='Consumer-%d' % num, target=self.consume, args=(task_queues[num], report_queue, logger.queue)) + t = multiprocessing.Process(name='Consumer-%d' % num, target=self.consume, args=(task_queues[num], report_queue, logger.queue)) t.daemon = True t.start() self.thread_report.append(report_queue) self.thread_progress.append(Progress()) # producer - t = Process(name='Producer', target=self.produce, args=(task_queues, logger.queue)) + t = multiprocessing.Process(name='Producer', target=self.produce, args=(task_queues, logger.queue)) t.daemon = True t.start() @@ -2130,7 +2162,6 @@ class Timing: # TCP_Cache {{{ class TCP_Connection: - def __init__(self, fp, banner=None): self.fp = fp self.banner = banner @@ -2197,16 +2228,16 @@ class TCP_Cache: # FTP {{{ from ftplib import FTP, Error as FTP_Error try: - from ftplib import FTP_TLS # New in python 2.7 + from ftplib import FTP_TLS # only available since python 2.7 except ImportError: - notfound.append('ftp-tls') + notfound.append('python') class FTP_login(TCP_Cache): '''Brute-force FTP''' usage_hints = ( - """%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt""" - """ -x ignore:mesg='Login incorrect.' -x ignore,reset,retry:code=500""", + '''%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt''' + ''' -x ignore:mesg='Login incorrect.' -x ignore,reset,retry:code=500''', ) available_options = ( @@ -2237,23 +2268,25 @@ class FTP_login(TCP_Cache): def execute(self, host, port='21', tls='0', user=None, password=None, timeout='10', persistent='1'): - with Timing() as timing: - fp, resp = self.bind(host, port, tls, timeout=timeout) - try: + with Timing() as timing: + fp, resp = self.bind(host, port, tls, timeout=timeout) + if user is not None or password is not None: with Timing() as timing: + if user is not None: resp = fp.sendcmd('USER ' + user) + if password is not None: resp = fp.sendcmd('PASS ' + password) - logger.debug('No error: %s' % resp) + logger.debug('No error: %r' % resp) self.reset() except FTP_Error as e: + logger.debug('FTP_Error: %s' % e) resp = str(e) - logger.debug('FTP_Error: %s' % resp) if persistent == '0': self.reset() @@ -2264,7 +2297,6 @@ class FTP_login(TCP_Cache): # }}} # SSH {{{ - try: from logging import NullHandler # only available since python 2.7 except ImportError: @@ -2314,10 +2346,10 @@ class SSH_login(TCP_Cache): def execute(self, host, port='22', user=None, password=None, auth_type='password', keyfile=None, persistent='1'): - with Timing() as timing: - fp, banner = self.bind(host, port, user) - try: + with Timing() as timing: + fp, banner = self.bind(host, port, user) + if user is not None: if keyfile is not None: @@ -2359,6 +2391,7 @@ class SSH_login(TCP_Cache): # Telnet {{{ from telnetlib import Telnet + class Telnet_login(TCP_Cache): '''Brute-force Telnet''' @@ -2389,7 +2422,8 @@ class Telnet_login(TCP_Cache): with Timing() as timing: fp, _ = self.bind(host, port, timeout=timeout) - trace = '' + trace = b'' + prompt_re = b(prompt_re) timeout = int(timeout) if self.prompt_count == 0: @@ -2400,9 +2434,10 @@ class Telnet_login(TCP_Cache): if inputs is not None: with Timing() as timing: + for val in inputs.split(r'\n'): logger.debug('input: %s' % val) - cmd = val + '\n' #'\r\x00' + cmd = b(val + '\n') #'\r\x00' fp.write(cmd) trace += cmd @@ -2414,13 +2449,17 @@ class Telnet_login(TCP_Cache): if persistent == '0': self.reset() + raw = B(raw) + trace = B(trace) + mesg = repr(raw)[1:-1] # strip enclosing single quotes return self.Response(0, mesg, timing, trace) # }}} # SMTP {{{ -from smtplib import SMTP, SMTP_SSL, SMTPAuthenticationError, SMTPHeloError, SMTPException +from smtplib import SMTP, SMTP_SSL, SMTPException, SMTPResponseException + class SMTP_Base(TCP_Cache): available_options = TCP_Cache.available_options @@ -2482,7 +2521,7 @@ class SMTP_vrfy(SMTP_Base): self.reset() code, mesg = resp - return self.Response(code, mesg, timing) + return self.Response(code, B(mesg), timing) class SMTP_rcpt(SMTP_Base): @@ -2516,7 +2555,7 @@ class SMTP_rcpt(SMTP_Base): self.reset() code, mesg = resp - return self.Response(code, mesg, timing) + return self.Response(code, B(mesg), timing) class SMTP_login(SMTP_Base): @@ -2545,14 +2584,19 @@ class SMTP_login(SMTP_Base): logger.debug('No error: %s' % str(resp)) self.reset() - except (SMTPHeloError,SMTPAuthenticationError,SMTPException) as resp: - logger.debug('SMTPError: %s' % str(resp)) + except SMTPResponseException as e: + logger.debug('SMTPResponseException: %s' % e) + resp = e.args + + except SMTPException as e: + logger.debug('SMTPException: %s' % e) + resp = '1', b(str(e)) if persistent == '0': self.reset() code, mesg = resp - return self.Response(code, mesg, timing) + return self.Response(code, B(mesg), timing) # }}} @@ -2595,10 +2639,10 @@ class Finger_lookup: s.connect((host, int(port))) if user: - s.send(user) - s.send('\r\n') + s.send(b(user)) + s.send(b'\r\n') - data = '' + data = b'' with Timing() as timing: while True: raw = s.recv(1024) @@ -2610,7 +2654,7 @@ class Finger_lookup: logger.debug('recv: %r' % data) - data = data.strip() + data = B(data).strip() mesg = repr(data) resp = self.Response(0, mesg, timing, data) @@ -2668,14 +2712,13 @@ class LDAP_login: try: from impacket.smbconnection import SMBConnection, SessionError from impacket import nt_errors - from impacket.dcerpc.v5 import transport, lsat, lsad + from impacket.dcerpc.v5 import transport, lsat, lsad from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED from impacket.dcerpc.v5.samr import SID_NAME_USE except ImportError: notfound.append('impacket') class SMB_Connection(TCP_Connection): - def close(self): self.fp.getSMBServer().get_socket().close() @@ -2743,7 +2786,6 @@ class SMB_login(TCP_Cache): return self.Response(code, mesg, timing) class DCE_Connection(TCP_Connection): - def __init__(self, fp, smbt): self.smbt = smbt TCP_Connection.__init__(self, fp) @@ -2816,6 +2858,7 @@ class SMB_lookupsid(TCP_Cache): # POP {{{ from poplib import POP3, POP3_SSL, error_proto as pop_error + class POP_Connection(TCP_Connection): def close(self): self.fp.quit() @@ -2916,8 +2959,8 @@ class POP_passd: trace += '%s\r\n%s\r\n' % (cmd, resp) except LineReceiver_Error as e: + logger.debug('LineReceiver_Error: %s' % e) resp = str(e) - logger.debug('LineReceiver_Error: %s' % resp) trace += '%s\r\n%s\r\n' % (cmd, resp) finally: @@ -2930,6 +2973,7 @@ class POP_passd: # IMAP {{{ from imaplib import IMAP4, IMAP4_SSL + class IMAP_login: '''Brute-force IMAP4''' @@ -3019,34 +3063,40 @@ class Rlogin_login(TCP_Cache): fp, _ = self.bind(host, port, timeout=int(timeout)) - trace = '' + trace = b'' timeout = int(timeout) with Timing() as timing: if self.need_handshake: - fp.write('\x00%s\x00%s\x00vt100/9600\x00' % (luser, user)) + fp.write(b('\x00%s\x00%s\x00vt100/9600\x00' % (luser, user))) self.need_handshake = False else: - fp.write('%s\r' % user) + fp.write(b('%s\r' % user)) _, _, resp = fp.expect([prompt_re], timeout=timeout) # expecting the Password: prompt trace += resp if password is not None: - fp.write('%s\r' % password) + fp.write(b('%s\r' % password)) _, _, resp = fp.expect([prompt_re], timeout=timeout) # expecting the login: prompt trace += resp if persistent == '0': self.reset() + resp = B(resp) + trace = B(trace) + mesg = repr(resp.strip())[1:-1] return self.Response(0, mesg, timing, trace) # }}} # VMauthd {{{ from ssl import wrap_socket -class LineReceiver_Error(Exception): pass + +class LineReceiver_Error(Exception): + pass + class LineReceiver: def connect(self, host, port, timeout, ssl=False): @@ -3062,15 +3112,15 @@ class LineReceiver: self.sock.close() def sendcmd(self, cmd): - self.sock.sendall(cmd + '\r\n') + self.sock.sendall(b(cmd + '\r\n')) return self.getresp() def getresp(self): resp = self.sock.recv(1024) - while not resp.endswith('\n'): + while not resp.endswith(b'\n'): resp += self.sock.recv(1024) - resp = resp.rstrip() + resp = B(resp).rstrip() code, _ = self.parse(resp) if not code.isdigit(): @@ -3133,8 +3183,8 @@ class VMauthd_login(TCP_Cache): trace += '%s\r\n%s\r\n' % (cmd, resp) except LineReceiver_Error as e: + logger.debug('LineReceiver_Error: %s' % e) resp = str(e) - logger.debug('LineReceiver_Error: %s' % resp) trace += '%s\r\n%s\r\n' % (cmd, resp) if persistent == '0': @@ -3228,7 +3278,8 @@ try: from impacket import tds from impacket.tds import TDS_ERROR_TOKEN, TDS_LOGINACK_TOKEN except ImportError: - notfound.append('impacket') + notfound.append('pyopenssl') + class MSSQL_login: '''Brute-force MSSQL''' @@ -3291,8 +3342,8 @@ class Oracle_login: '''Brute-force Oracle''' usage_hints = ( - """%prog host=10.0.0.1 sid=FILE0 0=sids.txt -x ignore:code=ORA-12505""", - """%prog host=10.0.0.1 user=SYS password=FILE0 0=passwords.txt -x ignore:code=ORA-01017""", + '''%prog host=10.0.0.1 sid=FILE0 0=sids.txt -x ignore:code=ORA-12505''', + '''%prog host=10.0.0.1 user=SYS password=FILE0 0=passwords.txt -x ignore:code=ORA-01017''', ) available_options = ( @@ -3418,14 +3469,18 @@ class Response_HTTP(Response_Base): return re.search(val, self.mesg, re.M) def str_target(self): - return ' '.join('%s=%s' % (k, xmlquoteattr(str(v))) for k, v in self.target.iteritems()) + return ' '.join('%s=%s' % (k, xmlquoteattr(str(v))) for k, v in self.target.items()) available_conditions = Response_Base.available_conditions available_conditions += ( ('clen', 'match Content-Length header (N or N-M or N- or -N)'), ) -from BaseHTTPServer import BaseHTTPRequestHandler +try: + from http.server import BaseHTTPRequestHandler +except ImportError: + from BaseHTTPServer import BaseHTTPRequestHandler + class HTTPRequestParser(BaseHTTPRequestHandler): def __init__(self, fd): self.rfile = fd @@ -3435,7 +3490,8 @@ class HTTPRequestParser(BaseHTTPRequestHandler): self.parse_request() if self.command == 'POST': - self.body = self.rfile.read(-1).rstrip('\r\n') + self.body = B(self.rfile.read(-1)).rstrip('\r\n') + if 'Content-Length' in self.headers: del self.headers['Content-Length'] @@ -3448,7 +3504,7 @@ class Controller_HTTP(Controller): key, val = arg.split('=', 1) if key == 'raw_request': - with open(val) as fd: + with open(val, 'rb') as fd: r = HTTPRequestParser(fd) if r.error: @@ -3466,7 +3522,7 @@ class Controller_HTTP(Controller): opts['method'] = r.command opts['body'] = r.body - for key, val in opts.iteritems(): + for key, val in opts.items(): if val: yield (key, val) @@ -3477,13 +3533,10 @@ class HTTP_fuzz(TCP_Cache): '''Brute-force HTTP''' usage_hints = [ - """%prog url=http://10.0.0.1/FILE0 0=paths.txt -x ignore:code=404 -x ignore,retry:code=500""", - - """%prog url=http://10.0.0.1/manager/html user_pass=COMBO00:COMBO01 0=combos.txt""" - """ -x ignore:code=401""", - - """%prog url=http://10.0.0.1/phpmyadmin/index.php method=POST""" - """ body='pma_username=root&pma_password=FILE0&server=1&lang=en' 0=passwords.txt follow=1""" + '''%prog url=http://10.0.0.1/FILE0 0=paths.txt -x ignore:code=404 -x ignore,retry:code=500''', + '''%prog url=http://10.0.0.1/manager/html user_pass=COMBO00:COMBO01 0=combos.txt -x ignore:code=401''', + '''%prog url=http://10.0.0.1/phpmyadmin/index.php method=POST''' + ''' body='pma_username=root&pma_password=FILE0&server=1&lang=en' 0=passwords.txt follow=1''' """ accept_cookie=1 -x ignore:fgrep='Cannot log in to the MySQL server'""", ] @@ -3574,9 +3627,14 @@ class HTTP_fuzz(TCP_Cache): if max_mem > 0 and trace.tell() > max_mem: return 0 + s = B(s) + if t in (pycurl.INFOTYPE_HEADER_OUT, pycurl.INFOTYPE_DATA_OUT): trace.write(s) + elif t == pycurl.INFOTYPE_TEXT and b'upload completely sent off' in s: + trace.write('\n\n') + elif t in (pycurl.INFOTYPE_HEADER_IN, pycurl.INFOTYPE_DATA_IN): trace.write(s) response.write(s) @@ -3697,12 +3755,12 @@ class Response_AJP(Response_HTTP): self.status_msg = status_msg def __str__(self): - return self.status_msg or self.mesg + return self.status_msg or self.mesg def prepare_ajp_forward_request(target_host, req_uri, method): fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER) fr.method = method - fr.protocol = "HTTP/1.1" + fr.protocol = 'HTTP/1.1' fr.req_uri = req_uri fr.remote_addr = target_host fr.remote_host = None @@ -3728,9 +3786,8 @@ class AJP_fuzz(TCP_Cache): '''Brute-force AJP''' usage_hints = [ - """%prog url=ajp://10.0.0.1/FILE0 0=paths.txt -x ignore:code=404 -x ignore,retry:code=500""", - """%prog url=ajp://10.0.0.1/manager/html user_pass=COMBO00:COMBO01 0=combos.txt""" - """ -x ignore:code=401""", + '''%prog url=ajp://10.0.0.1/FILE0 0=paths.txt -x ignore:code=404 -x ignore,retry:code=500''', + '''%prog url=ajp://10.0.0.1/manager/html user_pass=COMBO00:COMBO01 0=combos.txt -x ignore:code=401''', ] available_options = ( @@ -3801,7 +3858,7 @@ class RDP_login: '''Brute-force RDP (NLA)''' usage_hints = ( - """%prog host=10.0.0.1 user='administrator' password=FILE0 0=passwords.txt""", + '''%prog host=10.0.0.1 user='administrator' password=FILE0 0=passwords.txt''', ) available_options = ( @@ -3820,7 +3877,7 @@ class RDP_login: with Timing() as timing: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() + out, err = map(B, p.communicate()) code = p.returncode err = err.replace('''Authentication only. Don't connect to X. @@ -3847,35 +3904,37 @@ try: except ImportError: notfound.append('pycrypto') -class VNC_Error(Exception): pass +class VNC_Error(Exception): + pass + class VNC: def connect(self, host, port, timeout): self.fp = socket.create_connection((host, port), timeout=timeout) resp = self.fp.recv(99) # banner logger.debug('banner: %r' % resp) - self.version = resp[:11].decode('ascii') + self.version = B(resp[:11]) if len(resp) > 12: - raise VNC_Error('%s %s' % (self.version, resp[12:].decode('ascii', 'ignore'))) + raise VNC_Error('%s %r' % (self.version, B(resp[12:]))) return self.version def login(self, password): - logger.debug('Remote version: %s' % self.version) + logger.debug('Remote version: %r' % self.version) major, minor = self.version[6], self.version[10] if (major, minor) in [('3', '8'), ('4', '1')]: - proto = b'RFB 003.008\n' + proto = 'RFB 003.008\n' elif (major, minor) == ('3', '7'): - proto = b'RFB 003.007\n' + proto = 'RFB 003.007\n' else: - proto = b'RFB 003.003\n' + proto = 'RFB 003.003\n' - logger.debug('Client version: %s' % proto[:-1]) - self.fp.sendall(proto) + logger.debug('Client version: %r' % proto[:-1]) + self.fp.sendall(b(proto)) sleep(0.5) @@ -3885,7 +3944,7 @@ class VNC: if major == '4' or (major == '3' and int(minor) >= 7): code = ord(resp[0:1]) if code == 0: - raise VNC_Error('Session setup failed: %s' % resp.decode('ascii', 'ignore')) + raise VNC_Error('Session setup failed: %s' % B(resp)) self.fp.sendall(b'\x02') # always use classic VNC authentication resp = self.fp.recv(99) @@ -3893,7 +3952,7 @@ class VNC: else: # minor == '3': code = ord(resp[3:4]) if code != 2: - raise VNC_Error('Session setup failed: %s' % resp.decode('ascii', 'ignore')) + raise VNC_Error('Session setup failed: %s' % B(resp)) resp = resp[-16:] @@ -3916,7 +3975,7 @@ class VNC: logger.debug('resp: %r' % resp) code = ord(resp[3:4]) - mesg = resp[8:].decode('ascii', 'ignore') + mesg = B(resp[8:]) if code == 1: return code, mesg or 'Authentication failure' @@ -3938,17 +3997,16 @@ class VNC: btgt = btgt | (1 << 7-i) newkey.append(btgt) - if sys.version_info[0] == 2: - return ''.join(chr(c) for c in newkey) - else: + if PY3: return bytes(newkey) - + else: + return ''.join(chr(c) for c in newkey) class VNC_login: '''Brute-force VNC''' usage_hints = ( - """%prog host=10.0.0.1 password=FILE0 0=passwords.txt -t 1 -x retry:fgrep!='Authentication failure' --max-retries -1 -x quit:code=0""", + '''%prog host=10.0.0.1 password=FILE0 0=passwords.txt -t 1 -x retry:fgrep!='Authentication failure' --max-retries -1 -x quit:code=0''', ) available_options = ( @@ -3981,7 +4039,6 @@ class VNC_login: # }}} # DNS {{{ - try: import dns.rdatatype import dns.message @@ -4252,13 +4309,12 @@ class Controller_DNS(Controller): name, data = name[:-1], data[:-1] self.hostmap[data].alias.add(name) - class DNS_reverse: '''Reverse DNS lookup''' usage_hints = [ - """%prog host=NET0 0=192.168.0.0/24 -x ignore:code=3""", - """%prog host=NET0 0=216.239.32.0-216.239.47.255,8.8.8.0/24 -x ignore:code=3 -x ignore:fgrep!=google.com -x ignore:fgrep=216-239-""", + '''%prog host=NET0 0=192.168.0.0/24 -x ignore:code=3''', + '''%prog host=NET0 0=216.239.32.0-216.239.47.255,8.8.8.0/24 -x ignore:code=3 -x ignore:fgrep!=google.com -x ignore:fgrep=216-239-''', ] available_options = ( @@ -4291,9 +4347,9 @@ class DNS_forward: '''Forward DNS lookup''' usage_hints = [ - """%prog name=FILE0.google.com 0=names.txt -x ignore:code=3""", - """%prog name=google.MOD0 0=TLD -x ignore:code=3""", - """%prog name=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3""", + '''%prog name=FILE0.google.com 0=names.txt -x ignore:code=3''', + '''%prog name=google.MOD0 0=TLD -x ignore:code=3''', + '''%prog name=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3''', ] available_options = ( @@ -4337,6 +4393,11 @@ try: except ImportError: notfound.append('pysnmp') +try: + import pyasn1 +except ImportError: + notfound.append('pyasn1') + class SNMP_login: '''Brute-force SNMP v1/2/3''' @@ -4412,7 +4473,7 @@ IKE_GROUP = [('1', 'modp768'), ('2', 'modp1024'), ('5', 'modp1536'), # '27', '28', '29', '30', # RFC6932 def generate_transforms(): - lists = map(lambda l: [i[0] for i in l], [IKE_ENC, IKE_HASH, IKE_AUTH, IKE_GROUP]) + lists = list(map(lambda l: [i[0] for i in l], [IKE_ENC, IKE_HASH, IKE_AUTH, IKE_GROUP])) return map(lambda p: ','.join(p), product(*[chain(l) for l in lists])), reduce(lambda x,y: x*y, map(len, lists)) class Controller_IKE(Controller): @@ -4443,7 +4504,7 @@ class Controller_IKE(Controller): ike_ath = dict(IKE_AUTH) ike_grp = dict(IKE_GROUP) - for endpoint, transforms in self.results.iteritems(): + for endpoint, transforms in self.results.items(): print('\n+ %s' % endpoint) print(' %10s %10s %12s %10s' % ('Encryption', 'Hash', 'Auth', 'Group')) print(' %10s %10s %12s %10s' % ('-'*10, '-'*10, '-'*10, '-'*10)) @@ -4464,8 +4525,8 @@ class IKE_enum: '''Enumerate IKE transforms''' usage_hints = [ - """%prog host=10.0.0.1 transform=MOD0 0=TRANS -x ignore:fgrep=NO-PROPOSAL""", - """%prog host=10.0.0.1 transform=MOD0 0=TRANS -x ignore:fgrep=NO-PROPOSAL aggressive=RANGE1 1=int:0-1""", + '''%prog host=10.0.0.1 transform=MOD0 0=TRANS -x ignore:fgrep=NO-PROPOSAL''', + '''%prog host=10.0.0.1 transform=MOD0 0=TRANS -x ignore:fgrep=NO-PROPOSAL aggressive=RANGE1 1=int:0-1''', ] available_options = ( @@ -4486,7 +4547,7 @@ class IKE_enum: def __init__(self): uid = multiprocessing.current_process().name[9:] - self.sport = '51%s' % uid + self.sport = '51%d' % int(uid) def execute(self, host, port='500', transform='5,1,1,2', aggressive='0', groupname='foo', vid=''): @@ -4500,7 +4561,7 @@ class IKE_enum: with Timing() as timing: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() + out, err = map(B, p.communicate()) code = p.returncode trace = '%s\n[out]\n%s\n[err]\n%s' % (cmd, out, err) @@ -4532,7 +4593,7 @@ class Unzip_pass: '''Brute-force the password of encrypted ZIP files''' usage_hints = [ - """%prog zipfile=path/to/file.zip password=FILE0 0=passwords.txt -x ignore:code!=0""", + """%prog zipfile=file.zip password=FILE0 0=passwords.txt -x ignore:code!=0""", ] available_options = ( @@ -4550,7 +4611,7 @@ class Unzip_pass: with Timing() as timing: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() + out, err = map(B, p.communicate()) code = p.returncode mesg = repr(out.strip())[1:-1] @@ -4568,7 +4629,7 @@ class Keystore_pass: '''Brute-force the password of Java keystore files''' usage_hints = [ - """%prog keystore=path/to/keystore.jks password=FILE0 0=passwords.txt -x ignore:fgrep='password was incorrect'""", + """%prog keystore=keystore.jks password=FILE0 0=passwords.txt -x ignore:fgrep='password was incorrect'""", ] available_options = ( @@ -4587,7 +4648,7 @@ class Keystore_pass: with Timing() as timing: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() + out, err = map(B, p.communicate()) code = p.returncode mesg = repr(out.strip())[1:-1] @@ -4607,7 +4668,7 @@ class SQLCipher_pass: '''Brute-force the password of SQLCipher-encrypted databases''' usage_hints = [ - """%prog database=path/to/db.sqlite password=FILE0 0=passwords.txt -x ignore:fgrep='file is encrypted'""", + """%prog database=db.sqlite password=FILE0 0=passwords.txt -x ignore:fgrep='file is encrypted'""", ] available_options = ( @@ -4636,11 +4697,12 @@ class SQLCipher_pass: # Umbraco {{{ import hmac + class Umbraco_crack: '''Crack Umbraco HMAC-SHA1 password hashes''' usage_hints = ( - """%prog hashlist=@umbraco_users.pw password=FILE0 0=rockyou.txt""", + '''%prog hashlist=@umbraco_users.pw password=FILE0 0=passwords.txt''', ) available_options = ( @@ -4654,7 +4716,7 @@ class Umbraco_crack: def execute(self, password, hashlist): p = password.encode('utf-16-le') - h = b64encode(hmac.new(p, p, digestmod=hashlib.sha1).digest()) + h = B(b64encode(hmac.new(p, p, digestmod=hashlib.sha1).digest())) if h not in hashlist: code, mesg = 1, 'fail' @@ -4768,25 +4830,27 @@ modules = [ ] dependencies = { - 'paramiko': [('ssh_login',), 'http://www.lag.net/paramiko/', '1.7.7.1'], - 'pycurl': [('http_fuzz',), 'http://pycurl.sourceforge.net/', '7.43.0'], + 'paramiko': [('ssh_login',), 'http://www.paramiko.org/', '1.7.7.1'], + 'pycurl': [('http_fuzz',), 'http://pycurl.io/', '7.43.0'], 'libcurl': [('http_fuzz',), 'https://curl.haxx.se/', '7.21.0'], 'ajpy': [('ajp_fuzz',), 'https://github.com/hypn0s/AJPy/', '0.0.1'], 'openldap': [('ldap_login',), 'http://www.openldap.org/', '2.4.24'], - 'impacket': [('smb_login','smb_lookupsid','mssql_login'), 'https://github.com/CoreSecurity/impacket', '0.9.12'], + 'impacket': [('smb_login', 'smb_lookupsid', 'mssql_login'), 'https://github.com/CoreSecurity/impacket', '0.9.12'], + 'pyopenssl': [('mssql_login',), 'https://pyopenssl.org/', '17.5.0'], 'cx_Oracle': [('oracle_login',), 'http://cx-oracle.sourceforge.net/', '5.1.1'], 'mysqlclient': [('mysql_login',), 'https://github.com/PyMySQL/mysqlclient-python', '1.3.12'], 'xfreerdp': [('rdp_login',), 'https://github.com/FreeRDP/FreeRDP.git', '1.2.0-beta1'], 'psycopg': [('pgsql_login',), 'http://initd.org/psycopg/', '2.4.5'], - 'pycrypto': [('vnc_login',), 'http://www.dlitz.net/software/pycrypto/', '2.3'], + 'pycrypto': [('smb_login', 'smb_lookupsid', 'mssql_login', 'vnc_login',), 'http://www.dlitz.net/software/pycrypto/', '2.6.1'], 'dnspython': [('dns_reverse', 'dns_forward'), 'http://www.dnspython.org/', '1.10.0'], 'IPy': [('dns_reverse', 'dns_forward'), 'https://github.com/haypo/python-ipy', '0.75'], 'pysnmp': [('snmp_login',), 'http://pysnmp.sf.net/', '4.2.1'], + 'pyasn1': [('smb_login', 'smb_lookupsid', 'mssql_login', 'snmp_login'), 'http://sourceforge.net/projects/pyasn1/', '0.1.2'], 'ike-scan': [('ike_enum',), 'http://www.nta-monitor.com/tools-resources/security-tools/ike-scan', '1.9'], 'unzip': [('unzip_pass',), 'http://www.info-zip.org/', '6.0'], 'java': [('keystore_pass',), 'http://www.oracle.com/technetwork/java/javase/', '6'], 'pysqlcipher': [('sqlcipher_pass',), 'https://github.com/leapcode/pysqlcipher/', '2.6.10'], - 'ftp-tls': [('ftp_login',), 'TLS support unavailable before python 2.7'], + 'python': [('ftp_login',), 'Patator requires Python 2.7 or above. Some features may be unavailable otherwise, such as TLS support for FTP.'], } # }}}