Add docker-compose and fix bugs

pull/154/head
lanjelot 4 years ago
parent 902c650e04
commit da3d9751d7

@ -9,30 +9,44 @@ RUN apt-get update && apt-get install -y \
libcurl4-openssl-dev python3-dev libssl-dev \
ldap-utils \
libmariadbclient-dev \
libpq-dev \
ike-scan unzip default-jdk \
libsqlite3-dev libsqlcipher-dev \
libpq-dev \
python3-pip
python3-pip python-pip
# cx_oracle
RUN apt-get update && apt-get install -y libaio1 wget unzip
RUN apt-get update && apt-get install -y libaio1 wget unzip git
WORKDIR /opt/oracle
RUN wget https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip
RUN unzip instantclient-basiclite-linuxx64.zip
RUN rm -f instantclient-basiclite-linuxx64.zip
RUN cd /opt/oracle/instantclient*
RUN rm -f *jdbc* *occi* *mysql* *README *jar uidrvci genezi adrci
RUN echo /opt/oracle/instantclient* > /etc/ld.so.conf.d/oracle-instantclient.conf
RUN ldconfig
RUN wget https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip \
&& wget https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip \
&& unzip instantclient-basiclite-linuxx64.zip \
&& rm -f instantclient-basiclite-linuxx64.zip \
&& unzip instantclient-sdk-linuxx64.zip \
&& rm -f instantclient-sdk-linuxx64.zip \
&& cd /opt/oracle/instantclient* \
&& rm -f *jdbc* *occi* *mysql* *README *jar uidrvci genezi adrci \
&& echo /opt/oracle/instantclient* > /etc/ld.so.conf.d/oracle-instantclient.conf \
&& ldconfig
RUN git clone --branch 5.3 https://github.com/oracle/python-cx_Oracle \
&& cd python-cx_Oracle && export ORACLE_HOME=/opt/oracle/instantclient_19_6 && python2 setup.py build && python2 setup.py install
# xfreerdp (see https://github.com/FreeRDP/FreeRDP/wiki/Compilation)
RUN apt-get update && apt-get install -y ninja-build build-essential git-core debhelper cdbs dpkg-dev autotools-dev cmake pkg-config xmlto libssl-dev docbook-xsl xsltproc libxkbfile-dev libx11-dev libwayland-dev libxrandr-dev libxi-dev libxrender-dev libxext-dev libxinerama-dev libxfixes-dev libxcursor-dev libxv-dev libxdamage-dev libxtst-dev libcups2-dev libpcsclite-dev libasound2-dev libpulse-dev libjpeg-dev libgsm1-dev libusb-1.0-0-dev libudev-dev libdbus-glib-1-dev uuid-dev libxml2-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libfaad-dev libfaac-dev \
&& apt-get install -y libavutil-dev libavcodec-dev libavresample-dev
RUN git clone https://github.com/FreeRDP/FreeRDP/ /tmp/FreeRDP
WORKDIR /tmp/FreeRDP
WORKDIR /opt/FreeRDP
RUN git clone https://github.com/FreeRDP/FreeRDP/ .
RUN cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_SSE2=ON . && cmake --build . && cmake --build . --target install
WORKDIR /opt/patator
RUN python3 -m pip install patator
COPY ./requirements.txt ./
RUN python3 -m pip install -r requirements.txt
RUN sed -e '/cx_Oracle/d' -e 's,pysqlcipher3,pysqlcipher,' requirements.txt | python2 -m pip install -r /dev/stdin
# utils
RUN apt-get update && apt-get install -y ipython3 ipython iputils-ping iproute2 netcat curl rsh-client telnet vim mlocate nmap
RUN echo 'set bg=dark' > /root/.vimrc
ENTRYPOINT ["patator.py"]
COPY ./patator.py ./
ENTRYPOINT ["python3", "./patator.py"]

@ -0,0 +1,55 @@
version: "3"
services:
unix:
build: testing/unix
image: patator-unix-testing
# ports:
# - "21:21"
# - "22:22"
# - "23:23"
# - "25:25"
# - "79:79"
# - "80:80"
# - "106:106"
# - "110:110"
# - "139:139"
# - "143:143"
# - "389:389"
# - "445:445"
# - "513:513"
# - "636:636"
# - "993:993"
# - "995:995"
# - "3306:3306"
# - "4444:4444"
# - "5432:5432"
# - "5900:5900"
# - "8009:8009"
# - "8080:8080"
volumes:
- .:/opt/patator
oracle:
image: oracleinanutshell/oracle-xe-11g
environment:
- ORACLE_ENABLE_XDB=true
ports:
- "1521:1521"
mssql:
image: mcr.microsoft.com/mssql/server:2019-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=Password1
ports:
- "1433:1433"
patator:
build: .
image: patator
depends_on:
- unix
- oracle
- mssql
volumes:
- .:/opt/patator

@ -714,15 +714,12 @@ class TXTFormatter(logging.Formatter):
def __init__(self, indicatorsfmt):
self.resultfmt = '%(asctime)s %(name)-7s %(levelname)7s - ' + ' '.join('%%(%s)%ss' % (k, v) for k, v in indicatorsfmt) + ' | %(candidate)-34s | %(num)5s | %(mesg)s'
logging.Formatter.__init__(self, datefmt='%H:%M:%S')
super(TXTFormatter, self).__init__(datefmt='%H:%M:%S')
def format(self, record):
if not record.msg or record.msg == 'headers':
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:
fmt = '%(asctime)s %(name)-7s %(levelname)7s [%(pname)s] %(message)s'
@ -734,18 +731,30 @@ class TXTFormatter(logging.Formatter):
else:
self._fmt = fmt
return logging.Formatter.format(self, record)
pp = {}
for k, v in record.__dict__.items():
if k in ['candidate', 'mesg']:
pp[k] = repr23(v)
else:
pp[k] = v
return super(TXTFormatter, self).format(logging.makeLogRecord(pp))
class CSVFormatter(logging.Formatter):
def __init__(self, indicatorsfmt):
fmt = '%(asctime)s,%(levelname)s,'+','.join('%%(%s)s' % name for name, _ in indicatorsfmt)+',%(candidate)s,%(num)s,%(mesg)s'
logging.Formatter.__init__(self, fmt, datefmt='%H:%M:%S')
super(CSVFormatter, self).__init__(fmt=fmt, datefmt='%H:%M:%S')
def format(self, record):
for k in ['candidate', 'mesg']:
record.__dict__[k] = '"%s"' % record.__dict__[k].replace('"', '""')
return logging.Formatter.format(self, record)
pp = {}
for k, v in record.__dict__.items():
if k in ['candidate', 'mesg']:
pp[k] = '"%s"' % v.replace('"', '""')
else:
pp[k] = v
return super(CSVFormatter, self).format(logging.makeLogRecord(pp))
class XMLFormatter(logging.Formatter):
def __init__(self, indicatorsfmt):
@ -757,15 +766,17 @@ class XMLFormatter(logging.Formatter):
<target %(target)s/>
</result>'''
logging.Formatter.__init__(self, fmt, datefmt='%H:%M:%S')
super(XMLFormatter, self).__init__(fmt=fmt, datefmt='%H:%M:%S')
def format(self, record):
pp = {}
for k, v in record.__dict__.items():
if isinstance(v, str):
record.__dict__[k] = xmlescape(v)
pp[k] = xmlescape(v)
else:
pp[k] = v
return super(XMLFormatter, self).format(record)
return super(XMLFormatter, self).format(logging.makeLogRecord(pp))
class MsgFilter(logging.Filter):
@ -779,12 +790,12 @@ def process_logs(queue, indicatorsfmt, argv, log_dir, runtime_file, csv_file, xm
ignore_ctrlc()
try:
# python3
if PY3:
logging._levelToName[logging.ERROR] = 'FAIL'
except:
# python2
encoding = 'latin1'
else:
logging._levelNames[logging.ERROR] = 'FAIL'
encoding = None
handler_out = logging.StreamHandler()
handler_out.setFormatter(TXTFormatter(indicatorsfmt))
@ -801,7 +812,7 @@ def process_logs(queue, indicatorsfmt, argv, log_dir, runtime_file, csv_file, xm
with open(runtime_log, 'a') as f:
f.write('$ %s\n' % ' '.join(argv))
handler_log = logging.FileHandler(runtime_log)
handler_log = logging.FileHandler(runtime_log, encoding=encoding)
handler_log.setFormatter(TXTFormatter(indicatorsfmt))
logger.addHandler(handler_log)
@ -813,7 +824,7 @@ def process_logs(queue, indicatorsfmt, argv, log_dir, runtime_file, csv_file, xm
with open(results_csv, 'w') as f:
f.write('time,level,%s\n' % ','.join(names))
handler_csv = logging.FileHandler(results_csv)
handler_csv = logging.FileHandler(results_csv, encoding=encoding)
handler_csv.addFilter(MsgFilter())
handler_csv.setFormatter(CSVFormatter(indicatorsfmt))
@ -835,7 +846,7 @@ def process_logs(queue, indicatorsfmt, argv, log_dir, runtime_file, csv_file, xm
while i < len(argv):
arg = argv[i]
if arg[0] == '-':
if arg in ('-d', '--debug', '--allow-ignore-failures'):
if arg in ('-d', '--debug', '--allow-ignore-failures', '-y'):
f.write(' <option type="global" name=%s/>\n' % xmlquoteattr(arg))
else:
if not arg.startswith('--') and len(arg) > 2:
@ -860,7 +871,7 @@ def process_logs(queue, indicatorsfmt, argv, log_dir, runtime_file, csv_file, xm
f.seek(offset)
f.truncate(f.tell())
handler_xml = logging.FileHandler(results_xml)
handler_xml = logging.FileHandler(results_xml, encoding=encoding)
handler_xml.addFilter(MsgFilter())
handler_xml.setFormatter(XMLFormatter(indicatorsfmt))
@ -904,13 +915,13 @@ def process_logs(queue, indicatorsfmt, argv, log_dir, runtime_file, csv_file, xm
if log_dir:
filename = '%d_%s' % (num, '-'.join(map(str, resp.indicators())))
with open('%s.txt' % os.path.join(log_dir, filename), 'w') as f:
with open('%s.txt' % os.path.join(log_dir, filename), 'wb') as f:
f.write(resp.dump())
elif action == 'save_hit':
if hits_file:
with open(hits_file, 'a') as f:
f.write('%s\n' % ':'.join(args))
with open(hits_file, 'ab') as f:
f.write(b(args[0] +'\n'))
elif action == 'setLevel':
logger.setLevel(args[0])
@ -946,28 +957,33 @@ import glob
from xml.sax.saxutils import escape as xmlescape, quoteattr as xmlquoteattr
from ssl import wrap_socket
from binascii import hexlify, unhexlify
import mmap
try:
# python3+
PY3 = sys.version_info >= (3,)
if PY3:
from queue import Empty, Full
from urllib.parse import quote, urlencode, urlparse, urlunparse, parse_qsl, quote_plus
from io import StringIO
from sys import maxsize as maxint
except ImportError:
# python2.6+
else:
from Queue import Empty, Full
from urllib import quote, urlencode, quote_plus
from urlparse import urlparse, urlunparse, parse_qsl
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', errors='ignore')
if isinstance(x, bytes):
return x
else:
return x.encode('ISO-8859-1', errors='ignore')
def B(x):
return x.decode('ISO-8859-1', errors='ignore')
if isinstance(x, str):
return x
else:
return x.decode('ISO-8859-1', errors='ignore')
else:
def b(x):
return x
@ -1054,20 +1070,20 @@ def which(program):
return None
def build_logdir(opt_dir, opt_auto):
def build_logdir(opt_dir, opt_auto, assume_yes):
if opt_auto:
return create_time_dir(opt_dir or '/tmp/patator', opt_auto)
elif opt_dir:
return create_dir(opt_dir)
return create_dir(opt_dir, assume_yes)
else:
return None
def create_dir(top_path):
def create_dir(top_path, assume_yes):
top_path = os.path.abspath(top_path)
if os.path.isdir(top_path):
files = os.listdir(top_path)
if files:
if input("Directory '%s' is not empty, do you want to wipe it ? [Y/n]: " % top_path) != 'n':
if assume_yes or 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)
@ -1098,6 +1114,15 @@ def create_time_dir(top_path, desc):
def pprint_seconds(seconds, fmt):
return fmt % reduce(lambda x, y: divmod(x[0], y) + x[1:], [(seconds,), 60, 60])
def repr23(s):
if all(True if 0x20 <= ord(c) < 0x7f else False for c in s):
return s
if PY3:
return repr(s.encode('latin1'))[1:]
else:
return repr(s)
def md5hex(plain):
return hashlib.md5(plain).hexdigest()
@ -1344,6 +1369,7 @@ def flatten(l):
else:
r.append(ppstr(x))
return r
# }}}
# Controller {{{
@ -1464,7 +1490,8 @@ Please read the README inside for more examples and usage information.
exe_grp.add_option('-e', dest='encodings', action='append', default=[], metavar='arg', help='encode everything between two tags, see Syntax below')
exe_grp.add_option('-C', dest='combo_delim', default=':', metavar='str', help="delimiter string in combo files (default is ':')")
exe_grp.add_option('-X', dest='condition_delim', default=',', metavar='str', help="delimiter string in conditions (default is ',')")
exe_grp.add_option('--allow-ignore-failures', action='store_true', default=False, dest='allow_ignore_failures', help="failures cannot be ignored with -x (this is by design to avoid false negatives) this option overrides this safeguard")
exe_grp.add_option('--allow-ignore-failures', dest='allow_ignore_failures', action='store_true', help="failures cannot be ignored with -x (this is by design to avoid false negatives) this option overrides this safeguard")
exe_grp.add_option('-y', dest='assume_yes', action='store_true', help="automatically answer yes for all questions")
opt_grp = OptionGroup(parser, 'Optimization')
opt_grp.add_option('--rate-limit', dest='rate_limit', type='float', default=0, metavar='N', help='wait N seconds between each attempt (default is 0)')
@ -1482,7 +1509,7 @@ Please read the README inside for more examples and usage information.
log_grp.add_option('--hits', dest='hits_file', metavar='FILE', help="save found candidates to FILE")
dbg_grp = OptionGroup(parser, 'Debugging')
dbg_grp.add_option('-d', '--debug', dest='debug', action='store_true', default=False, help='enable debug messages')
dbg_grp.add_option('-d', '--debug', dest='debug', action='store_true', help='enable debug messages')
dbg_grp.add_option('--auto-progress', dest='auto_progress', type='int', default=0, metavar='N', help='automatically display progress every N seconds')
parser.option_groups.extend([exe_grp, opt_grp, log_grp, dbg_grp])
@ -1539,7 +1566,7 @@ Please read the README inside for more examples and usage information.
log_queue = multiprocessing.Queue()
logsvc = multiprocessing.Process(name='LogSvc', target=process_logs, args=(log_queue, module.Response.indicatorsfmt, argv, build_logdir(opts.log_dir, opts.auto_log), opts.runtime_file, opts.csv_file, opts.xml_file, opts.hits_file))
logsvc = multiprocessing.Process(name='LogSvc', target=process_logs, args=(log_queue, module.Response.indicatorsfmt, argv, build_logdir(opts.log_dir, opts.auto_log, opts.assume_yes), opts.runtime_file, opts.csv_file, opts.xml_file, opts.hits_file))
logsvc.daemon = True
logsvc.start()
@ -2244,6 +2271,7 @@ Please read the README inside for more examples and usage information.
total_count,
total_size/num_threads,
p.current))
# }}}
# Response_Base {{{
@ -2312,13 +2340,13 @@ class Response_Base:
return val == self.mesg
def match_fgrep(self, val):
return val in str(self)
return val in self.mesg
def match_egrep(self, val):
return re.search(val, str(self))
return re.search(val, self.mesg)
def dump(self):
return self.trace or str(self)
return b(self.trace or str(self))
def str_target(self):
return ''
@ -2353,7 +2381,7 @@ class TCP_Cache:
)
def __init__(self):
self.cache = {} # {'10.0.0.1:22': ('root', conn1), '10.0.0.2:22': ('admin', conn2),
self.cache = {} # '10.0.0.1:22': ('root', conn1), '10.0.0.2:22': ('admin', conn2),
self.curr = None
def __del__(self):
@ -2569,8 +2597,8 @@ class Telnet_login(TCP_Cache):
'''Brute-force Telnet'''
usage_hints = (
"""%prog host=10.0.0.1 inputs='FILE0\\nFILE1' 0=logins.txt 1=passwords.txt persistent=0"""
""" prompt_re='Username:|Password:' -x ignore:egrep='Login incorrect.+Username:'""",
"""%prog host=10.0.0.1 inputs='FILE0\\nFILE1' 0=logins.txt 1=passwords.txt"""
""" prompt_re='login:|Password:' -x ignore:fgrep='Login incorrect'""",
)
available_options = (
@ -2590,7 +2618,7 @@ class Telnet_login(TCP_Cache):
return TCP_Connection(fp)
def execute(self, host, port='23', inputs=None, prompt_re='\w+:', timeout='20', persistent='1'):
def execute(self, host, port='23', inputs=None, prompt_re='\w+:', timeout='20', persistent='0'):
with Timing() as timing:
fp, _ = self.bind(host, port, timeout=timeout)
@ -2622,10 +2650,7 @@ 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
mesg = B(raw).strip()
return self.Response(0, mesg, timing, trace)
# }}}
@ -2763,10 +2788,6 @@ class SMTP_login(SMTP_Base):
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()
@ -2817,25 +2838,25 @@ class Finger_lookup:
s.send(b(user))
s.send(b'\r\n')
data = b''
raw = b''
with Timing() as timing:
while True:
raw = s.recv(1024)
if not raw:
resp = s.recv(1024)
if not resp:
break
data += raw
raw += resp
s.close()
logger.debug('recv: %r' % data)
logger.debug('recv: %r' % raw)
data = B(data).strip()
mesg = repr(data)
mesg = B(raw).strip()
resp = self.Response(0, mesg, timing, data)
resp.lines = [l.strip('\r\n') for l in data.split('\n')]
resp = self.Response(0, mesg, timing, raw)
resp.lines = [l.strip('\r\n') for l in mesg.split('\n')]
return resp
# }}}
# LDAP {{{
@ -2869,14 +2890,13 @@ class LDAP_login:
def execute(self, host, port='389', binddn='', bindpw='', basedn='', ssl='0'):
uri = 'ldap%s://%s:%s' % ('s' if ssl != '0' else '', host, port)
cmd = ['ldapsearch', '-H', uri, '-e', 'ppolicy', '-D', binddn, '-w', bindpw, '-b', basedn, '-s', 'one']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={'LDAPTLS_REQCERT': 'never'})
out = p.stdout.read()
err = p.stderr.read()
with Timing() as timing:
code = p.wait()
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={'LDAPTLS_REQCERT': 'never'})
out, err = map(B, p.communicate())
code = p.returncode
mesg = repr((out + err).strip())[1:-1]
mesg = (out + err).strip()
trace = '[out]\n%s\n[err]\n%s' % (out, err)
return self.Response(code, mesg, timing, trace)
@ -2888,8 +2908,9 @@ try:
from impacket.smbconnection import SMBConnection, SessionError
from impacket import nt_errors
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
from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED
from impacket.dcerpc.v5.rpcrt import DCERPCException
except ImportError:
notfound.append('impacket')
@ -2900,6 +2921,17 @@ class SMB_Connection(TCP_Connection):
class Response_SMB(Response_Base):
indicatorsfmt = [('code', -8), ('size', -4), ('time', 6)]
def split_ntlm(password_hash):
if password_hash:
if ':' in password_hash:
lmhash, nthash = password_hash.split(':')
else:
lmhash, nthash = 'aad3b435b51404eeaad3b435b51404ee', password_hash
else:
lmhash, nthash = '', ''
return lmhash, nthash
class SMB_login(TCP_Cache):
'''Brute-force SMB'''
@ -2937,12 +2969,9 @@ class SMB_login(TCP_Cache):
else:
with Timing() as timing:
if password_hash:
if ':' in password_hash:
lmhash, nthash = password_hash.split(':')
else:
lmhash, nthash = 'aad3b435b51404eeaad3b435b51404ee', password_hash
fp.login(user, '', domain, lmhash, nthash)
lmhash, nthash = split_ntlm(password_hash)
fp.login(user, '', domain, lmhash, nthash)
else:
fp.login(user, password, domain)
@ -2961,12 +2990,12 @@ class SMB_login(TCP_Cache):
return self.Response(code, mesg, timing)
class DCE_Connection(TCP_Connection):
def __init__(self, fp, smbt):
self.smbt = smbt
def __init__(self, fp, rpct):
self.rpct = rpct
TCP_Connection.__init__(self, fp)
def close(self):
self.smbt.get_socket().close()
self.rpct.get_socket().close()
# impacket/examples/lookupsid.py is much faster because it queries 1000 SIDs per packet
class SMB_lookupsid(TCP_Cache):
@ -2978,24 +3007,32 @@ class SMB_lookupsid(TCP_Cache):
available_options = (
('host', 'target host'),
('port', 'target port [139]'),
('port', 'target port [445]'),
('sid', 'SID to test'),
('rid', 'RID to test'),
('user', 'username to use if auth required'),
('password', 'password to use if auth required'),
('password_hash', "LM/NT hashes to test, at least one hash must be provided ('lm:nt' or ':nt' or 'lm:')"),
('domain', 'domain to test'),
)
available_options += TCP_Cache.available_options
Response = Response_Base
def connect(self, host, port, user, password, sid):
smbt = transport.SMBTransport(host, int(port), r'\lsarpc', user, password)
def connect(self, host, port, user, password, domain, password_hash, sid):
lmhash, nthash = split_ntlm(password_hash)
dce = smbt.get_dce_rpc()
rpctransport = transport.DCERPCTransportFactory(r'ncacn_np:%s[\pipe\lsarpc]' % host) # remoteName
rpctransport.set_dport(port)
rpctransport.setRemoteHost(host)
rpctransport.set_credentials(user, password, domain, lmhash, nthash)
dce = rpctransport.get_dce_rpc()
dce.connect()
dce.bind(lsat.MSRPC_UUID_LSAT)
op2 = lsat.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
op2 = lsad.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
if sid is None:
res = lsad.hLsarQueryInformationPolicy2(dce, op2['PolicyHandle'], lsad.POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation)
@ -3004,11 +3041,11 @@ class SMB_lookupsid(TCP_Cache):
self.sid = sid
self.policy_handle = op2['PolicyHandle']
return DCE_Connection(dce, smbt)
return DCE_Connection(dce, rpctransport)
def execute(self, host, port='139', user='', password='', sid=None, rid=None, persistent='1'):
def execute(self, host, port='445', user='', password='', password_hash=None, domain='', sid=None, rid=None, persistent='1'):
fp, _ = self.bind(host, port, user, password, sid)
dce, _ = self.bind(host, port, user, password, domain, password_hash, sid)
if rid:
sid = '%s-%s' % (self.sid, rid)
@ -3016,24 +3053,32 @@ class SMB_lookupsid(TCP_Cache):
sid = self.sid
try:
res = lsat.hLsarLookupSids(fp, self.policy_handle, [sid], lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
res = lsat.hLsarLookupSids(dce, self.policy_handle, [sid], lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
code, names = 0, []
for n, item in enumerate(res['TranslatedNames']['Names']):
names.append("%s\\%s (%s)" % (res['ReferencedDomains']['Domains'][item['DomainIndex']]['Name'], item['Name'], SID_NAME_USE.enumItems(item['Use']).name[7:]))
except lsat.DCERPCSessionError:
except DCERPCException:
code, names = 1, ['unknown'] # STATUS_NONE_MAPPED
if persistent == '0':
self.reset()
return self.Response(code, ', '.join(names))
# }}}
# POP {{{
from poplib import POP3, POP3_SSL, error_proto as pop_error
class POP_Connection(TCP_Connection):
def close(self):
if PY3:
self.fp.close()
else:
self.fp.quit()
class POP_login(TCP_Cache):
'''Brute-force POP3'''
@ -3063,7 +3108,7 @@ class POP_login(TCP_Cache):
port = 995
fp = POP3_SSL(host, int(port)) # timeout=int(timeout)) # no timeout option in python2
return TCP_Connection(fp, fp.welcome)
return POP_Connection(fp, fp.welcome)
def execute(self, host, port='', ssl='0', user=None, password=None, timeout='10', persistent='1'):
@ -3084,7 +3129,7 @@ class POP_login(TCP_Cache):
except pop_error as e:
logger.debug('pop_error: %s' % e)
resp = e.args[0]
resp = ', '.join(map(B, e.args))
if persistent == '0':
self.reset()
@ -3178,19 +3223,17 @@ class IMAP_login:
with Timing() as timing:
fp = klass(host, port)
code, resp = 0, fp.welcome
code, resp = 0, B(fp.welcome)
try:
if user is not None and password is not None:
with Timing() as timing:
r = fp.login(user, password)
resp = ', '.join(r[1])
# doesn't it need to self.reset() to test more creds?
code, resp = r[0], ', '.join(map(B, r[1]))
except IMAP4.error as e:
logger.debug('imap_error: %s' % e)
code, resp = 1, str(e)
code, resp = 1, ', '.join(map(B, e.args))
return self.Response(code, resp, timing)
@ -3202,8 +3245,8 @@ class Rlogin_login(TCP_Cache):
usage_hints = (
"""Please note that rlogin requires to bind a socket to an Internet domain privileged port.""",
"""%prog host=10.0.0.1 user=root luser=FILE0 0=logins.txt persistent=0 -x ignore:fgrep=Password:""",
"""%prog host=10.0.0.1 user=john password=FILE0 0=passwords.txt -x 'reset:egrep!=Login incorrect.+login:'""",
"""%prog host=10.0.0.1 user=root luser=FILE0 0=logins.txt""",
"""%prog host=10.0.0.1 user=john password=FILE0 0=passwords.txt""",
)
available_options = (
@ -3234,7 +3277,7 @@ class Rlogin_login(TCP_Cache):
return TCP_Connection(fp)
def execute(self, host, port='513', luser='root', user='', password=None, prompt_re='\w+:', timeout='10', persistent='1'):
def execute(self, host, port='513', luser='root', user='', password=None, prompt_re='\w+:', timeout='10', persistent='0'):
fp, _ = self.bind(host, port, timeout=int(timeout))
@ -3249,22 +3292,22 @@ class Rlogin_login(TCP_Cache):
else:
fp.write(b('%s\r' % user))
_, _, resp = fp.expect([prompt_re], timeout=timeout) # expecting the Password: prompt
trace += resp
_, m, raw = fp.expect([prompt_re], timeout=timeout) # expecting the Password: prompt
logger.debug('raw: %r' % raw)
trace += raw
if password is not None:
if m and password is not None:
fp.write(b('%s\r' % password))
_, _, resp = fp.expect([prompt_re], timeout=timeout) # expecting the login: prompt
trace += resp
_, _, raw = fp.expect([prompt_re], timeout=timeout) # expecting the login: prompt
logger.debug('raw: %r' % raw)
trace += raw
if persistent == '0':
self.reset()
resp = B(resp)
trace = B(trace)
mesg = repr(resp.strip())[1:-1]
mesg = B(raw).strip()
return self.Response(0, mesg, timing, trace)
# }}}
# VMauthd {{{
@ -3439,9 +3482,11 @@ class MySQL_query(TCP_Cache):
fp.query(query)
rs = fp.store_result()
rows = rs.fetch_row(10, 0)
rows = rs.fetch_row(maxrows=0, how=0)
logger.debug('fetched %d rows: %s' % (len(rows), rows))
code, mesg = '0', '\n'.join(', '.join(map(str, r)) for r in filter(any, rows))
code, mesg = '0', '\n'.join(', '.join(map(B, r)) for r in filter(any, rows))
return self.Response(code, mesg, timing)
# }}}
@ -3501,6 +3546,7 @@ class MSSQL_login:
fp.disconnect()
return self.Response(code, mesg, timing)
# }}}
# Oracle {{{
@ -3589,7 +3635,7 @@ class Pgsql_login:
except psycopg2.OperationalError as e:
logger.debug('OperationalError: %s' % e)
code, mesg = '1', str(e)[:-1]
code, mesg = '1', str(e).strip()
return self.Response(code, mesg, timing)
@ -3636,9 +3682,6 @@ class Response_HTTP(Response_Base):
def match_clen(self, val):
return match_range(self.content_length, val)
def match_fgrep(self, val):
return val in self.mesg
def match_egrep(self, val):
return re.search(val, self.mesg, re.M)
@ -4065,7 +4108,7 @@ class AJP_fuzz(TCP_Cache):
# }}}
# {{{ RDP
# RDP {{{
if not which('xfreerdp'):
notfound.append('xfreerdp')
@ -4101,10 +4144,11 @@ class RDP_login:
elif 'Authentication only, exit status 0' in err:
err = 'OK'
mesg = repr((out + err).strip())[1:-1]
mesg = (out + err).strip()
trace = '[out]\n%s\n[err]\n%s' % (out, err)
return self.Response(code, mesg, timing, trace)
# }}}
# VNC {{{
@ -4340,16 +4384,17 @@ def generate_srv():
import re
files = ['/usr/share/nmap/nmap-protocols', '/usr/share/nmap/nmap-services', '/etc/protocols', '/etc/services']
ret = []
for f in files:
if not os.path.isfile(f):
logger.warn("File '%s' is missing, there will be less records to test" % f)
for filepath in files:
if not os.path.isfile(filepath):
logger.warn("File '%s' is missing, there will be less records to test" % filepath)
continue
for line in open(f):
match = re.match(r'([a-zA-Z0-9]+)\s', line)
if not match:
continue
for w in re.split(r'[^a-z0-9]', match.group(1).strip().lower()):
ret.extend(['_%s.%s' % (w, i) for i in ('_tcp', '_udp')])
with open(filepath, 'rb') as f:
for line in f:
match = re.match(r'([a-zA-Z0-9]+)\s', B(line))
if not match:
continue
for w in re.split(r'[^a-z0-9]', match.group(1).strip().lower()):
ret.extend(['_%s.%s' % (w, i) for i in ('_tcp', '_udp')])
return ret
srv = set(common + distro())
@ -4827,7 +4872,7 @@ class Unzip_pass:
out, err = map(B, p.communicate())
code = p.returncode
mesg = repr(out.strip())[1:-1]
mesg = out.strip()
trace = '%s\n[out]\n%s\n[err]\n%s' % (cmd, out, err)
return self.Response(code, mesg, timing, trace)
@ -4864,7 +4909,7 @@ class Keystore_pass:
out, err = map(B, p.communicate())
code = p.returncode
mesg = repr(out.strip())[1:-1]
mesg = out.strip()
trace = '%s\n[out]\n%s\n[err]\n%s' % (cmd, out, err)
return self.Response(code, mesg, timing, trace)
@ -4873,7 +4918,10 @@ class Keystore_pass:
# SQLCipher {{{
try:
from pysqlcipher3 import dbapi2 as sqlcipher
if PY3:
from pysqlcipher3 import dbapi2 as sqlcipher
else:
from pysqlcipher import dbapi2 as sqlcipher
except ImportError:
notfound.append('pysqlcipher')
@ -4964,12 +5012,14 @@ class TCP_fuzz:
if ssl != '0':
fp = wrap_socket(fp)
fp.send(unhexlify(data))
#fp.send(b(data))
with Timing() as timing:
resp = fp.recv(1024)
fp.close()
code = 0
mesg = B(hexlify(resp))
#mesg = B(hexlify(resp))
mesg = B(resp)
return self.Response(code, mesg, timing)
@ -5076,6 +5126,7 @@ dependencies = {
'pysqlcipher': [('sqlcipher_pass',), 'https://github.com/rigglemania/pysqlcipher3', '1.0.3'],
'python': [('ftp_login',), 'Patator requires Python 3.6 or above and may still work on Python 2.'],
}
# }}}
# main {{{

@ -0,0 +1,108 @@
#!/bin/bash
case "$1" in
python2|python3)
PYTHON=$1
;;
*)
docker-compose up -d --build
$0 python3
$0 python2
exit 0
;;
esac
UNIX='unix'
ORACLE='oracle'
MSSQL='mssql'
LOGS='-l ./asdf -y --hits ./hits.txt'
run()
{
echo
echo "$ $@"
docker-compose run --rm --entrypoint "$PYTHON patator.py" patator "$@"
}
echo
echo ">>> $PYTHON"
run ftp_login host=$UNIX
run ftp_login host=$UNIX user=userRANGE0 password=PasswordRANGE0 0=int:0-9
run ssh_login host=$UNIX
run ssh_login host=$UNIX user=userRANGE0 password=PasswordRANGE0 0=int:0-9
run telnet_login host=$UNIX
run telnet_login host=$UNIX inputs='userRANGE0\nPasswordRANGE0' 0=int:0-9 prompt_re='login:|Password:' timeout=5
run smtp_vrfy host=$UNIX
run smtp_vrfy host=$UNIX user=userRANGE0 0=int:1-500 -x ignore:fgrep='User unknown' -x ignore,reset,retry:code=421 --auto-progress 10
run smtp_rcpt host=$UNIX
run smtp_rcpt host=$UNIX mail_from=root@localhost user=userRANGE0@localhost 0=int:1-200 -x ignore:fgrep='User unknown'
run smtp_login host=$UNIX
run smtp_login host=$UNIX user=userRANGE0 password=PasswordRANGE0 0=int:0-30 starttls=1 #-x ignore,reset,retry:code=421
run finger_lookup host=$UNIX
run finger_lookup host=$UNIX user=userRANGE0 0=int:0-20 -x ignore:fgrep='no such user'
run ldap_login host=$UNIX
run ldap_login host=$UNIX binddn='cn=admin,dc=example,dc=com' bindpw=PasswordRANGE0 0=int:0-9 basedn='dc=example,dc=com'
run smb_login host=$UNIX
run smb_login host=$UNIX user=userRANGE0 password=PasswordRANGE0 0=int:0-9
run pop_login host=$UNIX
run pop_login host=$UNIX user=userRANGE0 password=PasswordRANGE0 0=int:0-9
run pop_passd host=$UNIX
run pop_passd host=$UNIX user=userRANGE0 password=PasswordRANGE0 0=int:0-9
run imap_login host=$UNIX
run imap_login host=$UNIX user=userRANGE0 password=PasswordRANGE0 0=int:0-9
run rlogin_login host=$UNIX user=userRANGE0 password=PasswordRANGE0 0=int:0-9
run mysql_login host=$UNIX
run mysql_login host=$UNIX user=root password=PasswordRANGE0 0=int:0-9
run mysql_query host=$UNIX user=root password=Password1 query='select host, user from mysql.user'
run mysql_query host=$UNIX user=root password=Password1 query='select load_file("/etc/hosts")'
run mssql_login host=$MSSQL user=sa password=PasswordRANGE0 0=int:0-9
run oracle_login host=$ORACLE sid=xRANGE0 0=lower:a-f -t 1
run oracle_login host=$ORACLE sid=xe user=sys password=oraclRANGE0 0=lower:a-f
run pgsql_login host=$UNIX
run pgsql_login host=$UNIX user=postgres password=PasswordRANGE0 0=int:0-9
run http_fuzz url="http://$UNIX/RANGE0" 0=lower:a-zzz -x ignore:code=404
run http_fuzz url=http://$UNIX:8080/manager/html user_pass=tomcat:PasswordRANGE0 0=int:0-9
run ajp_fuzz url=ajp://$UNIX/manager/html user_pass=tomcat:PasswordRANGE0 0=int:0-9
run vnc_login host=$UNIX port=5900 password=PassworRANGE0 0=lower:a-f
run dns_reverse host=NET0 0=216.239.32.0-216.239.32.255,8.8.8.0/24 -x ignore:code=3 -x ignore:fgrep!=google.com -x ignore:fgrep=216-239-
run dns_forward name=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3 --auto-progress 15
run unzip_pass zipfile=enc.zip password=PasswordRANGE0 0=int:0-9
run keystore_pass keystore=keystore.jks password=PasswordRANGE0 0=int:0-9
run sqlcipher_pass database=enc.db password=PasswordRANGE0 0=int:0-9
run umbraco_crack hashlist=@umbraco_users.pw password=PasswordRANGE0 0=int:0-9
run tcp_fuzz host=$UNIX port=4444 data=RANGE0 0=hex:0xf0-0xf9 # $LOGS
echo -e '\xde\xad\xbe\xef\nprintable ascii' > dummy.txt
run dummy_test delay=0 data=FILE0 0=dummy.txt data2=RANGE1 1=lower:a-b
echo -e 'wrong pass\np\x1fssw\x09rd' > user9.pass
run ssh_login host=unix user=user9 password=FILE0 0=user9.pass
rm -f dummy.txt user9.pass

@ -0,0 +1,118 @@
FROM ubuntu:18.04
MAINTAINER Sebastien Macke <lanjelot@gmail.com>
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN { for i in {3..5}; do useradd -m -s /bin/bash user$i; echo -e "Password$i\nPassword$i" | passwd user$i; done; } \
&& useradd -m user9 && echo -e 'p\x1fssw\x09rd\np\x1fssw\x09rd' | passwd user9
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y vsftpd openssh-server telnetd rsh-redone-server fingerd apache2 socat
RUN { echo "postfix postfix/mailname string ubuntu-bionic"; \
echo "postfix postfix/main_mailer_type string 'Internet Site'"; \
} | debconf-set-selections \
&& apt-get update && apt-get install -y postfix mail-stack-delivery \
&& postconf -e 'smtpd_sasl_exceptions_networks='
RUN echo 'ServerName localhost' >> /etc/apache2/apache2.conf \
&& mkdir /var/www/html/{wp,pma,bak} && echo secret > /var/www/html/key
RUN LDAPPW=Password1; \
{ \
echo slapd slapd/internal/generated_adminpw password $LDAPPW; \
echo slapd slapd/password2 password $LDAPPW; \
echo slapd slapd/internal/adminpw password $LDAPPW; \
echo slapd slapd/password1 password $LDAPPW; \
echo slapd slapd/domain string example.com; \
echo slapd shared/organization string example.com; \
} | debconf-set-selections \
&& apt-get update && apt-get install -y slapd ldap-utils
RUN MYSRP=Password1; \
{ echo "mysql-server mysql-server/root_password password $MYSRP"; \
echo "mysql-server mysql-server/root_password_again password $MYSRP"; \
} | debconf-set-selections \
&& apt-get update && apt-get install -y mysql-server \
&& sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/mysql.conf.d/mysqld.cnf \
&& echo secure_file_priv= >> /etc/mysql/mysql.conf.d/mysqld.cnf \
&& Q1="GRANT ALL ON *.* TO 'root'@'%' IDENTIFIED BY '$MYSRP' WITH GRANT OPTION;" \
&& Q2="FLUSH PRIVILEGES;" \
&& SQL="${Q1}${Q2}" \
&& rm -f /etc/apparmor.d/usr.sbin.mysqld \
&& service mysql start \
&& mysql -uroot -p"$MYSRP" -e "$SQL"
RUN PGPW=Password1 \
&& apt-get update && apt-get install -y postgresql \
&& sed -ie 's,127.0.0.1/32,0.0.0.0/0,' /etc/postgresql/10/main/pg_hba.conf \
&& sed -ie "s,^#listen_addresses = 'localhost',listen_addresses = '*'," /etc/postgresql/10/main/postgresql.conf \
&& service postgresql start \
&& su - postgres -c "psql -c \"ALTER USER postgres WITH PASSWORD '$PGPW';\" -c '\\q'" \
&& su - postgres -c "PGPASSWORD='$PGPW' psql -d postgres -w --no-password -h localhost -p 5432 -t -c 'SELECT version()'"
RUN apt-get update && apt-get install -y tomcat9 tomcat9-admin \
&& TOMCATPW=Password1 \
&& echo '<?xml version="1.0" encoding="UTF-8"?><tomcat-users xmlns="http://tomcat.apache.org/xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd" version="1.0"><user username="tomcat" password="Password1" roles="manager-gui"/></tomcat-users>' > /etc/tomcat9/tomcat-users.xml \
&& sed -ie 's,^.*Define an AJP .* Connector on port.*$,<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />,' /etc/tomcat9/server.xml \
&& sed -ie 's,catalina.realm.LockOutRealm",catalina.realm.LockOutRealm" lockOutTime="0",' /etc/tomcat9/server.xml \
&& echo -e "#!/bin/bash\n\
export CATALINA_HOME=/usr/share/tomcat9\n\
export CATALINA_BASE=/var/lib/tomcat9\n\
export CATALINA_TMPDIR=/tmp\n\
export SECURITY_MANAGER=true\n\
export JAVA_OPTS=-Djava.awt.headless=true\n\
/usr/libexec/tomcat9/tomcat-update-policy.sh\n\
/usr/libexec/tomcat9/tomcat-start.sh &\n" > /usr/local/sbin/start-tomcat.sh
RUN apt-get update && apt-get install -y dovecot-imapd dovecot-pop3d poppassd \
&& sed -ie 's,^#login_trusted_networks = *$,login_trusted_networks = 0.0.0.0/0,' /etc/dovecot/dovecot.conf
RUN apt-get update && apt-get install -y p7zip-full \
&& 7za a -pPassword1 /root/enc.zip /etc/passwd
RUN apt-get update && apt-get install -y openjdk-11-jre-headless \
&& keytool -genkey -alias test -storepass Password1 -keypass Password1 -keystore /root/keystore.jks -dname "CN=a,OU=b,O=c,L=d,ST=e,C=f"
RUN apt-get update && apt-get install -y sqlcipher \
&& sqlcipher /root/enc.db "PRAGMA key = 'Password1';create table a(id int);"
RUN echo -e 'user1:kW+7AlKMnSZQIRluNxwJOMiohAw=\nuser2:oBk37hmkFgZdZ247+g6c0Ay6Vw8=\nuser3:kW+7AlKMnSZQIRluNxwJOMiohAw=' > /root/umbraco_users.pw
RUN apt-get update && apt-get install -y tightvncserver \
&& useradd -m vncuser && mkdir ~vncuser/.vnc && echo Password | vncpasswd -f > ~vncuser/.vnc/passwd \
&& chmod 400 ~vncuser/.vnc/passwd && chown -R vncuser:vncuser ~vncuser/.vnc
# utils
RUN sed -i 's:^path-exclude=/usr/share/man:#path-exclude=/usr/share/man:' /etc/dpkg/dpkg.cfg.d/excludes \
&& apt-get update && apt-get install -y man manpages-posix iproute2 mlocate lsof sudo vim less \
telnet finger rsh-client smbclient \
&& echo 'set bg=dark' > /root/.vimrc \
&& usermod -aG sudo user3
RUN apt-get update && apt-get install -y samba \
&& { for i in {3..5}; do echo -e "Password$i\nPassword$i" | smbpasswd -a "user$i"; done; } \
&& sed -ie 's,map to guest =,#map to guest =,' /etc/samba/smb.conf
RUN echo -e "echo Starting services\n\
service vsftpd start\n\
service ssh start\n\
/usr/sbin/inetd\n\
service postfix start\n\
service dovecot start\n\
service apache2 start\n\
service slapd start\n\
service mysql start\n\
service postgresql start\n\
bash /usr/local/sbin/start-tomcat.sh\n\
socat tcp-l:106,fork,reuseaddr exec:/usr/sbin/poppassd &\n\
socat tcp-l:4444,fork,reuseaddr exec:\"echo -e 'W\xe1\xc0me'\" &\n\
cp -v /root/enc.zip /root/keystore.jks /root/enc.db /root/umbraco_users.pw /opt/patator/\n\
su - vncuser -c 'vncserver -rfbport 5900'\n\
service smbd start\n\
touch /opt/patator/.all-started\n\
tail -f /dev/null\n" > /usr/local/sbin/start-all-services.sh
CMD ["bash", "/usr/local/sbin/start-all-services.sh"]
Loading…
Cancel
Save