Guest User

Patator

a guest
Jun 23rd, 2016
2,026
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 99.61 KB | None | 0 0
  1. # }}}
  2. # logging {{{
  3. class Logger:
  4. def __init__(self, pipe):
  5. self.pipe = pipe
  6. self.name = multiprocessing.current_process().name
  7. # neat but wont work on windows
  8. # def __getattr__(self, action):
  9. # def send(*args):
  10. # self.pipe.send((self.name, action, args))
  11. # return send
  12. def send(self, action, *args):
  13. self.pipe.send((self.name, action, args))
  14. def quit(self):
  15. self.send('quit')
  16. def headers(self):
  17. self.send('headers')
  18. def result(self, *args):
  19. self.send('result', *args)
  20. def save(self, *args):
  21. self.send('save', *args)
  22. def setLevel(self, level):
  23. self.send('setLevel', level)
  24. def warn(self, msg):
  25. self.send('warn', msg)
  26. def info(self, msg):
  27. self.send('info', msg)
  28. def debug(self, msg):
  29. self.send('debug', msg)
  30. import logging
  31. class TXTFormatter(logging.Formatter):
  32. def __init__(self, indicatorsfmt):
  33. self.resultfmt = '%(asctime)s %(name)-7s %(levelname)7s - ' + ' '.join('%%(%s)%ss' % (k, v) for k, v in indicatorsfmt) + ' | %(candidate)-34s | %(num)5s | %(mesg)s'
  34. logging.Formatter.__init__(self, datefmt='%H:%M:%S')
  35. def format(self, record):
  36. if not record.msg or record.msg == 'headers':
  37. self._fmt = self.resultfmt
  38. if not all(True if 0x20 <= ord(c) < 0x7f else False for c in record.candidate):
  39. record.candidate = repr(record.candidate)
  40. else:
  41. if record.levelno == logging.DEBUG:
  42. self._fmt = '%(asctime)s %(name)-7s %(levelname)7s [%(pname)s] %(message)s'
  43. else:
  44. self._fmt = '%(asctime)s %(name)-7s %(levelname)7s - %(message)s'
  45. return logging.Formatter.format(self, record)
  46. class CSVFormatter(logging.Formatter):
  47. def __init__(self, indicatorsfmt):
  48. fmt = '%(asctime)s,%(levelname)s,'+','.join('%%(%s)s' % name for name, _ in indicatorsfmt)+',%(candidate)s,%(num)s,%(mesg)s'
  49. logging.Formatter.__init__(self, fmt, datefmt='%H:%M:%S')
  50. def format(self, record):
  51. for k in ['candidate', 'mesg']:
  52. record.__dict__[k] = '"%s"' % record.__dict__[k].replace('"', '""')
  53. return logging.Formatter.format(self, record)
  54. class XMLFormatter(logging.Formatter):
  55. def __init__(self, indicatorsfmt):
  56. fmt = '''<result time="%(asctime)s" level="%(levelname)s">
  57. ''' + '\n'.join(' <{0}>%({1})s</{0}>'.format(name.replace(':', '_'), name) for name, _ in indicatorsfmt) + '''
  58. <candidate>%(candidate)s</candidate>
  59. <num>%(num)s</num>
  60. <mesg>%(mesg)s</mesg>
  61. <target %(target)s/>
  62. </result>'''
  63. logging.Formatter.__init__(self, fmt, datefmt='%H:%M:%S')
  64. def format(self, record):
  65. for k, v in record.__dict__.iteritems():
  66. if isinstance(v, basestring):
  67. record.__dict__[k] = xmlescape(v)
  68. return super(XMLFormatter, self).format(record)
  69. class MsgFilter(logging.Filter):
  70. def filter(self, record):
  71. if record.msg:
  72. return 0
  73. else:
  74. return 1
  75. def process_logs(pipe, indicatorsfmt, argv, log_dir):
  76. ignore_ctrlc()
  77. try:
  78. # python3
  79. logging._levelToName[logging.ERROR] = 'FAIL'
  80. except:
  81. # python2
  82. logging._levelNames[logging.ERROR] = 'FAIL'
  83. handler_out = logging.StreamHandler()
  84. handler_out.setFormatter(TXTFormatter(indicatorsfmt))
  85. logger = logging.getLogger('patator')
  86. logger.setLevel(logging.DEBUG)
  87. logger.addHandler(handler_out)
  88. names = [name for name, _ in indicatorsfmt] + ['candidate', 'num', 'mesg']
  89. if log_dir:
  90. runtime_log = os.path.join(log_dir, 'RUNTIME.log')
  91. results_csv = os.path.join(log_dir, 'RESULTS.csv')
  92. results_xml = os.path.join(log_dir, 'RESULTS.xml')
  93. with open(runtime_log, 'a') as f:
  94. f.write('$ %s\n' % ' '.join(argv))
  95. if not os.path.exists(results_csv):
  96. with open(results_csv, 'w') as f:
  97. f.write('time,level,%s\n' % ','.join(names))
  98. if not os.path.exists(results_xml):
  99. with open(results_xml, 'w') as f:
  100. f.write('<?xml version="1.0" encoding="UTF-8"?>\n<root>\n')
  101. f.write('<start utc=%s local=%s/>\n' % (xmlquoteattr(strfutctime()), xmlquoteattr(strflocaltime())))
  102. f.write('<cmdline>%s</cmdline>\n' % xmlescape(' '.join(argv)))
  103. f.write('<module>%s</module>\n' % xmlescape(argv[0]))
  104. f.write('<options>\n')
  105. i = 0
  106. del argv[0]
  107. while i < len(argv):
  108. arg = argv[i]
  109. if arg[0] == '-':
  110. if arg in ('-d', '--debug'):
  111. f.write(' <option type="global" name=%s/>\n' % xmlquoteattr(arg))
  112. else:
  113. if not arg.startswith('--') and len(arg) > 2:
  114. name, value = arg[:2], arg[2:]
  115. elif '=' in arg:
  116. name, value = arg.split('=', 1)
  117. else:
  118. name, value = arg, argv[i+1]
  119. i += 1
  120. f.write(' <option type="global" name=%s>%s</option>\n' % (xmlquoteattr(name), xmlescape(value)))
  121. else:
  122. name, value = arg.split('=', 1)
  123. f.write(' <option type="module" name=%s>%s</option>\n' % (xmlquoteattr(name), xmlescape(value)))
  124. i += 1
  125. f.write('</options>\n')
  126. f.write('<results>\n')
  127. else: # remove "</results>...</root>"
  128. with open(results_xml, 'r+') as f:
  129. f.seek(f.read().find('</results>'))
  130. f.truncate(f.tell())
  131. handler_log = logging.FileHandler(runtime_log)
  132. handler_csv = logging.FileHandler(results_csv)
  133. handler_xml = logging.FileHandler(results_xml)
  134. handler_csv.addFilter(MsgFilter())
  135. handler_xml.addFilter(MsgFilter())
  136. handler_log.setFormatter(TXTFormatter(indicatorsfmt))
  137. handler_csv.setFormatter(CSVFormatter(indicatorsfmt))
  138. handler_xml.setFormatter(XMLFormatter(indicatorsfmt))
  139. logger.addHandler(handler_log)
  140. logger.addHandler(handler_csv)
  141. logger.addHandler(handler_xml)
  142. while True:
  143. pname, action, args = pipe.recv()
  144. if action == 'quit':
  145. if log_dir:
  146. with open(os.path.join(log_dir, 'RESULTS.xml'), 'a') as f:
  147. f.write('</results>\n<stop utc=%s local=%s/>\n</root>\n' % (xmlquoteattr(strfutctime()), xmlquoteattr(strflocaltime())))
  148. break
  149. elif action == 'headers':
  150. logger.info(' '*77)
  151. logger.info('headers', extra=dict((n, n) for n in names))
  152. logger.info('-'*77)
  153. elif action == 'result':
  154. typ, resp, candidate, num = args
  155. results = [(name, value) for (name, _), value in zip(indicatorsfmt, resp.indicators())]
  156. results += [('candidate', candidate), ('num', num), ('mesg', str(resp)), ('target', resp.str_target())]
  157. if typ == 'fail':
  158. logger.error(None, extra=dict(results))
  159. else:
  160. logger.info(None, extra=dict(results))
  161. elif action == 'save':
  162. resp, num = args
  163. if log_dir:
  164. filename = '%d_%s' % (num, '-'.join(map(str, resp.indicators())))
  165. with open('%s.txt' % os.path.join(log_dir, filename), 'w') as f:
  166. f.write(resp.dump())
  167. elif action == 'setLevel':
  168. logger.setLevel(args[0])
  169. else: # 'warn', 'info', 'debug'
  170. getattr(logger, action)(args[0], extra={'pname': pname})
  171. # }}}
  172. # imports {{{
  173. import re
  174. import os
  175. import sys
  176. from time import localtime, gmtime, strftime, sleep, time
  177. from platform import system
  178. from functools import reduce
  179. from select import select
  180. from itertools import islice
  181. import string
  182. import random
  183. from decimal import Decimal
  184. from base64 import b64encode
  185. from datetime import timedelta, datetime
  186. from struct import unpack
  187. import socket
  188. import subprocess
  189. import hashlib
  190. from collections import defaultdict
  191. import multiprocessing
  192. import signal
  193. import ctypes
  194. from xml.sax.saxutils import escape as xmlescape, quoteattr as xmlquoteattr
  195. try:
  196. # python3+
  197. from queue import Empty, Full
  198. from urllib.parse import quote, urlencode, urlparse, urlunparse, parse_qsl, quote_plus
  199. from io import StringIO
  200. from sys import maxsize as maxint
  201. except ImportError:
  202. # python2.6+
  203. from Queue import Empty, Full
  204. from urllib import quote, urlencode, quote_plus
  205. from urlparse import urlparse, urlunparse, parse_qsl
  206. from cStringIO import StringIO
  207. from sys import maxint
  208. notfound = []
  209. try:
  210. from IPy import IP
  211. has_ipy = True
  212. except ImportError:
  213. has_ipy = False
  214. notfound.append('IPy')
  215. import multiprocessing.forking
  216. class _Popen(multiprocessing.forking.Popen):
  217. def __init__(self, *args, **kw):
  218. if hasattr(sys, 'frozen'):
  219. # We have to set original _MEIPASS2 value from sys._MEIPASS
  220. # to get --onefile mode working.
  221. os.putenv('_MEIPASS2', sys._MEIPASS)
  222. try:
  223. super(_Popen, self).__init__(*args, **kw)
  224. finally:
  225. if hasattr(sys, 'frozen'):
  226. # On some platforms (e.g. AIX) 'os.unsetenv()' is not
  227. # available. In those cases we cannot delete the variable
  228. # but only set it to the empty string. The bootloader
  229. # can handle this case.
  230. if hasattr(os, 'unsetenv'):
  231. os.unsetenv('_MEIPASS2')
  232. else:
  233. os.putenv('_MEIPASS2', '')
  234. class Process(multiprocessing.Process):
  235. _Popen = _Popen
  236. # So BaseManager.start() uses this new Process class
  237. multiprocessing.Process = Process
  238. from multiprocessing.managers import SyncManager
  239. # imports }}}
  240. # utils {{{
  241. def strfutctime():
  242. return strftime("%Y-%m-%d %H:%M:%S", gmtime())
  243. def strflocaltime():
  244. return strftime("%Y-%m-%d %H:%M:%S %Z", localtime())
  245. def which(program):
  246. def is_exe(fpath):
  247. return os.path.exists(fpath) and os.access(fpath, os.X_OK)
  248. fpath, fname = os.path.split(program)
  249. if on_windows() and fname[-4:] != '.exe' :
  250. fname += '.exe'
  251. if fpath:
  252. if is_exe(program):
  253. return program
  254. else:
  255. for path in os.environ["PATH"].split(os.pathsep):
  256. exe_file = os.path.join(path, fname)
  257. if is_exe(exe_file):
  258. return exe_file
  259. return None
  260. def build_logdir(opt_dir, opt_auto):
  261. if opt_auto:
  262. return create_time_dir(opt_dir or '/tmp/patator', opt_auto)
  263. elif opt_dir:
  264. return create_dir(opt_dir)
  265. else:
  266. return None
  267. def create_dir(top_path):
  268. top_path = os.path.abspath(top_path)
  269. if os.path.isdir(top_path):
  270. files = os.listdir(top_path)
  271. if files:
  272. if raw_input("Directory '%s' is not empty, do you want to wipe it ? [Y/n]: " % top_path) != 'n':
  273. for root, dirs, files in os.walk(top_path):
  274. if dirs:
  275. print("Directory '%s' contains sub-directories, safely aborting..." % root)
  276. sys.exit(0)
  277. for f in files:
  278. os.unlink(os.path.join(root, f))
  279. break
  280. else:
  281. os.mkdir(top_path)
  282. return top_path
  283. def create_time_dir(top_path, desc):
  284. now = localtime()
  285. date, time = strftime('%Y-%m-%d', now), strftime('%H%M%S', now)
  286. top_path = os.path.abspath(top_path)
  287. date_path = os.path.join(top_path, date)
  288. time_path = os.path.join(top_path, date, time + '_' + desc)
  289. if not os.path.isdir(top_path):
  290. os.makedirs(top_path)
  291. if not os.path.isdir(date_path):
  292. os.mkdir(date_path)
  293. if not os.path.isdir(time_path):
  294. os.mkdir(time_path)
  295. return time_path
  296. def pprint_seconds(seconds, fmt):
  297. return fmt % reduce(lambda x,y: divmod(x[0], y) + x[1:], [(seconds,),60,60])
  298. def md5hex(plain):
  299. return hashlib.md5(plain).hexdigest()
  300. def sha1hex(plain):
  301. return hashlib.sha1(plain).hexdigest()
  302. # I rewrote itertools.product to avoid memory over-consumption when using large wordlists
  303. def product(xs, *rest):
  304. if len(rest) == 0:
  305. for x in xs():
  306. yield [x]
  307. else:
  308. for head in xs():
  309. for tail in product(*rest):
  310. yield [head] + tail
  311. def chain(*iterables):
  312. def xs():
  313. for iterable in iterables:
  314. for element in iterable:
  315. yield element
  316. return xs
  317. class FileIter:
  318. def __init__(self, filename):
  319. self.filename = filename
  320. def __iter__(self):
  321. return open(self.filename)
  322. def padhex(d):
  323. x = '%x' % d
  324. return '0' * (len(x) % 2) + x
  325. # These are examples. You can easily write your own iterator to fit your needs.
  326. # Or using the PROG keyword, you can call an external program such as:
  327. # - seq(1) from coreutils
  328. # - http://hashcat.net/wiki/doku.php?id=maskprocessor
  329. # - john -stdout -i
  330. # For example:
  331. # $ ./dummy_test data=PROG0 0='seq 1 80'
  332. # $ ./dummy_test data=PROG0 0='mp64.bin ?l?l?l',$(mp64.bin --combination ?l?l?l)
  333. class RangeIter:
  334. def __init__(self, typ, rng, random=None):
  335. if typ not in ['hex', 'int', 'float', 'letters', 'lower', 'lowercase', 'upper', 'uppercase']:
  336. raise ValueError("Incorrect range type '%s'" % typ)
  337. if typ in ('hex', 'int', 'float'):
  338. m = re.match('(-?[^-]+)-(-?[^-]+)$', rng) # 5-50 or -5-50 or 5--50 or -5--50
  339. if not m:
  340. raise ValueError("Unsupported range '%s'" % rng)
  341. mn = m.group(1)
  342. mx = m.group(2)
  343. if typ in ('hex', 'int'):
  344. mn = int(mn, 16 if '0x' in mn else 10)
  345. mx = int(mx, 16 if '0x' in mx else 10)
  346. if typ == 'hex':
  347. fmt = padhex
  348. elif typ == 'int':
  349. fmt = '%d'
  350. elif typ == 'float':
  351. mn = Decimal(mn)
  352. mx = Decimal(mx)
  353. if mn > mx:
  354. step = -1
  355. else:
  356. step = 1
  357. elif typ == 'letters':
  358. charset = [c for c in string.letters]
  359. elif typ in ('lower', 'lowercase'):
  360. charset = [c for c in string.lowercase]
  361. elif typ in ('upper', 'uppercase'):
  362. charset = [c for c in string.uppercase]
  363. def zrange(start, stop, step, fmt):
  364. x = start
  365. while x != stop+step:
  366. if callable(fmt):
  367. yield fmt(x)
  368. else:
  369. yield fmt % x
  370. x += step
  371. def letterrange(first, last, charset):
  372. for k in range(len(last)):
  373. for x in product(*[chain(charset)]*(k+1)):
  374. result = ''.join(x)
  375. if first:
  376. if first != result:
  377. continue
  378. else:
  379. first = None
  380. yield result
  381. if result == last:
  382. return
  383. if typ == 'float':
  384. precision = max(len(str(x).partition('.')[-1]) for x in (mn, mx))
  385. fmt = '%%.%df' % precision
  386. exp = 10**precision
  387. step *= Decimal(1) / exp
  388. self.generator = zrange, (mn, mx, step, fmt)
  389. self.size = int(abs(mx-mn) * exp) + 1
  390. def random_generator():
  391. while True:
  392. yield fmt % (Decimal(random.randint(mn*exp, mx*exp)) / exp)
  393. elif typ in ('hex', 'int'):
  394. self.generator = zrange, (mn, mx, step, fmt)
  395. self.size = abs(mx-mn) + 1
  396. def random_generator():
  397. while True:
  398. yield fmt % random.randint(mn, mx)
  399. else: # letters, lower, upper
  400. def count(f):
  401. total = 0
  402. i = 0
  403. for c in f[::-1]:
  404. z = charset.index(c) + 1
  405. total += (len(charset)**i)*z
  406. i += 1
  407. return total + 1
  408. first, last = rng.split('-')
  409. self.generator = letterrange, (first, last, charset)
  410. self.size = count(last) - count(first) + 1
  411. if random:
  412. self.generator = random_generator, ()
  413. self.size = maxint
  414. def __iter__(self):
  415. fn, args = self.generator
  416. return fn(*args)
  417. def __len__(self):
  418. return self.size
  419. class ProgIter:
  420. def __init__(self, prog):
  421. self.prog = prog
  422. def __iter__(self):
  423. p = subprocess.Popen(self.prog.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  424. return p.stdout
  425. class Progress:
  426. def __init__(self):
  427. self.current = ''
  428. self.done_count = 0
  429. self.hits_count = 0
  430. self.skip_count = 0
  431. self.fail_count = 0
  432. self.seconds = [1]*25 # avoid division by zero early bug condition
  433. class TimeoutError(Exception):
  434. pass
  435. def on_windows():
  436. return 'Win' in system()
  437. def ignore_ctrlc():
  438. if on_windows():
  439. ctypes.windll.kernel32.SetConsoleCtrlHandler(0, 1)
  440. else:
  441. signal.signal(signal.SIGINT, signal.SIG_IGN)
  442. def handle_alarm():
  443. if not on_windows():
  444. signal.signal(signal.SIGALRM, raise_timeout)
  445. def raise_timeout(signum, frame):
  446. if signum == signal.SIGALRM:
  447. raise TimeoutError('timed out')
  448. def enable_alarm(timeout):
  449. if not on_windows():
  450. signal.alarm(timeout)
  451. def disable_alarm():
  452. if not on_windows():
  453. signal.alarm(0)
  454. # SyncManager.start(initializer) only available since python2.7
  455. class MyManager(SyncManager):
  456. @classmethod
  457. def _run_server(cls, registry, address, authkey, serializer, writer, initializer=None, initargs=()):
  458. ignore_ctrlc()
  459. super(MyManager, cls)._run_server(registry, address, authkey, serializer, writer)
  460. # }}}
  461. # Controller {{{
  462. class Controller:
  463. builtin_actions = (
  464. ('ignore', 'do not report'),
  465. ('retry', 'try payload again'),
  466. ('free', 'dismiss future similar payloads'),
  467. ('quit', 'terminate execution now'),
  468. )
  469. available_encodings = {
  470. 'hex': (lambda s: s.encode('hex'), 'encode in hexadecimal'),
  471. 'unhex': (lambda s: s.decode('hex'), 'decode from hexadecimal'),
  472. 'b64': (b64encode, 'encode in base64'),
  473. 'md5': (md5hex, 'hash in md5'),
  474. 'sha1': (sha1hex, 'hash in sha1'),
  475. 'url': (quote_plus, 'url encode'),
  476. }
  477. def expand_key(self, arg):
  478. yield arg.split('=', 1)
  479. def find_file_keys(self, value):
  480. return map(int, re.findall(r'FILE(\d)', value))
  481. def find_net_keys(self, value):
  482. return map(int, re.findall(r'NET(\d)', value))
  483. def find_combo_keys(self, value):
  484. return [map(int, t) for t in re.findall(r'COMBO(\d)(\d)', value)]
  485. def find_module_keys(self, value):
  486. return map(int, re.findall(r'MOD(\d)', value))
  487. def find_range_keys(self, value):
  488. return map(int, re.findall(r'RANGE(\d)', value))
  489. def find_prog_keys(self, value):
  490. return map(int, re.findall(r'PROG(\d)', value))
  491. def usage_parser(self, name):
  492. from optparse import OptionParser
  493. from optparse import OptionGroup
  494. from optparse import IndentedHelpFormatter
  495. class MyHelpFormatter(IndentedHelpFormatter):
  496. def format_epilog(self, epilog):
  497. return epilog
  498. def format_heading(self, heading):
  499. if self.current_indent == 0 and heading == 'Options':
  500. heading = 'Global options'
  501. return "%*s%s:\n" % (self.current_indent, "", heading)
  502. def format_usage(self, usage):
  503. return '%s\nUsage: %s\n' % (__banner__, usage)
  504. available_actions = self.builtin_actions + self.module.available_actions
  505. available_conditions = self.module.Response.available_conditions
  506. usage = '''%%prog <module-options ...> [global-options ...]
  507. Examples:
  508. %s''' % '\n '.join(self.module.usage_hints)
  509. usage += '''
  510. Module options:
  511. %s ''' % ('\n'.join(' %-14s: %s' % (k, v) for k, v in self.module.available_options))
  512. epilog = '''
  513. Syntax:
  514. -x actions:conditions
  515. actions := action[,action]*
  516. action := "%s"
  517. conditions := condition=value[,condition=value]*
  518. condition := "%s"
  519. ''' % ('" | "'.join(k for k, v in available_actions),
  520. '" | "'.join(k for k, v in available_conditions))
  521. epilog += '''
  522. %s
  523. %s
  524. ''' % ('\n'.join(' %-12s: %s' % (k, v) for k, v in available_actions),
  525. '\n'.join(' %-12s: %s' % (k, v) for k, v in available_conditions))
  526. epilog += '''
  527. For example, to ignore all redirects to the home page:
  528. ... -x ignore:code=302,fgrep='Location: /home.html'
  529. -e tag:encoding
  530. tag := any unique string (eg. T@G or _@@_ or ...)
  531. encoding := "%s"
  532. %s''' % ('" | "'.join(k for k in self.available_encodings),
  533. '\n'.join(' %-12s: %s' % (k, v) for k, (f, v) in self.available_encodings.items()))
  534. epilog += '''
  535. For example, to encode every password in base64:
  536. ... host=10.0.0.1 user=admin password=_@@_FILE0_@@_ -e _@@_:b64
  537. Please read the README inside for more examples and usage information.
  538. '''
  539. parser = OptionParser(usage=usage, prog=name, epilog=epilog, version=__banner__, formatter=MyHelpFormatter())
  540. exe_grp = OptionGroup(parser, 'Execution')
  541. exe_grp.add_option('-x', dest='actions', action='append', default=[], metavar='arg', help='actions and conditions, see Syntax below')
  542. exe_grp.add_option('--start', dest='start', type='int', default=0, metavar='N', help='start from offset N in the wordlist product')
  543. exe_grp.add_option('--stop', dest='stop', type='int', default=None, metavar='N', help='stop at offset N')
  544. exe_grp.add_option('--resume', dest='resume', metavar='r1[,rN]*', help='resume previous run')
  545. exe_grp.add_option('-e', dest='encodings', action='append', default=[], metavar='arg', help='encode everything between two tags, see Syntax below')
  546. exe_grp.add_option('-C', dest='combo_delim', default=':', metavar='str', help="delimiter string in combo files (default is ':')")
  547. exe_grp.add_option('-X', dest='condition_delim', default=',', metavar='str', help="delimiter string in conditions (default is ',')")
  548. 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 behavior")
  549. opt_grp = OptionGroup(parser, 'Optimization')
  550. opt_grp.add_option('--rate-limit', dest='rate_limit', type='float', default=0, metavar='N', help='wait N seconds between each test (default is 0)')
  551. opt_grp.add_option('--timeout', dest='timeout', type='int', default=0, metavar='N', help='wait N seconds for a response before retrying payload (default is 0)')
  552. opt_grp.add_option('--max-retries', dest='max_retries', type='int', default=4, metavar='N', help='skip payload after N retries (default is 4) (-1 for unlimited)')
  553. opt_grp.add_option('-t', '--threads', dest='num_threads', type='int', default=10, metavar='N', help='number of threads (default is 10)')
  554. log_grp = OptionGroup(parser, 'Logging')
  555. log_grp.add_option('-l', dest='log_dir', metavar='DIR', help="save output and response data into DIR ")
  556. log_grp.add_option('-L', dest='auto_log', metavar='SFX', help="automatically save into DIR/yyyy-mm-dd/hh:mm:ss_SFX (DIR defaults to '/tmp/patator')")
  557. dbg_grp = OptionGroup(parser, 'Debugging')
  558. dbg_grp.add_option('-d', '--debug', dest='debug', action='store_true', default=False, help='enable debug messages')
  559. parser.option_groups.extend([exe_grp, opt_grp, log_grp, dbg_grp])
  560. return parser
  561. def parse_usage(self, argv):
  562. parser = self.usage_parser(argv[0])
  563. opts, args = parser.parse_args(argv[1:])
  564. if not len(args) > 0:
  565. parser.print_usage()
  566. print('ERROR: wrong usage. Please read the README inside for more information.')
  567. sys.exit(2)
  568. return opts, args
  569. def __init__(self, module, argv):
  570. self.thread_report = []
  571. self.thread_progress = []
  572. self.payload = {}
  573. self.iter_keys = {}
  574. self.enc_keys = []
  575. self.module = module
  576. opts, args = self.parse_usage(argv)
  577. self.combo_delim = opts.combo_delim
  578. self.condition_delim = opts.condition_delim
  579. self.rate_limit = opts.rate_limit
  580. self.timeout = opts.timeout
  581. self.max_retries = opts.max_retries
  582. self.num_threads = opts.num_threads
  583. self.start, self.stop = opts.start, opts.stop
  584. self.allow_ignore_failures = opts.allow_ignore_failures
  585. self.resume = [int(i) for i in opts.resume.split(',')] if opts.resume else None
  586. manager = MyManager()
  587. manager.start()
  588. self.ns = manager.Namespace()
  589. self.ns.actions = {}
  590. self.ns.free_list = []
  591. self.ns.paused = False
  592. self.ns.quit_now = False
  593. self.ns.start_time = 0
  594. self.ns.total_size = 1
  595. pipe = multiprocessing.Pipe(duplex=False)
  596. logsvc = Process(name='LogSvc', target=process_logs, args=(pipe[0], module.Response.indicatorsfmt, argv, build_logdir(opts.log_dir, opts.auto_log)))
  597. logsvc.daemon = True
  598. logsvc.start()
  599. global logger
  600. logger = Logger(pipe[1])
  601. if opts.debug:
  602. logger.setLevel(logging.DEBUG)
  603. else:
  604. logger.setLevel(logging.INFO)
  605. wlists = {}
  606. kargs = []
  607. for arg in args: # ('host=NET0', '0=10.0.0.0/24', 'user=COMBO10', 'password=COMBO11', '1=combos.txt', 'name=google.MOD2', '2=TLD')
  608. for k, v in self.expand_key(arg):
  609. logger.debug('k: %s, v: %s' % (k, v))
  610. if k.isdigit():
  611. wlists[k] = v
  612. else:
  613. if v.startswith('@'):
  614. p = os.path.expanduser(v[1:])
  615. v = open(p).read()
  616. kargs.append((k, v))
  617. iter_vals = [v for k, v in sorted(wlists.items())]
  618. logger.debug('kargs: %s' % kargs) # [('host', 'NET0'), ('user', 'COMBO10'), ('password', 'COMBO11'), ('domain', 'MOD2')]
  619. logger.debug('iter_vals: %s' % iter_vals) # ['10.0.0.0/24', 'combos.txt', 'TLD']
  620. for k, v in kargs:
  621. for e in opts.encodings:
  622. meta, enc = e.split(':')
  623. if re.search(r'{0}.+?{0}'.format(meta), v):
  624. self.enc_keys.append((k, meta, self.available_encodings[enc][0]))
  625. for i in self.find_file_keys(v):
  626. if i not in self.iter_keys:
  627. self.iter_keys[i] = ('FILE', iter_vals[i], [])
  628. self.iter_keys[i][2].append(k)
  629. else:
  630. for i in self.find_net_keys(v):
  631. if i not in self.iter_keys:
  632. self.iter_keys[i] = ('NET', iter_vals[i], [])
  633. self.iter_keys[i][2].append(k)
  634. if not has_ipy:
  635. print('IPy (https://github.com/haypo/python-ipy) is required for using NET keyword.')
  636. print('Please read the README inside for more information.')
  637. sys.exit(3)
  638. else:
  639. for i, j in self.find_combo_keys(v):
  640. if i not in self.iter_keys:
  641. self.iter_keys[i] = ('COMBO', iter_vals[i], [])
  642. self.iter_keys[i][2].append((j, k))
  643. else:
  644. for i in self.find_module_keys(v):
  645. if i not in self.iter_keys:
  646. self.iter_keys[i] = ('MOD', iter_vals[i], [])
  647. self.iter_keys[i][2].append(k)
  648. else:
  649. for i in self.find_range_keys(v):
  650. if i not in self.iter_keys:
  651. self.iter_keys[i] = ('RANGE', iter_vals[i], [])
  652. self.iter_keys[i][2].append(k)
  653. else:
  654. for i in self.find_prog_keys(v):
  655. if i not in self.iter_keys:
  656. self.iter_keys[i] = ('PROG', iter_vals[i], [])
  657. self.iter_keys[i][2].append(k)
  658. else:
  659. self.payload[k] = v
  660. logger.debug('iter_keys: %s' % self.iter_keys) # { 0: ('NET', '10.0.0.0/24', ['host']), 1: ('COMBO', 'combos.txt', [(0, 'user'), (1, 'password')]), 2: ('MOD', 'TLD', ['name'])
  661. logger.debug('enc_keys: %s' % self.enc_keys) # [('password', 'ENC', hex), ('header', 'B64', b64encode), ...
  662. logger.debug('payload: %s' % self.payload)
  663. self.available_actions = [k for k, _ in self.builtin_actions + self.module.available_actions]
  664. self.module_actions = [k for k, _ in self.module.available_actions]
  665. for x in opts.actions:
  666. self.update_actions(x)
  667. logger.debug('actions: %s' % self.ns.actions)
  668. def update_actions(self, arg):
  669. ns_actions = self.ns.actions
  670. actions, conditions = arg.split(':', 1)
  671. for action in actions.split(','):
  672. conds = [c.split('=', 1) for c in conditions.split(self.condition_delim)]
  673. if '=' in action:
  674. name, opts = action.split('=')
  675. else:
  676. name, opts = action, None
  677. if name not in self.available_actions:
  678. raise ValueError('Unsupported action: %s' % name)
  679. if name not in ns_actions:
  680. ns_actions[name] = []
  681. ns_actions[name].append((conds, opts))
  682. self.ns.actions = ns_actions
  683. def lookup_actions(self, resp):
  684. actions = {}
  685. for action, conditions in self.ns.actions.items():
  686. for condition, opts in conditions:
  687. for key, val in condition:
  688. if key[-1] == '!':
  689. if resp.match(key[:-1], val):
  690. break
  691. else:
  692. if not resp.match(key, val):
  693. break
  694. else:
  695. actions[action] = opts
  696. return actions
  697. def check_free(self, payload):
  698. # free_list: 'host=10.0.0.1', 'user=anonymous', 'host=10.0.0.7,user=test', ...
  699. for m in self.ns.free_list:
  700. args = m.split(',', 1)
  701. for arg in args:
  702. k, v = arg.split('=', 1)
  703. if payload[k] != v:
  704. break
  705. else:
  706. return True
  707. return False
  708. def register_free(self, payload, opts):
  709. self.ns.free_list += [','.join('%s=%s' % (k, payload[k]) for k in opts.split('+'))]
  710. logger.debug('free_list updated: %s' % self.ns.free_list)
  711. def fire(self):
  712. logger.info('Starting %s at %s' % (__banner__, strftime('%Y-%m-%d %H:%M %Z', localtime())))
  713. try:
  714. self.start_threads()
  715. self.monitor_progress()
  716. except KeyboardInterrupt:
  717. pass
  718. except:
  719. logging.exception(sys.exc_info()[1])
  720. finally:
  721. self.ns.quit_now = True
  722. try:
  723. # waiting for reports enqueued by consumers to be flushed
  724. while True:
  725. active = multiprocessing.active_children()
  726. self.report_progress()
  727. if not len(active) > 2: # SyncManager and LogSvc
  728. break
  729. logger.debug('active: %s' % active)
  730. sleep(.1)
  731. except KeyboardInterrupt:
  732. pass
  733. if self.ns.total_size >= maxint:
  734. total_size = -1
  735. else:
  736. total_size = self.ns.total_size
  737. total_time = time() - self.ns.start_time
  738. hits_count = sum(p.hits_count for p in self.thread_progress)
  739. done_count = sum(p.done_count for p in self.thread_progress)
  740. skip_count = sum(p.skip_count for p in self.thread_progress)
  741. fail_count = sum(p.fail_count for p in self.thread_progress)
  742. speed_avg = done_count / total_time
  743. self.show_final()
  744. logger.info('Hits/Done/Skip/Fail/Size: %d/%d/%d/%d/%d, Avg: %d r/s, Time: %s' % (
  745. hits_count, done_count, skip_count, fail_count, total_size, speed_avg,
  746. pprint_seconds(total_time, '%dh %dm %ds')))
  747. if done_count < total_size:
  748. resume = []
  749. for i, p in enumerate(self.thread_progress):
  750. c = p.done_count + p.skip_count
  751. if self.resume:
  752. if i < len(self.resume):
  753. c += self.resume[i]
  754. resume.append(str(c))
  755. logger.info('To resume execution, pass --resume %s' % ','.join(resume))
  756. logger.quit()
  757. while len(multiprocessing.active_children()) > 1:
  758. sleep(.1)
  759. def push_final(self, resp): pass
  760. def show_final(self): pass
  761. def start_threads(self):
  762. task_queues = [multiprocessing.Queue(maxsize=10000) for _ in range(self.num_threads)]
  763. # consumers
  764. for num in range(self.num_threads):
  765. report_queue = multiprocessing.Queue(maxsize=1000)
  766. t = Process(name='Consumer-%d' % num, target=self.consume, args=(task_queues[num], report_queue, logger.pipe))
  767. t.daemon = True
  768. t.start()
  769. self.thread_report.append(report_queue)
  770. self.thread_progress.append(Progress())
  771. # producer
  772. t = Process(name='Producer', target=self.produce, args=(task_queues, logger.pipe))
  773. t.daemon = True
  774. t.start()
  775. def produce(self, task_queues, pipe):
  776. ignore_ctrlc()
  777. global logger
  778. logger = Logger(pipe)
  779. iterables = []
  780. total_size = 1
  781. def abort(msg):
  782. logger.warn(msg)
  783. self.ns.quit_now = True
  784. for _, (t, v, _) in self.iter_keys.items():
  785. if t in ('FILE', 'COMBO'):
  786. size = 0
  787. files = []
  788. for fname in v.split(','):
  789. fpath = os.path.expanduser(fname)
  790. if not os.path.isfile(fpath):
  791. return abort("No such file '%s'" % fpath)
  792. with open(fpath) as f:
  793. for _ in f:
  794. size += 1
  795. files.append(FileIter(fpath))
  796. iterable = chain(*files)
  797. elif t == 'NET':
  798. subnets = [IP(n, make_net=True) for n in v.split(',')]
  799. size = sum(len(s) for s in subnets)
  800. iterable = chain(*subnets)
  801. elif t == 'MOD':
  802. elements, size = self.module.available_keys[v]()
  803. iterable = chain(elements)
  804. elif t == 'RANGE':
  805. size = 0
  806. ranges = []
  807. for r in v.split(','):
  808. typ, opt = r.split(':', 1)
  809. try:
  810. it = RangeIter(typ, opt)
  811. size += len(it)
  812. except ValueError as e:
  813. return abort("Invalid range '%s' of type '%s', %s" % (opt, typ, e))
  814. ranges.append(it)
  815. iterable = chain(*ranges)
  816. elif t == 'PROG':
  817. m = re.match(r'(.+),(\d+)$', v)
  818. if m:
  819. prog, size = m.groups()
  820. else:
  821. prog, size = v, maxint
  822. logger.debug('prog: %s, size: %s' % (prog, size))
  823. it = ProgIter(prog)
  824. iterable, size = chain(it), int(size)
  825. else:
  826. return abort("Incorrect keyword '%s'" % t)
  827. total_size *= size
  828. iterables.append(iterable)
  829. if not iterables:
  830. iterables.append(chain(['']))
  831. if self.stop:
  832. total_size = self.stop - self.start
  833. else:
  834. total_size -= self.start
  835. if self.resume:
  836. total_size -= sum(self.resume)
  837. self.ns.total_size = total_size
  838. self.ns.start_time = time()
  839. logger.headers()
  840. count = 0
  841. for pp in islice(product(*iterables), self.start, self.stop):
  842. if self.ns.quit_now:
  843. break
  844. cid = count % self.num_threads
  845. prod = [str(p).rstrip('\r\n') for p in pp]
  846. if self.resume:
  847. idx = count % len(self.resume)
  848. off = self.resume[idx]
  849. if count < off * len(self.resume):
  850. #logger.debug('Skipping %d %s, resume[%d]: %s' % (count, ':'.join(prod), idx, self.resume[idx]))
  851. count += 1
  852. continue
  853. while True:
  854. if self.ns.quit_now:
  855. break
  856. try:
  857. task_queues[cid].put_nowait(prod)
  858. break
  859. except Full:
  860. sleep(.1)
  861. count += 1
  862. if not self.ns.quit_now:
  863. for q in task_queues:
  864. q.put(None)
  865. logger.debug('producer done')
  866. while True:
  867. if self.ns.quit_now:
  868. for q in task_queues:
  869. q.cancel_join_thread()
  870. break
  871. sleep(.5)
  872. logger.debug('producer exits')
  873. def consume(self, task_queue, report_queue, pipe):
  874. ignore_ctrlc()
  875. handle_alarm()
  876. global logger
  877. logger = Logger(pipe)
  878. module = self.module()
  879. def shutdown():
  880. if hasattr(module, '__del__'):
  881. module.__del__()
  882. logger.debug('consumer done')
  883. while True:
  884. if self.ns.quit_now:
  885. return shutdown()
  886. try:
  887. prod = task_queue.get_nowait()
  888. except Empty:
  889. sleep(.1)
  890. continue
  891. if prod is None:
  892. return shutdown()
  893. payload = self.payload.copy()
  894. for i, (t, _, keys) in self.iter_keys.items():
  895. if t == 'FILE':
  896. for k in keys:
  897. payload[k] = payload[k].replace('FILE%d' % i, prod[i])
  898. elif t == 'NET':
  899. for k in keys:
  900. payload[k] = payload[k].replace('NET%d' % i, prod[i])
  901. elif t == 'COMBO':
  902. for j, k in keys:
  903. payload[k] = payload[k].replace('COMBO%d%d' % (i, j), prod[i].split(self.combo_delim)[j])
  904. elif t == 'MOD':
  905. for k in keys:
  906. payload[k] = payload[k].replace('MOD%d' %i, prod[i])
  907. elif t == 'RANGE':
  908. for k in keys:
  909. payload[k] = payload[k].replace('RANGE%d' %i, prod[i])
  910. elif t == 'PROG':
  911. for k in keys:
  912. payload[k] = payload[k].replace('PROG%d' %i, prod[i])
  913. for k, m, e in self.enc_keys:
  914. payload[k] = re.sub(r'{0}(.+?){0}'.format(m), lambda m: e(m.group(1)), payload[k])
  915. logger.debug('product: %s' % prod)
  916. pp_prod = ':'.join(prod)
  917. if self.check_free(payload):
  918. report_queue.put(('skip', pp_prod, None, 0))
  919. continue
  920. try_count = 0
  921. start_time = time()
  922. while True:
  923. while self.ns.paused and not self.ns.quit_now:
  924. sleep(1)
  925. if self.ns.quit_now:
  926. return shutdown()
  927. if self.rate_limit > 0:
  928. sleep(self.rate_limit)
  929. if try_count <= self.max_retries or self.max_retries < 0:
  930. actions = {}
  931. try_count += 1
  932. logger.debug('payload: %s [try %d/%d]' % (payload, try_count, self.max_retries+1))
  933. try:
  934. enable_alarm(self.timeout)
  935. resp = module.execute(**payload)
  936. except:
  937. mesg = '%s %s' % sys.exc_info()[:2]
  938. logger.debug('caught: %s' % mesg)
  939. #logging.exception(sys.exc_info()[1])
  940. resp = self.module.Response('xxx', mesg, timing=time()-start_time)
  941. if hasattr(module, 'reset'):
  942. module.reset()
  943. sleep(try_count * .1)
  944. continue
  945. finally:
  946. disable_alarm()
  947. else:
  948. actions = {'fail': None}
  949. actions.update(self.lookup_actions(resp))
  950. report_queue.put((actions, pp_prod, resp, time() - start_time))
  951. for name in self.module_actions:
  952. if name in actions:
  953. getattr(module, name)(**payload)
  954. if 'free' in actions:
  955. self.register_free(payload, actions['free'])
  956. break
  957. if 'fail' in actions:
  958. break
  959. if 'retry' in actions:
  960. continue
  961. break
  962. def monitor_progress(self):
  963. # loop until SyncManager, LogSvc and Producer are the only children left alive
  964. while len(multiprocessing.active_children()) > 3 and not self.ns.quit_now:
  965. self.report_progress()
  966. self.monitor_interaction()
  967. def report_progress(self):
  968. for i, pq in enumerate(self.thread_report):
  969. p = self.thread_progress[i]
  970. while True:
  971. try:
  972. actions, current, resp, seconds = pq.get_nowait()
  973. #logger.info('actions reported: %s' % '+'.join(actions))
  974. except Empty:
  975. break
  976. if actions == 'skip':
  977. p.skip_count += 1
  978. continue
  979. if self.resume:
  980. offset = p.done_count + self.resume[i]
  981. else:
  982. offset = p.done_count
  983. offset = (offset * self.num_threads) + i + 1 + self.start
  984. p.current = current
  985. p.seconds[p.done_count % len(p.seconds)] = seconds
  986. if 'fail' in actions:
  987. if not self.allow_ignore_failures or 'ignore' not in actions:
  988. logger.result('fail', resp, current, offset)
  989. elif 'ignore' not in actions:
  990. logger.result('hit', resp, current, offset)
  991. if 'fail' in actions:
  992. p.fail_count += 1
  993. elif 'retry' in actions:
  994. continue
  995. elif 'ignore' not in actions:
  996. p.hits_count += 1
  997. logger.save(resp, offset)
  998. self.push_final(resp)
  999. p.done_count += 1
  1000. if 'quit' in actions:
  1001. self.ns.quit_now = True
  1002. def monitor_interaction(self):
  1003. if on_windows():
  1004. import msvcrt
  1005. if not msvcrt.kbhit():
  1006. sleep(.1)
  1007. return
  1008. command = msvcrt.getche()
  1009. if command == 'x':
  1010. command += raw_input()
  1011. else:
  1012. i, _, _ = select([sys.stdin], [], [], .1)
  1013. if not i: return
  1014. command = i[0].readline().strip()
  1015. if command == 'h':
  1016. logger.info('''Available commands:
  1017. h show help
  1018. <Enter> show progress
  1019. d/D increase/decrease debug level
  1020. p pause progress
  1021. f show verbose progress
  1022. x arg add monitor condition
  1023. a show all active conditions
  1024. q terminate execution now
  1025. ''')
  1026. elif command == 'q':
  1027. self.ns.quit_now = True
  1028. elif command == 'p':
  1029. self.ns.paused = not self.ns.paused
  1030. logger.info(self.ns.paused and 'Paused' or 'Unpaused')
  1031. elif command == 'd':
  1032. logger.setLevel(logging.DEBUG)
  1033. elif command == 'D':
  1034. logger.setLevel(logging.INFO)
  1035. elif command == 'a':
  1036. logger.info(repr(self.ns.actions))
  1037. elif command.startswith('x'):
  1038. _, arg = command.split(' ', 1)
  1039. try:
  1040. self.update_actions(arg)
  1041. except ValueError:
  1042. logger.warn('usage: x actions:conditions')
  1043. else: # show progress
  1044. thread_progress = self.thread_progress
  1045. num_threads = self.num_threads
  1046. total_size = self.ns.total_size
  1047. total_count = sum(p.done_count+p.skip_count for p in thread_progress)
  1048. speed_avg = num_threads / (sum(sum(p.seconds) / len(p.seconds) for p in thread_progress) / num_threads)
  1049. if total_size >= maxint:
  1050. etc_time = 'inf'
  1051. remain_time = 'inf'
  1052. else:
  1053. remain_seconds = (total_size - total_count) / speed_avg
  1054. remain_time = pprint_seconds(remain_seconds, '%02d:%02d:%02d')
  1055. etc_seconds = datetime.now() + timedelta(seconds=remain_seconds)
  1056. etc_time = etc_seconds.strftime('%H:%M:%S')
  1057. logger.info('Progress: {0:>3}% ({1}/{2}) | Speed: {3:.0f} r/s | ETC: {4} ({5} remaining) {6}'.format(
  1058. total_count * 100/total_size,
  1059. total_count,
  1060. total_size,
  1061. speed_avg,
  1062. etc_time,
  1063. remain_time,
  1064. self.ns.paused and '| Paused' or ''))
  1065. if command == 'f':
  1066. for i, p in enumerate(thread_progress):
  1067. total_count = p.done_count + p.skip_count
  1068. logger.info(' {0:>3}: {1:>3}% ({2}/{3}) {4}'.format(
  1069. '#%d' % (i+1),
  1070. int(100*total_count/(1.0*total_size/num_threads)),
  1071. total_count,
  1072. total_size/num_threads,
  1073. p.current))
  1074. # }}}
  1075. # Response_Base {{{
  1076. def match_range(size, val):
  1077. if '-' in val:
  1078. size_min, size_max = val.split('-')
  1079. if not size_min and not size_max:
  1080. raise ValueError('Invalid interval')
  1081. elif not size_min: # size == -N
  1082. return size <= float(size_max)
  1083. elif not size_max: # size == N-
  1084. return size >= float(size_min)
  1085. else:
  1086. size_min, size_max = float(size_min), float(size_max)
  1087. if size_min >= size_max:
  1088. raise ValueError('Invalid interval')
  1089. return size_min <= size <= size_max
  1090. else:
  1091. return size == float(val)
  1092. class Response_Base:
  1093. available_conditions = (
  1094. ('code', 'match status code'),
  1095. ('size', 'match size (N or N-M or N- or -N)'),
  1096. ('time', 'match time (N or N-M or N- or -N)'),
  1097. ('mesg', 'match message'),
  1098. ('fgrep', 'search for string in mesg'),
  1099. ('egrep', 'search for regex in mesg'),
  1100. )
  1101. indicatorsfmt = [('code', -5), ('size', -4), ('time', 7)]
  1102. def __init__(self, code, mesg, timing=0, trace=None):
  1103. self.code = code
  1104. self.mesg = mesg
  1105. self.time = timing.time if isinstance(timing, Timing) else timing
  1106. self.size = len(mesg)
  1107. self.trace = trace
  1108. def indicators(self):
  1109. return self.code, self.size, '%.3f' % self.time
  1110. def __str__(self):
  1111. return self.mesg
  1112. def match(self, key, val):
  1113. return getattr(self, 'match_'+key)(val)
  1114. def match_code(self, val):
  1115. return re.match('%s$' % val, str(self.code))
  1116. def match_size(self, val):
  1117. return match_range(self.size, val)
  1118. def match_time(self, val):
  1119. return match_range(self.time, val)
  1120. def match_mesg(self, val):
  1121. return val == self.mesg
  1122. def match_fgrep(self, val):
  1123. return val in str(self)
  1124. def match_egrep(self, val):
  1125. return re.search(val, str(self))
  1126. def dump(self):
  1127. return self.trace or str(self)
  1128. def str_target(self):
  1129. return ''
  1130. class Timing:
  1131. def __enter__(self):
  1132. self.t1 = time()
  1133. return self
  1134. def __exit__(self, exc_type, exc_value, traceback):
  1135. self.time = time() - self.t1
  1136. # }}}
  1137. # TCP_Cache {{{
  1138. class TCP_Connection:
  1139. def __init__(self, fp, banner=None):
  1140. self.fp = fp
  1141. self.banner = banner
  1142. def close(self):
  1143. self.fp.close()
  1144. class TCP_Cache:
  1145. available_actions = (
  1146. ('reset', 'close current connection in order to reconnect next time'),
  1147. )
  1148. available_options = (
  1149. ('persistent', 'use persistent connections [1|0]'),
  1150. )
  1151. def __init__(self):
  1152. self.cache = {} # {'10.0.0.1:22': ('root', conn1), '10.0.0.2:22': ('admin', conn2),
  1153. self.curr = None
  1154. def __del__(self):
  1155. for _, (_, c) in self.cache.items():
  1156. c.close()
  1157. self.cache.clear()
  1158. def bind(self, host, port, *args, **kwargs):
  1159. hp = '%s:%s' % (host, port)
  1160. key = ':'.join(map(str, args))
  1161. if hp in self.cache:
  1162. k, c = self.cache[hp]
  1163. if key == k:
  1164. self.curr = hp, k, c
  1165. return c.fp, c.banner
  1166. else:
  1167. c.close()
  1168. del self.cache[hp]
  1169. self.curr = None
  1170. logger.debug('connect')
  1171. conn = self.connect(host, port, *args, **kwargs)
  1172. self.cache[hp] = (key, conn)
  1173. self.curr = hp, key, conn
  1174. return conn.fp, conn.banner
  1175. def reset(self, **kwargs):
  1176. if self.curr:
  1177. hp, _, c = self.curr
  1178. c.close()
  1179. del self.cache[hp]
  1180. self.curr = None
  1181. # }}}
  1182. # FTP {{{
  1183. from ftplib import FTP, Error as FTP_Error
  1184. try:
  1185. from ftplib import FTP_TLS # New in python 2.7
  1186. except ImportError:
  1187. notfound.append('ftp-tls')
  1188. class FTP_login(TCP_Cache):
  1189. '''Brute-force FTP'''
  1190. usage_hints = (
  1191. """%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt"""
  1192. """ -x ignore:mesg='Login incorrect.' -x ignore,reset,retry:code=500""",
  1193. )
  1194. available_options = (
  1195. ('host', 'target host'),
  1196. ('port', 'target port [21]'),
  1197. ('user', 'usernames to test'),
  1198. ('password', 'passwords to test'),
  1199. ('tls', 'use TLS [0|1]'),
  1200. ('timeout', 'seconds to wait for a response [10]'),
  1201. )
  1202. available_options += TCP_Cache.available_options
  1203. Response = Response_Base
  1204. def connect(self, host, port, tls, timeout):
  1205. if tls == '0':
  1206. fp = FTP(timeout=int(timeout))
  1207. else:
  1208. fp = FTP_TLS(timeout=int(timeout))
  1209. banner = fp.connect(host, int(port))
  1210. if tls != '0':
  1211. fp.auth()
  1212. return TCP_Connection(fp, banner)
  1213. def execute(self, host, port='21', tls='0', user=None, password=None, timeout='10', persistent='1'):
  1214. with Timing() as timing:
  1215. fp, resp = self.bind(host, port, tls, timeout=timeout)
  1216. try:
  1217. if user is not None or password is not None:
  1218. with Timing() as timing:
  1219. if user is not None:
  1220. resp = fp.sendcmd('USER ' + user)
  1221. if password is not None:
  1222. resp = fp.sendcmd('PASS ' + password)
  1223. logger.debug('No error: %s' % resp)
  1224. self.reset()
  1225. except FTP_Error as e:
  1226. resp = str(e)
  1227. logger.debug('FTP_Error: %s' % resp)
  1228. if persistent == '0':
  1229. self.reset()
  1230. code, mesg = resp.split(' ', 1)
  1231. return self.Response(code, mesg, timing)
  1232. # }}}
  1233. # SSH {{{
  1234. try:
  1235. from logging import NullHandler # only available since python 2.7
  1236. except ImportError:
  1237. class NullHandler(logging.Handler):
  1238. def emit(self, record):
  1239. pass
  1240. try:
  1241. import paramiko
  1242. logging.getLogger('paramiko.transport').addHandler(NullHandler())
  1243. except ImportError:
  1244. notfound.append('paramiko')
  1245. def load_keyfile(keyfile):
  1246. for cls in (paramiko.RSAKey, paramiko.DSSKey, paramiko.ECDSAKey):
  1247. try:
  1248. return cls.from_private_key_file(keyfile)
  1249. except paramiko.SSHException:
  1250. pass
  1251. else:
  1252. raise
  1253. class SSH_login(TCP_Cache):
  1254. '''Brute-force SSH'''
  1255. usage_hints = (
  1256. """%prog host=10.0.0.1 user=root password=FILE0 0=passwords.txt -x ignore:mesg='Authentication failed.'""",
  1257. )
  1258. available_options = (
  1259. ('host', 'target host'),
  1260. ('port', 'target port [22]'),
  1261. ('user', 'usernames to test'),
  1262. ('password', 'passwords to test'),
  1263. ('auth_type', 'type of password authentication to use [password|keyboard-interactive|auto]'),
  1264. ('keyfile', 'file with RSA, DSA or ECDSA private key to test'),
  1265. )
  1266. available_options += TCP_Cache.available_options
  1267. Response = Response_Base
  1268. def connect(self, host, port, user):
  1269. fp = paramiko.Transport('%s:%s' % (host, int(port)))
  1270. fp.start_client()
  1271. return TCP_Connection(fp, fp.remote_version)
  1272. def execute(self, host, port='22', user=None, password=None, auth_type='password', keyfile=None, persistent='1'):
  1273. with Timing() as timing:
  1274. fp, banner = self.bind(host, port, user)
  1275. try:
  1276. if user is not None:
  1277. if keyfile is not None:
  1278. key = load_keyfile(keyfile)
  1279. with Timing() as timing:
  1280. if keyfile is not None:
  1281. fp.auth_publickey(user, key)
  1282. elif password is not None:
  1283. if auth_type == 'password':
  1284. fp.auth_password(user, password, fallback=False)
  1285. elif auth_type == 'keyboard-interactive':
  1286. fp.auth_interactive(user, lambda a,b,c: [password] if len(c) == 1 else [])
  1287. elif auth_type == 'auto':
  1288. fp.auth_password(user, password, fallback=True)
  1289. else:
  1290. raise ValueError("Incorrect auth_type '%s'" % auth_type)
  1291. logger.debug('No error')
  1292. code, mesg = '0', banner
  1293. self.reset()
  1294. except paramiko.AuthenticationException as e:
  1295. logger.debug('AuthenticationException: %s' % e)
  1296. code, mesg = '1', str(e)
  1297. if persistent == '0':
  1298. self.reset()
  1299. return self.Response(code, mesg, timing)
  1300. # }}}
  1301. # Telnet {{{
  1302. from telnetlib import Telnet
  1303. class Telnet_login(TCP_Cache):
  1304. '''Brute-force Telnet'''
  1305. usage_hints = (
  1306. """%prog host=10.0.0.1 inputs='FILE0\\nFILE1' 0=logins.txt 1=passwords.txt persistent=0"""
  1307. """ prompt_re='Username:|Password:' -x ignore:egrep='Login incorrect.+Username:'""",
  1308. )
  1309. available_options = (
  1310. ('host', 'target host'),
  1311. ('port', 'target port [23]'),
  1312. ('inputs', 'list of values to input'),
  1313. ('prompt_re', 'regular expression to match prompts [\w+:]'),
  1314. ('timeout', 'seconds to wait for a response and for prompt_re to match received data [20]'),
  1315. )
  1316. available_options += TCP_Cache.available_options
  1317. Response = Response_Base
  1318. def connect(self, host, port, timeout):
  1319. self.prompt_count = 0
  1320. fp = Telnet(host, int(port), int(timeout))
  1321. return TCP_Connection(fp)
  1322. def execute(self, host, port='23', inputs=None, prompt_re='\w+:', timeout='20', persistent='1'):
  1323. with Timing() as timing:
  1324. fp, _ = self.bind(host, port, timeout=timeout)
  1325. trace = ''
  1326. timeout = int(timeout)
  1327. if self.prompt_count == 0:
  1328. _, _, raw = fp.expect([prompt_re], timeout=timeout)
  1329. logger.debug('raw banner: %s' % repr(raw))
  1330. trace += raw
  1331. self.prompt_count += 1
  1332. if inputs is not None:
  1333. with Timing() as timing:
  1334. for val in inputs.split(r'\n'):
  1335. logger.debug('input: %s' % val)
  1336. cmd = val + '\n' #'\r\x00'
  1337. fp.write(cmd)
  1338. trace += cmd
  1339. _, _, raw = fp.expect([prompt_re], timeout=timeout)
  1340. logger.debug('raw %d: %s' % (self.prompt_count, repr(raw)))
  1341. trace += raw
  1342. self.prompt_count += 1
  1343. if persistent == '0':
  1344. self.reset()
  1345. mesg = repr(raw)[1:-1] # strip enclosing single quotes
  1346. return self.Response(0, mesg, timing, trace)
  1347. # }}}
  1348. # SMTP {{{
  1349. from smtplib import SMTP, SMTP_SSL, SMTPAuthenticationError, SMTPHeloError, SMTPException
  1350. class SMTP_Base(TCP_Cache):
  1351. available_options = TCP_Cache.available_options
  1352. available_options += (
  1353. ('timeout', 'seconds to wait for a response [10]'),
  1354. ('host', 'target host'),
  1355. ('port', 'target port [25]'),
  1356. ('ssl', 'use SSL [0|1]'),
  1357. ('helo', 'helo or ehlo command to send after connect [skip]'),
  1358. ('starttls', 'send STARTTLS [0|1]'),
  1359. ('user', 'usernames to test'),
  1360. )
  1361. Response = Response_Base
  1362. def connect(self, host, port, ssl, helo, starttls, timeout):
  1363. if ssl == '0':
  1364. if not port: port = 25
  1365. fp = SMTP(timeout=int(timeout))
  1366. else:
  1367. if not port: port = 465
  1368. fp = SMTP_SSL(timeout=int(timeout))
  1369. resp = fp.connect(host, int(port))
  1370. if helo:
  1371. cmd, name = helo.split(' ', 1)
  1372. if cmd.lower() == 'ehlo':
  1373. resp = fp.ehlo(name)
  1374. else:
  1375. resp = fp.helo(name)
  1376. if not starttls == '0':
  1377. resp = fp.starttls()
  1378. return TCP_Connection(fp, resp)
  1379. class SMTP_vrfy(SMTP_Base):
  1380. '''Enumerate valid users using SMTP VRFY'''
  1381. usage_hints = (
  1382. '''%prog host=10.0.0.1 user=FILE0 0=logins.txt [helo='ehlo its.me.com']'''
  1383. ''' -x ignore:fgrep='User unknown' -x ignore,reset,retry:code=421''',
  1384. )
  1385. def execute(self, host, port='', ssl='0', helo='', starttls='0', user=None, timeout='10', persistent='1'):
  1386. with Timing() as timing:
  1387. fp, resp = self.bind(host, port, ssl, helo, starttls, timeout=timeout)
  1388. if user is not None:
  1389. with Timing() as timing:
  1390. resp = fp.verify(user)
  1391. if persistent == '0':
  1392. self.reset()
  1393. code, mesg = resp
  1394. return self.Response(code, mesg, timing)
  1395. class SMTP_rcpt(SMTP_Base):
  1396. '''Enumerate valid users using SMTP RCPT TO'''
  1397. usage_hints = (
  1398. '''%prog host=10.0.0.1 user=FILE0@localhost 0=logins.txt [helo='ehlo its.me.com']'''
  1399. ''' [mail_from=bar@example.com] -x ignore:fgrep='User unknown' -x ignore,reset,retry:code=421''',
  1400. )
  1401. available_options = SMTP_Base.available_options
  1402. available_options += (
  1403. ('mail_from', 'sender email [test@example.org]'),
  1404. )
  1405. def execute(self, host, port='', ssl='0', helo='', starttls='0', mail_from='test@example.org', user=None, timeout='10', persistent='1'):
  1406. with Timing() as timing:
  1407. fp, resp = self.bind(host, port, ssl, helo, starttls, timeout=timeout)
  1408. if mail_from or user is not None:
  1409. with Timing() as timing:
  1410. if mail_from:
  1411. resp = fp.mail(mail_from)
  1412. if user is not None:
  1413. resp = fp.rcpt(user)
  1414. fp.rset()
  1415. if persistent == '0':
  1416. self.reset()
  1417. code, mesg = resp
  1418. return self.Response(code, mesg, timing)
  1419. class SMTP_login(SMTP_Base):
  1420. '''Brute-force SMTP'''
  1421. usage_hints = (
  1422. '''%prog host=10.0.0.1 user=f.bar@dom.com password=FILE0 0=passwords.txt [helo='ehlo its.me.com']'''
  1423. ''' -x ignore:fgrep='Authentication failed' -x ignore,reset,retry:code=421''',
  1424. )
  1425. available_options = SMTP_Base.available_options
  1426. available_options += (
  1427. ('password', 'passwords to test'),
  1428. )
  1429. def execute(self, host, port='', ssl='0', helo='', starttls='0', user=None, password=None, timeout='10', persistent='1'):
  1430. with Timing() as timing:
  1431. fp, resp = self.bind(host, port, ssl, helo, starttls, timeout=timeout)
  1432. try:
  1433. if user is not None and password is not None:
  1434. with Timing() as timing:
  1435. resp = fp.login(user, password)
  1436. logger.debug('No error: %s' % str(resp))
  1437. self.reset()
  1438. except (SMTPHeloError,SMTPAuthenticationError,SMTPException) as resp:
  1439. logger.debug('SMTPError: %s' % str(resp))
  1440. if persistent == '0':
  1441. self.reset()
  1442. code, mesg = resp
  1443. return self.Response(code, mesg, timing)
  1444. # }}}
  1445. # Finger {{{
  1446. class Controller_Finger(Controller):
  1447. user_list = []
  1448. def push_final(self, resp):
  1449. if hasattr(resp, 'lines'):
  1450. for l in resp.lines:
  1451. if l not in self.user_list:
  1452. self.user_list.append(l)
  1453. def show_final(self):
  1454. print('\n'.join(self.user_list))
  1455. class Finger_lookup:
  1456. '''Enumerate valid users using Finger'''
  1457. usage_hints = (
  1458. """%prog host=10.0.0.1 user=FILE0 0=words.txt -x ignore:fgrep='no such user'""",
  1459. )
  1460. available_options = (
  1461. ('host', 'target host'),
  1462. ('port', 'target port [79]'),
  1463. ('user', 'usernames to test'),
  1464. ('timeout', 'seconds to wait for a response [5]'),
  1465. )
  1466. available_actions = ()
  1467. Response = Response_Base
  1468. def execute(self, host, port='79', user='', timeout='5'):
  1469. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  1470. s.settimeout(int(timeout))
  1471. with Timing() as timing:
  1472. s.connect((host, int(port)))
  1473. if user:
  1474. s.send(user)
  1475. s.send('\r\n')
  1476. data = ''
  1477. with Timing() as timing:
  1478. while True:
  1479. raw = s.recv(1024)
  1480. if not raw:
  1481. break
  1482. data += raw
  1483. s.close()
  1484. logger.debug('recv: %s' % repr(data))
  1485. data = data.strip()
  1486. mesg = repr(data)
  1487. resp = self.Response(0, mesg, timing, data)
  1488. resp.lines = [l.strip('\r\n') for l in data.split('\n')]
  1489. return resp
  1490. # }}}
  1491. # LDAP {{{
  1492. if not which('ldapsearch'):
  1493. notfound.append('openldap')
  1494. # Because python-ldap-2.4.4 did not allow using a PasswordPolicyControl
  1495. # during bind authentication (cf. http://article.gmane.org/gmane.comp.python.ldap/1003),
  1496. # I chose to wrap around ldapsearch with "-e ppolicy".
  1497. class LDAP_login:
  1498. '''Brute-force LDAP'''
  1499. usage_hints = (
  1500. """%prog host=10.0.0.1 binddn='cn=Directory Manager' bindpw=FILE0 0=passwords.txt"""
  1501. """ -x ignore:mesg='ldap_bind: Invalid credentials (49)'""",
  1502. )
  1503. available_options = (
  1504. ('host', 'target host'),
  1505. ('port', 'target port [389]'),
  1506. ('binddn', 'usernames to test'),
  1507. ('bindpw', 'passwords to test'),
  1508. ('basedn', 'base DN for search'),
  1509. ('ssl', 'use SSL/TLS [0|1]'),
  1510. )
  1511. available_actions = ()
  1512. Response = Response_Base
  1513. def execute(self, host, port='389', binddn='', bindpw='', basedn='', ssl='0'):
  1514. uri = 'ldap%s://%s:%s' % ('s' if ssl != '0' else '', host, port)
  1515. cmd = ['ldapsearch', '-H', uri, '-e', 'ppolicy', '-D', binddn, '-w', bindpw, '-b', basedn, '-s', 'one']
  1516. p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={'LDAPTLS_REQCERT': 'never'})
  1517. out = p.stdout.read()
  1518. err = p.stderr.read()
  1519. with Timing() as timing:
  1520. code = p.wait()
  1521. mesg = repr((out + err).strip())[1:-1]
  1522. trace = '[out]\n%s\n[err]\n%s' % (out, err)
  1523. return self.Response(code, mesg, timing, trace)
  1524. # }}}
  1525. # SMB {{{
  1526. try:
  1527. from impacket.smbconnection import SMBConnection, SessionError
  1528. from impacket import nt_errors
  1529. from impacket.dcerpc.v5 import transport, lsat, lsad
  1530. from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED
  1531. from impacket.dcerpc.v5.samr import SID_NAME_USE
  1532. except ImportError:
  1533. notfound.append('impacket')
  1534. class SMB_Connection(TCP_Connection):
  1535. def close(self):
  1536. self.fp.getSMBServer().get_socket().close()
  1537. class Response_SMB(Response_Base):
  1538. indicatorsfmt = [('code', -8), ('size', -4), ('time', 6)]
  1539. class SMB_login(TCP_Cache):
  1540. '''Brute-force SMB'''
  1541. usage_hints = (
  1542. """%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt"""
  1543. """ -x ignore:fgrep='unknown user name or bad password'""",
  1544. )
  1545. available_options = (
  1546. ('host', 'target host'),
  1547. ('port', 'target port [139]'),
  1548. ('user', 'usernames to test'),
  1549. ('password', 'passwords to test'),
  1550. ('password_hash', "LM/NT hashes to test, at least one hash must be provided ('lm:nt' or ':nt' or 'lm:')"),
  1551. ('domain', 'domain to test'),
  1552. )
  1553. available_options += TCP_Cache.available_options
  1554. Response = Response_SMB
  1555. def connect(self, host, port):
  1556. # if port == 445, impacket will use <host> instead of '*SMBSERVER' as the remote_name
  1557. fp = SMBConnection('*SMBSERVER', host, sess_port=int(port))
  1558. return SMB_Connection(fp)
  1559. def execute(self, host, port='139', user=None, password='', password_hash=None, domain='', persistent='1'):
  1560. with Timing() as timing:
  1561. fp, _ = self.bind(host, port)
  1562. try:
  1563. if user is None:
  1564. fp.login('', '') # retrieve workgroup/domain and computer name
  1565. else:
  1566. with Timing() as timing:
  1567. if password_hash:
  1568. if ':' in password_hash:
  1569. lmhash, nthash = password_hash.split(':')
  1570. else:
  1571. lmhash, nthash = 'aad3b435b51404eeaad3b435b51404ee', password_hash
  1572. fp.login(user, '', domain, lmhash, nthash)
  1573. else:
  1574. fp.login(user, password, domain)
  1575. logger.debug('No error')
  1576. code, mesg = '0', '%s\\%s (%s)' % (fp.getServerDomain(), fp.getServerName(), fp.getServerOS())
  1577. self.reset()
  1578. except SessionError as e:
  1579. code = '%x' % e.getErrorCode()
  1580. mesg = nt_errors.ERROR_MESSAGES[e.getErrorCode()][0]
  1581. if persistent == '0':
  1582. self.reset()
  1583. return self.Response(code, mesg, timing)
  1584. class DCE_Connection(TCP_Connection):
  1585. def __init__(self, fp, smbt):
  1586. self.smbt = smbt
  1587. TCP_Connection.__init__(self, fp)
  1588. def close(self):
  1589. self.smbt.get_socket().close()
  1590. # impacket/examples/lookupsid.py is much faster because it queries 1000 SIDs per packet
  1591. class SMB_lookupsid(TCP_Cache):
  1592. '''Brute-force SMB SID-lookup'''
  1593. usage_hints = (
  1594. '''%prog host=10.0.0.1 sid=S-1-5-21-1234567890-1234567890-1234567890 rid=RANGE0 0=int:500-2000 -x ignore:code=1''',
  1595. )
  1596. available_options = (
  1597. ('host', 'target host'),
  1598. ('port', 'target port [139]'),
  1599. ('sid', 'SID to test'),
  1600. ('rid', 'RID to test'),
  1601. ('user', 'username to use if auth required'),
  1602. ('password', 'password to use if auth required'),
  1603. )
  1604. available_options += TCP_Cache.available_options
  1605. Response = Response_Base
  1606. def connect(self, host, port, user, password, sid):
  1607. smbt = transport.SMBTransport(host, int(port), r'\lsarpc', user, password)
  1608. dce = smbt.get_dce_rpc()
  1609. dce.connect()
  1610. dce.bind(lsat.MSRPC_UUID_LSAT)
  1611. op2 = lsat.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
  1612. if sid is None:
  1613. res = lsad.hLsarQueryInformationPolicy2(dce, op2['PolicyHandle'], lsad.POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation)
  1614. sid = res['PolicyInformation']['PolicyAccountDomainInfo']['DomainSid'].formatCanonical()
  1615. self.sid = sid
  1616. self.policy_handle = op2['PolicyHandle']
  1617. return DCE_Connection(dce, smbt)
  1618. def execute(self, host, port='139', user='', password='', sid=None, rid=None, persistent='1'):
  1619. fp, _ = self.bind(host, port, user, password, sid)
  1620. if rid:
  1621. sid = '%s-%s' % (self.sid, rid)
  1622. else:
  1623. sid = self.sid
  1624. try:
  1625. res = lsat.hLsarLookupSids(fp, self.policy_handle, [sid], lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
  1626. code, names = 0, []
  1627. for n, item in enumerate(res['TranslatedNames']['Names']):
  1628. names.append("%s\\%s (%s)" % (res['ReferencedDomains']['Domains'][item['DomainIndex']]['Name'], item['Name'], SID_NAME_USE.enumItems(item['Use']).name[7:]))
  1629. except lsat.DCERPCSessionError:
  1630. code, names = 1, ['unknown'] # STATUS_NONE_MAPPED
  1631. if persistent == '0':
  1632. self.reset()
  1633. return self.Response(code, ', '.join(names))
  1634. # }}}
  1635. # POP {{{
  1636. from poplib import POP3, POP3_SSL, error_proto as pop_error
  1637. class POP_Connection(TCP_Connection):
  1638. def close(self):
  1639. self.fp.quit()
  1640. class POP_login(TCP_Cache):
  1641. '''Brute-force POP3'''
  1642. usage_hints = (
  1643. '''%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt -x ignore:code=-ERR''',
  1644. )
  1645. available_options = (
  1646. ('host', 'target host'),
  1647. ('port', 'target port [110]'),
  1648. ('user', 'usernames to test'),
  1649. ('password', 'passwords to test'),
  1650. ('ssl', 'use SSL [0|1]'),
  1651. ('timeout', 'seconds to wait for a response [10]'),
  1652. )
  1653. available_options += TCP_Cache.available_options
  1654. Response = Response_Base
  1655. def connect(self, host, port, ssl, timeout):
  1656. if ssl == '0':
  1657. if not port: port = 110
  1658. fp = POP3(host, int(port), timeout=int(timeout))
  1659. else:
  1660. if not port: port = 995
  1661. fp = POP3_SSL(host, int(port)) # timeout=int(timeout)) # no timeout option in python2
  1662. return POP_Connection(fp, fp.welcome)
  1663. def execute(self, host, port='', ssl='0', user=None, password=None, timeout='10', persistent='1'):
  1664. with Timing() as timing:
  1665. fp, resp = self.bind(host, port, ssl, timeout=timeout)
  1666. try:
  1667. if user is not None or password is not None:
  1668. with Timing() as timing:
  1669. if user is not None:
  1670. resp = fp.user(user)
  1671. if password is not None:
  1672. resp = fp.pass_(password)
  1673. logger.debug('No error: %s' % resp)
  1674. self.reset()
  1675. except pop_error as e:
  1676. logger.debug('pop_error: %s' % e)
  1677. resp = str(e)
  1678. if persistent == '0':
  1679. self.reset()
  1680. code, mesg = resp.split(' ', 1)
  1681. return self.Response(code, mesg, timing)
  1682. class POP_passd:
  1683. '''Brute-force poppassd (http://netwinsite.com/poppassd/)'''
  1684. usage_hints = (
  1685. '''%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt -x ignore:code=500''',
  1686. )
  1687. available_options = (
  1688. ('host', 'target host'),
  1689. ('port', 'target port [106]'),
  1690. ('user', 'usernames to test'),
  1691. ('password', 'passwords to test'),
  1692. ('timeout', 'seconds to wait for a response [10]'),
  1693. )
  1694. available_actions = ()
  1695. Response = Response_Base
  1696. def execute(self, host, port='106', user=None, password=None, timeout='10'):
  1697. fp = LineReceiver()
  1698. with Timing() as timing:
  1699. resp = fp.connect(host, int(port), int(timeout))
  1700. trace = resp + '\r\n'
  1701. try:
  1702. if user is not None or password is not None:
  1703. with Timing() as timing:
  1704. if user is not None:
  1705. cmd = 'USER %s' % user
  1706. resp = fp.sendcmd(cmd)
  1707. trace += '%s\r\n%s\r\n' % (cmd, resp)
  1708. if password is not None:
  1709. cmd = 'PASS %s' % password
  1710. resp = fp.sendcmd(cmd)
  1711. trace += '%s\r\n%s\r\n' % (cmd, resp)
  1712. except LineReceiver_Error as e:
  1713. resp = str(e)
  1714. logger.debug('LineReceiver_Error: %s' % resp)
  1715. trace += '%s\r\n%s\r\n' % (cmd, resp)
  1716. finally:
  1717. fp.close()
  1718. code, mesg = fp.parse(resp)
  1719. return self.Response(code, mesg, timing, trace)
  1720. # }}}
  1721. # IMAP {{{
  1722. from imaplib import IMAP4, IMAP4_SSL
  1723. class IMAP_login:
  1724. '''Brute-force IMAP4'''
  1725. usage_hints = (
  1726. '''%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt''',
  1727. )
  1728. available_options = (
  1729. ('host', 'target host'),
  1730. ('port', 'target port [143]'),
  1731. ('user', 'usernames to test'),
  1732. ('password', 'passwords to test'),
  1733. ('ssl', 'use SSL [0|1]'),
  1734. )
  1735. available_actions = ()
  1736. Response = Response_Base
  1737. def execute(self, host, port='', ssl='0', user=None, password=None):
  1738. if ssl == '0':
  1739. if not port: port = 143
  1740. klass = IMAP4
  1741. else:
  1742. if not port: port = 993
  1743. klass = IMAP4_SSL
  1744. with Timing() as timing:
  1745. fp = klass(host, port)
  1746. code, resp = 0, fp.welcome
  1747. try:
  1748. if user is not None and password is not None:
  1749. with Timing() as timing:
  1750. r = fp.login(user, password)
  1751. resp = ', '.join(r[1])
  1752. # doesn't it need to self.reset() to test more creds?
  1753. except IMAP4.error as e:
  1754. logger.debug('imap_error: %s' % e)
  1755. code, resp = 1, str(e)
  1756. return self.Response(code, resp, timing)
  1757. # }}}
  1758. # rlogin {{{
  1759. class Rlogin_login(TCP_Cache):
  1760. '''Brute-force rlogin'''
  1761. usage_hints = (
  1762. """Please note that rlogin requires to bind a socket to an Internet domain privileged port.""",
  1763. """%prog host=10.0.0.1 user=root luser=FILE0 0=logins.txt persistent=0 -x ignore:fgrep=Password:""",
  1764. """%prog host=10.0.0.1 user=john password=FILE0 0=passwords.txt -x 'reset:egrep!=Login incorrect.+login:'""",
  1765. )
  1766. available_options = (
  1767. ('host', 'target host'),
  1768. ('port', 'target port [513]'),
  1769. ('luser', 'client username [root]'),
  1770. ('user', 'usernames to test'),
  1771. ('password', 'passwords to test'),
  1772. ('prompt_re', 'regular expression to match prompts [\w+:]'),
  1773. ('timeout', 'seconds to wait for a response and for prompt_re to match received data [10]'),
  1774. )
  1775. available_options += TCP_Cache.available_options
  1776. Response = Response_Base
  1777. def connect(self, host, port, timeout):
  1778. fp = Telnet()
  1779. for i in range(50):
  1780. try:
  1781. fp.sock = socket.create_connection((host, int(port)), timeout=int(timeout), source_address=('', 1023 - i))
  1782. break
  1783. except socket.error as e:
  1784. if (e.errno, e.strerror) != (98, 'Address already in use'):
  1785. raise e
  1786. self.need_handshake = True
  1787. return TCP_Connection(fp)
  1788. def execute(self, host, port='513', luser='root', user='', password=None, prompt_re='\w+:', timeout='10', persistent='1'):
  1789. fp, _ = self.bind(host, port, timeout=int(timeout))
  1790. trace = ''
  1791. timeout = int(timeout)
  1792. with Timing() as timing:
  1793. if self.need_handshake:
  1794. fp.write('\x00%s\x00%s\x00vt100/9600\x00' % (luser, user))
  1795. self.need_handshake = False
  1796. else:
  1797. fp.write('%s\r' % user)
  1798. _, _, resp = fp.expect([prompt_re], timeout=timeout) # expecting the Password: prompt
  1799. trace += resp
  1800. if password is not None:
  1801. fp.write('%s\r' % password)
  1802. _, _, resp = fp.expect([prompt_re], timeout=timeout) # expecting the login: prompt
  1803. trace += resp
  1804. if persistent == '0':
  1805. self.reset()
  1806. mesg = repr(resp.strip())[1:-1]
  1807. return self.Response(0, mesg, timing, trace)
  1808. # }}}
  1809. # VMauthd {{{
  1810. from ssl import wrap_socket
  1811. class LineReceiver_Error(Exception): pass
  1812. class LineReceiver:
  1813. def connect(self, host, port, timeout, ssl=False):
  1814. self.sock = socket.create_connection((host, port), timeout)
  1815. banner = self.getresp()
  1816. if ssl:
  1817. self.sock = wrap_socket(self.sock)
  1818. return banner # welcome banner
  1819. def close(self):
  1820. self.sock.close()
  1821. def sendcmd(self, cmd):
  1822. self.sock.sendall(cmd + '\r\n')
  1823. return self.getresp()
  1824. def getresp(self):
  1825. resp = self.sock.recv(1024)
  1826. while not resp.endswith('\n'):
  1827. resp += self.sock.recv(1024)
  1828. resp = resp.rstrip()
  1829. code, _ = self.parse(resp)
  1830. if not code.isdigit():
  1831. raise Exception('Unexpected response: %s' % resp)
  1832. if code[0] not in ('1', '2', '3'):
  1833. raise LineReceiver_Error(resp)
  1834. return resp
  1835. def parse(self, resp):
  1836. i = resp.rfind('\n') + 1
  1837. code = resp[i:i+3]
  1838. mesg = resp[i+4:]
  1839. return code, mesg
  1840. class VMauthd_login(TCP_Cache):
  1841. '''Brute-force VMware Authentication Daemon'''
  1842. usage_hints = (
  1843. '''%prog host=10.0.0.1 user=root password=FILE0 0=passwords.txt''',
  1844. )
  1845. available_options = (
  1846. ('host', 'target host'),
  1847. ('port', 'target port [902]'),
  1848. ('user', 'usernames to test'),
  1849. ('password', 'passwords to test'),
  1850. ('ssl', 'use SSL [1|0]'),
  1851. ('timeout', 'seconds to wait for a response [10]'),
  1852. )
  1853. available_options += TCP_Cache.available_options
  1854. Response = Response_Base
  1855. def connect(self, host, port, ssl, timeout):
  1856. fp = LineReceiver()
  1857. banner = fp.connect(host, int(port), int(timeout), ssl != '0')
  1858. return TCP_Connection(fp, banner)
  1859. def execute(self, host, port='902', user=None, password=None, ssl='1', timeout='10', persistent='1'):
  1860. with Timing() as timing:
  1861. fp, resp = self.bind(host, port, ssl, timeout=timeout)
  1862. trace = resp + '\r\n'
  1863. try:
  1864. if user is not None or password is not None:
  1865. with Timing() as timing:
  1866. if user is not None:
  1867. cmd = 'USER %s' % user
  1868. resp = fp.sendcmd(cmd)
  1869. trace += '%s\r\n%s\r\n' % (cmd, resp)
  1870. if password is not None:
  1871. cmd = 'PASS %s' % password
  1872. resp = fp.sendcmd(cmd)
  1873. trace += '%s\r\n%s\r\n' % (cmd, resp)
  1874. except LineReceiver_Error as e:
  1875. resp = str(e)
  1876. logger.debug('LineReceiver_Error: %s' % resp)
  1877. trace += '%s\r\n%s\r\n' % (cmd, resp)
  1878. if persistent == '0':
  1879. self.reset()
  1880. code, mesg = fp.parse(resp)
  1881. return self.Response(code, mesg, timing, trace)
  1882. # }}}
  1883. # MySQL {{{
  1884. try:
  1885. import _mysql
  1886. except ImportError:
  1887. notfound.append('mysql-python')
  1888. class MySQL_login:
  1889. '''Brute-force MySQL'''
  1890. usage_hints = (
  1891. """%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt -x ignore:fgrep='Access denied for user'""",
  1892. )
  1893. available_options = (
  1894. ('host', 'target host'),
  1895. ('port', 'target port [3306]'),
  1896. ('user', 'usernames to test'),
  1897. ('password', 'passwords to test'),
  1898. ('timeout', 'seconds to wait for a response [10]'),
  1899. )
  1900. available_actions = ()
  1901. Response = Response_Base
  1902. def execute(self, host, port='3306', user='anony', password='', timeout='10'):
  1903. try:
  1904. with Timing() as timing:
  1905. fp = _mysql.connect(host=host, port=int(port), user=user, passwd=password, connect_timeout=int(timeout))
  1906. resp = '0', fp.get_server_info()
  1907. except _mysql.Error as resp:
  1908. logger.debug('MysqlError: %s' % resp)
  1909. code, mesg = resp
  1910. return self.Response(code, mesg, timing)
  1911. class MySQL_query(TCP_Cache):
  1912. '''Brute-force MySQL queries'''
  1913. usage_hints = (
  1914. '''%prog host=10.0.0.1 user=root password=s3cr3t query="select length(load_file('/home/adam/FILE0'))" 0=files.txt -x ignore:size=0''',
  1915. )
  1916. available_options = (
  1917. ('host', 'target host'),
  1918. ('port', 'target port [3306]'),
  1919. ('user', 'username to use'),
  1920. ('password', 'password to use'),
  1921. ('query', 'SQL query to execute'),
  1922. )
  1923. available_actions = ()
  1924. Response = Response_Base
  1925. def connect(self, host, port, user, password):
  1926. fp = _mysql.connect(host=host, port=int(port), user=user, passwd=password) # db=db
  1927. return TCP_Connection(fp)
  1928. def execute(self, host, port='3306', user='', password='', query='select @@version'):
  1929. fp, _ = self.bind(host, port, user, password)
  1930. with Timing() as timing:
  1931. fp.query(query)
  1932. rs = fp.store_result()
  1933. rows = rs.fetch_row(10, 0)
  1934. code, mesg = '0', '\n'.join(', '.join(map(str, r)) for r in filter(any, rows))
  1935. return self.Response(code, mesg, timing)
  1936. # }}}
  1937. # MSSQL {{{
  1938. # I did not use pymssql because neither version 1.x nor 2.0.0b1_dev were multithreads safe (they all segfault)
  1939. try:
  1940. from impacket import tds
  1941. from impacket.tds import TDS_ERROR_TOKEN, TDS_LOGINACK_TOKEN
  1942. except ImportError:
  1943. notfound.append('impacket')
  1944. class MSSQL_login:
  1945. '''Brute-force MSSQL'''
  1946. usage_hints = (
  1947. """%prog host=10.0.0.1 user=sa password=FILE0 0=passwords.txt -x ignore:fgrep='Login failed for user'""",
  1948. )
  1949. available_options = (
  1950. ('host', 'target host'),
  1951. ('port', 'target port [1433]'),
  1952. ('user', 'usernames to test'),
  1953. ('password', 'passwords to test'),
  1954. ('windows_auth', 'use Windows auth [0|1]'),
  1955. ('domain', 'domain to test []'),
  1956. ('password_hash', "LM/NT hashes to test ('lm:nt' or ':nt')"),
  1957. #('timeout', 'seconds to wait for a response [10]'),
  1958. )
  1959. available_actions = ()
  1960. Response = Response_Base
  1961. def execute(self, host, port='1433', user='', password='', windows_auth='0', domain='', password_hash=None): #, timeout='10'):
  1962. fp = tds.MSSQL(host, int(port))
  1963. fp.connect()
  1964. with Timing() as timing:
  1965. if windows_auth == '0':
  1966. r = fp.login(None, user, password, None, None, False)
  1967. else:
  1968. r = fp.login(None, user, password, domain, password_hash, True)
  1969. if not r:
  1970. key = fp.replies[TDS_ERROR_TOKEN][0]
  1971. code = key['Number']
  1972. mesg = key['MsgText'].decode('utf-16le')
  1973. else:
  1974. key = fp.replies[TDS_LOGINACK_TOKEN][0]
  1975. code = '0'
  1976. mesg = '%s (%d%d %d%d)' % (key['ProgName'].decode('utf-16le'), key['MajorVer'], key['MinorVer'], key['BuildNumHi'], key['BuildNumLow'])
  1977. fp.disconnect()
  1978. return self.Response(code, mesg, timing)
  1979. # }}}
  1980. # Oracle {{{
  1981. try:
  1982. import cx_Oracle
  1983. except ImportError:
  1984. notfound.append('cx_Oracle')
  1985. class Response_Oracle(Response_Base):
  1986. indicatorsfmt = [('code', -9), ('size', -4), ('time', 6)]
  1987. class Oracle_login:
  1988. '''Brute-force Oracle'''
  1989. usage_hints = (
  1990. """%prog host=10.0.0.1 sid=FILE0 0=sids.txt -x ignore:code=ORA-12505""",
  1991. """%prog host=10.0.0.1 user=SYS password=FILE0 0=passwords.txt -x ignore:code=ORA-01017""",
  1992. )
  1993. available_options = (
  1994. ('host', 'hostnames or subnets to target'),
  1995. ('port', 'ports to target [1521]'),
  1996. ('user', 'usernames to test'),
  1997. ('password', 'passwords to test'),
  1998. ('sid', 'sid to test'),
  1999. ('service_name', 'service name to test'),
  2000. )
  2001. available_actions = ()
  2002. Response = Response_Oracle
  2003. def execute(self, host, port='1521', user='', password='', sid='', service_name=''):
  2004. if sid:
  2005. dsn = cx_Oracle.makedsn(host=host, port=port, sid=sid)
  2006. elif service_name:
  2007. dsn = cx_Oracle.makedsn(host=host, port=port, service_name=service_name)
  2008. else:
  2009. raise ValueError("Options sid and service_name cannot be both empty")
  2010. try:
  2011. with Timing() as timing:
  2012. fp = cx_Oracle.connect(user, password, dsn, threaded=True)
  2013. code, mesg = '0', fp.version
  2014. except cx_Oracle.DatabaseError as e:
  2015. code, mesg = e.args[0].message[:-1].split(': ', 1)
  2016. return self.Response(code, mesg, timing)
  2017. # }}}
  2018. # PostgreSQL {{{
  2019. try:
  2020. import psycopg2
  2021. except ImportError:
  2022. notfound.append('psycopg')
  2023. class Pgsql_login:
  2024. '''Brute-force PostgreSQL'''
  2025. usage_hints = (
  2026. """%prog host=10.0.0.1 user=postgres password=FILE0 0=passwords.txt -x ignore:fgrep='password authentication failed for user'""",
  2027. )
  2028. available_options = (
  2029. ('host', 'target host'),
  2030. ('port', 'target port [5432]'),
  2031. ('user', 'usernames to test'),
  2032. ('password', 'passwords to test'),
  2033. ('database', 'databases to test [postgres]'),
  2034. ('timeout', 'seconds to wait for a response [10]'),
  2035. )
  2036. available_actions = ()
  2037. Response = Response_Base
  2038. def execute(self, host, port='5432', user=None, password=None, database='postgres', ssl='disable', timeout='10'):
  2039. try:
  2040. with Timing() as timing:
  2041. psycopg2.connect(host=host, port=int(port), user=user, password=password, database=database, sslmode=ssl, connect_timeout=int(timeout))
  2042. code, mesg = '0', 'OK'
  2043. except psycopg2.OperationalError as e:
  2044. logger.debug('OperationalError: %s' % e)
  2045. code, mesg = '1', str(e)[:-1]
  2046. return self.Response(code, mesg, timing)
  2047. # }}}
  2048. # HTTP {{{
  2049. try:
  2050. import pycurl
  2051. except ImportError:
  2052. notfound.append('pycurl')
  2053. class Response_HTTP(Response_Base):
  2054. indicatorsfmt = [('code', -4), ('size:clen', -13), ('time', 6)]
  2055. def __init__(self, code, response, timing=0, trace=None, content_length=-1, target={}):
  2056. Response_Base.__init__(self, code, response, timing, trace=trace)
  2057. self.content_length = content_length
  2058. self.target = target
  2059. def indicators(self):
  2060. return self.code, '%d:%d' % (self.size, self.content_length), '%.3f' % self.time
  2061. def __str__(self):
  2062. lines = re.findall('^(HTTP/.+)$', self.mesg, re.M)
  2063. if lines:
  2064. return lines[-1].rstrip('\r')
  2065. else:
  2066. return self.mesg
  2067. def match_clen(self, val):
  2068. return match_range(self.content_length, val)
  2069. def match_fgrep(self, val):
  2070. return val in self.mesg
  2071. def match_egrep(self, val):
  2072. return re.search(val, self.mesg, re.M)
  2073. def str_target(self):
  2074. return ' '.join('%s=%s' % (k, xmlquoteattr(str(v))) for k, v in self.target.iteritems())
  2075. available_conditions = Response_Base.available_conditions
  2076. available_conditions += (
  2077. ('clen', 'match Content-Length header (N or N-M or N- or -N)'),
  2078. )
  2079. class HTTP_fuzz(TCP_Cache):
  2080. '''Brute-force HTTP'''
  2081. usage_hints = [
  2082. """%prog url=http://10.0.0.1/FILE0 0=paths.txt -x ignore:code=404 -x ignore,retry:code=500""",
  2083. """%prog url=http://10.0.0.1/manager/html user_pass=COMBO00:COMBO01 0=combos.txt"""
  2084. """ -x ignore:code=401""",
  2085. """%prog url=http://10.0.0.1/phpmyadmin/index.php method=POST"""
  2086. """ body='pma_username=root&pma_password=FILE0&server=1&lang=en' 0=passwords.txt follow=1"""
  2087. """ accept_cookie=1 -x ignore:fgrep='Cannot log in to the MySQL server'""",
  2088. ]
  2089. available_options = (
  2090. ('url', 'target url (scheme://host[:port]/path?query)'),
  2091. #('host', 'target host'),
  2092. #('port', 'target port'),
  2093. #('scheme', 'scheme [http | https]'),
  2094. #('path', 'web path [/]'),
  2095. #('query', 'query string'),
  2096. ('body', 'body data'),
  2097. ('header', 'use custom headers'),
  2098. ('method', 'method to use [GET | POST | HEAD | ...]'),
  2099. ('auto_urlencode', 'automatically perform URL-encoding [1|0]'),
  2100. ('user_pass', 'username and password for HTTP authentication (user:pass)'),
  2101. ('auth_type', 'type of HTTP authentication [basic | digest | ntlm]'),
  2102. ('follow', 'follow any Location redirect [0|1]'),
  2103. ('max_follow', 'redirection limit [5]'),
  2104. ('accept_cookie', 'save received cookies to issue them in future requests [0|1]'),
  2105. ('http_proxy', 'HTTP proxy to use (host:port)'),
  2106. ('ssl_cert', 'client SSL certificate file (cert+key in PEM format)'),
  2107. ('timeout_tcp', 'seconds to wait for a TCP handshake [10]'),
  2108. ('timeout', 'seconds to wait for a HTTP response [20]'),
  2109. ('before_urls', 'comma-separated URLs to query before the main request'),
  2110. ('before_header', 'use a custom header in the before_urls request'),
  2111. ('before_egrep', 'extract data from the before_urls response to place in the main request'),
  2112. ('after_urls', 'comma-separated URLs to query after the main request'),
  2113. ('max_mem', 'store no more than N bytes of request+response data in memory [-1 (unlimited)]'),
  2114. )
  2115. available_options += TCP_Cache.available_options
  2116. Response = Response_HTTP
  2117. def connect(self, host, port, scheme):
  2118. fp = pycurl.Curl()
  2119. fp.setopt(pycurl.SSL_VERIFYPEER, 0)
  2120. fp.setopt(pycurl.SSL_VERIFYHOST, 0)
  2121. fp.setopt(pycurl.HEADER, 1)
  2122. fp.setopt(pycurl.USERAGENT, 'Mozilla/5.0')
  2123. fp.setopt(pycurl.NOSIGNAL, 1)
  2124. return TCP_Connection(fp)
  2125. def execute(self, url=None, host=None, port='', scheme='http', path='/', params='', query='', fragment='', body='',
  2126. header='', method='GET', auto_urlencode='1', user_pass='', auth_type='basic',
  2127. follow='0', max_follow='5', accept_cookie='0', http_proxy='', ssl_cert='', timeout_tcp='10', timeout='20', persistent='1',
  2128. before_urls='', before_header='', before_egrep='', after_urls='', max_mem='-1'):
  2129. if url:
  2130. scheme, host, path, params, query, fragment = urlparse(url)
  2131. if ':' in host:
  2132. host, port = host.split(':')
  2133. del url
  2134. fp, _ = self.bind(host, port, scheme)
  2135. fp.setopt(pycurl.FOLLOWLOCATION, int(follow))
  2136. fp.setopt(pycurl.MAXREDIRS, int(max_follow))
  2137. fp.setopt(pycurl.CONNECTTIMEOUT, int(timeout_tcp))
  2138. fp.setopt(pycurl.TIMEOUT, int(timeout))
  2139. fp.setopt(pycurl.PROXY, http_proxy)
  2140. def noop(buf): pass
  2141. fp.setopt(pycurl.WRITEFUNCTION, noop)
  2142. def debug_func(t, s):
  2143. if max_mem > 0 and trace.tell() > max_mem:
  2144. return 0
  2145. if t in (pycurl.INFOTYPE_HEADER_OUT, pycurl.INFOTYPE_DATA_OUT):
  2146. trace.write(s)
  2147. elif t in (pycurl.INFOTYPE_HEADER_IN, pycurl.INFOTYPE_DATA_IN):
  2148. trace.write(s)
  2149. response.write(s)
  2150. max_mem = int(max_mem)
  2151. response, trace = StringIO(), StringIO()
  2152. fp.setopt(pycurl.DEBUGFUNCTION, debug_func)
  2153. fp.setopt(pycurl.VERBOSE, 1)
  2154. if user_pass:
  2155. fp.setopt(pycurl.USERPWD, user_pass)
  2156. if auth_type == 'basic':
  2157. fp.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
  2158. elif auth_type == 'digest':
  2159. fp.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)
  2160. elif auth_type == 'ntlm':
  2161. fp.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_NTLM)
  2162. else:
  2163. raise ValueError("Incorrect auth_type '%s'" % auth_type)
  2164. if ssl_cert:
  2165. fp.setopt(pycurl.SSLCERT, ssl_cert)
  2166. if accept_cookie == '1':
  2167. fp.setopt(pycurl.COOKIEFILE, '')
  2168. # warning: do not pass a Cookie: header into HTTPHEADER if using COOKIEFILE as it will
  2169. # produce requests with more than one Cookie: header
  2170. # and the server will process only one of them (eg. Apache only reads the last one)
  2171. def perform_fp(fp, method, url, header='', body=''):
  2172. #logger.debug('perform: %s' % url)
  2173. fp.setopt(pycurl.URL, url)
  2174. if method == 'GET':
  2175. fp.setopt(pycurl.HTTPGET, 1)
  2176. elif method == 'POST':
  2177. fp.setopt(pycurl.POST, 1)
  2178. fp.setopt(pycurl.POSTFIELDS, body)
  2179. elif method == 'HEAD':
  2180. fp.setopt(pycurl.NOBODY, 1)
  2181. else:
  2182. fp.setopt(pycurl.CUSTOMREQUEST, method)
  2183. headers = [h.strip('\r') for h in header.split('\n') if h]
  2184. fp.setopt(pycurl.HTTPHEADER, headers)
  2185. fp.perform()
  2186. if before_urls:
  2187. for before_url in before_urls.split(','):
  2188. perform_fp(fp, 'GET', before_url, before_header)
  2189. if before_egrep:
  2190. for be in before_egrep.split('|'):
  2191. mark, regex = be.split(':', 1)
  2192. val = re.search(regex, response.getvalue(), re.M).group(1)
  2193. header = header.replace(mark, val)
  2194. query = query.replace(mark, val)
  2195. body = body.replace(mark, val)
  2196. if auto_urlencode == '1':
  2197. path = quote(path)
  2198. query = urlencode(parse_qsl(query, True))
  2199. body = urlencode(parse_qsl(body, True))
  2200. if port:
  2201. host = '%s:%s' % (host, port)
  2202. url = urlunparse((scheme, host, path, params, query, fragment))
  2203. perform_fp(fp, method, url, header, body)
  2204. target = {}
  2205. target['ip'] = fp.getinfo(pycurl.PRIMARY_IP)
  2206. target['port'] = fp.getinfo(pycurl.PRIMARY_PORT)
  2207. target['hostname'] = host
  2208. for h in header.split('\n'):
  2209. if ': ' in h:
  2210. k, v = h.split(': ', 1)
  2211. if k.lower() == 'host':
  2212. target['vhost'] = v.rstrip('\r')
  2213. break
  2214. if after_urls:
  2215. for after_url in after_urls.split(','):
  2216. perform_fp(fp, 'GET', after_url)
  2217. http_code = fp.getinfo(pycurl.HTTP_CODE)
  2218. content_length = fp.getinfo(pycurl.CONTENT_LENGTH_DOWNLOAD)
  2219. response_time = fp.getinfo(pycurl.TOTAL_TIME) - fp.getinfo(pycurl.PRETRANSFER_TIME)
  2220. if persistent == '0':
  2221. self.reset()
  2222. return self.Response(http_code, response.getvalue(), response_time, trace.getvalue(), content_length, target)
  2223. # }}}
  2224. # {{{ RDP
  2225. if not which('xfreerdp'):
  2226. notfound.append('xfreerdp')
  2227. class RDP_login:
  2228. '''Brute-force RDP (NLA)'''
  2229. usage_hints = (
  2230. """%prog host=10.0.0.1 user='administrator' password=FILE0 0=passwords.txt""",
  2231. )
  2232. available_options = (
  2233. ('host', 'target host'),
  2234. ('port', 'target port [3389]'),
  2235. ('user', 'usernames to test'),
  2236. ('password', 'passwords to test'),
  2237. )
  2238. available_actions = ()
  2239. Response = Response_Base
  2240. def execute(self, host, port='3389', user=None, password=None):
  2241. cmd = ['xfreerdp', '/v:%s:%d' % (host, int(port)), '/u:%s' % user, '/p:%s' % password, '/cert-ignore', '+auth-only', '/sec:nla']
  2242. with Timing() as timing:
  2243. p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  2244. out, err = p.communicate()
  2245. code = p.returncode
  2246. err = err.replace('''Authentication only. Don't connect to X.
  2247. credssp_recv() error: -1
  2248. freerdp_set_last_error 0x20009\n''', '')
  2249. err = err.replace(''', check credentials.
  2250. If credentials are valid, the NTLMSSP implementation may be to blame.
  2251. Error: protocol security negotiation or connection failure
  2252. Authentication only, exit status 1
  2253. Authentication only, exit status 1''', '')
  2254. err = err.replace('''Authentication only. Don't connect to X.
  2255. Authentication only, exit status 0
  2256. Authentication only, exit status 0''', 'OK')
  2257. mesg = repr((out + err).strip())[1:-1]
  2258. trace = '[out]\n%s\n[err]\n%s' % (out, err)
  2259. return self.Response(code, mesg, timing, trace)
  2260. # }}}
  2261. # VNC {{{
  2262. try:
  2263. from Crypto.Cipher import DES
  2264. except ImportError:
  2265. notfound.append('pycrypto')
  2266. class VNC_Error(Exception): pass
  2267. class VNC:
  2268. def connect(self, host, port, timeout):
  2269. self.fp = socket.create_connection((host, port), timeout=timeout)
  2270. resp = self.fp.recv(99) # banner
  2271. logger.debug('banner: %s' % repr(resp))
  2272. self.version = resp[:11].decode('ascii')
  2273. if len(resp) > 12:
  2274. raise VNC_Error('%s %s' % (self.version, resp[12:].decode('ascii', 'ignore')))
  2275. return self.version
  2276. def login(self, password):
  2277. logger.debug('Remote version: %s' % self.version)
  2278. major, minor = self.version[6], self.version[10]
  2279. if (major, minor) in [('3', '8'), ('4', '1')]:
  2280. proto = b'RFB 003.008\n'
  2281. elif (major, minor) == ('3', '7'):
  2282. proto = b'RFB 003.007\n'
  2283. else:
  2284. proto = b'RFB 003.003\n'
  2285. logger.debug('Client version: %s' % proto[:-1])
  2286. self.fp.sendall(proto)
  2287. sleep(0.5)
  2288. resp = self.fp.recv(99)
  2289. logger.debug('Security types supported: %s' % repr(resp))
  2290. if major == '4' or (major == '3' and int(minor) >= 7):
  2291. code = ord(resp[0:1])
  2292. if code == 0:
  2293. raise VNC_Error('Session setup failed: %s' % resp.decode('ascii', 'ignore'))
  2294. self.fp.sendall(b'\x02') # always use classic VNC authentication
  2295. resp = self.fp.recv(99)
  2296. else: # minor == '3':
  2297. code = ord(resp[3:4])
  2298. if code != 2:
  2299. raise VNC_Error('Session setup failed: %s' % resp.decode('ascii', 'ignore'))
  2300. resp = resp[-16:]
  2301. if len(resp) != 16:
  2302. raise VNC_Error('Unexpected challenge size (No authentication required? Unsupported authentication type?)')
  2303. logger.debug('challenge: %s' % repr(resp))
  2304. pw = password.ljust(8, '\x00')[:8] # make sure it is 8 chars long, zero padded
  2305. key = self.gen_key(pw)
  2306. logger.debug('key: %s' % repr(key))
  2307. des = DES.new(key, DES.MODE_ECB)
  2308. enc = des.encrypt(resp)
  2309. logger.debug('enc: %s' % repr(enc))
  2310. self.fp.sendall(enc)
  2311. resp = self.fp.recv(99)
  2312. logger.debug('resp: %s' % repr(resp))
  2313. code = ord(resp[3:4])
  2314. mesg = resp[8:].decode('ascii', 'ignore')
  2315. if code == 1:
  2316. return code, mesg or 'Authentication failure'
  2317. elif code == 0:
  2318. return code, mesg or 'OK'
  2319. else:
  2320. raise VNC_Error('Unknown response: %s (code: %s)' % (repr(resp), code))
  2321. def gen_key(self, key):
  2322. newkey = []
  2323. for ki in range(len(key)):
  2324. bsrc = ord(key[ki])
  2325. btgt = 0
  2326. for i in range(8):
  2327. if bsrc & (1 << i):
  2328. btgt = btgt | (1 << 7-i)
  2329. newkey.append(btgt)
  2330. if sys.version_info[0] == 2:
  2331. return ''.join(chr(c) for c in newkey)
  2332. else:
  2333. return bytes(newkey)
  2334. class VNC_login:
  2335. '''Brute-force VNC'''
  2336. usage_hints = (
  2337. """%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""",
  2338. )
  2339. available_options = (
  2340. ('host', 'target host'),
  2341. ('port', 'target port [5900]'),
  2342. ('password', 'passwords to test'),
  2343. ('timeout', 'seconds to wait for a response [10]'),
  2344. )
  2345. available_actions = ()
  2346. Response = Response_Base
  2347. def execute(self, host, port=None, password=None, timeout='10'):
  2348. v = VNC()
  2349. try:
  2350. with Timing() as timing:
  2351. code, mesg = 0, v.connect(host, int(port or 5900), int(timeout))
  2352. if password is not None:
  2353. with Timing() as timing:
  2354. code, mesg = v.login(password)
  2355. except VNC_Error as e:
  2356. logger.debug('VNC_Error: %s' % e)
  2357. code, mesg = 2, str(e)
  2358. return self.Response(code, mesg, timing)
  2359. # }}}
  2360. # DNS {{{
  2361. try:
  2362. import dns.rdatatype
  2363. import dns.message
  2364. import dns.query
  2365. import dns.reversename
  2366. except ImportError:
  2367. notfound.append('dnspython')
  2368. def dns_query(server, timeout, protocol, qname, qtype, qclass):
  2369. request = dns.message.make_query(qname, qtype, qclass)
  2370. if protocol == 'tcp':
  2371. response = dns.query.tcp(request, server, timeout=timeout, one_rr_per_rrset=True)
  2372. else:
  2373. response = dns.query.udp(request, server, timeout=timeout, one_rr_per_rrset=True)
  2374. if response.flags & dns.flags.TC:
  2375. response = dns.query.tcp(request, server, timeout=timeout, one_rr_per_rrset=True)
  2376. return response
  2377. def generate_tld():
  2378. # NB. does not return an exhaustive list (ie. missing co.uk, co.nz etc.)
  2379. from itertools import product
  2380. from string import ascii_lowercase
  2381. # http://data.iana.org/TLD/tlds-alpha-by-domain.txt
  2382. gtld = ['academy', 'actor', 'aero', 'agency', 'archi', 'arpa', 'asia', 'axa',
  2383. 'bar', 'bargains', 'berlin', 'best', 'bid', 'bike', 'biz', 'black', 'blue',
  2384. 'boutique', 'build', 'builders', 'buzz', 'cab', 'camera', 'camp', 'cards',
  2385. 'careers', 'cat', 'catering', 'center', 'ceo', 'cheap', 'christmas',
  2386. 'cleaning', 'clothing', 'club', 'codes', 'coffee', 'cologne', 'com',
  2387. 'community', 'company', 'computer', 'condos', 'construction', 'contractors',
  2388. 'cooking', 'cool', 'coop', 'country', 'cruises', 'dance', 'dating', 'democrat',
  2389. 'diamonds', 'directory', 'dnp', 'domains', 'edu', 'education', 'email',
  2390. 'enterprises', 'equipment', 'estate', 'events', 'expert', 'exposed', 'farm',
  2391. 'fish', 'fishing', 'flights', 'florist', 'foundation', 'futbol', 'gallery',
  2392. 'gift', 'glass', 'gov', 'graphics', 'guitars', 'guru', 'haus', 'holdings',
  2393. 'holiday', 'horse', 'house', 'immobilien', 'industries', 'info', 'ink',
  2394. 'institute', 'int', 'international', 'jetzt', 'jobs', 'kaufen', 'kim',
  2395. 'kitchen', 'kiwi', 'koeln', 'kred', 'land', 'lighting', 'limo', 'link',
  2396. 'london', 'luxury', 'maison', 'management', 'mango', 'marketing', 'meet',
  2397. 'menu', 'miami', 'mil', 'mobi', 'moda', 'moe', 'monash', 'museum', 'nagoya',
  2398. 'name', 'net', 'neustar', 'ninja', 'nyc', 'okinawa', 'onl', 'org', 'partners',
  2399. 'parts', 'photo', 'photography', 'photos', 'pics', 'pink', 'plumbing', 'post',
  2400. 'pro', 'productions', 'properties', 'pub', 'qpon', 'recipes', 'red', 'ren',
  2401. 'rentals', 'repair', 'report', 'reviews', 'rich', 'rodeo', 'ruhr', 'sexy',
  2402. 'shiksha', 'shoes', 'singles', 'social', 'sohu', 'solar', 'solutions',
  2403. 'supplies', 'supply', 'support', 'systems', 'tattoo', 'technology', 'tel',
  2404. 'tienda', 'tips', 'today', 'tokyo', 'tools', 'trade', 'training', 'travel',
  2405. 'uno', 'vacations', 'vegas', 'ventures', 'viajes', 'villas', 'vision', 'vodka',
  2406. 'vote', 'voting', 'voto', 'voyage', 'wang', 'watch', 'webcam', 'wed', 'wien',
  2407. 'wiki', 'works', 'xn--3bst00m', 'xn--3ds443g', 'xn--3e0b707e', 'xn--45brj9c',
  2408. 'xn--55qw42g', 'xn--55qx5d', 'xn--6frz82g', 'xn--6qq986b3xl', 'xn--80ao21a',
  2409. 'xn--80asehdb', 'xn--80aswg', 'xn--90a3ac', 'xn--c1avg', 'xn--cg4bki',
  2410. 'xn--clchc0ea0b2g2a9gcd', 'xn--czru2d', 'xn--d1acj3b', 'xn--fiq228c5hs',
  2411. 'xn--fiq64b', 'xn--fiqs8s', 'xn--fiqz9s', 'xn--fpcrj9c3d', 'xn--fzc2c9e2c',
  2412. 'xn--gecrj9c', 'xn--h2brj9c', 'xn--i1b6b1a6a2e', 'xn--io0a7i', 'xn--j1amh',
  2413. 'xn--j6w193g', 'xn--kprw13d', 'xn--kpry57d', 'xn--l1acc', 'xn--lgbbat1ad8j',
  2414. 'xn--mgb9awbf', 'xn--mgba3a4f16a', 'xn--mgbaam7a8h', 'xn--mgbab2bd',
  2415. 'xn--mgbayh7gpa', 'xn--mgbbh1a71e', 'xn--mgbc0a9azcg', 'xn--mgberp4a5d4ar',
  2416. 'xn--mgbx4cd0ab', 'xn--ngbc5azd', 'xn--nqv7f', 'xn--nqv7fs00ema', 'xn--o3cw4h',
  2417. 'xn--ogbpf8fl', 'xn--p1ai', 'xn--pgbs0dh', 'xn--q9jyb4c', 'xn--rhqv96g',
  2418. 'xn--s9brj9c', 'xn--unup4y', 'xn--wgbh1c', 'xn--wgbl6a', 'xn--xkc2al3hye2a',
  2419. 'xn--xkc2dl3a5ee0h', 'xn--yfro4i67o', 'xn--ygbi2ammx', 'xn--zfr164b', 'xxx',
  2420. 'xyz', 'zone']
  2421. cctld = [''.join(i) for i in product(*[ascii_lowercase]*2)]
  2422. tld = gtld + cctld
  2423. return tld, len(tld)
  2424. def generate_srv():
  2425. common = [
  2426. '_gc._tcp', '_kerberos._tcp', '_kerberos._udp', '_ldap._tcp',
  2427. '_test._tcp', '_sips._tcp', '_sip._udp', '_sip._tcp', '_aix._tcp', '_aix._udp',
  2428. '_finger._tcp', '_ftp._tcp', '_http._tcp', '_nntp._tcp', '_telnet._tcp',
  2429. '_whois._tcp', '_h323cs._tcp', '_h323cs._udp', '_h323be._tcp', '_h323be._udp',
  2430. '_h323ls._tcp', '_h323ls._udp', '_sipinternal._tcp', '_sipinternaltls._tcp',
  2431. '_sip._tls', '_sipfederationtls._tcp', '_jabber._tcp', '_xmpp-server._tcp', '_xmpp-client._tcp',
  2432. '_imap.tcp', '_certificates._tcp', '_crls._tcp', '_pgpkeys._tcp', '_pgprevokations._tcp',
  2433. '_cmp._tcp', '_svcp._tcp', '_crl._tcp', '_ocsp._tcp', '_PKIXREP._tcp',
  2434. '_smtp._tcp', '_hkp._tcp', '_hkps._tcp', '_jabber._udp', '_xmpp-server._udp',
  2435. '_xmpp-client._udp', '_jabber-client._tcp', '_jabber-client._udp',
  2436. '_adsp._domainkey', '_policy._domainkey', '_domainkey', '_ldap._tcp.dc._msdcs', '_ldap._udp.dc._msdcs']
  2437. def distro():
  2438. import os
  2439. import re
  2440. files = ['/usr/share/nmap/nmap-protocols', '/usr/share/nmap/nmap-services', '/etc/protocols', '/etc/services']
  2441. ret = []
  2442. for f in files:
  2443. if not os.path.isfile(f):
  2444. logger.warn("File '%s' is missing, there will be less records to test" % f)
  2445. continue
  2446. for line in open(f):
  2447. match = re.match(r'([a-zA-Z0-9]+)\s', line)
  2448. if not match: continue
  2449. for w in re.split(r'[^a-z0-9]', match.group(1).strip().lower()):
  2450. ret.extend(['_%s.%s' % (w, i) for i in ('_tcp', '_udp')])
  2451. return ret
  2452. srv = set(common + distro())
  2453. return srv, len(srv)
  2454. class HostInfo:
  2455. def __init__(self):
  2456. self.name = set()
  2457. self.ip = set()
  2458. self.alias = set()
  2459. def __str__(self):
  2460. line = ''
  2461. if self.name:
  2462. line = ' '.join(self.name)
  2463. if self.ip:
  2464. if line: line += ' / '
  2465. line += ' '.join(map(str, self.ip))
  2466. if self.alias:
  2467. if line: line += ' / '
  2468. line += ' '.join(self.alias)
  2469. return line
  2470. class Controller_DNS(Controller):
  2471. records = defaultdict(list)
  2472. hostmap = defaultdict(HostInfo)
  2473. # show_final {{{
  2474. def show_final(self):
  2475. ''' Expected output:
  2476. Records -----
  2477. ftp.example.com. IN A 10.0.1.1
  2478. www.example.com. IN A 10.0.1.1
  2479. prod.example.com. IN CNAME www.example.com.
  2480. ipv6.example.com. IN AAAA dead:beef::
  2481. dev.example.com. IN A 10.0.1.2
  2482. svn.example.com. IN A 10.0.2.1
  2483. websrv1.example.com. IN CNAME prod.example.com.
  2484. blog.example.com. IN CNAME example.wordpress.com.
  2485. '''
  2486. print('Records ' + '-'*42)
  2487. for name, infos in sorted(self.records.items()):
  2488. for qclass, qtype, rdata in infos:
  2489. print('%34s %4s %-7s %s' % (name, qclass, qtype, rdata))
  2490. ''' Expected output:
  2491. Hostmap ------
  2492. ipv6.example.com dead:beef::
  2493. ftp.example.com 10.0.1.1
  2494. www.example.com 10.0.1.1
  2495. prod.example.com
  2496. websrv1.example.com
  2497. dev.example.com 10.0.1.2
  2498. svn.example.com 10.0.2.1
  2499. example.wordpress.com ?
  2500. blog.example.com
  2501. Domains ---------------------------
  2502. example.com 8
  2503. Networks --------------------------
  2504. dead:beef::
  2505. 10.0.1.x
  2506. 10.0.2.1
  2507. '''
  2508. ipmap = defaultdict(HostInfo)
  2509. noips = defaultdict(list)
  2510. '''
  2511. hostmap = {
  2512. 'www.example.com': {'ip': ['10.0.1.1'], 'alias': ['prod.example.com']},
  2513. 'ftp.example.com': {'ip': ['10.0.1.1'], 'alias': []},
  2514. 'prod.example.com': {'ip': [], 'alias': ['websrv1.example.com']},
  2515. 'ipv6.example.com': {'ip': ['dead:beef::'], 'alias': []},
  2516. 'dev.example.com': {'ip': ['10.0.1.2'], 'alias': []},
  2517. 'example.wordpress.com': {'ip': [], 'alias': ['blog.example.com']},
  2518. ipmap = {'10.0.1.1': {'name': ['www.example.com', 'ftp.example.com'], 'alias': ['prod.example.com', 'websrv1.example.com']}, ...
  2519. noips = {'example.wordpress.com': ['blog.example.com'],
  2520. '''
  2521. for name, hinfo in self.hostmap.items():
  2522. for ip in hinfo.ip:
  2523. ip = IP(ip)
  2524. ipmap[ip].name.add(name)
  2525. ipmap[ip].alias.update(hinfo.alias)
  2526. for name, hinfo in self.hostmap.items():
  2527. if not hinfo.ip and hinfo.alias:
  2528. found = False
  2529. for ip, v in ipmap.items():
  2530. if name in v.alias:
  2531. for alias in hinfo.alias:
  2532. ipmap[ip].alias.add(alias)
  2533. found = True
  2534. if not found: # orphan CNAME hostnames (with no IP address) may be still valid virtual hosts
  2535. noips[name].extend(hinfo.alias)
  2536. print('Hostmap ' + '-'*42)
  2537. for ip, hinfo in sorted(ipmap.items()):
  2538. for name in hinfo.name:
  2539. print('%34s %s' % (name, ip))
  2540. for alias in hinfo.alias:
  2541. print('%34s' % alias)
  2542. for k, v in noips.items():
  2543. print('%34s ?' % k)
  2544. for alias in v:
  2545. print('%34s' % alias)
  2546. print('Domains ' + '-'*42)
  2547. domains = {}
  2548. for ip, hinfo in ipmap.items():
  2549. for name in hinfo.name.union(hinfo.alias):
  2550. if name.count('.') > 1:
  2551. i = 1
  2552. else:
  2553. i = 0
  2554. d = '.'.join(name.split('.')[i:])
  2555. if d not in domains: domains[d] = 0
  2556. domains[d] += 1
  2557. for domain, count in sorted(domains.items(), key=lambda a:a[0].split('.')[-1::-1]):
  2558. print('%34s %d' % (domain, count))
  2559. print('Networks ' + '-'*41)
  2560. nets = {}
  2561. for ip in set(ipmap):
  2562. if not ip.version() == 4:
  2563. nets[ip] = [ip]
  2564. else:
  2565. n = ip.make_net('255.255.255.0')
  2566. if n not in nets: nets[n] = []
  2567. nets[n].append(ip)
  2568. for net, ips in sorted(nets.items()):
  2569. if len(ips) == 1:
  2570. print(' '*34 + ' %s' % ips[0])
  2571. else:
  2572. print(' '*34 + ' %s.x' % '.'.join(str(net).split('.')[:-1]))
  2573. # }}}
  2574. def push_final(self, resp):
  2575. if hasattr(resp, 'rrs'):
  2576. for rr in resp.rrs:
  2577. name, qclass, qtype, data = rr
  2578. info = (qclass, qtype, data)
  2579. if info not in self.records[name]:
  2580. self.records[name].append(info)
  2581. if not qclass == 'IN':
  2582. continue
  2583. if qtype == 'PTR':
  2584. data = data[:-1]
  2585. self.hostmap[data].ip.add(name)
  2586. else:
  2587. if qtype in ('A', 'AAAA'):
  2588. name = name[:-1]
  2589. self.hostmap[name].ip.add(data)
  2590. elif qtype == 'CNAME':
  2591. name, data = name[:-1], data[:-1]
  2592. self.hostmap[data].alias.add(name)
  2593. class DNS_reverse:
  2594. '''Reverse DNS lookup'''
  2595. usage_hints = [
  2596. """%prog host=NET0 0=192.168.0.0/24 -x ignore:code=3""",
  2597. """%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-""",
  2598. ]
  2599. available_options = (
  2600. ('host', 'IP addresses to reverse lookup'),
  2601. ('server', 'name server to query (directly asking a zone authoritative NS may return more results) [8.8.8.8]'),
  2602. ('timeout', 'seconds to wait for a response [5]'),
  2603. ('protocol', 'send queries over udp or tcp [udp]'),
  2604. )
  2605. available_actions = ()
  2606. Response = Response_Base
  2607. def execute(self, host, server='8.8.8.8', timeout='5', protocol='udp'):
  2608. with Timing() as timing:
  2609. response = dns_query(server, int(timeout), protocol, dns.reversename.from_address(host), qtype='PTR', qclass='IN')
  2610. code = response.rcode()
  2611. status = dns.rcode.to_text(code)
  2612. rrs = [[host, c, t, d] for _, _, c, t, d in [rr.to_text().split(' ', 4) for rr in response.answer]]
  2613. mesg = '%s %s' % (status, ''.join('[%s]' % ' '.join(rr) for rr in rrs))
  2614. resp = self.Response(code, mesg, timing)
  2615. resp.rrs = rrs
  2616. return resp
  2617. class DNS_forward:
  2618. '''Forward DNS lookup'''
  2619. usage_hints = [
  2620. """%prog name=FILE0.google.com 0=names.txt -x ignore:code=3""",
  2621. """%prog name=google.MOD0 0=TLD -x ignore:code=3""",
  2622. """%prog name=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3""",
  2623. ]
  2624. available_options = (
  2625. ('name', 'domain names to lookup'),
  2626. ('server', 'name server to query (directly asking the zone authoritative NS may return more results) [8.8.8.8]'),
  2627. ('timeout', 'seconds to wait for a response [5]'),
  2628. ('protocol', 'send queries over udp or tcp [udp]'),
  2629. ('qtype', 'type to query [ANY]'),
  2630. ('qclass', 'class to query [IN]'),
  2631. )
  2632. available_actions = ()
  2633. available_keys = {
  2634. 'TLD': generate_tld,
  2635. 'SRV': generate_srv,
  2636. }
  2637. Response = Response_Base
  2638. def execute(self, name, server='8.8.8.8', timeout='5', protocol='udp', qtype='ANY', qclass='IN'):
  2639. with Timing() as timing:
  2640. response = dns_query(server, int(timeout), protocol, name, qtype=qtype, qclass=qclass)
  2641. code = response.rcode()
  2642. status = dns.rcode.to_text(code)
  2643. rrs = [[n, c, t, d] for n, _, c, t, d in [rr.to_text().split(' ', 4) for rr in response.answer + response.additional + response.authority]]
  2644. mesg = '%s %s' % (status, ''.join('[%s]' % ' '.join(rr) for rr in rrs))
  2645. resp = self.Response(code, mesg, timing)
  2646. resp.rrs = rrs
  2647. return resp
  2648. # }}}
  2649. # SNMP {{{
  2650. try:
  2651. from pysnmp.entity.rfc3413.oneliner import cmdgen
  2652. except ImportError:
  2653. notfound.append('pysnmp')
  2654. class SNMP_login:
  2655. '''Brute-force SNMP v1/2/3'''
  2656. usage_hints = (
  2657. """%prog host=10.0.0.1 version=2 community=FILE0 0=names.txt -x ignore:mesg='No SNMP response received before timeout'""",
  2658. """%prog host=10.0.0.1 version=3 user=FILE0 0=logins.txt -x ignore:mesg=unknownUserName""",
  2659. """%prog host=10.0.0.1 version=3 user=myuser auth_key=FILE0 0=passwords.txt -x ignore:mesg=wrongDigest""",
  2660. )
  2661. available_options = (
  2662. ('host', 'target host'),
  2663. ('port', 'target port [161]'),
  2664. ('version', 'SNMP version to use [2|3|1]'),
  2665. #('security_name', 'SNMP v1/v2 username, for most purposes it can be any arbitrary string [test-agent]'),
  2666. ('community', 'SNMPv1/2c community names to test [public]'),
  2667. ('user', 'SNMPv3 usernames to test [myuser]'),
  2668. ('auth_key', 'SNMPv3 pass-phrases to test [my_password]'),
  2669. #('priv_key', 'SNMP v3 secret key for encryption'), # see http://pysnmp.sourceforge.net/docs/4.x/index.html#UsmUserData
  2670. #('auth_protocol', ''),
  2671. #('priv_protocol', ''),
  2672. ('timeout', 'seconds to wait for a response [1]'),
  2673. ('retries', 'number of successive request retries [2]'),
  2674. )
  2675. available_actions = ()
  2676. Response = Response_Base
  2677. def execute(self, host, port=None, version='2', community='public', user='myuser', auth_key='my_password', timeout='1', retries='2'):
  2678. if version in ('1', '2'):
  2679. security_model = cmdgen.CommunityData('test-agent', community, 0 if version == '1' else 1)
  2680. elif version == '3':
  2681. security_model = cmdgen.UsmUserData(user, auth_key) # , priv_key)
  2682. if len(auth_key) < 8:
  2683. return self.Response('1', 'SNMPv3 requires passphrases to be at least 8 characters long')
  2684. else:
  2685. raise ValueError("Incorrect SNMP version '%s'" % version)
  2686. with Timing() as timing:
  2687. errorIndication, errorStatus, errorIndex, varBinds = cmdgen.CommandGenerator().getCmd(
  2688. security_model,
  2689. cmdgen.UdpTransportTarget((host, int(port or 161)), timeout=int(timeout), retries=int(retries)),
  2690. (1,3,6,1,2,1,1,1,0)
  2691. )
  2692. code = '%d-%d' % (errorStatus, errorIndex)
  2693. if not errorIndication:
  2694. mesg = '%s' % varBinds
  2695. else:
  2696. mesg = '%s' % errorIndication
  2697. return self.Response(code, mesg, timing)
  2698. # }}}
  2699. # IKE {{{
  2700. if not which('ike-scan'):
  2701. notfound.append('ike-scan')
  2702. # http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xhtml
  2703. IKE_ENC = [('1', 'DES'), ('2', 'IDEA'), ('3', 'BLOWFISH'), ('4', 'RC5'), ('5', '3DES'), ('6', 'CAST'), ('7/128', 'AES128'), ('7/192', 'AES192'), ('7/256', 'AES256'), ('8', 'Camellia')]
  2704. IKE_HASH = [('1', 'MD5'), ('2', 'SHA1'), ('3', 'Tiger'), ('4', 'SHA2-256'), ('5', 'SHA2-384'), ('6', 'SHA2-512')]
  2705. IKE_AUTH = [('1', 'PSK'), ('2', 'DSS Sig'), ('3', 'RSA Sig'), ('4', 'RSA Enc'), ('5', 'Revised RSA Enc'),
  2706. #('6', 'EIGAMEL Enc'), ('7', 'Revised EIGAMEL Enc'), ('8', 'ECDSA Sig'), # Reserved
  2707. #('9', 'ECDSA SHA-256'), ('10', 'ECDSA SHA-384'), ('11', 'ECDSA SHA-512'), # RFC4754
  2708. ('65001', 'XAUTH'), ('64221', 'Hybrid'), ('64222', 'Hybrid 64222')] #, ('64223', 'Hybrid 64223'), ... ('65002', 'Hybrid 65002') ...
  2709. IKE_GROUP = [('1', 'modp768'), ('2', 'modp1024'), ('5', 'modp1536'),
  2710. #('3', 'ecc3'), ('4', 'ecc4'), # any implementations?
  2711. # '6', '7', '8', '9', '10', '11', '12', '13', # only in draft, not RFC
  2712. ('14', 'modp2048')] #, ('15', 'modp3072'), ('16', 'modp4096'), ('17', 'modp6144'), ('18', 'modp8192')] # RFC3526
  2713. # '19', '20', '21', '22', '23', '24', '25', '26', # RFC5903
  2714. # '27', '28', '29', '30', # RFC6932
  2715. def generate_transforms():
  2716. lists = map(lambda l: [i[0] for i in l], [IKE_ENC, IKE_HASH, IKE_AUTH, IKE_GROUP])
  2717. return map(lambda p: ','.join(p), product(*[chain(l) for l in lists])), reduce(lambda x,y: x*y, map(len, lists))
  2718. class Controller_IKE(Controller):
  2719. results = defaultdict(list)
  2720. def show_final(self):
  2721. ''' Expected output:
  2722. + 10.0.0.1:500 (Main Mode)
  2723. Encryption Hash Auth Group
  2724. ---------- ---------- ---------- ----------
  2725. 3DES MD5 PSK modp1024
  2726. 3DES MD5 XAUTH modp1024
  2727. AES128 SHA1 PSK modp1024
  2728. AES128 SHA1 XAUTH modp1024
  2729. + 10.0.0.1:500 (Aggressive Mode)
  2730. Encryption Hash Auth Group
  2731. ---------- ---------- ---------- ----------
  2732. 3DES MD5 PSK modp1024
  2733. 3DES MD5 XAUTH modp1024
  2734. AES128 SHA1 PSK modp1024
  2735. AES128 SHA1 XAUTH modp1024
  2736. '''
  2737. ike_enc = dict(IKE_ENC)
  2738. ike_hsh = dict(IKE_HASH)
  2739. ike_ath = dict(IKE_AUTH)
  2740. ike_grp = dict(IKE_GROUP)
  2741. for endpoint, transforms in self.results.iteritems():
  2742. print('\n+ %s' % endpoint)
  2743. print(' %10s %10s %12s %10s' % ('Encryption', 'Hash', 'Auth', 'Group'))
  2744. print(' %10s %10s %12s %10s' % ('-'*10, '-'*10, '-'*10, '-'*10))
  2745. for transform in transforms:
  2746. e, h, a, g = transform.split(',')
  2747. enc = ike_enc[e]
  2748. hsh = ike_hsh[h]
  2749. ath = ike_ath[a]
  2750. grp = ike_grp[g]
  2751. print(' %10s %10s %12s %10s' % (enc, hsh, ath, grp))
  2752. def push_final(self, resp):
  2753. if hasattr(resp, 'rrs'):
  2754. endpoint, transform = resp.rrs
  2755. self.results[endpoint].append(transform)
  2756. class IKE_enum:
  2757. '''Enumerate IKE transforms'''
  2758. usage_hints = [
  2759. """%prog host=10.0.0.1 transform=MOD0 0=TRANS -x ignore:fgrep=NO-PROPOSAL""",
  2760. """%prog host=10.0.0.1 transform=MOD0 0=TRANS -x ignore:fgrep=NO-PROPOSAL aggressive=RANGE1 1=int:0-1""",
  2761. ]
  2762. available_options = (
  2763. ('host', 'target host'),
  2764. ('host', 'target port [500]'),
  2765. ('transform', 'transform to test [5,1,1,2]'),
  2766. ('aggressive', 'use aggressive mode [0|1]'),
  2767. ('groupname', 'identification value for aggressive mode [foo]'),
  2768. ('vid', 'comma-separated vendor IDs to use'),
  2769. )
  2770. available_actions = ()
  2771. available_keys = {
  2772. 'TRANS': generate_transforms,
  2773. }
  2774. Response = Response_Base
  2775. def __init__(self):
  2776. uid = multiprocessing.current_process().name[9:]
  2777. self.sport = '51%s' % uid
  2778. def execute(self, host, port='500', transform='5,1,1,2', aggressive='0', groupname='foo', vid=''):
  2779. cmd = ['ike-scan', '-M', '--sport', self.sport, host, '--dport', port, '--trans', transform]
  2780. if aggressive == '1':
  2781. cmd.append('-A')
  2782. if groupname:
  2783. cmd.extend(['--id', groupname])
  2784. for v in vid.split(','):
  2785. cmd.extend(['--vendor', v])
  2786. with Timing() as timing:
  2787. p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  2788. out, err = p.communicate()
  2789. code = p.returncode
  2790. trace = '%s\n[out]\n%s\n[err]\n%s' % (cmd, out, err)
  2791. logger.debug('trace: %s' % repr(trace))
  2792. has_sa = 'SA=(' in out
  2793. if has_sa:
  2794. mesg = 'Handshake returned: %s (%s)' % (re.search('SA=\((.+) LifeType', out).group(1), re.search('\t(.+) Mode Handshake returned', out).group(1))
  2795. else:
  2796. try:
  2797. mesg = out.strip().split('\n')[1].split('\t')[-1]
  2798. except:
  2799. mesg = ' '.join(repr(s) for s in filter(None, [out, err]))
  2800. resp = self.Response(code, mesg, timing, trace)
  2801. if has_sa:
  2802. endpoint = '%s:%s (%s Mode)' % (host, port, 'Aggressive' if aggressive == '1' else 'Main')
  2803. resp.rrs = endpoint, transform
  2804. return resp
  2805. # }}}
  2806. # Unzip {{{
  2807. if not which('unzip'):
  2808. notfound.append('unzip')
  2809. class Unzip_pass:
  2810. '''Brute-force the password of encrypted ZIP files'''
  2811. usage_hints = [
  2812. """%prog zipfile=path/to/file.zip password=FILE0 0=passwords.txt -x ignore:code!=0""",
  2813. ]
  2814. available_options = (
  2815. ('zipfile', 'ZIP files to test'),
  2816. ('password', 'passwords to test'),
  2817. )
  2818. available_actions = ()
  2819. Response = Response_Base
  2820. def execute(self, zipfile, password):
  2821. zipfile = os.path.abspath(zipfile)
  2822. cmd = ['unzip', '-t', '-q', '-P', password, zipfile]
  2823. with Timing() as timing:
  2824. p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  2825. out, err = p.communicate()
  2826. code = p.returncode
  2827. mesg = repr(out.strip())[1:-1]
  2828. trace = '%s\n[out]\n%s\n[err]\n%s' % (cmd, out, err)
  2829. return self.Response(code, mesg, timing, trace)
  2830. # }}}
  2831. # Keystore {{{
  2832. if not which('keytool'):
  2833. notfound.append('java')
  2834. class Keystore_pass:
  2835. '''Brute-force the password of Java keystore files'''
  2836. usage_hints = [
  2837. """%prog keystore=path/to/keystore.jks password=FILE0 0=passwords.txt -x ignore:fgrep='password was incorrect'""",
  2838. ]
  2839. available_options = (
  2840. ('keystore', 'keystore files to test'),
  2841. ('password', 'passwords to test'),
  2842. ('storetype', 'type of keystore to test'),
  2843. )
  2844. available_actions = ()
  2845. Response = Response_Base
  2846. def execute(self, keystore, password, storetype='jks'):
  2847. keystore = os.path.abspath(keystore)
  2848. cmd = ['keytool', '-list', '-keystore', keystore, '-storepass', password, '-storetype', storetype]
  2849. with Timing() as timing:
  2850. p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  2851. out, err = p.communicate()
  2852. code = p.returncode
  2853. mesg = repr(out.strip())[1:-1]
  2854. trace = '%s\n[out]\n%s\n[err]\n%s' % (cmd, out, err)
  2855. return self.Response(code, mesg, timing, trace)
  2856. # }}}
  2857. # Umbraco {{{
  2858. import hmac
  2859. class Umbraco_crack:
  2860. '''Crack Umbraco HMAC-SHA1 password hashes'''
  2861. usage_hints = (
  2862. """%prog hashlist=@umbraco_users.pw password=FILE0 0=rockyou.txt""",
  2863. )
  2864. available_options = (
  2865. ('hashlist', 'hashes to crack'),
  2866. ('password', 'password to test'),
  2867. )
  2868. available_actions = ()
  2869. Response = Response_Base
  2870. def execute(self, password, hashlist):
  2871. p = password.encode('utf-16-le')
  2872. h = b64encode(hmac.new(p, p, digestmod=hashlib.sha1).digest())
  2873. if h not in hashlist:
  2874. code, mesg = 1, 'fail'
  2875. else:
  2876. cracked = [line.rstrip() for line in hashlist.split('\n') if h in line]
  2877. code, mesg = 0, ' '.join(cracked)
  2878. return self.Response(code, mesg)
  2879. # }}}
  2880. # TCP Fuzz {{{
  2881. class TCP_fuzz:
  2882. '''Fuzz TCP services'''
  2883. usage_hints = (
  2884. '''%prog host=10.0.0.1 data=RANGE0 0=hex:0x00-0xffffff''',
  2885. )
  2886. available_options = (
  2887. ('host', 'target host'),
  2888. ('port', 'target port'),
  2889. ('timeout', 'seconds to wait for a response [10]'),
  2890. )
  2891. available_actions = ()
  2892. Response = Response_Base
  2893. def execute(self, host, port, data='', timeout='2'):
  2894. fp = socket.create_connection((host, port), int(timeout))
  2895. fp.send(data.decode('hex'))
  2896. with Timing() as timing:
  2897. resp = fp.recv(1024)
  2898. fp.close()
  2899. code = 0
  2900. mesg = resp.encode('hex')
  2901. return self.Response(code, mesg, timing)
  2902. # }}}
  2903. # Dummy Test {{{
  2904. class Dummy_test:
  2905. '''Testing module'''
  2906. usage_hints = (
  2907. """%prog data=_@@_RANGE0_@@_ 0=hex:0x00-0xff -e _@@_:unhex""",
  2908. """%prog data=RANGE0 0=int:10-0""",
  2909. """%prog data=PROG0 0='seq -w 10 -1 0'""",
  2910. """%prog data=PROG0 0='mp64.bin -i ?l?l?l',$(mp64.bin --combination -i ?l?l?l)""",
  2911. )
  2912. available_options = (
  2913. ('data', 'data to test'),
  2914. ('data2', 'data2 to test'),
  2915. ('delay', 'fake random delay'),
  2916. )
  2917. available_actions = ()
  2918. Response = Response_Base
  2919. def execute(self, data, data2='', delay='1'):
  2920. code, mesg = 0, '%s / %s' % (data, data2)
  2921. with Timing() as timing:
  2922. sleep(random.randint(0, int(delay)*1000)/1000.0)
  2923. return self.Response(code, mesg, timing)
  2924. # }}}
Add Comment
Please, Sign In to add comment