SHARE
TWEET

Untitled

a guest Mar 20th, 2016 2,895 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env python2
  2.  
  3. # Copyright (C) 2012 Sebastien MACKE
  4. #
  5. # This program is free software; you can redistribute it and/or modify it under
  6. # the terms of the GNU General Public License version 2, as published by the
  7. # Free Software Foundation
  8. #
  9. # This program is distributed in the hope that it will be useful, but WITHOUT
  10. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  12. # details (http://www.gnu.org/licenses/gpl.txt).
  13.  
  14. __author__  = 'Sebastien Macke'
  15. __email__   = 'patator@hsc.fr'
  16. __url__     = 'http://www.hsc.fr/ressources/outils/patator/'
  17. __git__     = 'https://github.com/lanjelot/patator'
  18. __twitter__ = 'http://twitter.com/lanjelot'
  19. __version__ = '0.7-beta'
  20. __license__ = 'GPLv2'
  21. __banner__  = 'Patator v%s (%s)' % (__version__, __git__)
  22.  
  23. # README {{{
  24.  
  25. '''
  26. INTRODUCTION
  27. ------------
  28.  
  29. * What ?
  30.  
  31. Patator is a multi-purpose brute-forcer, with a modular design and a flexible usage.
  32.  
  33. Currently it supports the following modules:
  34.   + ftp_login     : Brute-force FTP
  35.   + ssh_login     : Brute-force SSH
  36.   + telnet_login  : Brute-force Telnet
  37.   + smtp_login    : Brute-force SMTP
  38.   + smtp_vrfy     : Enumerate valid users using SMTP VRFY
  39.   + smtp_rcpt     : Enumerate valid users using SMTP RCPT TO
  40.   + finger_lookup : Enumerate valid users using Finger
  41.   + http_fuzz     : Brute-force HTTP
  42.   + pop_login     : Brute-force POP3
  43.   + pop_passd     : Brute-force poppassd (http://netwinsite.com/poppassd/)
  44.   + imap_login    : Brute-force IMAP4
  45.   + ldap_login    : Brute-force LDAP
  46.   + smb_login     : Brute-force SMB
  47.   + smb_lookupsid : Brute-force SMB SID-lookup
  48.   + rlogin_login  : Brute-force rlogin
  49.   + vmauthd_login : Brute-force VMware Authentication Daemon
  50.   + mssql_login   : Brute-force MSSQL
  51.   + oracle_login  : Brute-force Oracle
  52.   + mysql_login   : Brute-force MySQL
  53.   + mysql_query   : Brute-force MySQL queries
  54.   * rdp_login     : Brute-force RDP (NLA)
  55.   + pgsql_login   : Brute-force PostgreSQL
  56.   + vnc_login     : Brute-force VNC
  57.  
  58.   + dns_forward   : Forward DNS lookup
  59.   + dns_reverse   : Reverse DNS lookup
  60.   + snmp_login    : Brute-force SNMP v1/2/3
  61.   + ike_enum      : Enumerate IKE transforms
  62.  
  63.   + unzip_pass    : Brute-force the password of encrypted ZIP files
  64.   + keystore_pass : Brute-force the password of Java keystore files
  65.   + umbraco_crack : Crack Umbraco HMAC-SHA1 password hashes
  66.  
  67.   + tcp_fuzz      : Fuzz TCP services
  68.   + dummy_test    : Testing module
  69.  
  70. Future modules to be implemented:
  71.   - rdp_login w/no NLA
  72.  
  73. The name "Patator" comes from http://www.youtube.com/watch?v=xoBkBvnTTjo
  74.  
  75. * Why ?
  76.  
  77. Basically, I got tired of using Medusa, Hydra, Ncrack, Metasploit auxiliary modules, Nmap NSE scripts and the like because:
  78.   - they either do not work or are not reliable (got me false negatives several times in the past)
  79.   - they are not flexible enough (how to iterate over all wordlists, fuzz any module parameter)
  80.   - they lack useful features (display progress or pause during execution)
  81.  
  82.  
  83. FEATURES
  84. --------
  85.   * No false negatives, as it is the user that decides what results to ignore based on:
  86.       + status code of response
  87.       + size of response
  88.       + matching string or regex in response data
  89.       + ... see --help
  90.  
  91.   * Modular design
  92.       + not limited to network modules (eg. the unzip_pass module)
  93.       + not limited to brute-forcing (eg. remote exploit testing, or vulnerable version probing)
  94.  
  95.   * Interactive runtime
  96.       + show progress during execution (press Enter)
  97.       + pause/unpause execution (press p)
  98.       + increase/decrease verbosity
  99.       + add new actions & conditions during runtime (eg. to exclude more types of response from showing)
  100.       + ... press h to see all available interactive commands
  101.  
  102.   * Use persistent connections (ie. will test several passwords until the server disconnects)
  103.  
  104.   * Multi-threaded
  105.  
  106.   * Flexible user input
  107.     - Any module parameter can be fuzzed:
  108.       + use the FILE keyword to iterate over a file
  109.       + use the COMBO keyword to iterate over a combo file
  110.       + use the NET keyword to iterate over every hosts of a network subnet
  111.       + use the RANGE keyword to iterate over hexadecimal, decimal or alphabetical ranges
  112.       + use the PROG keyword to iterate over the output of an external program
  113.  
  114.     - Iteration over the joined wordlists can be done in any order
  115.  
  116.   * Save every response (along with request) to seperate log files for later reviewing
  117.  
  118.  
  119. INSTALL
  120. -------
  121.  
  122. * Dependencies (best tested versions)
  123.  
  124.                  |  Required for  |                        URL                         | Version |
  125. --------------------------------------------------------------------------------------------------
  126. paramiko         | SSH            | http://www.lag.net/paramiko/                       | 1.7.7.1 |
  127. --------------------------------------------------------------------------------------------------
  128. pycurl           | HTTP           | http://pycurl.sourceforge.net/                     |  7.19.0 |
  129. --------------------------------------------------------------------------------------------------
  130. openldap         | LDAP           | http://www.openldap.org/                           |  2.4.24 |
  131. --------------------------------------------------------------------------------------------------
  132. impacket         | SMB            | https://github.com/CoreSecurity/impacket           |  0.9.12 |
  133. --------------------------------------------------------------------------------------------------
  134. cx_Oracle        | Oracle         | http://cx-oracle.sourceforge.net/                  |   5.1.1 |
  135. --------------------------------------------------------------------------------------------------
  136. mysql-python     | MySQL          | http://sourceforge.net/projects/mysql-python/      |   1.2.3 |
  137. --------------------------------------------------------------------------------------------------
  138. xfreerdp         | RDP (NLA)      | https://github.com/FreeRDP/FreeRDP/                |   1.2.0 |
  139. --------------------------------------------------------------------------------------------------
  140. psycopg          | PostgreSQL     | http://initd.org/psycopg/                          |   2.4.5 |
  141. --------------------------------------------------------------------------------------------------
  142. pycrypto         | VNC            | http://www.dlitz.net/software/pycrypto/            |     2.3 |
  143. --------------------------------------------------------------------------------------------------
  144. dnspython        | DNS            | http://www.dnspython.org/                          |  1.10.0 |
  145. --------------------------------------------------------------------------------------------------
  146. IPy              | NET keyword    | https://github.com/haypo/python-ipy                |    0.75 |
  147. --------------------------------------------------------------------------------------------------
  148. pysnmp           | SNMP           | http://pysnmp.sourceforge.net/                     |   4.2.1 |
  149. --------------------------------------------------------------------------------------------------
  150. pyasn1           | SNMP           | http://sourceforge.net/projects/pyasn1/            |   0.1.2 |
  151. --------------------------------------------------------------------------------------------------
  152. ike-scan         | IKE            | http://www.nta-monitor.com/tools-resources/        |     1.9 |
  153. --------------------------------------------------------------------------------------------------
  154. unzip            | ZIP passwords  | http://www.info-zip.org/                           |     6.0 |
  155. --------------------------------------------------------------------------------------------------
  156. Java             | keystore files | http://www.oracle.com/technetwork/java/javase/     |       6 |
  157. --------------------------------------------------------------------------------------------------
  158. python           |                | http://www.python.org/                             |     2.7 |
  159. --------------------------------------------------------------------------------------------------
  160.  
  161. * Shortcuts (optional)
  162. ln -s path/to/patator.py /usr/bin/ftp_login
  163. ln -s path/to/patator.py /usr/bin/http_fuzz
  164. so on ...
  165.  
  166.  
  167. USAGE
  168. -----
  169.  
  170. $ python patator.py <module> -h
  171. or
  172. $ <module> -h  (if you created the shortcuts)
  173.  
  174. There are global options and module options:
  175.   - all global options start with - or --
  176.   - all module options are of the form option=value
  177.  
  178. All module options are fuzzable:
  179. ---------
  180. ./module host=FILE0 port=FILE1 foobar=FILE2.google.FILE3 0=hosts.txt 1=ports.txt 2=foo.txt 3=bar.txt
  181.  
  182. The keywords (FILE, COMBO, NET, ...) act as place-holders. They indicate the type of wordlist
  183. and where to replace themselves with the actual words to test.
  184.  
  185. Each keyword is numbered in order to:
  186.   - match the corresponding wordlist
  187.   - and indicate in what order to iterate over all the wordlists
  188.  
  189. For example, this would be the classic order:
  190. ---------
  191. $ ./module host=FILE0 user=FILE1 password=FILE2 0=hosts.txt 1=logins.txt 2=passwords.txt
  192. 10.0.0.1 root password
  193. 10.0.0.1 root 123456
  194. 10.0.0.1 root qsdfghj
  195. ... (trying all passwords before testing next login)
  196. 10.0.0.1 admin password
  197. 10.0.0.1 admin 123456
  198. 10.0.0.1 admin qsdfghj
  199. ... (trying all logins before testing next host)
  200. 10.0.0.2 root password
  201. ...
  202.  
  203. While a smarter way might be:
  204. ---------
  205. $ ./module host=FILE2 user=FILE1 password=FILE0 2=hosts.txt 1=logins.txt 0=passwords.txt
  206. 10.0.0.1 root password
  207. 10.0.0.2 root password
  208. 10.0.0.1 admin password
  209. 10.0.0.2 admin password
  210. 10.0.0.1 root 123456
  211. 10.0.0.2 root 123456
  212. 10.0.0.1 admin 123456
  213. ...
  214.  
  215.  
  216. * Keywords
  217.  
  218. Brute-force a list of hosts with a file containing combo entries (each line => login:password).
  219. ---------
  220. ./module host=FILE0 user=COMBO10 password=COMBO11 0=hosts.txt 1=combos.txt
  221.  
  222. Scan subnets to just grab version banners.
  223. ---------
  224. ./module host=NET0 0=10.0.1.0/24,10.0.2.0/24,10.0.3.128-10.0.3.255
  225.  
  226. Fuzzing a parameter by iterating over a range of values.
  227. ---------
  228. ./module param=RANGE0 0=hex:0x00-0xffff
  229. ./module param=RANGE0 0=int:0-500
  230. ./module param=RANGE0 0=lower:a-zzz
  231.  
  232. Fuzzing a parameter by iterating over the output of an external program.
  233. ---------
  234. ./module param=PROG0 0='john -stdout -i'
  235. ./module param=PROG0 0='mp64.bin ?l?l?l',$(mp64.bin --combination ?l?l?l) # http://hashcat.net/wiki/doku.php?id=maskprocessor
  236.  
  237.  
  238. * Actions & Conditions
  239.  
  240. Use the -x option to do specific actions upon receiving expected results. For example:
  241.  
  242. To ignore responses with status code 200 *AND* a size within a specific range.
  243. ---------
  244. ./module host=10.0.0.1 user=FILE0 -x ignore:code=200,size=57-74
  245.  
  246. To ignore responses with status code 500 *OR* containing "Internal error".
  247. ---------
  248. ./module host=10.0.0.1 user=FILE0 -x ignore:code=500 -x ignore:fgrep='Internal error'
  249.  
  250. Remember that conditions are ANDed within the same -x option, use multiple -x options to
  251. specify ORed conditions.
  252.  
  253.  
  254. * Failures
  255.  
  256. During execution, failures may happen, such as a TCP connect timeout for
  257. example. By definition a failure is an exception that the module does not expect,
  258. and as a result the exception is caught upstream by the controller.
  259.  
  260. Such exceptions, or failures, are not immediately reported to the user, the
  261. controller will retry 4 more times (see --max-retries) before reporting the
  262. failed payload to the user with the logging level "FAIL".
  263.  
  264.  
  265. * Read carefully the following examples to get a good understanding of how patator works.
  266. {{{ FTP
  267.  
  268. * Brute-force authentication. Do not report wrong passwords.
  269. ---------
  270. ftp_login host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt -x ignore:mesg='Login incorrect.'
  271.  
  272. NB0. If you get errors like "500 OOPS: priv_sock_get_cmd", use -x ignore,reset,retry:code=500
  273.      in order to retry the last login/password using a new TCP connection. Odd servers like vsftpd
  274.      return this when they shut down the TCP connection (ie. max login attempts reached).
  275.  
  276. NB1. If you get errors like "too many connections from your IP address", try decreasing the number of
  277.      threads, the server may be enforcing a maximum number of concurrent connections.
  278.  
  279. * Same as before, but stop testing a user after his password is found.
  280. ---------
  281. ftp_login ... -x free=user:code=0
  282.  
  283.  
  284. * Find anonymous FTP servers on a subnet.
  285. ---------
  286. ftp_login host=NET0 user=anonymous password=test@example.com 0=10.0.0.0/24
  287.  
  288. }}}
  289. {{{ SSH
  290. * Brute-force authentication with password same as login (aka single mode). Do not report wrong passwords.
  291. ---------
  292. ssh_login host=10.0.0.1 user=FILE0 password=FILE0 0=logins.txt -x ignore:mesg='Authentication failed.'
  293.  
  294. NB. If you get errors like "Error reading SSH protocol banner ... Connection reset by peer",
  295.     try decreasing the number of threads, the server may be enforcing a maximum
  296.     number of concurrent connections (eg. MaxStartups in OpenSSH).
  297.  
  298.  
  299. * Brute-force several hosts and stop testing a host after a valid password is found.
  300. ---------
  301. ssh_login host=FILE0 user=FILE1 password=FILE2 0=hosts.txt 1=logins.txt 2=passwords.txt -x free=host:code=0
  302.  
  303.  
  304. * Same as previous, but stop testing a user on a host after his password is found.
  305. ---------
  306. ssh_login host=FILE0 user=FILE1 password=FILE2 0=hosts.txt 1=logins.txt 2=passwords.txt -x free=host+user:code=0
  307.  
  308. }}}
  309. {{{ Telnet
  310.  
  311. * Brute-force authentication.
  312.   (a) Enter login after first prompt is detected, enter password after second prompt.
  313.   (b) The regex to detect the login and password prompts.
  314.   (c) Reconnect when we get no login prompt back (max number of tries reached or successful login).
  315. ------------                    (a)
  316. telnet_login host=10.0.0.1 inputs='FILE0\nFILE1' 0=logins.txt 1=passwords.txt
  317.  prompt_re='tux login:|Password:' -x reset:egrep!='Login incorrect.+tux login:'
  318.  (b)                             (c)
  319.  
  320. NB. If you get errors like "telnet connection closed", try decreasing the number of threads,
  321.     the server may be enforcing a maximum number of concurrent connections.
  322.  
  323. }}}
  324. {{{ SMTP
  325.  
  326. * Enumerate valid users using the VRFY command.
  327.   (a) Do not report invalid recipients.
  328.   (b) Do not report when the server shuts us down with "421 too many errors",
  329.       reconnect and resume testing.
  330. ---------                                               (a)
  331. smtp_vrfy host=10.0.0.1 user=FILE0 0=logins.txt -x ignore:fgrep='User unknown in local
  332.  recipient table' -x ignore,reset,retry:code=421
  333.                              (b)
  334.  
  335. * Use the RCPT TO command in case the VRFY command is not available.
  336. ---------
  337. smtp_rcpt host=10.0.0.1 user=FILE0@localhost 0=logins.txt helo='ehlo mx.fb.com' mail_from=root
  338.  
  339.  
  340. * Brute-force authentication.
  341.   (a) Send a fake hostname (by default your host fqdn is sent)
  342. ------------             (a)
  343. smtp_login host=10.0.0.1 helo='ehlo its.me.com' user=FILE0@dom.com password=FILE1 0=logins.txt 1=passwords.txt
  344.  
  345. }}}
  346. {{{ HTTP
  347.  
  348. * Find hidden web resources.
  349.   (a) Use a specific header.
  350.   (b) Follow redirects.
  351.   (c) Do not report 404 errors.
  352.   (d) Retry on 500 errors.
  353. ---------                                             (a)
  354. http_fuzz url=http://localhost/FILE0 0=words.txt header='Cookie: SESSID=A2FD8B2DA4'
  355.  follow=1 -x ignore:code=404 -x ignore,retry:code=500
  356.  (b)            (c)                  (d)
  357.  
  358. NB. You may be able to go 10 times faster using webef (http://www.hsc.fr/ressources/outils/webef/).
  359.     It is the fastest HTTP brute-forcer I know, yet at the moment it still lacks useful features
  360.     that will prevent you from performing the following attacks.
  361.  
  362. * Brute-force phpMyAdmin logon.
  363.   (a) Use POST requests.
  364.   (b) Follow redirects using cookies sent by server.
  365.   (c) Ignore failed authentications.
  366. ---------                                             (a)         (b)        (b)
  367. http_fuzz url=http://10.0.0.1/phpmyadmin/index.php method=POST follow=1 accept_cookie=1
  368.  body='pma_username=root&pma_password=FILE0&server=1&lang=en' 0=passwords.txt
  369.  -x ignore:fgrep='Cannot log in to the MySQL server'
  370.              (c)
  371.  
  372. * Scan subnet for directory listings.
  373.   (a) Ignore not matching reponses.
  374.   (b) Save matching responses into directory.
  375. ---------
  376. http_fuzz url=http://NET0/FILE1 0=10.0.0.0/24 1=dirs.txt -x ignore:fgrep!='Index of'
  377.  -l /tmp/directory_listings                                             (a)
  378.       (b)
  379.  
  380. * Brute-force Basic authentication.
  381.   (a) Single mode (login == password).
  382.   (b) Do not report failed login attempts.
  383. ---------
  384. http_fuzz url=http://10.0.0.1/manager/html user_pass=FILE0:FILE0 0=logins.txt -x ignore:code=401
  385.                                                    (a)                                (b)
  386.  
  387. * Find hidden virtual hosts.
  388.   (a) Read template from file.
  389.   (b) Fuzz both the Host and User-Agent headers.
  390. ---------
  391. echo -e 'Host: FILE0\nUser-Agent: FILE1' > headers.txt
  392. http_fuzz url=http://10.0.0.1/ header=@headers.txt 0=vhosts.txt 1=agents.txt
  393.                                     (a)                       (b)
  394.  
  395. * Brute-force logon using GET requests.
  396.   (a) Encode everything surrounded by the two tags _@@_ in hexadecimal.
  397.   (b) Ignore HTTP 200 responses with a content size (header+body) within given range
  398.       and that also contain the given string.
  399.   (c) Use a different delimiter string because the comma cannot be escaped.
  400. ---------                                                         (a)             (a)
  401. http_fuzz url='http://10.0.0.1/login?username=admin&password=_@@_FILE0_@@_' -e _@@_:hex
  402.  0=words.txt -x ignore:'code=200|size=1500-|fgrep=Welcome, unauthenticated user' -X '|'
  403.                 (b)                                                              (c)
  404.  
  405. * Brute-force logon that enforces two random nonces to be submitted along every POST.
  406.   (a) First, request the page that provides the nonces as hidden input fields.
  407.   (b) Use regular expressions to extract the nonces that are to be submitted along the main request.
  408. ---------
  409. http_fuzz url=http://10.0.0.1/login method=POST body='user=admin&pass=FILE0&nonce1=_N1_&nonce2=_N2_' 0=passwords.txt accept_cookie=1
  410.  before_urls=http://10.0.0.1/index before_egrep='_N1_:<input type="hidden" name="nonce1" value="(\w+)"|_N2_:name="nonce2" value="(\w+)"'
  411.            (a)                                (b)
  412.  
  413. * Test the OPTIONS method against a list of URLs.
  414.   (a) Ignore URLs that only allow the HEAD and GET methods.
  415.   (b) Header end of line is '\r\n'.
  416.   (c) Use a different delimiter string because the comma cannot be escaped.
  417. ---------
  418. http_fuzz url=FILE0 0=urls.txt method=OPTIONS -x ignore:egrep='^Allow: HEAD, GET\r$' -X '|'
  419.                                                             (a)                 (b)  (c)
  420. }}}
  421. {{{ LDAP
  422.  
  423. * Brute-force authentication.
  424.   (a) Do not report wrong passwords.
  425.   (b) Talk SSL/TLS to port 636.
  426. ---------
  427. ldap_login host=10.0.0.1 binddn='cn=FILE0,dc=example,dc=com' 0=logins.txt bindpw=FILE1 1=passwords.txt
  428.  -x ignore:mesg='ldap_bind: Invalid credentials (49)' ssl=1 port=636
  429.          (a)                                              (b)
  430. }}}
  431. {{{ SMB
  432.  
  433. * Brute-force authentication.
  434. ---------
  435. smb_login host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt -x ignore:fgrep=STATUS_LOGON_FAILURE
  436.  
  437. NB. If you suddenly get STATUS_ACCOUNT_LOCKED_OUT errors for an account
  438.     although it is not the first password you test on this account, then you must
  439.     have locked it.
  440.  
  441.  
  442. * Pass-the-hash.
  443.   (a) Test a list of hosts.
  444.   (b) Test every user (each line := login:rid:LM hash:NT hash).
  445. ---------
  446. smb_login host=FILE0 0=hosts.txt user=COMBO10 password_hash=COMBO12:COMBO13 1=pwdump.txt -x ...
  447.              (a)                                         (b)
  448. }}}
  449. {{{ rlogin
  450.  
  451. * Brute-force usernames that root might be allowed to login as with no password (eg. a ~/.rhosts file with the line "+ root").
  452. rlogin_login host=10.0.0.1 luser=root user=FILE0 0=logins.txt persistent=0 -x ignore:fgrep=Password:
  453.  
  454. * Brute-force usernames that might be allowed to login as root with no password (eg. a /root/.rhosts file with the line "+ john").
  455. rlogin_login host=10.0.0.1 user=root luser=FILE0 0=logins.txt persistent=0 -x ignore:fgrep=Password:
  456.  
  457. }}}
  458. {{{ MSSQL
  459.  
  460. * Brute-force authentication.
  461. -----------
  462. mssql_login host=10.0.0.1 user=sa password=FILE0 0=passwords.txt -x ignore:fgrep='Login failed for user'
  463.  
  464. }}}
  465. {{{ Oracle
  466. Beware, by default in Oracle, accounts are permanently locked out after 10 wrong passwords,
  467. except for the SYS account.
  468.  
  469. * Brute-force authentication.
  470. ------------
  471. oracle_login host=10.0.0.1 user=SYS password=FILE0 0=passwords.txt sid=ORCL -x ignore:code=ORA-01017
  472.  
  473. NB0. With Oracle 10g XE (Express Edition), you do not need to pass a SID.
  474.  
  475. NB1. If you get ORA-12516 errors, it may be because you reached the limit of
  476.      concurrent connections or db processes, try using "--rate-limit 0.5 -t 2" to be
  477.      more polite. Also you can run "alter system set processes=150 scope=spfile;"
  478.      and restart your database to get rid of this.
  479.  
  480.  
  481. * Brute-force SID.
  482. ------------
  483. oracle_login host=10.0.0.1 sid=FILE0 0=sids.txt -x ignore:code=ORA-12505
  484.  
  485. NB. Against Oracle9, it may crash (Segmentation fault) as soon as a valid SID is
  486.     found (cx_Oracle bug). Sometimes, the SID gets printed out before the crash,
  487.     so try running the same command again if it did not.
  488.  
  489. }}}
  490. {{{ MySQL
  491.  
  492. * Brute-force authentication.
  493. -----------
  494. mysql_login host=10.0.0.1 user=FILE0 password=FILE0 0=logins.txt -x ignore:fgrep='Access denied for user'
  495.  
  496. }}}
  497. {{{ PostgresSQL
  498.  
  499. * Brute-force authentication.
  500. -----------
  501. pgsql_login host=10.0.0.1 user=postgres password=FILE0 0=passwords.txt -x ignore:fgrep='password authentication failed'
  502.  
  503. }}}
  504. {{{ VNC
  505. Some VNC servers have built-in anti-bruteforce functionnality that temporarily
  506. blacklists the attacker IP address after too many wrong passwords.
  507.  - RealVNC-4.1.3 or TightVNC-1.3.10 for example, allow 5 failed attempts and
  508.    then enforce a 10 second delay. For each subsequent failed attempt that
  509.    delay is doubled.
  510.  - RealVNC-3.3.7 or UltraVNC allow 6 failed attempts and then enforce a 10
  511.    second delay between each following attempt.
  512.  
  513. * Brute-force authentication.
  514.   (a) No need to use more than one thread.
  515.   (b) Keep retrying the same password when we are blacklisted by the server.
  516.   (c) Exit execution as soon as a valid password is found.
  517. ---------                                               (a)
  518. vnc_login host=10.0.0.1 password=FILE0 0=passwords.txt --threads 1
  519.  -x retry:fgrep!='Authentication failure' --max-retries -1 -x quit:code=0
  520.         (b)                                 (b)                 (c)
  521. }}}
  522. {{{ DNS
  523.  
  524. * Brute-force subdomains.
  525.   (a) Ignore NXDOMAIN responses (rcode 3).
  526. -----------
  527. dns_forward name=FILE0.google.com 0=names.txt -x ignore:code=3
  528.                                               (a)
  529. * Brute-force domain with every possible TLDs.
  530. -----------
  531. dns_forward name=google.MOD0 0=TLD -x ignore:code=3
  532.  
  533. * Brute-force SRV records.
  534. -----------
  535. dns_forward name=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3
  536.  
  537. * Grab the version of several hosts.
  538. -----------
  539. dns_forward server=FILE0 0=hosts.txt name=version.bind qtype=txt qclass=ch
  540.  
  541. * Reverse lookup several networks.
  542.   (a) Ignore names that do not contain 'google.com'.
  543.   (b) Ignore generic PTR records.
  544. -----------
  545. dns_reverse 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-
  546.                                                                                 (a)                         (b)
  547. }}}
  548. {{{ SNMP
  549.  
  550. * SNMPv1/2 : Find valid community names.
  551. ----------
  552. snmp_login host=10.0.0.1 community=FILE0 0=names.txt -x ignore:mesg='No SNMP response received before timeout'
  553.  
  554.  
  555. * SNMPv3 : Find valid usernames.
  556. ----------
  557. snmp_login host=10.0.0.1 version=3 user=FILE0 0=logins.txt -x ignore:mesg=unknownUserName
  558.  
  559.  
  560. * SNMPv3 : Find valid passwords.
  561. ----------
  562. snmp_login host=10.0.0.1 version=3 user=myuser auth_key=FILE0 0=passwords.txt -x ignore:mesg=wrongDigest
  563.  
  564. NB0. If you get "notInTimeWindow" error messages, increase the retries option.
  565. NB1. SNMPv3 requires passphrases to be at least 8 characters long.
  566.  
  567. }}}
  568. {{{ Unzip
  569.  
  570. * Brute-force the ZIP file password (cracking older pkzip encryption used to be not supported in JtR).
  571. ----------
  572. unzip_pass zipfile=path/to/file.zip password=FILE0 0=passwords.txt -x ignore:code!=0
  573.  
  574. }}}
  575.  
  576. CHANGELOG
  577. ---------
  578.  
  579. * v0.6 2014/08/25
  580.   - added CSV and XML output formats
  581.   - added module execution time column
  582.   - improved RANGE keyword
  583.   - new modules: rlogin_login, umbrack_crack
  584.   - minor bug fixes/improvements in http_fuzz and smb_login
  585.   - added more TLDs to dns_forward
  586.  
  587. * v0.5 2013/07/05
  588.   - new modules: mysql_query, tcp_fuzz
  589.   - new RANGE and PROG keywords (supersedes the reading from stdin feature)
  590.   - switched to impacket for mssql_login
  591.   - output more intuitive
  592.   - fixed connection cache
  593.   - minor bug fixes
  594.  
  595. * v0.4 2012/11/02
  596.   - new modules: smb_lookupsid, finger_lookup, pop_login, imap_login, vmauthd_login
  597.   - improved connection cache
  598.   - improved usage, user can now act upon specific reponses (eg. stop brute-forcing host if down, or stop testing login if password found)
  599.   - improved dns brute-forcing presentation
  600.   - switched to dnspython which is not limited to the IN class (eg. can now scan for {hostname,version}.bind)
  601.   - rewrote itertools.product to avoid memory over-consumption when using large wordlists
  602.   - can now read wordlist from stdin
  603.   - added timeout option to most of the network brute-forcing modules
  604.   - added SSL and/or TLS support to a few modules
  605.   - before_egrep now allows more than one expression (ie. useful when more than one random nonce needs to be submitted)
  606.   - fixed numerous bugs
  607.  
  608. * v0.3 2011/12/16
  609.     - minor bugs fixed in http_fuzz
  610.     - option -e better implemented
  611.     - better warnings about missing dependencies
  612.  
  613. * v0.2 2011/12/01
  614.     - new smtp_login module
  615.     - several bugs fixed
  616.  
  617. * v0.1 2011/11/25 : Public release
  618.  
  619.  
  620. TODO
  621. ----
  622.   * new option -e ns like in Medusa (not likely to be implemented due to design)
  623.   * replace dnspython|paramiko|IPy with a better module (scapy|libssh2|netaddr... ?) // https://netaddr.readthedocs.org/en/latest/tutorial_01.html
  624.   * use impacket/enum_lookupsids to automatically get the sid
  625. '''
  626.  
  627. # }}}
  628.  
  629. # logging {{{
  630. class Logger:
  631.   def __init__(self, pipe):
  632.     self.pipe = pipe
  633.     self.name = multiprocessing.current_process().name
  634.  
  635. # neat but wont work on windows
  636. #  def __getattr__(self, action):
  637. #    def send(*args):
  638. #      self.pipe.send((self.name, action, args))
  639. #    return send
  640.  
  641.   def send(self, action, *args):
  642.     self.pipe.send((self.name, action, args))
  643.  
  644.   def quit(self):
  645.     self.send('quit')
  646.  
  647.   def headers(self):
  648.     self.send('headers')
  649.  
  650.   def result(self, *args):
  651.     self.send('result', *args)
  652.  
  653.   def save(self, *args):
  654.     self.send('save', *args)
  655.  
  656.   def setLevel(self, level):
  657.     self.send('setLevel', level)
  658.  
  659.   def warn(self, msg):
  660.     self.send('warn', msg)
  661.  
  662.   def info(self, msg):
  663.     self.send('info', msg)
  664.  
  665.   def debug(self, msg):
  666.     self.send('debug', msg)
  667.  
  668. import logging
  669. class TXTFormatter(logging.Formatter):
  670.   def __init__(self, indicatorsfmt):
  671.     self.resultfmt = '%(asctime)s %(name)-7s %(levelname)7s - ' + ' '.join('%%(%s)%ss' % (k, v) for k, v in indicatorsfmt) + ' | %(candidate)-34s | %(num)5s | %(mesg)s'
  672.  
  673.     logging.Formatter.__init__(self, datefmt='%H:%M:%S')
  674.  
  675.   def format(self, record):
  676.     if not record.msg or record.msg == 'headers':
  677.       self._fmt = self.resultfmt
  678.  
  679.       if not all(True if 0x20 <= ord(c) < 0x7f else False for c in record.candidate):
  680.         record.candidate = repr(record.candidate)
  681.  
  682.     else:
  683.       if record.levelno == logging.DEBUG:
  684.         self._fmt = '%(asctime)s %(name)-7s %(levelname)7s [%(pname)s] %(message)s'
  685.       else:
  686.         self._fmt = '%(asctime)s %(name)-7s %(levelname)7s - %(message)s'
  687.  
  688.     return logging.Formatter.format(self, record)
  689.  
  690. class CSVFormatter(logging.Formatter):
  691.   def __init__(self, indicatorsfmt):
  692.     fmt = '%(asctime)s,%(levelname)s,'+','.join('%%(%s)s' % name for name, _ in indicatorsfmt)+',%(candidate)s,%(num)s,%(mesg)s'
  693.  
  694.     logging.Formatter.__init__(self, fmt, datefmt='%H:%M:%S')
  695.  
  696.   def format(self, record):
  697.     for k in ['candidate', 'mesg']:
  698.       record.__dict__[k] = '"%s"' % record.__dict__[k].replace('"', '""')
  699.     return logging.Formatter.format(self, record)
  700.  
  701. class XMLFormatter(logging.Formatter):
  702.   def __init__(self, indicatorsfmt):
  703.     fmt = '''<result time="%(asctime)s" level="%(levelname)s">
  704. ''' + '\n'.join('  <{0}>%({1})s</{0}>'.format(name.replace(':', '_'), name) for name, _ in indicatorsfmt) + '''
  705.   <candidate>%(candidate)s</candidate>
  706.   <num>%(num)s</num>
  707.   <mesg>%(mesg)s</mesg>
  708.   <target %(target)s/>
  709. </result>'''
  710.  
  711.     logging.Formatter.__init__(self, fmt, datefmt='%H:%M:%S')
  712.  
  713.   def format(self, record):
  714.  
  715.     for k, v in record.__dict__.iteritems():
  716.       if isinstance(v, basestring):
  717.         record.__dict__[k] = xmlescape(v)
  718.  
  719.     return super(XMLFormatter, self).format(record)
  720.  
  721. class MsgFilter(logging.Filter):
  722.  
  723.   def filter(self, record):
  724.     if record.msg:
  725.       return 0
  726.     else:
  727.       return 1
  728.  
  729. def process_logs(pipe, indicatorsfmt, argv, log_dir):
  730.  
  731.   ignore_ctrlc()
  732.  
  733.   try:
  734.     # python3
  735.     logging._levelToName[logging.ERROR] = 'FAIL'
  736.   except:
  737.     # python2
  738.     logging._levelNames[logging.ERROR] = 'FAIL'
  739.  
  740.   handler_out = logging.StreamHandler()
  741.   handler_out.setFormatter(TXTFormatter(indicatorsfmt))
  742.  
  743.   logger = logging.getLogger('patator')
  744.   logger.setLevel(logging.DEBUG)
  745.   logger.addHandler(handler_out)
  746.  
  747.   names = [name for name, _ in indicatorsfmt] + ['candidate', 'num', 'mesg']
  748.  
  749.   if log_dir:
  750.     runtime_log = os.path.join(log_dir, 'RUNTIME.log')
  751.     results_csv = os.path.join(log_dir, 'RESULTS.csv')
  752.     results_xml = os.path.join(log_dir, 'RESULTS.xml')
  753.  
  754.     with open(runtime_log, 'a') as f:
  755.       f.write('$ %s\n' % ' '.join(argv))
  756.  
  757.     if not os.path.exists(results_csv):
  758.       with open(results_csv, 'w') as f:
  759.         f.write('time,level,%s\n' % ','.join(names))
  760.  
  761.     if not os.path.exists(results_xml):
  762.       with open(results_xml, 'w') as f:
  763.         f.write('<?xml version="1.0" encoding="UTF-8"?>\n<root>\n')
  764.         f.write('<start utc=%s local=%s/>\n' % (xmlquoteattr(strfutctime()), xmlquoteattr(strflocaltime())))
  765.         f.write('<cmdline>%s</cmdline>\n' % xmlescape(' '.join(argv)))
  766.         f.write('<module>%s</module>\n' % xmlescape(argv[0]))
  767.         f.write('<options>\n')
  768.  
  769.         i = 0
  770.         del argv[0]
  771.         while i < len(argv):
  772.           arg = argv[i]
  773.           if arg[0] == '-':
  774.             if arg in ('-d', '--debug'):
  775.               f.write('  <option type="global" name=%s/>\n' % xmlquoteattr(arg))
  776.             else:
  777.               if not arg.startswith('--') and len(arg) > 2:
  778.                 name, value = arg[:2], arg[2:]
  779.               elif '=' in arg:
  780.                 name, value = arg.split('=', 1)
  781.               else:
  782.                 name, value = arg, argv[i+1]
  783.                 i += 1
  784.               f.write('  <option type="global" name=%s>%s</option>\n' % (xmlquoteattr(name), xmlescape(value)))
  785.           else:
  786.             name, value = arg.split('=', 1)
  787.             f.write('  <option type="module" name=%s>%s</option>\n' % (xmlquoteattr(name), xmlescape(value)))
  788.           i += 1
  789.         f.write('</options>\n')
  790.         f.write('<results>\n')
  791.  
  792.     else: # remove "</results>...</root>"
  793.       with open(results_xml, 'r+') as f:
  794.         f.seek(f.read().find('</results>'))
  795.         f.truncate(f.tell())
  796.  
  797.     handler_log = logging.FileHandler(runtime_log)
  798.     handler_csv = logging.FileHandler(results_csv)
  799.     handler_xml = logging.FileHandler(results_xml)
  800.  
  801.     handler_csv.addFilter(MsgFilter())
  802.     handler_xml.addFilter(MsgFilter())
  803.  
  804.     handler_log.setFormatter(TXTFormatter(indicatorsfmt))
  805.     handler_csv.setFormatter(CSVFormatter(indicatorsfmt))
  806.     handler_xml.setFormatter(XMLFormatter(indicatorsfmt))
  807.  
  808.     logger.addHandler(handler_log)
  809.     logger.addHandler(handler_csv)
  810.     logger.addHandler(handler_xml)
  811.  
  812.   while True:
  813.  
  814.     pname, action, args = pipe.recv()
  815.  
  816.     if action == 'quit':
  817.       if log_dir:
  818.         with open(os.path.join(log_dir, 'RESULTS.xml'), 'a') as f:
  819.           f.write('</results>\n<stop utc=%s local=%s/>\n</root>\n' % (xmlquoteattr(strfutctime()), xmlquoteattr(strflocaltime())))
  820.       break
  821.  
  822.     elif action == 'headers':
  823.  
  824.       logger.info(' '*77)
  825.       logger.info('headers', extra=dict((n, n) for n in names))
  826.       logger.info('-'*77)
  827.  
  828.     elif action == 'result':
  829.  
  830.       typ, resp, candidate, num = args
  831.  
  832.       results = [(name, value) for (name, _), value in zip(indicatorsfmt, resp.indicators())]
  833.       results += [('candidate', candidate), ('num', num), ('mesg', str(resp)), ('target', resp.str_target())]
  834.  
  835.       if typ == 'fail':
  836.         logger.error(None, extra=dict(results))
  837.       else:
  838.         logger.info(None, extra=dict(results))
  839.  
  840.     elif action == 'save':
  841.  
  842.       resp, num = args
  843.  
  844.       if log_dir:
  845.         filename = '%d_%s' % (num, '-'.join(map(str, resp.indicators())))
  846.         with open('%s.txt' % os.path.join(log_dir, filename), 'w') as f:
  847.           f.write(resp.dump())
  848.  
  849.     elif action == 'setLevel':
  850.       logger.setLevel(args[0])
  851.  
  852.     else: # 'warn', 'info', 'debug'
  853.       getattr(logger, action)(args[0], extra={'pname': pname})
  854.  
  855. # }}}
  856.  
  857. # imports {{{
  858. import re
  859. import os
  860. import sys
  861. from time import localtime, gmtime, strftime, sleep, time
  862. from platform import system
  863. from functools import reduce
  864. from select import select
  865. from itertools import islice
  866. import string
  867. import random
  868. from base64 import b64encode
  869. from datetime import timedelta, datetime
  870. from struct import unpack
  871. import socket
  872. import subprocess
  873. import hashlib
  874. from collections import defaultdict
  875. import multiprocessing
  876. import signal
  877. import ctypes
  878. from xml.sax.saxutils import escape as xmlescape, quoteattr as xmlquoteattr
  879. try:
  880.   # python3+
  881.   from queue import Empty, Full
  882.   from urllib.parse import quote, urlencode, urlparse, urlunparse, parse_qsl, quote_plus
  883.   from io import StringIO
  884.   from sys import maxsize as maxint
  885. except ImportError:
  886.   # python2.6+
  887.   from Queue import Empty, Full
  888.   from urllib import quote, urlencode, quote_plus
  889.   from urlparse import urlparse, urlunparse, parse_qsl
  890.   from cStringIO import StringIO
  891.   from sys import maxint
  892.  
  893. notfound = []
  894. try:
  895.   from IPy import IP
  896.   has_ipy = True
  897. except ImportError:
  898.   has_ipy = False
  899.   notfound.append('IPy')
  900.  
  901. import multiprocessing.forking
  902. class _Popen(multiprocessing.forking.Popen):
  903.     def __init__(self, *args, **kw):
  904.         if hasattr(sys, 'frozen'):
  905.             # We have to set original _MEIPASS2 value from sys._MEIPASS
  906.             # to get --onefile mode working.
  907.             os.putenv('_MEIPASS2', sys._MEIPASS)
  908.         try:
  909.             super(_Popen, self).__init__(*args, **kw)
  910.         finally:
  911.             if hasattr(sys, 'frozen'):
  912.                 # On some platforms (e.g. AIX) 'os.unsetenv()' is not
  913.                 # available. In those cases we cannot delete the variable
  914.                 # but only set it to the empty string. The bootloader
  915.                 # can handle this case.
  916.                 if hasattr(os, 'unsetenv'):
  917.                     os.unsetenv('_MEIPASS2')
  918.                 else:
  919.                     os.putenv('_MEIPASS2', '')
  920.  
  921. class Process(multiprocessing.Process):
  922.     _Popen = _Popen
  923.  
  924. # So BaseManager.start() uses this new Process class
  925. multiprocessing.Process = Process
  926. from multiprocessing.managers import SyncManager
  927.  
  928. # imports }}}
  929.  
  930. # utils {{{
  931. def strfutctime():
  932.   return strftime("%Y-%m-%d %H:%M:%S", gmtime())
  933.  
  934. def strflocaltime():
  935.   return strftime("%Y-%m-%d %H:%M:%S %Z", localtime())
  936.  
  937. def which(program):
  938.   def is_exe(fpath):
  939.     return os.path.exists(fpath) and os.access(fpath, os.X_OK)
  940.  
  941.   fpath, fname = os.path.split(program)
  942.   if on_windows() and fname[-4:] != '.exe' :
  943.     fname += '.exe'
  944.  
  945.   if fpath:
  946.     if is_exe(program):
  947.       return program
  948.   else:
  949.     for path in os.environ["PATH"].split(os.pathsep):
  950.       exe_file = os.path.join(path, fname)
  951.       if is_exe(exe_file):
  952.         return exe_file
  953.  
  954.   return None
  955.  
  956. def build_logdir(opt_dir, opt_auto):
  957.     if opt_auto:
  958.       return create_time_dir(opt_dir or '/tmp/patator', opt_auto)
  959.     elif opt_dir:
  960.       return create_dir(opt_dir)
  961.     else:
  962.       return None
  963.  
  964. def create_dir(top_path):
  965.   top_path = os.path.abspath(top_path)
  966.   if os.path.isdir(top_path):
  967.     files = os.listdir(top_path)
  968.     if files:
  969.       if raw_input("Directory '%s' is not empty, do you want to wipe it ? [Y/n]: " % top_path) != 'n':
  970.         for root, dirs, files in os.walk(top_path):
  971.           if dirs:
  972.             print("Directory '%s' contains sub-directories, safely aborting..." % root)
  973.             sys.exit(0)
  974.           for f in files:
  975.             os.unlink(os.path.join(root, f))
  976.           break
  977.   else:
  978.     os.mkdir(top_path)
  979.   return top_path
  980.  
  981. def create_time_dir(top_path, desc):
  982.   now = localtime()
  983.   date, time = strftime('%Y-%m-%d', now), strftime('%H%M%S', now)
  984.   top_path = os.path.abspath(top_path)
  985.   date_path = os.path.join(top_path, date)
  986.   time_path = os.path.join(top_path, date, time + '_' + desc)
  987.  
  988.   if not os.path.isdir(top_path):
  989.     os.makedirs(top_path)
  990.   if not os.path.isdir(date_path):
  991.     os.mkdir(date_path)
  992.   if not os.path.isdir(time_path):
  993.     os.mkdir(time_path)
  994.  
  995.   return time_path
  996.  
  997. def pprint_seconds(seconds, fmt):
  998.   return fmt % reduce(lambda x,y: divmod(x[0], y) + x[1:], [(seconds,),60,60])
  999.  
  1000. def md5hex(plain):
  1001.   return hashlib.md5(plain).hexdigest()
  1002.  
  1003. def sha1hex(plain):
  1004.   return hashlib.sha1(plain).hexdigest()
  1005.  
  1006. # I rewrote itertools.product to avoid memory over-consumption when using large wordlists
  1007. def product(xs, *rest):
  1008.   if len(rest) == 0:
  1009.     for x in xs():
  1010.       yield [x]
  1011.   else:
  1012.     for head in xs():
  1013.       for tail in product(*rest):
  1014.         yield [head] + tail
  1015.  
  1016. def chain(*iterables):
  1017.   def xs():
  1018.     for iterable in iterables:
  1019.       for element in iterable:
  1020.         yield element
  1021.   return xs
  1022.  
  1023. class FileIter:
  1024.   def __init__(self, filename):
  1025.     self.filename = filename
  1026.  
  1027.   def __iter__(self):
  1028.     return open(self.filename)
  1029.  
  1030. def padhex(d):
  1031.   x = '%x' % d
  1032.   return '0' * (len(x) % 2) + x
  1033.  
  1034. # These are examples. You can easily write your own iterator to fit your needs.
  1035. # Or using the PROG keyword, you can call an external program such as:
  1036. #   - seq(1) from coreutils
  1037. #   - http://hashcat.net/wiki/doku.php?id=maskprocessor
  1038. #   - john -stdout -i
  1039. # For example:
  1040. # $ ./dummy_test data=PROG0 0='seq 1 80'
  1041. # $ ./dummy_test data=PROG0 0='mp64.bin ?l?l?l',$(mp64.bin --combination ?l?l?l)
  1042. class RangeIter:
  1043.  
  1044.   def __init__(self, typ, rng, random=None):
  1045.  
  1046.     if typ in ('hex', 'int', 'float'):
  1047.  
  1048.       m = re.match(r'(-?.+?)-(-?.+)$', rng) # 5-50 or -5-50 or 5--50 or -5--50
  1049.       if not m:
  1050.         raise NotImplementedError("Unsupported range '%s'" % rng)
  1051.  
  1052.       mn = m.group(1)
  1053.       mx = m.group(2)
  1054.  
  1055.       if typ in ('hex', 'int'):
  1056.  
  1057.         mn = int(mn, 16 if '0x' in mn else 10)
  1058.         mx = int(mx, 16 if '0x' in mx else 10)
  1059.  
  1060.         if typ == 'hex':
  1061.           fmt = padhex
  1062.         elif typ == 'int':
  1063.           fmt = '%d'
  1064.  
  1065.       elif typ == 'float':
  1066.         from decimal import Decimal
  1067.         mn = Decimal(mn)
  1068.         mx = Decimal(mx)
  1069.  
  1070.       if mn > mx:
  1071.         step = -1
  1072.       else:
  1073.         step = 1
  1074.  
  1075.     elif typ == 'letters':
  1076.       charset = [c for c in string.letters]
  1077.  
  1078.     elif typ in ('lower', 'lowercase'):
  1079.       charset = [c for c in string.lowercase]
  1080.  
  1081.     elif typ in ('upper', 'uppercase'):
  1082.       charset = [c for c in string.uppercase]
  1083.  
  1084.     else:
  1085.       raise NotImplementedError("Incorrect type '%s'" % typ)
  1086.  
  1087.     def zrange(start, stop, step, fmt):
  1088.       x = start
  1089.       while x != stop+step:
  1090.  
  1091.         if callable(fmt):
  1092.           yield fmt(x)
  1093.         else:
  1094.           yield fmt % x
  1095.         x += step
  1096.  
  1097.     def letterrange(first, last, charset):
  1098.       for k in range(len(last)):
  1099.         for x in product(*[chain(charset)]*(k+1)):
  1100.           result = ''.join(x)
  1101.           if first:
  1102.             if first != result:
  1103.               continue
  1104.             else:
  1105.               first = None
  1106.           yield result
  1107.           if result == last:
  1108.             return
  1109.  
  1110.     if typ == 'float':
  1111.       precision = max(len(str(x).partition('.')[-1]) for x in (mn, mx))
  1112.  
  1113.       fmt = '%%.%df' % precision
  1114.       exp = 10**precision
  1115.       step *= Decimal(1) / exp
  1116.  
  1117.       self.generator = zrange, (mn, mx, step, fmt)
  1118.       self.size = int(abs(mx-mn) * exp) + 1
  1119.  
  1120.       def random_generator():
  1121.         while True:
  1122.           yield fmt % (Decimal(random.randint(mn*exp, mx*exp)) / exp)
  1123.  
  1124.     elif typ in ('hex', 'int'):
  1125.       self.generator = zrange, (mn, mx, step, fmt)
  1126.       self.size = abs(mx-mn) + 1
  1127.  
  1128.       def random_generator():
  1129.         while True:
  1130.           yield fmt % random.randint(mn, mx)
  1131.  
  1132.     else: # letters, lower, upper
  1133.       def count(f):
  1134.         total = 0
  1135.         i = 0
  1136.         for c in f[::-1]:
  1137.           z = charset.index(c) + 1
  1138.           total += (len(charset)**i)*z
  1139.           i += 1
  1140.         return total + 1
  1141.  
  1142.       first, last = rng.split('-')
  1143.       self.generator = letterrange, (first, last, charset)
  1144.       self.size = count(last) - count(first) + 1
  1145.  
  1146.     if random:
  1147.       self.generator = random_generator, ()
  1148.       self.size = maxint
  1149.  
  1150.   def __iter__(self):
  1151.     fn, args = self.generator
  1152.     return fn(*args)
  1153.  
  1154.   def __len__(self):
  1155.     return self.size
  1156.  
  1157. class ProgIter:
  1158.  
  1159.   def __init__(self, prog):
  1160.     self.prog = prog
  1161.  
  1162.   def __iter__(self):
  1163.     p = subprocess.Popen(self.prog.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  1164.     return p.stdout
  1165.  
  1166. class Progress:
  1167.   def __init__(self):
  1168.     self.current = ''
  1169.     self.done_count = 0
  1170.     self.hits_count = 0
  1171.     self.skip_count = 0
  1172.     self.fail_count = 0
  1173.     self.seconds = [1]*25 # avoid division by zero early bug condition
  1174.  
  1175. class TimeoutError(Exception):
  1176.   pass
  1177.  
  1178. def on_windows():
  1179.   return 'Win' in system()
  1180.  
  1181. def ignore_ctrlc():
  1182.   if on_windows():
  1183.     ctypes.windll.kernel32.SetConsoleCtrlHandler(0, 1)
  1184.   else:
  1185.     signal.signal(signal.SIGINT, signal.SIG_IGN)
  1186.  
  1187. def handle_alarm():
  1188.   if not on_windows():
  1189.     signal.signal(signal.SIGALRM, raise_timeout)
  1190.  
  1191. def raise_timeout(signum, frame):
  1192.   if signum == signal.SIGALRM:
  1193.     raise TimeoutError('timed out')
  1194.  
  1195. def enable_alarm(timeout):
  1196.   if not on_windows():
  1197.     signal.alarm(timeout)
  1198.  
  1199. def disable_alarm():
  1200.   if not on_windows():
  1201.      signal.alarm(0)
  1202.  
  1203. # SyncManager.start(initializer) only available since python2.7
  1204. class MyManager(SyncManager):
  1205.   @classmethod
  1206.   def _run_server(cls, registry, address, authkey, serializer, writer, initializer=None, initargs=()):
  1207.     ignore_ctrlc()
  1208.     super(MyManager, cls)._run_server(registry, address, authkey, serializer, writer)
  1209.  
  1210. # }}}
  1211.  
  1212. # Controller {{{
  1213. class Controller:
  1214.  
  1215.   builtin_actions = (
  1216.     ('ignore', 'do not report'),
  1217.     ('retry', 'try payload again'),
  1218.     ('free', 'dismiss future similar payloads'),
  1219.     ('quit', 'terminate execution now'),
  1220.     )
  1221.  
  1222.   available_encodings = {
  1223.     'hex': (lambda s: s.encode('hex'), 'encode in hexadecimal'),
  1224.     'unhex': (lambda s: s.decode('hex'), 'decode from hexadecimal'),
  1225.     'b64': (b64encode, 'encode in base64'),
  1226.     'md5': (md5hex, 'hash in md5'),
  1227.     'sha1': (sha1hex, 'hash in sha1'),
  1228.     'url': (quote_plus, 'url encode'),
  1229.     }
  1230.  
  1231.   def expand_key(self, arg):
  1232.     yield arg.split('=', 1)
  1233.  
  1234.   def find_file_keys(self, value):
  1235.     return map(int, re.findall(r'FILE(\d)', value))
  1236.  
  1237.   def find_net_keys(self, value):
  1238.     return map(int, re.findall(r'NET(\d)', value))
  1239.  
  1240.   def find_combo_keys(self, value):
  1241.     return [map(int, t) for t in re.findall(r'COMBO(\d)(\d)', value)]
  1242.  
  1243.   def find_module_keys(self, value):
  1244.     return map(int, re.findall(r'MOD(\d)', value))
  1245.  
  1246.   def find_range_keys(self, value):
  1247.     return map(int, re.findall(r'RANGE(\d)', value))
  1248.  
  1249.   def find_prog_keys(self, value):
  1250.     return map(int, re.findall(r'PROG(\d)', value))
  1251.  
  1252.   def usage_parser(self, name):
  1253.     from optparse import OptionParser
  1254.     from optparse import OptionGroup
  1255.     from optparse import IndentedHelpFormatter
  1256.  
  1257.     class MyHelpFormatter(IndentedHelpFormatter):
  1258.       def format_epilog(self, epilog):
  1259.         return epilog
  1260.  
  1261.       def format_heading(self, heading):
  1262.         if self.current_indent == 0 and heading == 'Options':
  1263.           heading = 'Global options'
  1264.         return "%*s%s:\n" % (self.current_indent, "", heading)
  1265.  
  1266.       def format_usage(self, usage):
  1267.         return '%s\nUsage: %s\n' % (__banner__, usage)
  1268.  
  1269.     available_actions = self.builtin_actions + self.module.available_actions
  1270.     available_conditions = self.module.Response.available_conditions
  1271.  
  1272.     usage = '''%%prog <module-options ...> [global-options ...]
  1273.  
  1274. Examples:
  1275.   %s''' % '\n  '.join(self.module.usage_hints)
  1276.  
  1277.     usage += '''
  1278.  
  1279. Module options:
  1280. %s ''' % ('\n'.join('  %-14s: %s' % (k, v) for k, v in self.module.available_options))
  1281.  
  1282.     epilog = '''
  1283. Syntax:
  1284.  -x actions:conditions
  1285.  
  1286.     actions    := action[,action]*
  1287.     action     := "%s"
  1288.     conditions := condition=value[,condition=value]*
  1289.     condition  := "%s"
  1290. ''' % ('" | "'.join(k for k, v in available_actions),
  1291.        '" | "'.join(k for k, v in available_conditions))
  1292.  
  1293.     epilog += '''
  1294. %s
  1295.  
  1296. %s
  1297. ''' % ('\n'.join('    %-12s: %s' % (k, v) for k, v in available_actions),
  1298.        '\n'.join('    %-12s: %s' % (k, v) for k, v in available_conditions))
  1299.  
  1300.     epilog += '''
  1301. For example, to ignore all redirects to the home page:
  1302. ... -x ignore:code=302,fgrep='Location: /home.html'
  1303.  
  1304.  -e tag:encoding
  1305.  
  1306.     tag        := any unique string (eg. T@G or _@@_ or ...)
  1307.     encoding   := "%s"
  1308.  
  1309. %s''' % ('" | "'.join(k for k in self.available_encodings),
  1310.        '\n'.join('    %-12s: %s' % (k, v) for k, (f, v) in self.available_encodings.items()))
  1311.  
  1312.     epilog += '''
  1313.  
  1314. For example, to encode every password in base64:
  1315. ... host=10.0.0.1 user=admin password=_@@_FILE0_@@_ -e _@@_:b64
  1316.  
  1317. Please read the README inside for more examples and usage information.
  1318. '''
  1319.  
  1320.     parser = OptionParser(usage=usage, prog=name, epilog=epilog, version=__banner__, formatter=MyHelpFormatter())
  1321.  
  1322.     exe_grp = OptionGroup(parser, 'Execution')
  1323.     exe_grp.add_option('-x', dest='actions', action='append', default=[], metavar='arg', help='actions and conditions, see Syntax below')
  1324.     exe_grp.add_option('--start', dest='start', type='int', default=0, metavar='N', help='start from offset N in the wordlist product')
  1325.     exe_grp.add_option('--stop', dest='stop', type='int', default=None, metavar='N', help='stop at offset N')
  1326.     exe_grp.add_option('--resume', dest='resume', metavar='r1[,rN]*', help='resume previous run')
  1327.     exe_grp.add_option('-e', dest='encodings', action='append', default=[], metavar='arg', help='encode everything between two tags, see Syntax below')
  1328.     exe_grp.add_option('-C', dest='combo_delim', default=':', metavar='str', help="delimiter string in combo files (default is ':')")
  1329.     exe_grp.add_option('-X', dest='condition_delim', default=',', metavar='str', help="delimiter string in conditions (default is ',')")
  1330.     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")
  1331.  
  1332.     opt_grp = OptionGroup(parser, 'Optimization')
  1333.     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)')
  1334.     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)')
  1335.     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)')
  1336.     opt_grp.add_option('-t', '--threads', dest='num_threads', type='int', default=10, metavar='N', help='number of threads (default is 10)')
  1337.  
  1338.     log_grp = OptionGroup(parser, 'Logging')
  1339.     log_grp.add_option('-l', dest='log_dir', metavar='DIR', help="save output and response data into DIR ")
  1340.     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')")
  1341.  
  1342.     dbg_grp = OptionGroup(parser, 'Debugging')
  1343.     dbg_grp.add_option('-d', '--debug', dest='debug', action='store_true', default=False, help='enable debug messages')
  1344.  
  1345.     parser.option_groups.extend([exe_grp, opt_grp, log_grp, dbg_grp])
  1346.  
  1347.     return parser
  1348.  
  1349.   def parse_usage(self, argv):
  1350.     parser = self.usage_parser(argv[0])
  1351.     opts, args = parser.parse_args(argv[1:])
  1352.  
  1353.     if not len(args) > 0:
  1354.       parser.print_usage()
  1355.       print('ERROR: wrong usage. Please read the README inside for more information.')
  1356.       sys.exit(2)
  1357.  
  1358.     return opts, args
  1359.  
  1360.   def __init__(self, module, argv):
  1361.     self.thread_report = []
  1362.     self.thread_progress = []
  1363.  
  1364.     self.payload = {}
  1365.     self.iter_keys = {}
  1366.     self.enc_keys = []
  1367.  
  1368.     self.module = module
  1369.  
  1370.     opts, args = self.parse_usage(argv)
  1371.  
  1372.     self.combo_delim = opts.combo_delim
  1373.     self.condition_delim = opts.condition_delim
  1374.     self.rate_limit = opts.rate_limit
  1375.     self.timeout = opts.timeout
  1376.     self.max_retries = opts.max_retries
  1377.     self.num_threads = opts.num_threads
  1378.     self.start, self.stop = opts.start, opts.stop
  1379.     self.allow_ignore_failures = opts.allow_ignore_failures
  1380.  
  1381.     self.resume = [int(i) for i in opts.resume.split(',')] if opts.resume else None
  1382.  
  1383.     manager = MyManager()
  1384.     manager.start()
  1385.  
  1386.     self.ns = manager.Namespace()
  1387.     self.ns.actions = {}
  1388.     self.ns.free_list = []
  1389.     self.ns.paused = False
  1390.     self.ns.quit_now = False
  1391.     self.ns.start_time = 0
  1392.     self.ns.total_size = 1
  1393.  
  1394.     pipe = multiprocessing.Pipe(duplex=False)
  1395.  
  1396.     logsvc = Process(name='LogSvc', target=process_logs, args=(pipe[0], module.Response.indicatorsfmt, argv, build_logdir(opts.log_dir, opts.auto_log)))
  1397.     logsvc.daemon = True
  1398.     logsvc.start()
  1399.  
  1400.     global logger
  1401.     logger = Logger(pipe[1])
  1402.  
  1403.     if opts.debug:
  1404.       logger.setLevel(logging.DEBUG)
  1405.     else:
  1406.       logger.setLevel(logging.INFO)
  1407.  
  1408.     wlists = {}
  1409.     kargs = []
  1410.     for arg in args: # ('host=NET0', '0=10.0.0.0/24', 'user=COMBO10', 'password=COMBO11', '1=combos.txt', 'name=google.MOD2', '2=TLD')
  1411.       for k, v in self.expand_key(arg):
  1412.         logger.debug('k: %s, v: %s' % (k, v))
  1413.  
  1414.         if k.isdigit():
  1415.           wlists[k] = v
  1416.  
  1417.         else:
  1418.           if v.startswith('@'):
  1419.             p = os.path.expanduser(v[1:])
  1420.             v = open(p).read()
  1421.           kargs.append((k, v))
  1422.  
  1423.     iter_vals = [v for k, v in sorted(wlists.items())]
  1424.     logger.debug('kargs: %s' % kargs) # [('host', 'NET0'), ('user', 'COMBO10'), ('password', 'COMBO11'), ('domain', 'MOD2')]
  1425.     logger.debug('iter_vals: %s' % iter_vals) # ['10.0.0.0/24', 'combos.txt', 'TLD']
  1426.  
  1427.     for k, v in kargs:
  1428.  
  1429.       for e in opts.encodings:
  1430.         meta, enc = e.split(':')
  1431.         if re.search(r'{0}.+?{0}'.format(meta), v):
  1432.           self.enc_keys.append((k, meta, self.available_encodings[enc][0]))
  1433.  
  1434.       for i in self.find_file_keys(v):
  1435.         if i not in self.iter_keys:
  1436.           self.iter_keys[i] = ('FILE', iter_vals[i], [])
  1437.         self.iter_keys[i][2].append(k)
  1438.  
  1439.       else:
  1440.         for i in self.find_net_keys(v):
  1441.           if i not in self.iter_keys:
  1442.             self.iter_keys[i] = ('NET', iter_vals[i], [])
  1443.           self.iter_keys[i][2].append(k)
  1444.  
  1445.           if not has_ipy:
  1446.             print('IPy (https://github.com/haypo/python-ipy) is required for using NET keyword.')
  1447.             print('Please read the README inside for more information.')
  1448.             sys.exit(3)
  1449.  
  1450.         else:
  1451.           for i, j in self.find_combo_keys(v):
  1452.             if i not in self.iter_keys:
  1453.               self.iter_keys[i] = ('COMBO', iter_vals[i], [])
  1454.             self.iter_keys[i][2].append((j, k))
  1455.  
  1456.           else:
  1457.             for i in self.find_module_keys(v):
  1458.               if i not in self.iter_keys:
  1459.                 self.iter_keys[i] = ('MOD', iter_vals[i], [])
  1460.               self.iter_keys[i][2].append(k)
  1461.  
  1462.             else:
  1463.               for i in self.find_range_keys(v):
  1464.                 if i not in self.iter_keys:
  1465.                   self.iter_keys[i] = ('RANGE', iter_vals[i], [])
  1466.                 self.iter_keys[i][2].append(k)
  1467.  
  1468.               else:
  1469.                 for i in self.find_prog_keys(v):
  1470.                   if i not in self.iter_keys:
  1471.                     self.iter_keys[i] = ('PROG', iter_vals[i], [])
  1472.                   self.iter_keys[i][2].append(k)
  1473.  
  1474.                 else:
  1475.                   self.payload[k] = v
  1476.  
  1477.     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'])
  1478.     logger.debug('enc_keys: %s' % self.enc_keys) # [('password', 'ENC', hex), ('header', 'B64', b64encode), ...
  1479.     logger.debug('payload: %s' % self.payload)
  1480.  
  1481.     self.available_actions = [k for k, _ in self.builtin_actions + self.module.available_actions]
  1482.     self.module_actions = [k for k, _ in self.module.available_actions]
  1483.  
  1484.     for x in opts.actions:
  1485.       self.update_actions(x)
  1486.  
  1487.     logger.debug('actions: %s' % self.ns.actions)
  1488.  
  1489.   def update_actions(self, arg):
  1490.     ns_actions = self.ns.actions
  1491.  
  1492.     actions, conditions = arg.split(':', 1)
  1493.     for action in actions.split(','):
  1494.  
  1495.       conds = [c.split('=', 1) for c in conditions.split(self.condition_delim)]
  1496.  
  1497.       if '=' in action:
  1498.         name, opts = action.split('=')
  1499.       else:
  1500.         name, opts = action, None
  1501.  
  1502.       if name not in self.available_actions:
  1503.         raise NotImplementedError('Unsupported action: %s' % name)
  1504.  
  1505.       if name not in ns_actions:
  1506.         ns_actions[name] = []
  1507.  
  1508.       ns_actions[name].append((conds, opts))
  1509.  
  1510.     self.ns.actions = ns_actions
  1511.  
  1512.   def lookup_actions(self, resp):
  1513.     actions = {}
  1514.     for action, conditions in self.ns.actions.items():
  1515.       for condition, opts in conditions:
  1516.         for key, val in condition:
  1517.           if key[-1] == '!':
  1518.             if resp.match(key[:-1], val):
  1519.               break
  1520.           else:
  1521.             if not resp.match(key, val):
  1522.               break
  1523.         else:
  1524.           actions[action] = opts
  1525.     return actions
  1526.  
  1527.   def check_free(self, payload):
  1528.     # free_list: 'host=10.0.0.1', 'user=anonymous', 'host=10.0.0.7,user=test', ...
  1529.     for m in self.ns.free_list:
  1530.       args = m.split(',', 1)
  1531.       for arg in args:
  1532.         k, v = arg.split('=', 1)
  1533.         if payload[k] != v:
  1534.           break
  1535.       else:
  1536.         return True
  1537.  
  1538.     return False
  1539.  
  1540.   def register_free(self, payload, opts):
  1541.     self.ns.free_list += [','.join('%s=%s' % (k, payload[k]) for k in opts.split('+'))]
  1542.     logger.debug('free_list updated: %s' % self.ns.free_list)
  1543.  
  1544.   def fire(self):
  1545.     logger.info('Starting %s at %s' % (__banner__, strftime('%Y-%m-%d %H:%M %Z', localtime())))
  1546.  
  1547.     try:
  1548.       self.start_threads()
  1549.       self.monitor_progress()
  1550.     except KeyboardInterrupt:
  1551.       pass
  1552.     except:
  1553.       logging.exception(sys.exc_info()[1])
  1554.     finally:
  1555.       self.ns.quit_now = True
  1556.  
  1557.     try:
  1558.       # waiting for reports enqueued by consumers to be flushed
  1559.       while True:
  1560.         active = multiprocessing.active_children()
  1561.         self.report_progress()
  1562.         if not len(active) > 2: # SyncManager and LogSvc
  1563.           break
  1564.         logger.debug('active: %s' % active)
  1565.         sleep(.1)
  1566.     except KeyboardInterrupt:
  1567.       pass
  1568.  
  1569.     if self.ns.total_size >= maxint:
  1570.       total_size = -1
  1571.     else:
  1572.       total_size = self.ns.total_size
  1573.  
  1574.     total_time = time() - self.ns.start_time
  1575.  
  1576.     hits_count = sum(p.hits_count for p in self.thread_progress)
  1577.     done_count = sum(p.done_count for p in self.thread_progress)
  1578.     skip_count = sum(p.skip_count for p in self.thread_progress)
  1579.     fail_count = sum(p.fail_count for p in self.thread_progress)
  1580.  
  1581.     speed_avg = done_count / total_time
  1582.  
  1583.     self.show_final()
  1584.  
  1585.     logger.info('Hits/Done/Skip/Fail/Size: %d/%d/%d/%d/%d, Avg: %d r/s, Time: %s' % (
  1586.       hits_count, done_count, skip_count, fail_count, total_size, speed_avg,
  1587.       pprint_seconds(total_time, '%dh %dm %ds')))
  1588.  
  1589.     if done_count < total_size:
  1590.       resume = []
  1591.       for i, p in enumerate(self.thread_progress):
  1592.         c = p.done_count + p.skip_count
  1593.         if self.resume:
  1594.           if i < len(self.resume):
  1595.             c += self.resume[i]
  1596.         resume.append(str(c))
  1597.  
  1598.       logger.info('To resume execution, pass --resume %s' % ','.join(resume))
  1599.  
  1600.     logger.quit()
  1601.     while len(multiprocessing.active_children()) > 1:
  1602.       sleep(.1)
  1603.  
  1604.   def push_final(self, resp): pass
  1605.   def show_final(self): pass
  1606.  
  1607.   def start_threads(self):
  1608.  
  1609.     task_queues = [multiprocessing.Queue(maxsize=10000) for _ in range(self.num_threads)]
  1610.  
  1611.     # consumers
  1612.     for num in range(self.num_threads):
  1613.       report_queue = multiprocessing.Queue(maxsize=1000)
  1614.       t = Process(name='Consumer-%d' % num, target=self.consume, args=(task_queues[num], report_queue, logger.pipe))
  1615.       t.daemon = True
  1616.       t.start()
  1617.       self.thread_report.append(report_queue)
  1618.       self.thread_progress.append(Progress())
  1619.  
  1620.     # producer
  1621.     t = Process(name='Producer', target=self.produce, args=(task_queues, logger.pipe))
  1622.     t.daemon = True
  1623.     t.start()
  1624.  
  1625.   def produce(self, task_queues, pipe):
  1626.  
  1627.     ignore_ctrlc()
  1628.  
  1629.     global logger
  1630.     logger = Logger(pipe)
  1631.  
  1632.     iterables = []
  1633.     total_size = 1
  1634.  
  1635.     for _, (t, v, _) in self.iter_keys.items():
  1636.  
  1637.       if t in ('FILE', 'COMBO'):
  1638.         size = 0
  1639.         files = []
  1640.  
  1641.         for fname in v.split(','):
  1642.           fpath = os.path.expanduser(fname)
  1643.           size += sum(1 for _ in open(fpath))
  1644.           files.append(FileIter(fpath))
  1645.  
  1646.         iterable = chain(*files)
  1647.  
  1648.       elif t == 'NET':
  1649.         subnets = [IP(n, make_net=True) for n in v.split(',')]
  1650.         size = sum(len(s) for s in subnets)
  1651.         iterable = chain(*subnets)
  1652.  
  1653.       elif t == 'MOD':
  1654.         elements, size = self.module.available_keys[v]()
  1655.         iterable = chain(elements)
  1656.  
  1657.       elif t == 'RANGE':
  1658.         size = 0
  1659.         ranges = []
  1660.  
  1661.         for r in v.split(','):
  1662.           typ, opt = r.split(':', 1)
  1663.  
  1664.           if typ not in ['hex', 'int', 'float', 'letters', 'lower', 'lowercase', 'upper', 'uppercase']:
  1665.             raise NotImplementedError("Incorrect range type '%s'" % typ)
  1666.  
  1667.           it = RangeIter(typ, opt)
  1668.           size += len(it)
  1669.           ranges.append(it)
  1670.  
  1671.         iterable = chain(*ranges)
  1672.  
  1673.       elif t == 'PROG':
  1674.         m = re.match(r'(.+),(\d+)$', v)
  1675.         if m:
  1676.           prog, size = m.groups()
  1677.         else:
  1678.           prog, size = v, maxint
  1679.  
  1680.         logger.debug('prog: %s, size: %s' % (prog, size))
  1681.  
  1682.         it = ProgIter(prog)
  1683.         iterable, size = chain(it), int(size)
  1684.  
  1685.       else:
  1686.         raise NotImplementedError("Incorrect keyword '%s'" % t)
  1687.  
  1688.       total_size *= size
  1689.       iterables.append(iterable)
  1690.  
  1691.     if not iterables:
  1692.       iterables.append(chain(['']))
  1693.  
  1694.     if self.stop:
  1695.       total_size = self.stop - self.start
  1696.     else:
  1697.       total_size -= self.start
  1698.  
  1699.     if self.resume:
  1700.       total_size -= sum(self.resume)
  1701.  
  1702.     self.ns.total_size = total_size
  1703.     self.ns.start_time = time()
  1704.  
  1705.     logger.headers()
  1706.  
  1707.     count = 0
  1708.     for pp in islice(product(*iterables), self.start, self.stop):
  1709.  
  1710.       if self.ns.quit_now:
  1711.         break
  1712.  
  1713.       cid = count % self.num_threads
  1714.       prod = [str(p).rstrip('\r\n') for p in pp]
  1715.  
  1716.       if self.resume:
  1717.         idx = count % len(self.resume)
  1718.         off = self.resume[idx]
  1719.  
  1720.         if count < off * len(self.resume):
  1721.           #logger.debug('Skipping %d %s, resume[%d]: %s' % (count, ':'.join(prod), idx, self.resume[idx]))
  1722.           count += 1
  1723.           continue
  1724.  
  1725.       while True:
  1726.         if self.ns.quit_now:
  1727.           break
  1728.  
  1729.         try:
  1730.           task_queues[cid].put_nowait(prod)
  1731.           break
  1732.         except Full:
  1733.           sleep(.1)
  1734.  
  1735.       count += 1
  1736.  
  1737.     if not self.ns.quit_now:
  1738.       for q in task_queues:
  1739.         q.put(None)
  1740.  
  1741.     logger.debug('producer done')
  1742.  
  1743.     while True:
  1744.       if self.ns.quit_now:
  1745.         for q in task_queues:
  1746.           q.cancel_join_thread()
  1747.         break
  1748.       sleep(.5)
  1749.  
  1750.     logger.debug('producer exits')
  1751.  
  1752.   def consume(self, task_queue, report_queue, pipe):
  1753.  
  1754.     ignore_ctrlc()
  1755.     handle_alarm()
  1756.  
  1757.     global logger
  1758.     logger = Logger(pipe)
  1759.  
  1760.     module = self.module()
  1761.  
  1762.     def shutdown():
  1763.       if hasattr(module, '__del__'):
  1764.         module.__del__()
  1765.       logger.debug('consumer done')
  1766.  
  1767.     while True:
  1768.       if self.ns.quit_now:
  1769.         return shutdown()
  1770.  
  1771.       try:
  1772.         prod = task_queue.get_nowait()
  1773.       except Empty:
  1774.         sleep(.1)
  1775.         continue
  1776.  
  1777.       if prod is None:
  1778.         return shutdown()
  1779.  
  1780.       payload = self.payload.copy()
  1781.  
  1782.       for i, (t, _, keys) in self.iter_keys.items():
  1783.         if t == 'FILE':
  1784.           for k in keys:
  1785.             payload[k] = payload[k].replace('FILE%d' % i, prod[i])
  1786.         elif t == 'NET':
  1787.           for k in keys:
  1788.             payload[k] = payload[k].replace('NET%d' % i, prod[i])
  1789.         elif t == 'COMBO':
  1790.           for j, k in keys:
  1791.             payload[k] = payload[k].replace('COMBO%d%d' % (i, j), prod[i].split(self.combo_delim)[j])
  1792.         elif t == 'MOD':
  1793.           for k in keys:
  1794.             payload[k] = payload[k].replace('MOD%d' %i, prod[i])
  1795.         elif t == 'RANGE':
  1796.           for k in keys:
  1797.             payload[k] = payload[k].replace('RANGE%d' %i, prod[i])
  1798.         elif t == 'PROG':
  1799.           for k in keys:
  1800.             payload[k] = payload[k].replace('PROG%d' %i, prod[i])
  1801.  
  1802.       for k, m, e in self.enc_keys:
  1803.         payload[k] = re.sub(r'{0}(.+?){0}'.format(m), lambda m: e(m.group(1)), payload[k])
  1804.  
  1805.       logger.debug('product: %s' % prod)
  1806.       pp_prod = ':'.join(prod)
  1807.  
  1808.       if self.check_free(payload):
  1809.         report_queue.put(('skip', pp_prod, None, 0))
  1810.         continue
  1811.  
  1812.       try_count = 0
  1813.       start_time = time()
  1814.  
  1815.       while True:
  1816.  
  1817.         while self.ns.paused and not self.ns.quit_now:
  1818.           sleep(1)
  1819.  
  1820.         if self.ns.quit_now:
  1821.           return shutdown()
  1822.  
  1823.         if self.rate_limit > 0:
  1824.           sleep(self.rate_limit)
  1825.  
  1826.         if try_count <= self.max_retries or self.max_retries < 0:
  1827.  
  1828.           actions = {}
  1829.           try_count += 1
  1830.  
  1831.           logger.debug('payload: %s [try %d/%d]' % (payload, try_count, self.max_retries+1))
  1832.  
  1833.           try:
  1834.             enable_alarm(self.timeout)
  1835.             resp = module.execute(**payload)
  1836.  
  1837.           except:
  1838.             mesg = '%s %s' % sys.exc_info()[:2]
  1839.             logger.debug('caught: %s' % mesg)
  1840.  
  1841.             #logging.exception(sys.exc_info()[1])
  1842.  
  1843.             resp = self.module.Response('xxx', mesg, timing=time()-start_time)
  1844.  
  1845.             if hasattr(module, 'reset'):
  1846.               module.reset()
  1847.  
  1848.             sleep(try_count * .1)
  1849.             continue
  1850.  
  1851.           finally:
  1852.             disable_alarm()
  1853.  
  1854.         else:
  1855.           actions = {'fail': None}
  1856.  
  1857.         actions.update(self.lookup_actions(resp))
  1858.         report_queue.put((actions, pp_prod, resp, time() - start_time))
  1859.  
  1860.         for name in self.module_actions:
  1861.           if name in actions:
  1862.             getattr(module, name)(**payload)
  1863.  
  1864.         if 'free' in actions:
  1865.           self.register_free(payload, actions['free'])
  1866.           break
  1867.  
  1868.         if 'fail' in actions:
  1869.           break
  1870.  
  1871.         if 'retry' in actions:
  1872.           continue
  1873.  
  1874.         break
  1875.  
  1876.   def monitor_progress(self):
  1877.     # loop until SyncManager, LogSvc and Producer are the only children left alive
  1878.     while len(multiprocessing.active_children()) > 3 and not self.ns.quit_now:
  1879.       self.report_progress()
  1880.       self.monitor_interaction()
  1881.  
  1882.   def report_progress(self):
  1883.     for i, pq in enumerate(self.thread_report):
  1884.       p = self.thread_progress[i]
  1885.  
  1886.       while True:
  1887.  
  1888.         try:
  1889.           actions, current, resp, seconds = pq.get_nowait()
  1890.           #logger.info('actions reported: %s' % '+'.join(actions))
  1891.  
  1892.         except Empty:
  1893.           break
  1894.  
  1895.         if actions == 'skip':
  1896.           p.skip_count += 1
  1897.           continue
  1898.  
  1899.         if self.resume:
  1900.           offset = p.done_count + self.resume[i]
  1901.         else:
  1902.           offset = p.done_count
  1903.  
  1904.         offset = (offset * self.num_threads) + i + 1 + self.start
  1905.  
  1906.         p.current = current
  1907.         p.seconds[p.done_count % len(p.seconds)] = seconds
  1908.  
  1909.         if 'fail' in actions:
  1910.           if not self.allow_ignore_failures or 'ignore' not in actions:
  1911.             logger.result('fail', resp, current, offset)
  1912.  
  1913.         elif 'ignore' not in actions:
  1914.           logger.result('hit', resp, current, offset)
  1915.  
  1916.         if 'fail' in actions:
  1917.           p.fail_count += 1
  1918.  
  1919.         elif 'retry' in actions:
  1920.           continue
  1921.  
  1922.         elif 'ignore' not in actions:
  1923.           p.hits_count += 1
  1924.  
  1925.           logger.save(resp, offset)
  1926.  
  1927.           self.push_final(resp)
  1928.  
  1929.         p.done_count += 1
  1930.  
  1931.         if 'quit' in actions:
  1932.           self.ns.quit_now = True
  1933.  
  1934.  
  1935.   def monitor_interaction(self):
  1936.  
  1937.     if on_windows():
  1938.       import msvcrt
  1939.       if not msvcrt.kbhit():
  1940.         sleep(.1)
  1941.         return
  1942.  
  1943.       command = msvcrt.getche()
  1944.       if command == 'x':
  1945.         command += raw_input()
  1946.  
  1947.     else:
  1948.       i, _, _ = select([sys.stdin], [], [], .1)
  1949.       if not i: return
  1950.       command = i[0].readline().strip()
  1951.  
  1952.     if command == 'h':
  1953.       logger.info('''Available commands:
  1954.        h       show help
  1955.        <Enter> show progress
  1956.        d/D     increase/decrease debug level
  1957.        p       pause progress
  1958.        f       show verbose progress
  1959.        x arg   add monitor condition
  1960.        a       show all active conditions
  1961.        q       terminate execution now
  1962.        ''')
  1963.  
  1964.     elif command == 'q':
  1965.       self.ns.quit_now = True
  1966.  
  1967.     elif command == 'p':
  1968.       self.ns.paused = not self.ns.paused
  1969.       logger.info(self.ns.paused and 'Paused' or 'Unpaused')
  1970.  
  1971.     elif command == 'd':
  1972.       logger.setLevel(logging.DEBUG)
  1973.  
  1974.     elif command == 'D':
  1975.       logger.setLevel(logging.INFO)
  1976.  
  1977.     elif command == 'a':
  1978.       logger.info(repr(self.ns.actions))
  1979.  
  1980.     elif command.startswith('x'):
  1981.       _, arg = command.split(' ', 1)
  1982.       try:
  1983.         self.update_actions(arg)
  1984.       except ValueError:
  1985.         logger.warn('usage: x actions:conditions')
  1986.  
  1987.     else: # show progress
  1988.  
  1989.       thread_progress = self.thread_progress
  1990.       num_threads = self.num_threads
  1991.       total_size = self.ns.total_size
  1992.  
  1993.       total_count = sum(p.done_count+p.skip_count for p in thread_progress)
  1994.       speed_avg = num_threads / (sum(sum(p.seconds) / len(p.seconds) for p in thread_progress) / num_threads)
  1995.       if total_size >= maxint:
  1996.         etc_time = 'inf'
  1997.         remain_time = 'inf'
  1998.       else:
  1999.         remain_seconds = (total_size - total_count) / speed_avg
  2000.         remain_time = pprint_seconds(remain_seconds, '%02d:%02d:%02d')
  2001.         etc_seconds = datetime.now() + timedelta(seconds=remain_seconds)
  2002.         etc_time = etc_seconds.strftime('%H:%M:%S')
  2003.  
  2004.       logger.info('Progress: {0:>3}% ({1}/{2}) | Speed: {3:.0f} r/s | ETC: {4} ({5} remaining) {6}'.format(
  2005.         total_count * 100/total_size,
  2006.         total_count,
  2007.         total_size,
  2008.         speed_avg,
  2009.         etc_time,
  2010.         remain_time,
  2011.         self.ns.paused and '| Paused' or ''))
  2012.  
  2013.       if command == 'f':
  2014.         for i, p in enumerate(thread_progress):
  2015.           total_count = p.done_count + p.skip_count
  2016.           logger.info(' {0:>3}: {1:>3}% ({2}/{3}) {4}'.format(
  2017.             '#%d' % (i+1),
  2018.             int(100*total_count/(1.0*total_size/num_threads)),
  2019.             total_count,
  2020.             total_size/num_threads,
  2021.             p.current))
  2022.  
  2023. # }}}
  2024.  
  2025. # Response_Base {{{
  2026. def match_range(size, val):
  2027.   if '-' in val:
  2028.     size_min, size_max = val.split('-')
  2029.  
  2030.     if not size_min and not size_max:
  2031.       raise ValueError('Invalid interval')
  2032.  
  2033.     elif not size_min: # size == -N
  2034.       return size <= float(size_max)
  2035.  
  2036.     elif not size_max: # size == N-
  2037.       return size >= float(size_min)
  2038.  
  2039.     else:
  2040.       size_min, size_max = float(size_min), float(size_max)
  2041.       if size_min >= size_max:
  2042.         raise ValueError('Invalid interval')
  2043.  
  2044.       return size_min <= size <= size_max
  2045.  
  2046.   else:
  2047.     return size == float(val)
  2048.  
  2049. class Response_Base:
  2050.  
  2051.   available_conditions = (
  2052.     ('code', 'match status code'),
  2053.     ('size', 'match size (N or N-M or N- or -N)'),
  2054.     ('time', 'match time (N or N-M or N- or -N)'),
  2055.     ('mesg', 'match message'),
  2056.     ('fgrep', 'search for string in mesg'),
  2057.     ('egrep', 'search for regex in mesg'),
  2058.     )
  2059.  
  2060.   indicatorsfmt = [('code', -5), ('size', -4), ('time', 7)]
  2061.  
  2062.   def __init__(self, code, mesg, timing=0, trace=None):
  2063.     self.code = code
  2064.     self.mesg = mesg
  2065.     self.time = timing.time if isinstance(timing, Timing) else timing
  2066.     self.size = len(mesg)
  2067.     self.trace = trace
  2068.  
  2069.   def indicators(self):
  2070.     return self.code, self.size, '%.3f' % self.time
  2071.  
  2072.   def __str__(self):
  2073.     return self.mesg
  2074.  
  2075.   def match(self, key, val):
  2076.     return getattr(self, 'match_'+key)(val)
  2077.  
  2078.   def match_code(self, val):
  2079.     return re.match('%s$' % val, str(self.code))
  2080.  
  2081.   def match_size(self, val):
  2082.     return match_range(self.size, val)
  2083.  
  2084.   def match_time(self, val):
  2085.     return match_range(self.time, val)
  2086.  
  2087.   def match_mesg(self, val):
  2088.     return val == self.mesg
  2089.  
  2090.   def match_fgrep(self, val):
  2091.     return val in str(self)
  2092.  
  2093.   def match_egrep(self, val):
  2094.     return re.search(val, str(self))
  2095.  
  2096.   def dump(self):
  2097.     return self.trace or str(self)
  2098.  
  2099.   def str_target(self):
  2100.     return ''
  2101.  
  2102. class Timing:
  2103.   def __enter__(self):
  2104.     self.t1 = time()
  2105.     return self
  2106.  
  2107.   def __exit__(self, exc_type, exc_value, traceback):
  2108.     self.time = time() - self.t1
  2109.  
  2110. # }}}
  2111.  
  2112. # TCP_Cache {{{
  2113. class TCP_Connection:
  2114.  
  2115.   def __init__(self, fp, banner=None):
  2116.     self.fp = fp
  2117.     self.banner = banner
  2118.  
  2119.   def close(self):
  2120.     self.fp.close()
  2121.  
  2122. class TCP_Cache:
  2123.  
  2124.   available_actions = (
  2125.     ('reset', 'close current connection in order to reconnect next time'),
  2126.     )
  2127.  
  2128.   available_options = (
  2129.     ('persistent', 'use persistent connections [1|0]'),
  2130.     )
  2131.  
  2132.   def __init__(self):
  2133.     self.cache = {} # {'10.0.0.1:22': ('root', conn1), '10.0.0.2:22': ('admin', conn2),
  2134.     self.curr = None
  2135.  
  2136.   def __del__(self):
  2137.     for _, (_, c) in self.cache.items():
  2138.       c.close()
  2139.     self.cache.clear()
  2140.  
  2141.   def bind(self, host, port, *args, **kwargs):
  2142.  
  2143.     hp = '%s:%s' % (host, port)
  2144.     key = ':'.join(map(str, args))
  2145.  
  2146.     if hp in self.cache:
  2147.       k, c = self.cache[hp]
  2148.  
  2149.       if key == k:
  2150.         self.curr = hp, k, c
  2151.         return c.fp, c.banner
  2152.  
  2153.       else:
  2154.         c.close()
  2155.         del self.cache[hp]
  2156.  
  2157.     self.curr = None
  2158.  
  2159.     logger.debug('connect')
  2160.     conn = self.connect(host, port, *args, **kwargs)
  2161.  
  2162.     self.cache[hp] = (key, conn)
  2163.     self.curr = hp, key, conn
  2164.  
  2165.     return conn.fp, conn.banner
  2166.  
  2167.   def reset(self, **kwargs):
  2168.     if self.curr:
  2169.       hp, _, c = self.curr
  2170.  
  2171.       c.close()
  2172.       del self.cache[hp]
  2173.  
  2174.       self.curr = None
  2175.  
  2176. # }}}
  2177.  
  2178. # FTP {{{
  2179. from ftplib import FTP, Error as FTP_Error
  2180. try:
  2181.   from ftplib import FTP_TLS # New in python 2.7
  2182. except ImportError:
  2183.   notfound.append('ftp-tls')
  2184.  
  2185. class FTP_login(TCP_Cache):
  2186.   '''Brute-force FTP'''
  2187.  
  2188.   usage_hints = (
  2189.     """%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt"""
  2190.     """ -x ignore:mesg='Login incorrect.' -x ignore,reset,retry:code=500""",
  2191.     )
  2192.  
  2193.   available_options = (
  2194.     ('host', 'target host'),
  2195.     ('port', 'target port [21]'),
  2196.     ('user', 'usernames to test'),
  2197.     ('password', 'passwords to test'),
  2198.     ('tls', 'use TLS [0|1]'),
  2199.     ('timeout', 'seconds to wait for a response [10]'),
  2200.     )
  2201.   available_options += TCP_Cache.available_options
  2202.  
  2203.   Response = Response_Base
  2204.  
  2205.   def connect(self, host, port, tls, timeout):
  2206.  
  2207.     if tls == '0':
  2208.       fp = FTP(timeout=int(timeout))
  2209.     else:
  2210.       fp = FTP_TLS(timeout=int(timeout))
  2211.  
  2212.     banner = fp.connect(host, int(port))
  2213.  
  2214.     if tls != '0':
  2215.       fp.auth()
  2216.  
  2217.     return TCP_Connection(fp, banner)
  2218.  
  2219.   def execute(self, host, port='21', tls='0', user=None, password=None, timeout='10', persistent='1'):
  2220.  
  2221.     with Timing() as timing:
  2222.       fp, resp = self.bind(host, port, tls, timeout=timeout)
  2223.  
  2224.     try:
  2225.       if user is not None or password is not None:
  2226.         with Timing() as timing:
  2227.           if user is not None:
  2228.             resp = fp.sendcmd('USER ' + user)
  2229.           if password is not None:
  2230.             resp = fp.sendcmd('PASS ' + password)
  2231.  
  2232.       logger.debug('No error: %s' % resp)
  2233.       self.reset()
  2234.  
  2235.     except FTP_Error as e:
  2236.       resp = str(e)
  2237.       logger.debug('FTP_Error: %s' % resp)
  2238.  
  2239.     if persistent == '0':
  2240.       self.reset()
  2241.  
  2242.     code, mesg = resp.split(' ', 1)
  2243.     return self.Response(code, mesg, timing)
  2244.  
  2245. # }}}
  2246.  
  2247. # SSH {{{
  2248.  
  2249. try:
  2250.   from logging import NullHandler # only available since python 2.7
  2251. except ImportError:
  2252.   class NullHandler(logging.Handler):
  2253.     def emit(self, record):
  2254.       pass
  2255.  
  2256. try:
  2257.   import paramiko
  2258.   logging.getLogger('paramiko.transport').addHandler(NullHandler())
  2259. except ImportError:
  2260.   notfound.append('paramiko')
  2261.  
  2262. def load_keyfile(keyfile):
  2263.   for cls in (paramiko.RSAKey, paramiko.DSSKey, paramiko.ECDSAKey):
  2264.     try:
  2265.       return cls.from_private_key_file(keyfile)
  2266.     except paramiko.SSHException:
  2267.       pass
  2268.   else:
  2269.     raise
  2270.  
  2271. class SSH_login(TCP_Cache):
  2272.   '''Brute-force SSH'''
  2273.  
  2274.   usage_hints = (
  2275.     """%prog host=10.0.0.1 user=root password=FILE0 0=passwords.txt -x ignore:mesg='Authentication failed.'""",
  2276.     )
  2277.  
  2278.   available_options = (
  2279.     ('host', 'target host'),
  2280.     ('port', 'target port [22]'),
  2281.     ('user', 'usernames to test'),
  2282.     ('password', 'passwords to test'),
  2283.     ('auth_type', 'type of password authentication to use [password|keyboard-interactive|auto]'),
  2284.     ('keyfile', 'file with RSA, DSA or ECDSA private key to test'),
  2285.     )
  2286.   available_options += TCP_Cache.available_options
  2287.  
  2288.   Response = Response_Base
  2289.  
  2290.   def connect(self, host, port, user):
  2291.     fp = paramiko.Transport('%s:%s' % (host, int(port)))
  2292.     fp.start_client()
  2293.  
  2294.     return TCP_Connection(fp, fp.remote_version)
  2295.  
  2296.   def execute(self, host, port='22', user=None, password=None, auth_type='password', keyfile=None, persistent='1'):
  2297.  
  2298.     with Timing() as timing:
  2299.       fp, banner = self.bind(host, port, user)
  2300.  
  2301.     try:
  2302.       if user is not None:
  2303.  
  2304.         if keyfile is not None:
  2305.           key = load_keyfile(keyfile)
  2306.  
  2307.         with Timing() as timing:
  2308.  
  2309.           if keyfile is not None:
  2310.             fp.auth_publickey(user, key)
  2311.  
  2312.           elif password is not None:
  2313.             if auth_type == 'password':
  2314.               fp.auth_password(user, password, fallback=False)
  2315.  
  2316.             elif auth_type == 'keyboard-interactive':
  2317.               fp.auth_interactive(user, lambda a,b,c: [password] if len(c) == 1 else [])
  2318.  
  2319.             elif auth_type == 'auto':
  2320.               fp.auth_password(user, password, fallback=True)
  2321.  
  2322.             else:
  2323.               raise NotImplementedError("Incorrect auth_type '%s'" % auth_type)
  2324.  
  2325.       logger.debug('No error')
  2326.       code, mesg = '0', banner
  2327.  
  2328.       self.reset()
  2329.  
  2330.     except paramiko.AuthenticationException as e:
  2331.       logger.debug('AuthenticationException: %s' % e)
  2332.       code, mesg = '1', str(e)
  2333.  
  2334.     if persistent == '0':
  2335.       self.reset()
  2336.  
  2337.     return self.Response(code, mesg, timing)
  2338.  
  2339. # }}}
  2340.  
  2341. # Telnet {{{
  2342. from telnetlib import Telnet
  2343. class Telnet_login(TCP_Cache):
  2344.   '''Brute-force Telnet'''
  2345.  
  2346.   usage_hints = (
  2347.     """%prog host=10.0.0.1 inputs='FILE0\\nFILE1' 0=logins.txt 1=passwords.txt persistent=0"""
  2348.     """ prompt_re='Username:|Password:' -x ignore:egrep='Login incorrect.+Username:'""",
  2349.     )
  2350.  
  2351.   available_options = (
  2352.     ('host', 'target host'),
  2353.     ('port', 'target port [23]'),
  2354.     ('inputs', 'list of values to input'),
  2355.     ('prompt_re', 'regular expression to match prompts [\w+:]'),
  2356.     ('timeout', 'seconds to wait for a response and for prompt_re to match received data [20]'),
  2357.     )
  2358.   available_options += TCP_Cache.available_options
  2359.  
  2360.   Response = Response_Base
  2361.  
  2362.   def connect(self, host, port, timeout):
  2363.     self.prompt_count = 0
  2364.     fp = Telnet(host, int(port), int(timeout))
  2365.  
  2366.     return TCP_Connection(fp)
  2367.  
  2368.   def execute(self, host, port='23', inputs=None, prompt_re='\w+:', timeout='20', persistent='1'):
  2369.  
  2370.     with Timing() as timing:
  2371.       fp, _ = self.bind(host, port, timeout=timeout)
  2372.  
  2373.     trace = ''
  2374.     timeout = int(timeout)
  2375.  
  2376.     if self.prompt_count == 0:
  2377.       _, _, raw = fp.expect([prompt_re], timeout=timeout)
  2378.       logger.debug('raw banner: %s' % repr(raw))
  2379.       trace += raw
  2380.       self.prompt_count += 1
  2381.  
  2382.     if inputs is not None:
  2383.       with Timing() as timing:
  2384.         for val in inputs.split(r'\n'):
  2385.           logger.debug('input: %s' % val)
  2386.           cmd = val + '\n' #'\r\x00'
  2387.           fp.write(cmd)
  2388.           trace += cmd
  2389.  
  2390.           _, _, raw = fp.expect([prompt_re], timeout=timeout)
  2391.           logger.debug('raw %d: %s' % (self.prompt_count, repr(raw)))
  2392.           trace += raw
  2393.           self.prompt_count += 1
  2394.  
  2395.     if persistent == '0':
  2396.       self.reset()
  2397.  
  2398.     mesg = repr(raw)[1:-1] # strip enclosing single quotes
  2399.     return self.Response(0, mesg, timing, trace)
  2400.  
  2401. # }}}
  2402.  
  2403. # SMTP {{{
  2404. from smtplib import SMTP, SMTP_SSL, SMTPAuthenticationError, SMTPHeloError, SMTPException
  2405. class SMTP_Base(TCP_Cache):
  2406.  
  2407.   available_options = TCP_Cache.available_options
  2408.   available_options += (
  2409.     ('timeout', 'seconds to wait for a response [10]'),
  2410.     ('host', 'target host'),
  2411.     ('port', 'target port [25]'),
  2412.     ('ssl', 'use SSL [0|1]'),
  2413.     ('helo', 'helo or ehlo command to send after connect [skip]'),
  2414.     ('starttls', 'send STARTTLS [0|1]'),
  2415.     ('user', 'usernames to test'),
  2416.     )
  2417.  
  2418.   Response = Response_Base
  2419.  
  2420.   def connect(self, host, port, ssl, helo, starttls, timeout):
  2421.  
  2422.     if ssl == '0':
  2423.       if not port: port = 25
  2424.       fp = SMTP(timeout=int(timeout))
  2425.     else:
  2426.       if not port: port = 465
  2427.       fp = SMTP_SSL(timeout=int(timeout))
  2428.  
  2429.     resp = fp.connect(host, int(port))
  2430.  
  2431.     if helo:
  2432.       cmd, name = helo.split(' ', 1)
  2433.  
  2434.       if cmd.lower() == 'ehlo':
  2435.         resp = fp.ehlo(name)
  2436.       else:
  2437.         resp = fp.helo(name)
  2438.  
  2439.     if not starttls == '0':
  2440.       resp = fp.starttls()
  2441.  
  2442.     return TCP_Connection(fp, resp)
  2443.  
  2444.  
  2445. class SMTP_vrfy(SMTP_Base):
  2446.   '''Enumerate valid users using SMTP VRFY'''
  2447.  
  2448.   usage_hints = (
  2449.     '''%prog host=10.0.0.1 user=FILE0 0=logins.txt [helo='ehlo its.me.com']'''
  2450.     ''' -x ignore:fgrep='User unknown' -x ignore,reset,retry:code=421''',
  2451.     )
  2452.  
  2453.   def execute(self, host, port='', ssl='0', helo='', starttls='0', user=None, timeout='10', persistent='1'):
  2454.  
  2455.     with Timing() as timing:
  2456.       fp, resp = self.bind(host, port, ssl, helo, starttls, timeout=timeout)
  2457.  
  2458.     if user is not None:
  2459.       with Timing() as timing:
  2460.         resp = fp.verify(user)
  2461.  
  2462.     if persistent == '0':
  2463.       self.reset()
  2464.  
  2465.     code, mesg = resp
  2466.     return self.Response(code, mesg, timing)
  2467.  
  2468.  
  2469. class SMTP_rcpt(SMTP_Base):
  2470.   '''Enumerate valid users using SMTP RCPT TO'''
  2471.  
  2472.   usage_hints = (
  2473.     '''%prog host=10.0.0.1 user=FILE0@localhost 0=logins.txt [helo='ehlo its.me.com']'''
  2474.     ''' [mail_from=bar@example.com] -x ignore:fgrep='User unknown' -x ignore,reset,retry:code=421''',
  2475.     )
  2476.  
  2477.   available_options = SMTP_Base.available_options
  2478.   available_options += (
  2479.     ('mail_from', 'sender email [test@example.org]'),
  2480.     )
  2481.  
  2482.   def execute(self, host, port='', ssl='0', helo='', starttls='0', mail_from='test@example.org', user=None, timeout='10', persistent='1'):
  2483.  
  2484.     with Timing() as timing:
  2485.       fp, resp = self.bind(host, port, ssl, helo, starttls, timeout=timeout)
  2486.  
  2487.     if mail_from or user is not None:
  2488.       with Timing() as timing:
  2489.         if mail_from:
  2490.           resp = fp.mail(mail_from)
  2491.         if user is not None:
  2492.           resp = fp.rcpt(user)
  2493.  
  2494.     fp.rset()
  2495.  
  2496.     if persistent == '0':
  2497.       self.reset()
  2498.  
  2499.     code, mesg = resp
  2500.     return self.Response(code, mesg, timing)
  2501.  
  2502.  
  2503. class SMTP_login(SMTP_Base):
  2504.   '''Brute-force SMTP'''
  2505.  
  2506.   usage_hints = (
  2507.     '''%prog host=10.0.0.1 user=f.bar@dom.com password=FILE0 0=passwords.txt [helo='ehlo its.me.com']'''
  2508.     ''' -x ignore:fgrep='Authentication failed' -x ignore,reset,retry:code=421''',
  2509.     )
  2510.  
  2511.   available_options = SMTP_Base.available_options
  2512.   available_options += (
  2513.     ('password', 'passwords to test'),
  2514.     )
  2515.  
  2516.   def execute(self, host, port='', ssl='0', helo='', starttls='0', user=None, password=None, timeout='10', persistent='1'):
  2517.  
  2518.     with Timing() as timing:
  2519.       fp, resp = self.bind(host, port, ssl, helo, starttls, timeout=timeout)
  2520.  
  2521.     try:
  2522.       if user is not None and password is not None:
  2523.         with Timing() as timing:
  2524.           resp = fp.login(user, password)
  2525.  
  2526.       logger.debug('No error: %s' % str(resp))
  2527.       self.reset()
  2528.  
  2529.     except (SMTPHeloError,SMTPAuthenticationError,SMTPException) as resp:
  2530.       logger.debug('SMTPError: %s' % str(resp))
  2531.  
  2532.     if persistent == '0':
  2533.       self.reset()
  2534.  
  2535.     code, mesg = resp
  2536.     return self.Response(code, mesg, timing)
  2537.  
  2538. # }}}
  2539.  
  2540. # Finger {{{
  2541. class Controller_Finger(Controller):
  2542.  
  2543.   user_list = []
  2544.  
  2545.   def push_final(self, resp):
  2546.     if hasattr(resp, 'lines'):
  2547.       for l in resp.lines:
  2548.          if l not in self.user_list:
  2549.            self.user_list.append(l)
  2550.  
  2551.   def show_final(self):
  2552.     print('\n'.join(self.user_list))
  2553.  
  2554. class Finger_lookup:
  2555.   '''Enumerate valid users using Finger'''
  2556.  
  2557.   usage_hints = (
  2558.     """%prog host=10.0.0.1 user=FILE0 0=words.txt -x ignore:fgrep='no such user'""",
  2559.     )
  2560.  
  2561.   available_options = (
  2562.     ('host', 'target host'),
  2563.     ('port', 'target port [79]'),
  2564.     ('user', 'usernames to test'),
  2565.     ('timeout', 'seconds to wait for a response [5]'),
  2566.     )
  2567.   available_actions = ()
  2568.  
  2569.   Response = Response_Base
  2570.  
  2571.   def execute(self, host, port='79', user='', timeout='5'):
  2572.     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  2573.     s.settimeout(int(timeout))
  2574.  
  2575.     with Timing() as timing:
  2576.       s.connect((host, int(port)))
  2577.  
  2578.     if user:
  2579.       s.send(user)
  2580.     s.send('\r\n')
  2581.  
  2582.     data = ''
  2583.     with Timing() as timing:
  2584.       while True:
  2585.         raw = s.recv(1024)
  2586.         if not raw:
  2587.           break
  2588.         data += raw
  2589.  
  2590.     s.close()
  2591.  
  2592.     logger.debug('recv: %s' % repr(data))
  2593.  
  2594.     data = data.strip()
  2595.     mesg = repr(data)
  2596.  
  2597.     resp = self.Response(0, mesg, timing, data)
  2598.     resp.lines = [l.strip('\r\n') for l in data.split('\n')]
  2599.  
  2600.     return resp
  2601. # }}}
  2602.  
  2603. # LDAP {{{
  2604. if not which('ldapsearch'):
  2605.   notfound.append('openldap')
  2606.  
  2607. # Because python-ldap-2.4.4 did not allow using a PasswordPolicyControl
  2608. # during bind authentication (cf. http://article.gmane.org/gmane.comp.python.ldap/1003),
  2609. # I chose to wrap around ldapsearch with "-e ppolicy".
  2610.  
  2611. class LDAP_login:
  2612.   '''Brute-force LDAP'''
  2613.  
  2614.   usage_hints = (
  2615.     """%prog host=10.0.0.1 binddn='cn=Directory Manager' bindpw=FILE0 0=passwords.txt"""
  2616.     """ -x ignore:mesg='ldap_bind: Invalid credentials (49)'""",
  2617.     )
  2618.  
  2619.   available_options = (
  2620.     ('host', 'target host'),
  2621.     ('port', 'target port [389]'),
  2622.     ('binddn', 'usernames to test'),
  2623.     ('bindpw', 'passwords to test'),
  2624.     ('basedn', 'base DN for search'),
  2625.     ('ssl', 'use SSL/TLS [0|1]'),
  2626.     )
  2627.   available_actions = ()
  2628.  
  2629.   Response = Response_Base
  2630.  
  2631.   def execute(self, host, port='389', binddn='', bindpw='', basedn='', ssl='0'):
  2632.     uri = 'ldap%s://%s:%s' % ('s' if ssl != '0' else '', host, port)
  2633.     cmd = ['ldapsearch', '-H', uri, '-e', 'ppolicy', '-D', binddn, '-w', bindpw, '-b', basedn, '-s', 'one']
  2634.     p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={'LDAPTLS_REQCERT': 'never'})
  2635.     out = p.stdout.read()
  2636.     err = p.stderr.read()
  2637.  
  2638.     with Timing() as timing:
  2639.       code = p.wait()
  2640.  
  2641.     mesg = repr((out + err).strip())[1:-1]
  2642.     trace = '[out]\n%s\n[err]\n%s' % (out, err)
  2643.  
  2644.     return self.Response(code, mesg, timing, trace)
  2645.  
  2646. # }}}
  2647.  
  2648. # SMB {{{
  2649. try:
  2650.   from impacket.smbconnection import SMBConnection, SessionError
  2651.   from impacket import nt_errors
  2652.   from impacket.dcerpc.v5 import transport,  lsat, lsad
  2653.   from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED
  2654.   from impacket.dcerpc.v5.samr import SID_NAME_USE
  2655. except ImportError:
  2656.   notfound.append('impacket')
  2657.  
  2658. class SMB_Connection(TCP_Connection):
  2659.  
  2660.   def close(self):
  2661.     self.fp.getSMBServer().get_socket().close()
  2662.  
  2663. class Response_SMB(Response_Base):
  2664.   indicatorsfmt = [('code', -8), ('size', -4), ('time', 6)]
  2665.  
  2666. class SMB_login(TCP_Cache):
  2667.   '''Brute-force SMB'''
  2668.  
  2669.   usage_hints = (
  2670.     """%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt"""
  2671.     """ -x ignore:fgrep='unknown user name or bad password'""",
  2672.     )
  2673.  
  2674.   available_options = (
  2675.     ('host', 'target host'),
  2676.     ('port', 'target port [139]'),
  2677.     ('user', 'usernames to test'),
  2678.     ('password', 'passwords to test'),
  2679.     ('password_hash', "LM/NT hashes to test, at least one hash must be provided ('lm:nt' or ':nt' or 'lm:')"),
  2680.     ('domain', 'domain to test'),
  2681.     )
  2682.   available_options += TCP_Cache.available_options
  2683.  
  2684.   Response = Response_SMB
  2685.  
  2686.   def connect(self, host, port):
  2687.     # if port == 445, impacket will use <host> instead of '*SMBSERVER' as the remote_name
  2688.     fp = SMBConnection('*SMBSERVER', host, sess_port=int(port))
  2689.  
  2690.     return SMB_Connection(fp)
  2691.  
  2692.   def execute(self, host, port='139', user=None, password='', password_hash=None, domain='', persistent='1'):
  2693.  
  2694.     with Timing() as timing:
  2695.       fp, _ = self.bind(host, port)
  2696.  
  2697.     try:
  2698.       if user is None:
  2699.         fp.login('', '') # retrieve workgroup/domain and computer name
  2700.       else:
  2701.         with Timing() as timing:
  2702.           if password_hash:
  2703.             if ':' in password_hash:
  2704.               lmhash, nthash = password_hash.split(':')
  2705.             else:
  2706.               lmhash, nthash = 'aad3b435b51404eeaad3b435b51404ee', password_hash
  2707.             fp.login(user, '', domain, lmhash, nthash)
  2708.  
  2709.           else:
  2710.             fp.login(user, password, domain)
  2711.  
  2712.       logger.debug('No error')
  2713.       code, mesg = '0', '%s\\%s (%s)' % (fp.getServerDomain(), fp.getServerName(), fp.getServerOS())
  2714.  
  2715.       self.reset()
  2716.  
  2717.     except SessionError as e:
  2718.       code = '%x' % e.getErrorCode()
  2719.       mesg = nt_errors.ERROR_MESSAGES[e.getErrorCode()][0]
  2720.  
  2721.     if persistent == '0':
  2722.       self.reset()
  2723.  
  2724.     return self.Response(code, mesg, timing)
  2725.  
  2726. class DCE_Connection(TCP_Connection):
  2727.  
  2728.   def __init__(self, fp, smbt):
  2729.     self.smbt = smbt
  2730.     TCP_Connection.__init__(self, fp)
  2731.  
  2732.   def close(self):
  2733.     self.smbt.get_socket().close()
  2734.  
  2735. # impacket/examples/lookupsid.py is much faster because it queries 1000 SIDs per packet
  2736. class SMB_lookupsid(TCP_Cache):
  2737.   '''Brute-force SMB SID-lookup'''
  2738.  
  2739.   usage_hints = (
  2740.     '''%prog host=10.0.0.1 sid=S-1-5-21-1234567890-1234567890-1234567890 rid=RANGE0 0=int:500-2000 -x ignore:code=1''',
  2741.     )
  2742.  
  2743.   available_options = (
  2744.     ('host', 'target host'),
  2745.     ('port', 'target port [139]'),
  2746.     ('sid', 'SID to test'),
  2747.     ('rid', 'RID to test'),
  2748.     ('user', 'username to use if auth required'),
  2749.     ('password', 'password to use if auth required'),
  2750.     )
  2751.   available_options += TCP_Cache.available_options
  2752.  
  2753.   Response = Response_Base
  2754.  
  2755.   def connect(self, host, port, user, password, sid):
  2756.     smbt = transport.SMBTransport(host, int(port), r'\lsarpc', user, password)
  2757.  
  2758.     dce = smbt.get_dce_rpc()
  2759.     dce.connect()
  2760.     dce.bind(lsat.MSRPC_UUID_LSAT)
  2761.  
  2762.     op2 = lsat.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
  2763.  
  2764.     if sid is None:
  2765.       res = lsad.hLsarQueryInformationPolicy2(dce, op2['PolicyHandle'], lsad.POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation)
  2766.       sid = res['PolicyInformation']['PolicyAccountDomainInfo']['DomainSid'].formatCanonical()
  2767.  
  2768.     self.sid = sid
  2769.     self.policy_handle = op2['PolicyHandle']
  2770.  
  2771.     return DCE_Connection(dce, smbt)
  2772.  
  2773.   def execute(self, host, port='139', user='', password='', sid=None, rid=None, persistent='1'):
  2774.  
  2775.     fp, _ = self.bind(host, port, user, password, sid)
  2776.  
  2777.     if rid:
  2778.       sid = '%s-%s' % (self.sid, rid)
  2779.     else:
  2780.       sid = self.sid
  2781.  
  2782.     try:
  2783.       res = lsat.hLsarLookupSids(fp, self.policy_handle, [sid], lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
  2784.  
  2785.       code, names = 0, []
  2786.       for n, item in enumerate(res['TranslatedNames']['Names']):
  2787.         names.append("%s\\%s (%s)" % (res['ReferencedDomains']['Domains'][item['DomainIndex']]['Name'], item['Name'], SID_NAME_USE.enumItems(item['Use']).name[7:]))
  2788.  
  2789.     except lsat.DCERPCSessionError:
  2790.       code, names = 1, ['unknown'] # STATUS_NONE_MAPPED
  2791.  
  2792.     if persistent == '0':
  2793.       self.reset()
  2794.  
  2795.     return self.Response(code, ', '.join(names))
  2796. # }}}
  2797.  
  2798. # POP {{{
  2799. from poplib import POP3, POP3_SSL, error_proto as pop_error
  2800. class POP_Connection(TCP_Connection):
  2801.   def close(self):
  2802.     self.fp.quit()
  2803.  
  2804. class POP_login(TCP_Cache):
  2805.   '''Brute-force POP3'''
  2806.  
  2807.   usage_hints = (
  2808.     '''%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt -x ignore:code=-ERR''',
  2809.     )
  2810.  
  2811.   available_options = (
  2812.     ('host', 'target host'),
  2813.     ('port', 'target port [110]'),
  2814.     ('user', 'usernames to test'),
  2815.     ('password', 'passwords to test'),
  2816.     ('ssl', 'use SSL [0|1]'),
  2817.     ('timeout', 'seconds to wait for a response [10]'),
  2818.     )
  2819.   available_options += TCP_Cache.available_options
  2820.  
  2821.   Response = Response_Base
  2822.  
  2823.   def connect(self, host, port, ssl, timeout):
  2824.     if ssl == '0':
  2825.       if not port: port = 110
  2826.       fp = POP3(host, int(port), timeout=int(timeout))
  2827.     else:
  2828.       if not port: port = 995
  2829.       fp = POP3_SSL(host, int(port)) # timeout=int(timeout)) # no timeout option in python2
  2830.  
  2831.     return POP_Connection(fp, fp.welcome)
  2832.  
  2833.   def execute(self, host, port='', ssl='0', user=None, password=None, timeout='10', persistent='1'):
  2834.  
  2835.     with Timing() as timing:
  2836.       fp, resp = self.bind(host, port, ssl, timeout=timeout)
  2837.  
  2838.     try:
  2839.       if user is not None or password is not None:
  2840.         with Timing() as timing:
  2841.  
  2842.           if user is not None:
  2843.             resp = fp.user(user)
  2844.           if password is not None:
  2845.             resp = fp.pass_(password)
  2846.  
  2847.       logger.debug('No error: %s' % resp)
  2848.       self.reset()
  2849.  
  2850.     except pop_error as e:
  2851.       logger.debug('pop_error: %s' % e)
  2852.       resp = str(e)
  2853.  
  2854.     if persistent == '0':
  2855.       self.reset()
  2856.  
  2857.     code, mesg = resp.split(' ', 1)
  2858.     return self.Response(code, mesg, timing)
  2859.  
  2860. class POP_passd:
  2861.   '''Brute-force poppassd (http://netwinsite.com/poppassd/)'''
  2862.  
  2863.   usage_hints = (
  2864.     '''%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt -x ignore:code=500''',
  2865.     )
  2866.  
  2867.   available_options = (
  2868.     ('host', 'target host'),
  2869.     ('port', 'target port [106]'),
  2870.     ('user', 'usernames to test'),
  2871.     ('password', 'passwords to test'),
  2872.     ('timeout', 'seconds to wait for a response [10]'),
  2873.     )
  2874.   available_actions = ()
  2875.  
  2876.   Response = Response_Base
  2877.  
  2878.   def execute(self, host, port='106', user=None, password=None, timeout='10'):
  2879.  
  2880.     fp = LineReceiver()
  2881.     with Timing() as timing:
  2882.       resp = fp.connect(host, int(port), int(timeout))
  2883.     trace = resp + '\r\n'
  2884.  
  2885.     try:
  2886.       if user is not None or password is not None:
  2887.         with Timing() as timing:
  2888.  
  2889.           if user is not None:
  2890.             cmd = 'USER %s' % user
  2891.             resp = fp.sendcmd(cmd)
  2892.             trace += '%s\r\n%s\r\n' % (cmd, resp)
  2893.  
  2894.           if password is not None:
  2895.             cmd = 'PASS %s' % password
  2896.             resp = fp.sendcmd(cmd)
  2897.             trace += '%s\r\n%s\r\n' % (cmd, resp)
  2898.  
  2899.     except LineReceiver_Error as e:
  2900.       resp = str(e)
  2901.       logger.debug('LineReceiver_Error: %s' % resp)
  2902.       trace += '%s\r\n%s\r\n' % (cmd, resp)
  2903.  
  2904.     finally:
  2905.       fp.close()
  2906.  
  2907.     code, mesg = fp.parse(resp)
  2908.     return self.Response(code, mesg, timing, trace)
  2909.  
  2910. # }}}
  2911.  
  2912. # IMAP {{{
  2913. from imaplib import IMAP4, IMAP4_SSL
  2914. class IMAP_login:
  2915.   '''Brute-force IMAP4'''
  2916.  
  2917.   usage_hints = (
  2918.     '''%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt''',
  2919.     )
  2920.  
  2921.   available_options = (
  2922.     ('host', 'target host'),
  2923.     ('port', 'target port [143]'),
  2924.     ('user', 'usernames to test'),
  2925.     ('password', 'passwords to test'),
  2926.     ('ssl', 'use SSL [0|1]'),
  2927.     )
  2928.   available_actions = ()
  2929.  
  2930.   Response = Response_Base
  2931.  
  2932.   def execute(self, host, port='', ssl='0', user=None, password=None):
  2933.     if ssl == '0':
  2934.       if not port: port = 143
  2935.       klass = IMAP4
  2936.     else:
  2937.       if not port: port = 993
  2938.       klass = IMAP4_SSL
  2939.  
  2940.     with Timing() as timing:
  2941.       fp = klass(host, port)
  2942.  
  2943.     code, resp = 0, fp.welcome
  2944.  
  2945.     try:
  2946.       if user is not None and password is not None:
  2947.         with Timing() as timing:
  2948.           r = fp.login(user, password)
  2949.         resp = ', '.join(r[1])
  2950.  
  2951.       # doesn't it need to self.reset() to test more creds?
  2952.  
  2953.     except IMAP4.error as e:
  2954.       logger.debug('imap_error: %s' % e)
  2955.       code, resp = 1, str(e)
  2956.  
  2957.     return self.Response(code, resp, timing)
  2958.  
  2959. # }}}
  2960.  
  2961. # rlogin {{{
  2962. class Rlogin_login(TCP_Cache):
  2963.   '''Brute-force rlogin'''
  2964.  
  2965.   usage_hints = (
  2966.     """Please note that rlogin requires to bind a socket to an Internet domain privileged port.""",
  2967.     """%prog host=10.0.0.1 user=root luser=FILE0 0=logins.txt persistent=0 -x ignore:fgrep=Password:""",
  2968.     """%prog host=10.0.0.1 user=john password=FILE0 0=passwords.txt -x 'reset:egrep!=Login incorrect.+login:'""",
  2969.     )
  2970.  
  2971.   available_options = (
  2972.     ('host', 'target host'),
  2973.     ('port', 'target port [513]'),
  2974.     ('luser', 'client username [root]'),
  2975.     ('user', 'usernames to test'),
  2976.     ('password', 'passwords to test'),
  2977.     ('prompt_re', 'regular expression to match prompts [\w+:]'),
  2978.     ('timeout', 'seconds to wait for a response and for prompt_re to match received data [10]'),
  2979.     )
  2980.   available_options += TCP_Cache.available_options
  2981.  
  2982.   Response = Response_Base
  2983.  
  2984.   def connect(self, host, port, timeout):
  2985.     fp = Telnet()
  2986.  
  2987.     for i in range(50):
  2988.       try:
  2989.         fp.sock = socket.create_connection((host, int(port)), timeout=int(timeout), source_address=('', 1023 - i))
  2990.         break
  2991.       except socket.error as e:
  2992.         if (e.errno, e.strerror) != (98, 'Address already in use'):
  2993.           raise e
  2994.  
  2995.     self.need_handshake = True
  2996.  
  2997.     return TCP_Connection(fp)
  2998.  
  2999.   def execute(self, host, port='513', luser='root', user='', password=None, prompt_re='\w+:', timeout='10', persistent='1'):
  3000.  
  3001.     fp, _ = self.bind(host, port, timeout=int(timeout))
  3002.  
  3003.     trace = ''
  3004.     timeout = int(timeout)
  3005.  
  3006.     with Timing() as timing:
  3007.       if self.need_handshake:
  3008.         fp.write('\x00%s\x00%s\x00vt100/9600\x00' % (luser, user))
  3009.         self.need_handshake = False
  3010.       else:
  3011.         fp.write('%s\r' % user)
  3012.  
  3013.       _, _, resp = fp.expect([prompt_re], timeout=timeout) # expecting the Password: prompt
  3014.       trace += resp
  3015.  
  3016.       if password is not None:
  3017.         fp.write('%s\r' % password)
  3018.         _, _, resp = fp.expect([prompt_re], timeout=timeout) # expecting the login: prompt
  3019.         trace += resp
  3020.  
  3021.     if persistent == '0':
  3022.       self.reset()
  3023.  
  3024.     mesg = repr(resp.strip())[1:-1]
  3025.     return self.Response(0, mesg, timing, trace)
  3026. # }}}
  3027.  
  3028. # VMauthd {{{
  3029. from ssl import wrap_socket
  3030. class LineReceiver_Error(Exception): pass
  3031. class LineReceiver:
  3032.  
  3033.   def connect(self, host, port, timeout, ssl=False):
  3034.     self.sock = socket.create_connection((host, port), timeout)
  3035.     banner = self.getresp()
  3036.  
  3037.     if ssl:
  3038.       self.sock = wrap_socket(self.sock)
  3039.  
  3040.     return banner # welcome banner
  3041.  
  3042.   def close(self):
  3043.     self.sock.close()
  3044.  
  3045.   def sendcmd(self, cmd):
  3046.     self.sock.sendall(cmd + '\r\n')
  3047.     return self.getresp()
  3048.  
  3049.   def getresp(self):
  3050.     resp = self.sock.recv(1024)
  3051.     while not resp.endswith('\n'):
  3052.       resp += self.sock.recv(1024)
  3053.  
  3054.     resp = resp.rstrip()
  3055.     code, _ = self.parse(resp)
  3056.  
  3057.     if not code.isdigit():
  3058.       raise Exception('Unexpected response: %s' % resp)
  3059.  
  3060.     if code[0] not in ('1', '2', '3'):
  3061.       raise LineReceiver_Error(resp)
  3062.  
  3063.     return resp
  3064.  
  3065.   def parse(self, resp):
  3066.     i = resp.rfind('\n') + 1
  3067.     code = resp[i:i+3]
  3068.     mesg = resp[i+4:]
  3069.  
  3070.     return code, mesg
  3071.  
  3072. class VMauthd_login(TCP_Cache):
  3073.   '''Brute-force VMware Authentication Daemon'''
  3074.  
  3075.   usage_hints = (
  3076.     '''%prog host=10.0.0.1 user=root password=FILE0 0=passwords.txt''',
  3077.     )
  3078.  
  3079.   available_options = (
  3080.     ('host', 'target host'),
  3081.     ('port', 'target port [902]'),
  3082.     ('user', 'usernames to test'),
  3083.     ('password', 'passwords to test'),
  3084.     ('ssl', 'use SSL [1|0]'),
  3085.     ('timeout', 'seconds to wait for a response [10]'),
  3086.     )
  3087.   available_options += TCP_Cache.available_options
  3088.  
  3089.   Response = Response_Base
  3090.  
  3091.   def connect(self, host, port, ssl, timeout):
  3092.     fp = LineReceiver()
  3093.     banner = fp.connect(host, int(port), int(timeout), ssl != '0')
  3094.     return TCP_Connection(fp, banner)
  3095.  
  3096.   def execute(self, host, port='902', user=None, password=None, ssl='1', timeout='10', persistent='1'):
  3097.  
  3098.     with Timing() as timing:
  3099.       fp, resp = self.bind(host, port, ssl, timeout=timeout)
  3100.     trace = resp + '\r\n'
  3101.  
  3102.     try:
  3103.       if user is not None or password is not None:
  3104.         with Timing() as timing:
  3105.  
  3106.           if user is not None:
  3107.             cmd = 'USER %s' % user
  3108.             resp = fp.sendcmd(cmd)
  3109.             trace += '%s\r\n%s\r\n' % (cmd, resp)
  3110.  
  3111.           if password is not None:
  3112.             cmd = 'PASS %s' % password
  3113.             resp = fp.sendcmd(cmd)
  3114.             trace += '%s\r\n%s\r\n' % (cmd, resp)
  3115.  
  3116.     except LineReceiver_Error as e:
  3117.       resp = str(e)
  3118.       logger.debug('LineReceiver_Error: %s' % resp)
  3119.       trace += '%s\r\n%s\r\n' % (cmd, resp)
  3120.  
  3121.     if persistent == '0':
  3122.       self.reset()
  3123.  
  3124.     code, mesg = fp.parse(resp)
  3125.     return self.Response(code, mesg, timing, trace)
  3126.  
  3127. # }}}
  3128.  
  3129. # MySQL {{{
  3130. try:
  3131.   import _mysql
  3132. except ImportError:
  3133.   notfound.append('mysql-python')
  3134.  
  3135. class MySQL_login:
  3136.   '''Brute-force MySQL'''
  3137.  
  3138.   usage_hints = (
  3139.     """%prog host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt -x ignore:fgrep='Access denied for user'""",
  3140.     )
  3141.  
  3142.   available_options = (
  3143.     ('host', 'target host'),
  3144.     ('port', 'target port [3306]'),
  3145.     ('user', 'usernames to test'),
  3146.     ('password', 'passwords to test'),
  3147.     ('timeout', 'seconds to wait for a response [10]'),
  3148.     )
  3149.   available_actions = ()
  3150.  
  3151.   Response = Response_Base
  3152.  
  3153.   def execute(self, host, port='3306', user='anony', password='', timeout='10'):
  3154.  
  3155.     try:
  3156.       with Timing() as timing:
  3157.         fp = _mysql.connect(host=host, port=int(port), user=user, passwd=password, connect_timeout=int(timeout))
  3158.  
  3159.       resp = '0', fp.get_server_info()
  3160.  
  3161.     except _mysql.Error as resp:
  3162.       logger.debug('MysqlError: %s' % resp)
  3163.  
  3164.     code, mesg = resp
  3165.     return self.Response(code, mesg, timing)
  3166.  
  3167. class MySQL_query(TCP_Cache):
  3168.   '''Brute-force MySQL queries'''
  3169.  
  3170.   usage_hints = (
  3171.     '''%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''',
  3172.     )
  3173.  
  3174.   available_options = (
  3175.     ('host', 'target host'),
  3176.     ('port', 'target port [3306]'),
  3177.     ('user', 'username to use'),
  3178.     ('password', 'password to use'),
  3179.     ('query', 'SQL query to execute'),
  3180.     )
  3181.  
  3182.   available_actions = ()
  3183.  
  3184.   Response = Response_Base
  3185.  
  3186.   def connect(self, host, port, user, password):
  3187.     fp = _mysql.connect(host=host, port=int(port), user=user, passwd=password) # db=db
  3188.     return TCP_Connection(fp)
  3189.  
  3190.   def execute(self, host, port='3306', user='', password='', query='select @@version'):
  3191.  
  3192.     fp, _ = self.bind(host, port, user, password)
  3193.  
  3194.     with Timing() as timing:
  3195.       fp.query(query)
  3196.  
  3197.     rs = fp.store_result()
  3198.     rows = rs.fetch_row(10, 0)
  3199.  
  3200.     code, mesg = '0', '\n'.join(', '.join(map(str, r)) for r in filter(any, rows))
  3201.     return self.Response(code, mesg, timing)
  3202.  
  3203. # }}}
  3204.  
  3205. # MSSQL {{{
  3206. # I did not use pymssql because neither version 1.x nor 2.0.0b1_dev were multithreads safe (they all segfault)
  3207. try:
  3208.   from impacket import tds
  3209.   from impacket.tds import TDS_ERROR_TOKEN, TDS_LOGINACK_TOKEN
  3210. except ImportError:
  3211.   notfound.append('impacket')
  3212. class MSSQL_login:
  3213.   '''Brute-force MSSQL'''
  3214.  
  3215.   usage_hints = (
  3216.     """%prog host=10.0.0.1 user=sa password=FILE0 0=passwords.txt -x ignore:fgrep='Login failed for user'""",
  3217.     )
  3218.  
  3219.   available_options = (
  3220.     ('host', 'target host'),
  3221.     ('port', 'target port [1433]'),
  3222.     ('user', 'usernames to test'),
  3223.     ('password', 'passwords to test'),
  3224.     ('windows_auth', 'use Windows auth [0|1]'),
  3225.     ('domain', 'domain to test []'),
  3226.     ('password_hash', "LM/NT hashes to test ('lm:nt' or ':nt')"),
  3227.     #('timeout', 'seconds to wait for a response [10]'),
  3228.     )
  3229.   available_actions = ()
  3230.  
  3231.   Response = Response_Base
  3232.  
  3233.   def execute(self, host, port='1433', user='', password='', windows_auth='0', domain='', password_hash=None): #, timeout='10'):
  3234.  
  3235.     fp = tds.MSSQL(host, int(port))
  3236.     fp.connect()
  3237.  
  3238.     with Timing() as timing:
  3239.       if windows_auth == '0':
  3240.         r = fp.login(None, user, password, None, None, False)
  3241.       else:
  3242.         r = fp.login(None, user, password, domain, password_hash, True)
  3243.  
  3244.     if not r:
  3245.       key = fp.replies[TDS_ERROR_TOKEN][0]
  3246.  
  3247.       code = key['Number']
  3248.       mesg = key['MsgText'].decode('utf-16le')
  3249.  
  3250.     else:
  3251.       key = fp.replies[TDS_LOGINACK_TOKEN][0]
  3252.  
  3253.       code = '0'
  3254.       mesg = '%s (%d%d %d%d)' % (key['ProgName'].decode('utf-16le'), key['MajorVer'], key['MinorVer'], key['BuildNumHi'], key['BuildNumLow'])
  3255.  
  3256.     fp.disconnect()
  3257.  
  3258.     return self.Response(code, mesg, timing)
  3259. # }}}
  3260.  
  3261. # Oracle {{{
  3262. try:
  3263.   import cx_Oracle
  3264. except ImportError:
  3265.   notfound.append('cx_Oracle')
  3266.  
  3267. class Response_Oracle(Response_Base):
  3268.   indicatorsfmt = [('code', -9), ('size', -4), ('time', 6)]
  3269.  
  3270. class Oracle_login:
  3271.   '''Brute-force Oracle'''
  3272.  
  3273.   usage_hints = (
  3274.     """%prog host=10.0.0.1 sid=FILE0 0=sids.txt -x ignore:code=ORA-12505""",
  3275.     """%prog host=10.0.0.1 user=SYS password=FILE0 0=passwords.txt -x ignore:code=ORA-01017""",
  3276.     )
  3277.  
  3278.   available_options = (
  3279.     ('host', 'hostnames or subnets to target'),
  3280.     ('port', 'ports to target [1521]'),
  3281.     ('user', 'usernames to test'),
  3282.     ('password', 'passwords to test'),
  3283.     ('sid', 'sid to test'),
  3284.     ('service_name', 'service name to test'),
  3285.     )
  3286.   available_actions = ()
  3287.  
  3288.   Response = Response_Oracle
  3289.  
  3290.   def execute(self, host, port='1521', user='', password='', sid='', service_name=''):
  3291.  
  3292.     if sid:
  3293.       dsn = cx_Oracle.makedsn(host=host, port=port, sid=sid)
  3294.     elif service_name:
  3295.       dsn = cx_Oracle.makedsn(host=host, port=port, service_name=service_name)
  3296.     else:
  3297.       raise NotImplementedError("Options sid and service_name cannot be both empty")
  3298.  
  3299.     try:
  3300.       with Timing() as timing:
  3301.         fp = cx_Oracle.connect(user, password, dsn, threaded=True)
  3302.  
  3303.       code, mesg = '0', fp.version
  3304.  
  3305.     except cx_Oracle.DatabaseError as e:
  3306.       code, mesg = e.args[0].message[:-1].split(': ', 1)
  3307.  
  3308.     return self.Response(code, mesg, timing)
  3309.  
  3310. # }}}
  3311.  
  3312. # PostgreSQL {{{
  3313. try:
  3314.   import psycopg2
  3315. except ImportError:
  3316.   notfound.append('psycopg')
  3317.  
  3318. class Pgsql_login:
  3319.   '''Brute-force PostgreSQL'''
  3320.  
  3321.   usage_hints = (
  3322.     """%prog host=10.0.0.1 user=postgres password=FILE0 0=passwords.txt -x ignore:fgrep='password authentication failed for user'""",
  3323.     )
  3324.  
  3325.   available_options = (
  3326.     ('host', 'target host'),
  3327.     ('port', 'target port [5432]'),
  3328.     ('user', 'usernames to test'),
  3329.     ('password', 'passwords to test'),
  3330.     ('database', 'databases to test [postgres]'),
  3331.     ('timeout', 'seconds to wait for a response [10]'),
  3332.     )
  3333.   available_actions = ()
  3334.  
  3335.   Response = Response_Base
  3336.  
  3337.   def execute(self, host, port='5432', user=None, password=None, database='postgres', ssl='disable', timeout='10'):
  3338.  
  3339.     try:
  3340.       with Timing() as timing:
  3341.         psycopg2.connect(host=host, port=int(port), user=user, password=password, database=database, sslmode=ssl, connect_timeout=int(timeout))
  3342.  
  3343.       code, mesg = '0', 'OK'
  3344.  
  3345.     except psycopg2.OperationalError as e:
  3346.       logger.debug('OperationalError: %s' % e)
  3347.       code, mesg = '1', str(e)[:-1]
  3348.  
  3349.     return self.Response(code, mesg, timing)
  3350.  
  3351. # }}}
  3352.  
  3353. # HTTP {{{
  3354. try:
  3355.   import pycurl
  3356. except ImportError:
  3357.   notfound.append('pycurl')
  3358.  
  3359. class Response_HTTP(Response_Base):
  3360.  
  3361.   indicatorsfmt = [('code', -4), ('size:clen', -13), ('time', 6)]
  3362.  
  3363.   def __init__(self, code, response, timing=0, trace=None, content_length=-1, target={}):
  3364.     Response_Base.__init__(self, code, response, timing, trace=trace)
  3365.     self.content_length = content_length
  3366.     self.target = target
  3367.  
  3368.   def indicators(self):
  3369.     return self.code, '%d:%d' % (self.size, self.content_length), '%.3f' % self.time
  3370.  
  3371.   def __str__(self):
  3372.     lines = re.findall('^(HTTP/.+)$', self.mesg, re.M)
  3373.     if lines:
  3374.       return lines[-1].rstrip('\r')
  3375.     else:
  3376.       return self.mesg
  3377.  
  3378.   def match_clen(self, val):
  3379.     return match_range(self.content_length, val)
  3380.  
  3381.   def match_fgrep(self, val):
  3382.     return val in self.mesg
  3383.  
  3384.   def match_egrep(self, val):
  3385.     return re.search(val, self.mesg, re.M)
  3386.  
  3387.   def str_target(self):
  3388.     return ' '.join('%s=%s' % (k, xmlquoteattr(str(v))) for k, v in self.target.iteritems())
  3389.  
  3390.   available_conditions = Response_Base.available_conditions
  3391.   available_conditions += (
  3392.     ('clen', 'match Content-Length header (N or N-M or N- or -N)'),
  3393.     )
  3394.  
  3395. class HTTP_fuzz(TCP_Cache):
  3396.   '''Brute-force HTTP'''
  3397.  
  3398.   usage_hints = [
  3399.     """%prog url=http://10.0.0.1/FILE0 0=paths.txt -x ignore:code=404 -x ignore,retry:code=500""",
  3400.  
  3401.     """%prog url=http://10.0.0.1/manager/html user_pass=COMBO00:COMBO01 0=combos.txt"""
  3402.     """ -x ignore:code=401""",
  3403.  
  3404.     """%prog url=http://10.0.0.1/phpmyadmin/index.php method=POST"""
  3405.     """ body='pma_username=root&pma_password=FILE0&server=1&lang=en' 0=passwords.txt follow=1"""
  3406.     """ accept_cookie=1 -x ignore:fgrep='Cannot log in to the MySQL server'""",
  3407.     ]
  3408.  
  3409.   available_options = (
  3410.     ('url', 'target url (scheme://host[:port]/path?query)'),
  3411.     #('host', 'target host'),
  3412.     #('port', 'target port'),
  3413.     #('scheme', 'scheme [http | https]'),
  3414.     #('path', 'web path [/]'),
  3415.     #('query', 'query string'),
  3416.     ('body', 'body data'),
  3417.     ('header', 'use custom headers'),
  3418.     ('method', 'method to use [GET | POST | HEAD | ...]'),
  3419.     ('auto_urlencode', 'automatically perform URL-encoding [1|0]'),
  3420.     ('user_pass', 'username and password for HTTP authentication (user:pass)'),
  3421.     ('auth_type', 'type of HTTP authentication [basic | digest | ntlm]'),
  3422.     ('follow', 'follow any Location redirect [0|1]'),
  3423.     ('max_follow', 'redirection limit [5]'),
  3424.     ('accept_cookie', 'save received cookies to issue them in future requests [0|1]'),
  3425.     ('http_proxy', 'HTTP proxy to use (host:port)'),
  3426.     ('ssl_cert', 'client SSL certificate file (cert+key in PEM format)'),
  3427.     ('timeout_tcp', 'seconds to wait for a TCP handshake [10]'),
  3428.     ('timeout', 'seconds to wait for a HTTP response [20]'),
  3429.     ('before_urls', 'comma-separated URLs to query before the main request'),
  3430.     ('before_header', 'use a custom header in the before_urls request'),
  3431.     ('before_egrep', 'extract data from the before_urls response to place in the main request'),
  3432.     ('after_urls', 'comma-separated URLs to query after the main request'),
  3433.     ('max_mem', 'store no more than N bytes of request+response data in memory [-1 (unlimited)]'),
  3434.     )
  3435.   available_options += TCP_Cache.available_options
  3436.  
  3437.   Response = Response_HTTP
  3438.  
  3439.   def connect(self, host, port, scheme):
  3440.     fp = pycurl.Curl()
  3441.     fp.setopt(pycurl.SSL_VERIFYPEER, 0)
  3442.     fp.setopt(pycurl.SSL_VERIFYHOST, 0)
  3443.     fp.setopt(pycurl.HEADER, 1)
  3444.     fp.setopt(pycurl.USERAGENT, 'Mozilla/5.0')
  3445.     fp.setopt(pycurl.NOSIGNAL, 1)
  3446.  
  3447.     return TCP_Connection(fp)
  3448.  
  3449.   def execute(self, url=None, host=None, port='', scheme='http', path='/', params='', query='', fragment='', body='',
  3450.     header='', method='GET', auto_urlencode='1', user_pass='', auth_type='basic',
  3451.     follow='0', max_follow='5', accept_cookie='0', http_proxy='', ssl_cert='', timeout_tcp='10', timeout='20', persistent='1',
  3452.     before_urls='', before_header='', before_egrep='', after_urls='', max_mem='-1'):
  3453.  
  3454.     if url:
  3455.       scheme, host, path, params, query, fragment = urlparse(url)
  3456.       if ':' in host:
  3457.         host, port = host.split(':')
  3458.       del url
  3459.  
  3460.     fp, _ = self.bind(host, port, scheme)
  3461.  
  3462.     fp.setopt(pycurl.FOLLOWLOCATION, int(follow))
  3463.     fp.setopt(pycurl.MAXREDIRS, int(max_follow))
  3464.     fp.setopt(pycurl.CONNECTTIMEOUT, int(timeout_tcp))
  3465.     fp.setopt(pycurl.TIMEOUT, int(timeout))
  3466.     fp.setopt(pycurl.PROXY, http_proxy)
  3467.  
  3468.     def noop(buf): pass
  3469.     fp.setopt(pycurl.WRITEFUNCTION, noop)
  3470.  
  3471.     def debug_func(t, s):
  3472.       if max_mem > 0 and trace.tell() > max_mem:
  3473.         return 0
  3474.  
  3475.       if t in (pycurl.INFOTYPE_HEADER_OUT, pycurl.INFOTYPE_DATA_OUT):
  3476.         trace.write(s)
  3477.  
  3478.       elif t in (pycurl.INFOTYPE_HEADER_IN, pycurl.INFOTYPE_DATA_IN):
  3479.         trace.write(s)
  3480.         response.write(s)
  3481.  
  3482.     max_mem = int(max_mem)
  3483.     response, trace = StringIO(), StringIO()
  3484.  
  3485.     fp.setopt(pycurl.DEBUGFUNCTION, debug_func)
  3486.     fp.setopt(pycurl.VERBOSE, 1)
  3487.  
  3488.     if user_pass:
  3489.       fp.setopt(pycurl.USERPWD, user_pass)
  3490.       if auth_type == 'basic':
  3491.         fp.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
  3492.       elif auth_type == 'digest':
  3493.         fp.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST)
  3494.       elif auth_type == 'ntlm':
  3495.         fp.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_NTLM)
  3496.       else:
  3497.         raise NotImplementedError("Incorrect auth_type '%s'" % auth_type)
  3498.  
  3499.     if ssl_cert:
  3500.       fp.setopt(pycurl.SSLCERT, ssl_cert)
  3501.  
  3502.     if accept_cookie == '1':
  3503.       fp.setopt(pycurl.COOKIEFILE, '')
  3504.       # warning: do not pass a Cookie: header into HTTPHEADER if using COOKIEFILE as it will
  3505.       # produce requests with more than one Cookie: header
  3506.       # and the server will process only one of them (eg. Apache only reads the last one)
  3507.  
  3508.     def perform_fp(fp, method, url, header='', body=''):
  3509.       #logger.debug('perform: %s' % url)
  3510.       fp.setopt(pycurl.URL, url)
  3511.  
  3512.       if method == 'GET':
  3513.         fp.setopt(pycurl.HTTPGET, 1)
  3514.  
  3515.       elif method == 'POST':
  3516.         fp.setopt(pycurl.POST, 1)
  3517.         fp.setopt(pycurl.POSTFIELDS, body)
  3518.  
  3519.       elif method == 'HEAD':
  3520.         fp.setopt(pycurl.NOBODY, 1)
  3521.  
  3522.       else:
  3523.         fp.setopt(pycurl.CUSTOMREQUEST, method)
  3524.  
  3525.       headers = [h.strip('\r') for h in header.split('\n') if h]
  3526.       fp.setopt(pycurl.HTTPHEADER, headers)
  3527.  
  3528.       fp.perform()
  3529.  
  3530.     if before_urls:
  3531.       for before_url in before_urls.split(','):
  3532.         perform_fp(fp, 'GET', before_url, before_header)
  3533.  
  3534.       if before_egrep:
  3535.         for be in before_egrep.split('|'):
  3536.           mark, regex = be.split(':', 1)
  3537.           val = re.search(regex, response.getvalue(), re.M).group(1)
  3538.  
  3539.           header = header.replace(mark, val)
  3540.           query = query.replace(mark, val)
  3541.           body = body.replace(mark, val)
  3542.  
  3543.     if auto_urlencode == '1':
  3544.       path = quote(path)
  3545.       query = urlencode(parse_qsl(query, True))
  3546.       body = urlencode(parse_qsl(body, True))
  3547.  
  3548.     if port:
  3549.       host = '%s:%s' % (host, port)
  3550.  
  3551.     url = urlunparse((scheme, host, path, params, query, fragment))
  3552.     perform_fp(fp, method, url, header, body)
  3553.  
  3554.     target = {}
  3555.     target['ip'] = fp.getinfo(pycurl.PRIMARY_IP)
  3556.     target['port'] = fp.getinfo(pycurl.PRIMARY_PORT)
  3557.     target['hostname'] = host
  3558.  
  3559.     for h in header.split('\n'):
  3560.       if ': ' in h:
  3561.         k, v = h.split(': ', 1)
  3562.         if k.lower() == 'host':
  3563.           target['vhost'] = v.rstrip('\r')
  3564.           break
  3565.  
  3566.     if after_urls:
  3567.       for after_url in after_urls.split(','):
  3568.         perform_fp(fp, 'GET', after_url)
  3569.  
  3570.     http_code = fp.getinfo(pycurl.HTTP_CODE)
  3571.     content_length = fp.getinfo(pycurl.CONTENT_LENGTH_DOWNLOAD)
  3572.     response_time = fp.getinfo(pycurl.TOTAL_TIME) - fp.getinfo(pycurl.PRETRANSFER_TIME)
  3573.  
  3574.     if persistent == '0':
  3575.       self.reset()
  3576.  
  3577.     return self.Response(http_code, response.getvalue(), response_time, trace.getvalue(), content_length, target)
  3578.  
  3579. # }}}
  3580.  
  3581. # {{{ RDP
  3582. if not which('xfreerdp'):
  3583.   notfound.append('xfreerdp')
  3584.  
  3585. class RDP_login:
  3586.   '''Brute-force RDP (NLA)'''
  3587.  
  3588.   usage_hints = (
  3589.     """%prog host=10.0.0.1 user='administrator' password=FILE0 0=passwords.txt""",
  3590.     )
  3591.  
  3592.   available_options = (
  3593.     ('host', 'target host'),
  3594.     ('port', 'target port [3389]'),
  3595.     ('user', 'usernames to test'),
  3596.     ('password', 'passwords to test'),
  3597.     )
  3598.   available_actions = ()
  3599.  
  3600.   Response = Response_Base
  3601.  
  3602.   def execute(self, host, port='3389', user=None, password=None):
  3603.  
  3604.     cmd = ['xfreerdp', '/v:%s:%d' % (host, int(port)), '/u:%s' % user, '/p:%s' % password, '/cert-ignore', '+auth-only', '/sec:nla']
  3605.  
  3606.     with Timing() as timing:
  3607.       p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  3608.       out, err = p.communicate()
  3609.       code = p.returncode
  3610.  
  3611.     err = err.replace('''Authentication only. Don't connect to X.
  3612. credssp_recv() error: -1
  3613. freerdp_set_last_error 0x20009\n''', '')
  3614.     err = err.replace(''', check credentials.
  3615. If credentials are valid, the NTLMSSP implementation may be to blame.
  3616. Error: protocol security negotiation or connection failure
  3617. Authentication only, exit status 1
  3618. Authentication only, exit status 1''', '')
  3619.     err = err.replace('''Authentication only. Don't connect to X.
  3620. Authentication only, exit status 0
  3621. Authentication only, exit status 0''', 'OK')
  3622.  
  3623.     mesg = repr((out + err).strip())[1:-1]
  3624.     trace = '[out]\n%s\n[err]\n%s' % (out, err)
  3625.  
  3626.     return self.Response(code, mesg, timing, trace)
  3627. # }}}
  3628.  
  3629. # VNC {{{
  3630. try:
  3631.   from Crypto.Cipher import DES
  3632. except ImportError:
  3633.   notfound.append('pycrypto')
  3634.  
  3635. class VNC_Error(Exception): pass
  3636. class VNC:
  3637.   def connect(self, host, port, timeout):
  3638.     self.fp = socket.create_connection((host, port), timeout=timeout)
  3639.     resp = self.fp.recv(99) # banner
  3640.  
  3641.     logger.debug('banner: %s' % repr(resp))
  3642.     self.version = resp[:11].decode('ascii')
  3643.  
  3644.     if len(resp) > 12:
  3645.       raise VNC_Error('%s %s' % (self.version, resp[12:].decode('ascii', 'ignore')))
  3646.  
  3647.     return self.version
  3648.  
  3649.   def login(self, password):
  3650.     logger.debug('Remote version: %s' % self.version)
  3651.     major, minor = self.version[6], self.version[10]
  3652.  
  3653.     if (major, minor) in [('3', '8'), ('4', '1')]:
  3654.       proto = b'RFB 003.008\n'
  3655.  
  3656.     elif (major, minor) == ('3', '7'):
  3657.       proto = b'RFB 003.007\n'
  3658.  
  3659.     else:
  3660.       proto = b'RFB 003.003\n'
  3661.  
  3662.     logger.debug('Client version: %s' % proto[:-1])
  3663.     self.fp.sendall(proto)
  3664.  
  3665.     sleep(0.5)
  3666.  
  3667.     resp = self.fp.recv(99)
  3668.     logger.debug('Security types supported: %s' % repr(resp))
  3669.  
  3670.     if major == '4' or (major == '3' and int(minor) >= 7):
  3671.       code = ord(resp[0:1])
  3672.       if code == 0:
  3673.         raise VNC_Error('Session setup failed: %s' % resp.decode('ascii', 'ignore'))
  3674.  
  3675.       self.fp.sendall(b'\x02') # always use classic VNC authentication
  3676.       resp = self.fp.recv(99)
  3677.  
  3678.     else: # minor == '3':
  3679.       code = ord(resp[3:4])
  3680.       if code != 2:
  3681.         raise VNC_Error('Session setup failed: %s' % resp.decode('ascii', 'ignore'))
  3682.  
  3683.       resp = resp[-16:]
  3684.  
  3685.     if len(resp) != 16:
  3686.       raise VNC_Error('Unexpected challenge size (No authentication required? Unsupported authentication type?)')
  3687.  
  3688.     logger.debug('challenge: %s' % repr(resp))
  3689.     pw = password.ljust(8, '\x00')[:8] # make sure it is 8 chars long, zero padded
  3690.  
  3691.     key = self.gen_key(pw)
  3692.     logger.debug('key: %s' % repr(key))
  3693.  
  3694.     des = DES.new(key, DES.MODE_ECB)
  3695.     enc = des.encrypt(resp)
  3696.  
  3697.     logger.debug('enc: %s' % repr(enc))
  3698.     self.fp.sendall(enc)
  3699.  
  3700.     resp = self.fp.recv(99)
  3701.     logger.debug('resp: %s' % repr(resp))
  3702.  
  3703.     code = ord(resp[3:4])
  3704.     mesg = resp[8:].decode('ascii', 'ignore')
  3705.  
  3706.     if code == 1:
  3707.       return code, mesg or 'Authentication failure'
  3708.  
  3709.     elif code == 0:
  3710.       return code, mesg or 'OK'
  3711.  
  3712.     else:
  3713.       raise VNC_Error('Unknown response: %s (code: %s)' % (repr(resp), code))
  3714.  
  3715.  
  3716.   def gen_key(self, key):
  3717.     newkey = []
  3718.     for ki in range(len(key)):
  3719.       bsrc = ord(key[ki])
  3720.       btgt = 0
  3721.       for i in range(8):
  3722.         if bsrc & (1 << i):
  3723.           btgt = btgt | (1 << 7-i)
  3724.       newkey.append(btgt)
  3725.  
  3726.     if sys.version_info[0] == 2:
  3727.       return ''.join(chr(c) for c in newkey)
  3728.     else:
  3729.       return bytes(newkey)
  3730.  
  3731.  
  3732. class VNC_login:
  3733.   '''Brute-force VNC'''
  3734.  
  3735.   usage_hints = (
  3736.     """%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""",
  3737.     )
  3738.  
  3739.   available_options = (
  3740.     ('host', 'target host'),
  3741.     ('port', 'target port [5900]'),
  3742.     ('password', 'passwords to test'),
  3743.     ('timeout', 'seconds to wait for a response [10]'),
  3744.     )
  3745.   available_actions = ()
  3746.  
  3747.   Response = Response_Base
  3748.  
  3749.   def execute(self, host, port=None, password=None, timeout='10'):
  3750.     v = VNC()
  3751.  
  3752.     try:
  3753.       with Timing() as timing:
  3754.         code, mesg = 0, v.connect(host, int(port or 5900), int(timeout))
  3755.  
  3756.       if password is not None:
  3757.         with Timing() as timing:
  3758.           code, mesg = v.login(password)
  3759.  
  3760.     except VNC_Error as e:
  3761.       logger.debug('VNC_Error: %s' % e)
  3762.       code, mesg = 2, str(e)
  3763.  
  3764.     return self.Response(code, mesg, timing)
  3765.  
  3766. # }}}
  3767.  
  3768. # DNS {{{
  3769.  
  3770. try:
  3771.   import dns.rdatatype
  3772.   import dns.message
  3773.   import dns.query
  3774.   import dns.reversename
  3775. except ImportError:
  3776.   notfound.append('dnspython')
  3777.  
  3778. def dns_query(server, timeout, protocol, qname, qtype, qclass):
  3779.   request = dns.message.make_query(qname, qtype, qclass)
  3780.  
  3781.   if protocol == 'tcp':
  3782.     response = dns.query.tcp(request, server, timeout=timeout, one_rr_per_rrset=True)
  3783.  
  3784.   else:
  3785.     response = dns.query.udp(request, server, timeout=timeout, one_rr_per_rrset=True)
  3786.  
  3787.     if response.flags & dns.flags.TC:
  3788.       response = dns.query.tcp(request, server, timeout=timeout, one_rr_per_rrset=True)
  3789.  
  3790.   return response
  3791.  
  3792. def generate_tld():
  3793.   # NB. does not return an exhaustive list (ie. missing co.uk, co.nz etc.)
  3794.  
  3795.   from itertools import product
  3796.   from string import ascii_lowercase
  3797.  
  3798.   # http://data.iana.org/TLD/tlds-alpha-by-domain.txt
  3799.   gtld = ['academy', 'actor', 'aero', 'agency', 'archi', 'arpa', 'asia', 'axa',
  3800.     'bar', 'bargains', 'berlin', 'best', 'bid', 'bike', 'biz', 'black', 'blue',
  3801.     'boutique', 'build', 'builders', 'buzz', 'cab', 'camera', 'camp', 'cards',
  3802.     'careers', 'cat', 'catering', 'center', 'ceo', 'cheap', 'christmas',
  3803.     'cleaning', 'clothing', 'club', 'codes', 'coffee', 'cologne', 'com',
  3804.     'community', 'company', 'computer', 'condos', 'construction', 'contractors',
  3805.     'cooking', 'cool', 'coop', 'country', 'cruises', 'dance', 'dating', 'democrat',
  3806.     'diamonds', 'directory', 'dnp', 'domains', 'edu', 'education', 'email',
  3807.     'enterprises', 'equipment', 'estate', 'events', 'expert', 'exposed', 'farm',
  3808.     'fish', 'fishing', 'flights', 'florist', 'foundation', 'futbol', 'gallery',
  3809.     'gift', 'glass', 'gov', 'graphics', 'guitars', 'guru', 'haus', 'holdings',
  3810.     'holiday', 'horse', 'house', 'immobilien', 'industries', 'info', 'ink',
  3811.     'institute', 'int', 'international', 'jetzt', 'jobs', 'kaufen', 'kim',
  3812.     'kitchen', 'kiwi', 'koeln', 'kred', 'land', 'lighting', 'limo', 'link',
  3813.     'london', 'luxury', 'maison', 'management', 'mango', 'marketing', 'meet',
  3814.     'menu', 'miami', 'mil', 'mobi', 'moda', 'moe', 'monash', 'museum', 'nagoya',
  3815.     'name', 'net', 'neustar', 'ninja', 'nyc', 'okinawa', 'onl', 'org', 'partners',
  3816.     'parts', 'photo', 'photography', 'photos', 'pics', 'pink', 'plumbing', 'post',
  3817.     'pro', 'productions', 'properties', 'pub', 'qpon', 'recipes', 'red', 'ren',
  3818.     'rentals', 'repair', 'report', 'reviews', 'rich', 'rodeo', 'ruhr', 'sexy',
  3819.     'shiksha', 'shoes', 'singles', 'social', 'sohu', 'solar', 'solutions',
  3820.     'supplies', 'supply', 'support', 'systems', 'tattoo', 'technology', 'tel',
  3821.     'tienda', 'tips', 'today', 'tokyo', 'tools', 'trade', 'training', 'travel',
  3822.     'uno', 'vacations', 'vegas', 'ventures', 'viajes', 'villas', 'vision', 'vodka',
  3823.     'vote', 'voting', 'voto', 'voyage', 'wang', 'watch', 'webcam', 'wed', 'wien',
  3824.     'wiki', 'works', 'xn--3bst00m', 'xn--3ds443g', 'xn--3e0b707e', 'xn--45brj9c',
  3825.     'xn--55qw42g', 'xn--55qx5d', 'xn--6frz82g', 'xn--6qq986b3xl', 'xn--80ao21a',
  3826.     'xn--80asehdb', 'xn--80aswg', 'xn--90a3ac', 'xn--c1avg', 'xn--cg4bki',
  3827.     'xn--clchc0ea0b2g2a9gcd', 'xn--czru2d', 'xn--d1acj3b', 'xn--fiq228c5hs',
  3828.     'xn--fiq64b', 'xn--fiqs8s', 'xn--fiqz9s', 'xn--fpcrj9c3d', 'xn--fzc2c9e2c',
  3829.     'xn--gecrj9c', 'xn--h2brj9c', 'xn--i1b6b1a6a2e', 'xn--io0a7i', 'xn--j1amh',
  3830.     'xn--j6w193g', 'xn--kprw13d', 'xn--kpry57d', 'xn--l1acc', 'xn--lgbbat1ad8j',
  3831.     'xn--mgb9awbf', 'xn--mgba3a4f16a', 'xn--mgbaam7a8h', 'xn--mgbab2bd',
  3832.     'xn--mgbayh7gpa', 'xn--mgbbh1a71e', 'xn--mgbc0a9azcg', 'xn--mgberp4a5d4ar',
  3833.     'xn--mgbx4cd0ab', 'xn--ngbc5azd', 'xn--nqv7f', 'xn--nqv7fs00ema', 'xn--o3cw4h',
  3834.     'xn--ogbpf8fl', 'xn--p1ai', 'xn--pgbs0dh', 'xn--q9jyb4c', 'xn--rhqv96g',
  3835.     'xn--s9brj9c', 'xn--unup4y', 'xn--wgbh1c', 'xn--wgbl6a', 'xn--xkc2al3hye2a',
  3836.     'xn--xkc2dl3a5ee0h', 'xn--yfro4i67o', 'xn--ygbi2ammx', 'xn--zfr164b', 'xxx',
  3837.     'xyz', 'zone']
  3838.  
  3839.   cctld = [''.join(i) for i in product(*[ascii_lowercase]*2)]
  3840.  
  3841.   tld = gtld + cctld
  3842.   return tld, len(tld)
  3843.  
  3844. def generate_srv():
  3845.   common = [
  3846.     '_gc._tcp', '_kerberos._tcp', '_kerberos._udp', '_ldap._tcp',
  3847.     '_test._tcp', '_sips._tcp', '_sip._udp', '_sip._tcp', '_aix._tcp', '_aix._udp',
  3848.     '_finger._tcp', '_ftp._tcp', '_http._tcp', '_nntp._tcp', '_telnet._tcp',
  3849.     '_whois._tcp', '_h323cs._tcp', '_h323cs._udp', '_h323be._tcp', '_h323be._udp',
  3850.     '_h323ls._tcp', '_h323ls._udp', '_sipinternal._tcp', '_sipinternaltls._tcp',
  3851.     '_sip._tls', '_sipfederationtls._tcp', '_jabber._tcp', '_xmpp-server._tcp', '_xmpp-client._tcp',
  3852.     '_imap.tcp', '_certificates._tcp', '_crls._tcp', '_pgpkeys._tcp', '_pgprevokations._tcp',
  3853.     '_cmp._tcp', '_svcp._tcp', '_crl._tcp', '_ocsp._tcp', '_PKIXREP._tcp',
  3854.     '_smtp._tcp', '_hkp._tcp', '_hkps._tcp', '_jabber._udp', '_xmpp-server._udp',
  3855.     '_xmpp-client._udp', '_jabber-client._tcp', '_jabber-client._udp',
  3856.     '_adsp._domainkey', '_policy._domainkey', '_domainkey', '_ldap._tcp.dc._msdcs', '_ldap._udp.dc._msdcs']
  3857.  
  3858.   def distro():
  3859.     import os
  3860.     import re
  3861.     files = ['/usr/share/nmap/nmap-protocols', '/usr/share/nmap/nmap-services', '/etc/protocols', '/etc/services']
  3862.     ret = []
  3863.     for f in files:
  3864.       if not os.path.isfile(f):
  3865.         logger.warn("File '%s' is missing, there will be less records to test" % f)
  3866.         continue
  3867.       for line in open(f):
  3868.         match = re.match(r'([a-zA-Z0-9]+)\s', line)
  3869.         if not match: continue
  3870.         for w in re.split(r'[^a-z0-9]', match.group(1).strip().lower()):
  3871.           ret.extend(['_%s.%s' % (w, i) for i in ('_tcp', '_udp')])
  3872.     return ret
  3873.  
  3874.   srv = set(common + distro())
  3875.   return srv, len(srv)
  3876.  
  3877. class HostInfo:
  3878.   def __init__(self):
  3879.     self.name = set()
  3880.     self.ip = set()
  3881.     self.alias = set()
  3882.  
  3883.   def __str__(self):
  3884.     line = ''
  3885.     if self.name:
  3886.       line = ' '.join(self.name)
  3887.     if self.ip:
  3888.       if line: line += ' / '
  3889.       line += ' '.join(map(str, self.ip))
  3890.     if self.alias:
  3891.       if line: line += ' / '
  3892.       line += ' '.join(self.alias)
  3893.  
  3894.     return line
  3895.  
  3896. class Controller_DNS(Controller):
  3897.   records = defaultdict(list)
  3898.   hostmap = defaultdict(HostInfo)
  3899.  
  3900.   # show_final {{{
  3901.   def show_final(self):
  3902.     ''' Expected output:
  3903.     Records -----
  3904.           ftp.example.com.   IN A       10.0.1.1
  3905.           www.example.com.   IN A       10.0.1.1
  3906.          prod.example.com.   IN CNAME   www.example.com.
  3907.          ipv6.example.com.   IN AAAA    dead:beef::
  3908.           dev.example.com.   IN A       10.0.1.2
  3909.           svn.example.com.   IN A       10.0.2.1
  3910.       websrv1.example.com.   IN CNAME   prod.example.com.
  3911.          blog.example.com.   IN CNAME   example.wordpress.com.
  3912.     '''
  3913.     print('Records ' + '-'*42)
  3914.     for name, infos in sorted(self.records.items()):
  3915.       for qclass, qtype, rdata in infos:
  3916.         print('%34s %4s %-7s %s' % (name, qclass, qtype, rdata))
  3917.  
  3918.     ''' Expected output:
  3919.     Hostmap ------
  3920.            ipv6.example.com dead:beef::
  3921.             ftp.example.com 10.0.1.1
  3922.             www.example.com 10.0.1.1
  3923.            prod.example.com
  3924.         websrv1.example.com
  3925.             dev.example.com 10.0.1.2
  3926.             svn.example.com 10.0.2.1
  3927.       example.wordpress.com ?
  3928.            blog.example.com
  3929.     Domains ---------------------------
  3930.                 example.com 8
  3931.     Networks --------------------------
  3932.                            dead:beef::
  3933.                               10.0.1.x
  3934.                               10.0.2.1
  3935.     '''
  3936.     ipmap = defaultdict(HostInfo)
  3937.     noips = defaultdict(list)
  3938.  
  3939.     '''
  3940.     hostmap = {
  3941.        'www.example.com': {'ip': ['10.0.1.1'], 'alias': ['prod.example.com']},
  3942.        'ftp.example.com': {'ip': ['10.0.1.1'], 'alias': []},
  3943.        'prod.example.com': {'ip': [], 'alias': ['websrv1.example.com']},
  3944.        'ipv6.example.com': {'ip': ['dead:beef::'], 'alias': []},
  3945.        'dev.example.com': {'ip': ['10.0.1.2'], 'alias': []},
  3946.        'example.wordpress.com': {'ip': [], 'alias': ['blog.example.com']},
  3947.  
  3948.     ipmap = {'10.0.1.1': {'name': ['www.example.com', 'ftp.example.com'], 'alias': ['prod.example.com', 'websrv1.example.com']}, ...
  3949.     noips = {'example.wordpress.com': ['blog.example.com'],
  3950.     '''
  3951.  
  3952.     for name, hinfo in self.hostmap.items():
  3953.       for ip in hinfo.ip:
  3954.         ip = IP(ip)
  3955.         ipmap[ip].name.add(name)
  3956.         ipmap[ip].alias.update(hinfo.alias)
  3957.  
  3958.     for name, hinfo in self.hostmap.items():
  3959.       if not hinfo.ip and hinfo.alias:
  3960.         found = False
  3961.         for ip, v in ipmap.items():
  3962.           if name in v.alias:
  3963.             for alias in hinfo.alias:
  3964.               ipmap[ip].alias.add(alias)
  3965.               found = True
  3966.  
  3967.         if not found: # orphan CNAME hostnames (with no IP address) may be still valid virtual hosts
  3968.           noips[name].extend(hinfo.alias)
  3969.  
  3970.     print('Hostmap ' + '-'*42)
  3971.     for ip, hinfo in sorted(ipmap.items()):
  3972.       for name in hinfo.name:
  3973.         print('%34s %s' % (name, ip))
  3974.       for alias in hinfo.alias:
  3975.         print('%34s' % alias)
  3976.  
  3977.     for k, v in noips.items():
  3978.       print('%34s ?' % k)
  3979.       for alias in v:
  3980.         print('%34s' % alias)
  3981.  
  3982.     print('Domains ' + '-'*42)
  3983.     domains = {}
  3984.     for ip, hinfo in ipmap.items():
  3985.       for name in hinfo.name.union(hinfo.alias):
  3986.         if name.count('.') > 1:
  3987.           i = 1
  3988.         else:
  3989.           i = 0
  3990.         d = '.'.join(name.split('.')[i:])
  3991.         if d not in domains: domains[d] = 0
  3992.         domains[d] += 1
  3993.  
  3994.     for domain, count in sorted(domains.items(), key=lambda a:a[0].split('.')[-1::-1]):
  3995.       print('%34s %d' % (domain, count))
  3996.  
  3997.     print('Networks ' + '-'*41)
  3998.     nets = {}
  3999.     for ip in set(ipmap):
  4000.       if not ip.version() == 4:
  4001.         nets[ip] = [ip]
  4002.       else:
  4003.         n = ip.make_net('255.255.255.0')
  4004.         if n not in nets: nets[n] = []
  4005.         nets[n].append(ip)
  4006.  
  4007.     for net, ips in sorted(nets.items()):
  4008.       if len(ips) == 1:
  4009.         print(' '*34 + ' %s' % ips[0])
  4010.       else:
  4011.         print(' '*34 + ' %s.x' % '.'.join(str(net).split('.')[:-1]))
  4012.  
  4013.   # }}}
  4014.  
  4015.   def push_final(self, resp):
  4016.     if hasattr(resp, 'rrs'):
  4017.       for rr in resp.rrs:
  4018.         name, qclass, qtype, data = rr
  4019.  
  4020.         info = (qclass, qtype, data)
  4021.         if info not in self.records[name]:
  4022.           self.records[name].append(info)
  4023.  
  4024.         if not qclass == 'IN':
  4025.           continue
  4026.  
  4027.         if qtype == 'PTR':
  4028.           data = data[:-1]
  4029.           self.hostmap[data].ip.add(name)
  4030.  
  4031.         else:
  4032.           if qtype in ('A', 'AAAA'):
  4033.             name = name[:-1]
  4034.             self.hostmap[name].ip.add(data)
  4035.  
  4036.           elif qtype == 'CNAME':
  4037.             name, data = name[:-1], data[:-1]
  4038.             self.hostmap[data].alias.add(name)
  4039.  
  4040.  
  4041. class DNS_reverse:
  4042.   '''Reverse DNS lookup'''
  4043.  
  4044.   usage_hints = [
  4045.     """%prog host=NET0 0=192.168.0.0/24 -x ignore:code=3""",
  4046.     """%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-""",
  4047.     ]
  4048.  
  4049.   available_options = (
  4050.     ('host', 'IP addresses to reverse lookup'),
  4051.     ('server', 'name server to query (directly asking a zone authoritative NS may return more results) [8.8.8.8]'),
  4052.     ('timeout', 'seconds to wait for a response [5]'),
  4053.     ('protocol', 'send queries over udp or tcp [udp]'),
  4054.     )
  4055.   available_actions = ()
  4056.  
  4057.   Response = Response_Base
  4058.  
  4059.   def execute(self, host, server='8.8.8.8', timeout='5', protocol='udp'):
  4060.  
  4061.     with Timing() as timing:
  4062.       response = dns_query(server, int(timeout), protocol, dns.reversename.from_address(host), qtype='PTR', qclass='IN')
  4063.  
  4064.     code = response.rcode()
  4065.     status = dns.rcode.to_text(code)
  4066.     rrs = [[host, c, t, d] for _, _, c, t, d in [rr.to_text().split(' ', 4) for rr in response.answer]]
  4067.  
  4068.     mesg = '%s %s' % (status, ''.join('[%s]' % ' '.join(rr) for rr in rrs))
  4069.     resp = self.Response(code, mesg, timing)
  4070.  
  4071.     resp.rrs = rrs
  4072.  
  4073.     return resp
  4074.  
  4075. class DNS_forward:
  4076.   '''Forward DNS lookup'''
  4077.  
  4078.   usage_hints = [
  4079.     """%prog name=FILE0.google.com 0=names.txt -x ignore:code=3""",
  4080.     """%prog name=google.MOD0 0=TLD -x ignore:code=3""",
  4081.     """%prog name=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3""",
  4082.     ]
  4083.  
  4084.   available_options = (
  4085.     ('name', 'domain names to lookup'),
  4086.     ('server', 'name server to query (directly asking the zone authoritative NS may return more results) [8.8.8.8]'),
  4087.     ('timeout', 'seconds to wait for a response [5]'),
  4088.     ('protocol', 'send queries over udp or tcp [udp]'),
  4089.     ('qtype', 'type to query [ANY]'),
  4090.     ('qclass', 'class to query [IN]'),
  4091.     )
  4092.   available_actions = ()
  4093.  
  4094.   available_keys = {
  4095.     'TLD': generate_tld,
  4096.     'SRV': generate_srv,
  4097.     }
  4098.  
  4099.   Response = Response_Base
  4100.  
  4101.   def execute(self, name, server='8.8.8.8', timeout='5', protocol='udp', qtype='ANY', qclass='IN'):
  4102.  
  4103.     with Timing() as timing:
  4104.       response = dns_query(server, int(timeout), protocol, name, qtype=qtype, qclass=qclass)
  4105.  
  4106.     code = response.rcode()
  4107.     status = dns.rcode.to_text(code)
  4108.     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]]
  4109.  
  4110.     mesg = '%s %s' % (status, ''.join('[%s]' % ' '.join(rr) for rr in rrs))
  4111.     resp = self.Response(code, mesg, timing)
  4112.  
  4113.     resp.rrs = rrs
  4114.  
  4115.     return resp
  4116.  
  4117. # }}}
  4118.  
  4119. # SNMP {{{
  4120. try:
  4121.   from pysnmp.entity.rfc3413.oneliner import cmdgen
  4122. except ImportError:
  4123.   notfound.append('pysnmp')
  4124.  
  4125. class SNMP_login:
  4126.   '''Brute-force SNMP v1/2/3'''
  4127.  
  4128.   usage_hints = (
  4129.     """%prog host=10.0.0.1 version=2 community=FILE0 0=names.txt -x ignore:mesg='No SNMP response received before timeout'""",
  4130.     """%prog host=10.0.0.1 version=3 user=FILE0 0=logins.txt -x ignore:mesg=unknownUserName""",
  4131.     """%prog host=10.0.0.1 version=3 user=myuser auth_key=FILE0 0=passwords.txt -x ignore:mesg=wrongDigest""",
  4132.     )
  4133.  
  4134.   available_options = (
  4135.     ('host', 'target host'),
  4136.     ('port', 'target port [161]'),
  4137.     ('version', 'SNMP version to use [2|3|1]'),
  4138.     #('security_name', 'SNMP v1/v2 username, for most purposes it can be any arbitrary string [test-agent]'),
  4139.     ('community', 'SNMPv1/2c community names to test [public]'),
  4140.     ('user', 'SNMPv3 usernames to test [myuser]'),
  4141.     ('auth_key', 'SNMPv3 pass-phrases to test [my_password]'),
  4142.     #('priv_key', 'SNMP v3 secret key for encryption'), # see http://pysnmp.sourceforge.net/docs/4.x/index.html#UsmUserData
  4143.     #('auth_protocol', ''),
  4144.     #('priv_protocol', ''),
  4145.     ('timeout', 'seconds to wait for a response [1]'),
  4146.     ('retries', 'number of successive request retries [2]'),
  4147.     )
  4148.   available_actions = ()
  4149.  
  4150.   Response = Response_Base
  4151.  
  4152.   def execute(self, host, port=None, version='2', community='public', user='myuser', auth_key='my_password', timeout='1', retries='2'):
  4153.     if version in ('1', '2'):
  4154.       security_model = cmdgen.CommunityData('test-agent', community, 0 if version == '1' else 1)
  4155.  
  4156.     elif version == '3':
  4157.       security_model = cmdgen.UsmUserData(user, auth_key) # , priv_key)
  4158.       if len(auth_key) < 8:
  4159.         return self.Response('1', 'SNMPv3 requires passphrases to be at least 8 characters long')
  4160.  
  4161.     else:
  4162.       raise NotImplementedError("Incorrect SNMP version '%s'" % version)
  4163.  
  4164.     with Timing() as timing:
  4165.       errorIndication, errorStatus, errorIndex, varBinds = cmdgen.CommandGenerator().getCmd(
  4166.         security_model,
  4167.         cmdgen.UdpTransportTarget((host, int(port or 161)), timeout=int(timeout), retries=int(retries)),
  4168.         (1,3,6,1,2,1,1,1,0)
  4169.         )
  4170.  
  4171.     code = '%d-%d' % (errorStatus, errorIndex)
  4172.     if not errorIndication:
  4173.       mesg = '%s' % varBinds
  4174.     else:
  4175.       mesg = '%s' % errorIndication
  4176.  
  4177.     return self.Response(code, mesg, timing)
  4178.  
  4179. # }}}
  4180.  
  4181. # IKE {{{
  4182. if not which('ike-scan'):
  4183.   notfound.append('ike-scan')
  4184.  
  4185. # http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xhtml
  4186. 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')]
  4187. IKE_HASH  = [('1', 'MD5'), ('2', 'SHA1'), ('3', 'Tiger'), ('4', 'SHA2-256'), ('5', 'SHA2-384'), ('6', 'SHA2-512')]
  4188. IKE_AUTH  = [('1', 'PSK'), ('2', 'DSS Sig'), ('3', 'RSA Sig'), ('4', 'RSA Enc'), ('5', 'Revised RSA Enc'),
  4189.             #('6', 'EIGAMEL Enc'), ('7', 'Revised EIGAMEL Enc'), ('8', 'ECDSA Sig'), # Reserved
  4190.             #('9', 'ECDSA SHA-256'), ('10', 'ECDSA SHA-384'), ('11', 'ECDSA SHA-512'), # RFC4754
  4191.              ('65001', 'XAUTH'), ('64221', 'Hybrid'), ('64222', 'Hybrid 64222')] #, ('64223', 'Hybrid 64223'), ... ('65002', 'Hybrid 65002') ...
  4192. IKE_GROUP = [('1', 'modp768'), ('2', 'modp1024'), ('5', 'modp1536'),
  4193.             #('3', 'ecc3'), ('4', 'ecc4'), # any implementations?
  4194.             # '6', '7', '8', '9', '10', '11', '12', '13', # only in draft, not RFC
  4195.              ('14', 'modp2048')] #, ('15', 'modp3072'), ('16', 'modp4096'), ('17', 'modp6144'), ('18', 'modp8192')] # RFC3526
  4196.             # '19', '20', '21', '22', '23', '24', '25', '26', # RFC5903
  4197.             # '27', '28', '29', '30', # RFC6932
  4198.  
  4199. def generate_transforms():
  4200.   lists = map(lambda l: [i[0] for i in l], [IKE_ENC, IKE_HASH, IKE_AUTH, IKE_GROUP])
  4201.   return map(lambda p: ','.join(p), product(*[chain(l) for l in lists])), reduce(lambda x,y: x*y, map(len, lists))
  4202.  
  4203. class Controller_IKE(Controller):
  4204.  
  4205.   results = defaultdict(list)
  4206.  
  4207.   def show_final(self):
  4208.     ''' Expected output:
  4209. + 10.0.0.1:500 (Main Mode)
  4210.     Encryption       Hash         Auth      Group
  4211.     ---------- ----------   ---------- ----------
  4212.           3DES        MD5          PSK   modp1024
  4213.           3DES        MD5        XAUTH   modp1024
  4214.         AES128       SHA1          PSK   modp1024
  4215.         AES128       SHA1        XAUTH   modp1024
  4216.  
  4217. + 10.0.0.1:500 (Aggressive Mode)
  4218.     Encryption       Hash         Auth      Group
  4219.     ---------- ----------   ---------- ----------
  4220.           3DES        MD5          PSK   modp1024
  4221.           3DES        MD5        XAUTH   modp1024
  4222.         AES128       SHA1          PSK   modp1024
  4223.         AES128       SHA1        XAUTH   modp1024
  4224.     '''
  4225.  
  4226.     ike_enc = dict(IKE_ENC)
  4227.     ike_hsh = dict(IKE_HASH)
  4228.     ike_ath = dict(IKE_AUTH)
  4229.     ike_grp = dict(IKE_GROUP)
  4230.  
  4231.     for endpoint, transforms in self.results.iteritems():
  4232.       print('\n+ %s' % endpoint)
  4233.       print('    %10s %10s %12s %10s' % ('Encryption', 'Hash', 'Auth', 'Group'))
  4234.       print('    %10s %10s %12s %10s' % ('-'*10, '-'*10, '-'*10, '-'*10))
  4235.       for transform in transforms:
  4236.         e, h, a, g = transform.split(',')
  4237.         enc = ike_enc[e]
  4238.         hsh = ike_hsh[h]
  4239.         ath = ike_ath[a]
  4240.         grp = ike_grp[g]
  4241.         print('    %10s %10s %12s %10s' % (enc, hsh, ath, grp))
  4242.  
  4243.   def push_final(self, resp):
  4244.     if hasattr(resp, 'rrs'):
  4245.       endpoint, transform = resp.rrs
  4246.       self.results[endpoint].append(transform)
  4247.  
  4248. class IKE_enum:
  4249.   '''Enumerate IKE transforms'''
  4250.  
  4251.   usage_hints = [
  4252.     """%prog host=10.0.0.1 transform=MOD0 0=TRANS -x ignore:fgrep=NO-PROPOSAL""",
  4253.     """%prog host=10.0.0.1 transform=MOD0 0=TRANS -x ignore:fgrep=NO-PROPOSAL aggressive=RANGE1 1=int:0-1""",
  4254.     ]
  4255.  
  4256.   available_options = (
  4257.     ('host', 'target host'),
  4258.     ('host', 'target port [500]'),
  4259.     ('transform', 'transform to test [5,1,1,2]'),
  4260.     ('aggressive', 'use aggressive mode [0|1]'),
  4261.     ('groupname', 'identification value for aggressive mode [foo]'),
  4262.     ('vid', 'comma-separated vendor IDs to use'),
  4263.     )
  4264.   available_actions = ()
  4265.  
  4266.   available_keys = {
  4267.     'TRANS': generate_transforms,
  4268.     }
  4269.  
  4270.   Response = Response_Base
  4271.  
  4272.   def __init__(self):
  4273.     uid = multiprocessing.current_process().name[9:]
  4274.     self.sport = '51%s' % uid
  4275.  
  4276.   def execute(self, host, port='500', transform='5,1,1,2', aggressive='0', groupname='foo', vid=''):
  4277.  
  4278.     cmd = ['ike-scan', '-M', '--sport', self.sport, host, '--dport', port, '--trans', transform]
  4279.     if aggressive == '1':
  4280.       cmd.append('-A')
  4281.       if groupname:
  4282.         cmd.extend(['--id', groupname])
  4283.     for v in vid.split(','):
  4284.       cmd.extend(['--vendor', v])
  4285.  
  4286.     with Timing() as timing:
  4287.       p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  4288.       out, err = p.communicate()
  4289.       code = p.returncode
  4290.  
  4291.     trace = '%s\n[out]\n%s\n[err]\n%s' % (cmd, out, err)
  4292.     logger.debug('trace: %s' % repr(trace))
  4293.  
  4294.     has_sa = 'SA=(' in out
  4295.     if has_sa:
  4296.       mesg = 'Handshake returned: %s (%s)' % (re.search('SA=\((.+) LifeType', out).group(1), re.search('\t(.+) Mode Handshake returned', out).group(1))
  4297.     else:
  4298.       try:
  4299.         mesg = out.strip().split('\n')[1].split('\t')[-1]
  4300.       except:
  4301.         mesg = ' '.join(repr(s) for s in filter(None, [out, err]))
  4302.  
  4303.     resp = self.Response(code, mesg, timing, trace)
  4304.     if has_sa:
  4305.       endpoint = '%s:%s (%s Mode)' % (host, port, 'Aggressive' if aggressive == '1' else 'Main')
  4306.       resp.rrs = endpoint, transform
  4307.  
  4308.     return resp
  4309.  
  4310. # }}}
  4311.  
  4312. # Unzip {{{
  4313. if not which('unzip'):
  4314.   notfound.append('unzip')
  4315.  
  4316. class Unzip_pass:
  4317.   '''Brute-force the password of encrypted ZIP files'''
  4318.  
  4319.   usage_hints = [
  4320.     """%prog zipfile=path/to/file.zip password=FILE0 0=passwords.txt -x ignore:code!=0""",
  4321.     ]
  4322.  
  4323.   available_options = (
  4324.     ('zipfile', 'ZIP files to test'),
  4325.     ('password', 'passwords to test'),
  4326.     )
  4327.  
  4328.   available_actions = ()
  4329.  
  4330.   Response = Response_Base
  4331.  
  4332.   def execute(self, zipfile, password):
  4333.     zipfile = os.path.abspath(zipfile)
  4334.     cmd = ['unzip', '-t', '-q', '-P', password, zipfile]
  4335.  
  4336.     with Timing() as timing:
  4337.       p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  4338.       out, err = p.communicate()
  4339.       code = p.returncode
  4340.  
  4341.     mesg = repr(out.strip())[1:-1]
  4342.     trace = '%s\n[out]\n%s\n[err]\n%s' % (cmd, out, err)
  4343.  
  4344.     return self.Response(code, mesg, timing, trace)
  4345.  
  4346. # }}}
  4347.  
  4348. # Keystore {{{
  4349. if not which('keytool'):
  4350.   notfound.append('java')
  4351.  
  4352. class Keystore_pass:
  4353.   '''Brute-force the password of Java keystore files'''
  4354.  
  4355.   usage_hints = [
  4356.     """%prog keystore=path/to/keystore.jks password=FILE0 0=passwords.txt -x ignore:fgrep='password was incorrect'""",
  4357.     ]
  4358.  
  4359.   available_options = (
  4360.     ('keystore', 'keystore files to test'),
  4361.     ('password', 'passwords to test'),
  4362.     ('storetype', 'type of keystore to test'),
  4363.     )
  4364.  
  4365.   available_actions = ()
  4366.  
  4367.   Response = Response_Base
  4368.  
  4369.   def execute(self, keystore, password, storetype='jks'):
  4370.     keystore = os.path.abspath(keystore)
  4371.     cmd = ['keytool', '-list', '-keystore', keystore, '-storepass', password, '-storetype', storetype]
  4372.  
  4373.     with Timing() as timing:
  4374.       p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  4375.       out, err = p.communicate()
  4376.       code = p.returncode
  4377.  
  4378.     mesg = repr(out.strip())[1:-1]
  4379.     trace = '%s\n[out]\n%s\n[err]\n%s' % (cmd, out, err)
  4380.  
  4381.     return self.Response(code, mesg, timing, trace)
  4382.  
  4383. # }}}
  4384.  
  4385. # Umbraco {{{
  4386. import hmac
  4387. class Umbraco_crack:
  4388.   '''Crack Umbraco HMAC-SHA1 password hashes'''
  4389.  
  4390.   usage_hints = (
  4391.     """%prog hashlist=@umbraco_users.pw password=FILE0 0=rockyou.txt""",
  4392.     )
  4393.  
  4394.   available_options = (
  4395.     ('hashlist', 'hashes to crack'),
  4396.     ('password', 'password to test'),
  4397.     )
  4398.   available_actions = ()
  4399.  
  4400.   Response = Response_Base
  4401.  
  4402.   def execute(self, password, hashlist):
  4403.  
  4404.     p = password.encode('utf-16-le')
  4405.     h = b64encode(hmac.new(p, p, digestmod=hashlib.sha1).digest())
  4406.  
  4407.     if h not in hashlist:
  4408.       code, mesg = 1, 'fail'
  4409.     else:
  4410.       cracked = [line.rstrip() for line in hashlist.split('\n') if h in line]
  4411.       code, mesg = 0, ' '.join(cracked)
  4412.  
  4413.     return self.Response(code, mesg)
  4414.  
  4415. # }}}
  4416.  
  4417. # TCP Fuzz {{{
  4418. class TCP_fuzz:
  4419.   '''Fuzz TCP services'''
  4420.  
  4421.   usage_hints = (
  4422.     '''%prog host=10.0.0.1 data=RANGE0 0=hex:0x00-0xffffff''',
  4423.     )
  4424.  
  4425.   available_options = (
  4426.     ('host', 'target host'),
  4427.     ('port', 'target port'),
  4428.     ('timeout', 'seconds to wait for a response [10]'),
  4429.     )
  4430.   available_actions = ()
  4431.  
  4432.   Response = Response_Base
  4433.  
  4434.   def execute(self, host, port, data='', timeout='2'):
  4435.     fp = socket.create_connection((host, port), int(timeout))
  4436.     fp.send(data.decode('hex'))
  4437.     with Timing() as timing:
  4438.       resp = fp.recv(1024)
  4439.     fp.close()
  4440.  
  4441.     code = 0
  4442.     mesg = resp.encode('hex')
  4443.  
  4444.     return self.Response(code, mesg, timing)
  4445.  
  4446. # }}}
  4447.  
  4448. # Dummy Test {{{
  4449. class Dummy_test:
  4450.   '''Testing module'''
  4451.  
  4452.   usage_hints = (
  4453.     """%prog data=_@@_RANGE0_@@_ 0=hex:0x00-0xff -e _@@_:unhex""",
  4454.     """%prog data=RANGE0 0=int:10-0""",
  4455.     """%prog data=PROG0 0='seq -w 10 -1 0'""",
  4456.     """%prog data=PROG0 0='mp64.bin -i ?l?l?l',$(mp64.bin --combination -i ?l?l?l)""",
  4457.     )
  4458.  
  4459.   available_options = (
  4460.     ('data', 'data to test'),
  4461.     ('data2', 'data2 to test'),
  4462.     ('delay', 'fake random delay'),
  4463.     )
  4464.   available_actions = ()
  4465.  
  4466.   Response = Response_Base
  4467.  
  4468.   def execute(self, data, data2='', delay='1'):
  4469.     code, mesg = 0, '%s / %s' % (data, data2)
  4470.     with Timing() as timing:
  4471.       sleep(random.randint(0, int(delay)*1000)/1000.0)
  4472.  
  4473.     return self.Response(code, mesg, timing)
  4474.  
  4475. # }}}
  4476.  
  4477. # modules {{{
  4478. modules = [
  4479.   ('ftp_login', (Controller, FTP_login)),
  4480.   ('ssh_login', (Controller, SSH_login)),
  4481.   ('telnet_login', (Controller, Telnet_login)),
  4482.   ('smtp_login', (Controller, SMTP_login)),
  4483.   ('smtp_vrfy', (Controller, SMTP_vrfy)),
  4484.   ('smtp_rcpt', (Controller, SMTP_rcpt)),
  4485.   ('finger_lookup', (Controller_Finger, Finger_lookup)),
  4486.   ('http_fuzz', (Controller, HTTP_fuzz)),
  4487.   ('pop_login', (Controller, POP_login)),
  4488.   ('pop_passd', (Controller, POP_passd)),
  4489.   ('imap_login', (Controller, IMAP_login)),
  4490.   ('ldap_login', (Controller, LDAP_login)),
  4491.   ('smb_login', (Controller, SMB_login)),
  4492.   ('smb_lookupsid', (Controller, SMB_lookupsid)),
  4493.   ('rlogin_login', (Controller, Rlogin_login)),
  4494.   ('vmauthd_login', (Controller, VMauthd_login)),
  4495.   ('mssql_login', (Controller, MSSQL_login)),
  4496.   ('oracle_login', (Controller, Oracle_login)),
  4497.   ('mysql_login', (Controller, MySQL_login)),
  4498.   ('mysql_query', (Controller, MySQL_query)),
  4499.   ('rdp_login', (Controller, RDP_login)),
  4500.   ('pgsql_login', (Controller, Pgsql_login)),
  4501.   ('vnc_login', (Controller, VNC_login)),
  4502.  
  4503.   ('dns_forward', (Controller_DNS, DNS_forward)),
  4504.   ('dns_reverse', (Controller_DNS, DNS_reverse)),
  4505.   ('snmp_login', (Controller, SNMP_login)),
  4506.   ('ike_enum', (Controller_IKE, IKE_enum)),
  4507.  
  4508.   ('unzip_pass', (Controller, Unzip_pass)),
  4509.   ('keystore_pass', (Controller, Keystore_pass)),
  4510.   ('umbraco_crack', (Controller, Umbraco_crack)),
  4511.  
  4512.   ('tcp_fuzz', (Controller, TCP_fuzz)),
  4513.   ('dummy_test', (Controller, Dummy_test)),
  4514.   ]
  4515.  
  4516. dependencies = {
  4517.   'paramiko': [('ssh_login',), 'http://www.lag.net/paramiko/', '1.7.7.1'],
  4518.   'pycurl': [('http_fuzz',), 'http://pycurl.sourceforge.net/', '7.19.0'],
  4519.   'openldap': [('ldap_login',), 'http://www.openldap.org/', '2.4.24'],
  4520.   'impacket': [('smb_login','smb_lookupsid','mssql_login'), 'https://github.com/CoreSecurity/impacket', '0.9.12'],
  4521.   'cx_Oracle': [('oracle_login',), 'http://cx-oracle.sourceforge.net/', '5.1.1'],
  4522.   'mysql-python': [('mysql_login',), 'http://sourceforge.net/projects/mysql-python/', '1.2.3'],
  4523.   'xfreerdp': [('rdp_login',), 'https://github.com/FreeRDP/FreeRDP.git', '1.2.0-beta1'],
  4524.   'psycopg': [('pgsql_login',), 'http://initd.org/psycopg/', '2.4.5'],
  4525.   'pycrypto': [('vnc_login',), 'http://www.dlitz.net/software/pycrypto/', '2.3'],
  4526.   'dnspython': [('dns_reverse', 'dns_forward'), 'http://www.dnspython.org/', '1.10.0'],
  4527.   'IPy': [('dns_reverse', 'dns_forward'), 'https://github.com/haypo/python-ipy', '0.75'],
  4528.   'pysnmp': [('snmp_login',), 'http://pysnmp.sf.net/', '4.2.1'],
  4529.   'ike-scan': [('ike_enum',), 'http://www.nta-monitor.com/tools-resources/security-tools/ike-scan', '1.9'],
  4530.   'unzip': [('unzip_pass',), 'http://www.info-zip.org/', '6.0'],
  4531.   'java': [('keystore_pass',), 'http://www.oracle.com/technetwork/java/javase/', '6'],
  4532.   'ftp-tls': [('ftp_login',), 'TLS support unavailable before python 2.7'],
  4533.   }
  4534. # }}}
  4535.  
  4536. # main {{{
  4537. if __name__ == '__main__':
  4538.   multiprocessing.freeze_support()
  4539.  
  4540.   def show_usage():
  4541.     print(__banner__)
  4542.     print('''Usage: patator.py module --help
  4543.  
  4544. Available modules:
  4545. %s''' % '\n'.join('  + %-13s : %s' % (k, v[1].__doc__) for k, v in modules))
  4546.  
  4547.     sys.exit(2)
  4548.  
  4549.   available = dict(modules)
  4550.   name = os.path.basename(sys.argv[0]).lower()
  4551.  
  4552.   if name not in available:
  4553.     if len(sys.argv) == 1:
  4554.       show_usage()
  4555.  
  4556.     name = os.path.basename(sys.argv[1]).lower()
  4557.     if name not in available:
  4558.       show_usage()
  4559.  
  4560.     del sys.argv[0]
  4561.  
  4562.   # dependencies
  4563.   abort = False
  4564.   for k in set(notfound):
  4565.     args = dependencies[k]
  4566.     if name in args[0]:
  4567.       if len(args) == 2:
  4568.         print('WARNING: %s' % args[1])
  4569.       else:
  4570.         url, ver = args[1:]
  4571.         print('ERROR: %s %s (%s) is required to run %s.' % (k, ver, url, name))
  4572.         abort = True
  4573.  
  4574.   if abort:
  4575.     print('Please read the README inside for more information.')
  4576.     sys.exit(3)
  4577.  
  4578.   # start
  4579.   ctrl, module = available[name]
  4580.   powder = ctrl(module, [name] + sys.argv[1:])
  4581.   powder.fire()
  4582.  
  4583. # }}}
  4584.  
  4585. # vim: ts=2 sw=2 sts=2 et fdm=marker bg=dark
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top