/patator.py
Python | 1415 lines | 1351 code | 4 blank | 60 comment | 2 complexity | 84028051eeed1c86d0ec55f5bd27a628 MD5 | raw file
- #!/usr/bin/env python2
- # Copyright (C) 2012 Sebastien MACKE
- #
- # This program is free software; you can redistribute it and/or modify it under
- # the terms of the GNU General Public License version 2, as published by the
- # Free Software Foundation
- #
- # This program is distributed in the hope that it will be useful, but WITHOUT
- # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- # details (http://www.gnu.org/licenses/gpl.txt).
- __author__ = 'Sebastien Macke'
- __email__ = 'patator@hsc.fr'
- __url__ = 'http://www.hsc.fr/ressources/outils/patator/'
- __git__ = 'https://github.com/lanjelot/patator'
- __twitter__ = 'http://twitter.com/lanjelot'
- __version__ = '0.7-beta'
- __license__ = 'GPLv2'
- __banner__ = 'Patator v%s (%s)' % (__version__, __git__)
- # README {{{
- '''
- INTRODUCTION
- ------------
- * What ?
- Patator is a multi-purpose brute-forcer, with a modular design and a flexible usage.
- Currently it supports the following modules:
- + ftp_login : Brute-force FTP
- + ssh_login : Brute-force SSH
- + telnet_login : Brute-force Telnet
- + smtp_login : Brute-force SMTP
- + smtp_vrfy : Enumerate valid users using SMTP VRFY
- + smtp_rcpt : Enumerate valid users using SMTP RCPT TO
- + finger_lookup : Enumerate valid users using Finger
- + http_fuzz : Brute-force HTTP
- + pop_login : Brute-force POP3
- + pop_passd : Brute-force poppassd (http://netwinsite.com/poppassd/)
- + imap_login : Brute-force IMAP4
- + ldap_login : Brute-force LDAP
- + smb_login : Brute-force SMB
- + smb_lookupsid : Brute-force SMB SID-lookup
- + rlogin_login : Brute-force rlogin
- + vmauthd_login : Brute-force VMware Authentication Daemon
- + mssql_login : Brute-force MSSQL
- + oracle_login : Brute-force Oracle
- + mysql_login : Brute-force MySQL
- + mysql_query : Brute-force MySQL queries
- * rdp_login : Brute-force RDP (NLA)
- + pgsql_login : Brute-force PostgreSQL
- + vnc_login : Brute-force VNC
- + dns_forward : Forward DNS lookup
- + dns_reverse : Reverse DNS lookup
- + snmp_login : Brute-force SNMP v1/2/3
- + ike_enum : Enumerate IKE transforms
- + unzip_pass : Brute-force the password of encrypted ZIP files
- + keystore_pass : Brute-force the password of Java keystore files
- + umbraco_crack : Crack Umbraco HMAC-SHA1 password hashes
- + tcp_fuzz : Fuzz TCP services
- + dummy_test : Testing module
- Future modules to be implemented:
- - rdp_login w/no NLA
- The name "Patator" comes from http://www.youtube.com/watch?v=xoBkBvnTTjo
- * Why ?
- Basically, I got tired of using Medusa, Hydra, Ncrack, Metasploit auxiliary modules, Nmap NSE scripts and the like because:
- - they either do not work or are not reliable (got me false negatives several times in the past)
- - they are not flexible enough (how to iterate over all wordlists, fuzz any module parameter)
- - they lack useful features (display progress or pause during execution)
- FEATURES
- --------
- * No false negatives, as it is the user that decides what results to ignore based on:
- + status code of response
- + size of response
- + matching string or regex in response data
- + ... see --help
- * Modular design
- + not limited to network modules (eg. the unzip_pass module)
- + not limited to brute-forcing (eg. remote exploit testing, or vulnerable version probing)
- * Interactive runtime
- + show progress during execution (press Enter)
- + pause/unpause execution (press p)
- + increase/decrease verbosity
- + add new actions & conditions during runtime (eg. to exclude more types of response from showing)
- + ... press h to see all available interactive commands
- * Use persistent connections (ie. will test several passwords until the server disconnects)
- * Multi-threaded
- * Flexible user input
- - Any module parameter can be fuzzed:
- + use the FILE keyword to iterate over a file
- + use the COMBO keyword to iterate over a combo file
- + use the NET keyword to iterate over every hosts of a network subnet
- + use the RANGE keyword to iterate over hexadecimal, decimal or alphabetical ranges
- + use the PROG keyword to iterate over the output of an external program
- - Iteration over the joined wordlists can be done in any order
- * Save every response (along with request) to seperate log files for later reviewing
- INSTALL
- -------
- * Dependencies (best tested versions)
- | Required for | URL | Version |
- --------------------------------------------------------------------------------------------------
- paramiko | SSH | http://www.lag.net/paramiko/ | 1.7.7.1 |
- --------------------------------------------------------------------------------------------------
- pycurl | HTTP | http://pycurl.sourceforge.net/ | 7.19.0 |
- --------------------------------------------------------------------------------------------------
- openldap | LDAP | http://www.openldap.org/ | 2.4.24 |
- --------------------------------------------------------------------------------------------------
- impacket | SMB | https://github.com/CoreSecurity/impacket | 0.9.12 |
- --------------------------------------------------------------------------------------------------
- cx_Oracle | Oracle | http://cx-oracle.sourceforge.net/ | 5.1.1 |
- --------------------------------------------------------------------------------------------------
- mysql-python | MySQL | http://sourceforge.net/projects/mysql-python/ | 1.2.3 |
- --------------------------------------------------------------------------------------------------
- xfreerdp | RDP (NLA) | https://github.com/FreeRDP/FreeRDP/ | 1.2.0 |
- --------------------------------------------------------------------------------------------------
- psycopg | PostgreSQL | http://initd.org/psycopg/ | 2.4.5 |
- --------------------------------------------------------------------------------------------------
- pycrypto | VNC | http://www.dlitz.net/software/pycrypto/ | 2.3 |
- --------------------------------------------------------------------------------------------------
- dnspython | DNS | http://www.dnspython.org/ | 1.10.0 |
- --------------------------------------------------------------------------------------------------
- IPy | NET keyword | https://github.com/haypo/python-ipy | 0.75 |
- --------------------------------------------------------------------------------------------------
- pysnmp | SNMP | http://pysnmp.sourceforge.net/ | 4.2.1 |
- --------------------------------------------------------------------------------------------------
- pyasn1 | SNMP | http://sourceforge.net/projects/pyasn1/ | 0.1.2 |
- --------------------------------------------------------------------------------------------------
- ike-scan | IKE | http://www.nta-monitor.com/tools-resources/ | 1.9 |
- --------------------------------------------------------------------------------------------------
- unzip | ZIP passwords | http://www.info-zip.org/ | 6.0 |
- --------------------------------------------------------------------------------------------------
- Java | keystore files | http://www.oracle.com/technetwork/java/javase/ | 6 |
- --------------------------------------------------------------------------------------------------
- python | | http://www.python.org/ | 2.7 |
- --------------------------------------------------------------------------------------------------
- * Shortcuts (optional)
- ln -s path/to/patator.py /usr/bin/ftp_login
- ln -s path/to/patator.py /usr/bin/http_fuzz
- so on ...
- USAGE
- -----
- $ python patator.py <module> -h
- or
- $ <module> -h (if you created the shortcuts)
- There are global options and module options:
- - all global options start with - or --
- - all module options are of the form option=value
- All module options are fuzzable:
- ---------
- ./module host=FILE0 port=FILE1 foobar=FILE2.google.FILE3 0=hosts.txt 1=ports.txt 2=foo.txt 3=bar.txt
- The keywords (FILE, COMBO, NET, ...) act as place-holders. They indicate the type of wordlist
- and where to replace themselves with the actual words to test.
- Each keyword is numbered in order to:
- - match the corresponding wordlist
- - and indicate in what order to iterate over all the wordlists
- For example, this would be the classic order:
- ---------
- $ ./module host=FILE0 user=FILE1 password=FILE2 0=hosts.txt 1=logins.txt 2=passwords.txt
- 10.0.0.1 root password
- 10.0.0.1 root 123456
- 10.0.0.1 root qsdfghj
- ... (trying all passwords before testing next login)
- 10.0.0.1 admin password
- 10.0.0.1 admin 123456
- 10.0.0.1 admin qsdfghj
- ... (trying all logins before testing next host)
- 10.0.0.2 root password
- ...
- While a smarter way might be:
- ---------
- $ ./module host=FILE2 user=FILE1 password=FILE0 2=hosts.txt 1=logins.txt 0=passwords.txt
- 10.0.0.1 root password
- 10.0.0.2 root password
- 10.0.0.1 admin password
- 10.0.0.2 admin password
- 10.0.0.1 root 123456
- 10.0.0.2 root 123456
- 10.0.0.1 admin 123456
- ...
- * Keywords
- Brute-force a list of hosts with a file containing combo entries (each line => login:password).
- ---------
- ./module host=FILE0 user=COMBO10 password=COMBO11 0=hosts.txt 1=combos.txt
- Scan subnets to just grab version banners.
- ---------
- ./module host=NET0 0=10.0.1.0/24,10.0.2.0/24,10.0.3.128-10.0.3.255
- Fuzzing a parameter by iterating over a range of values.
- ---------
- ./module param=RANGE0 0=hex:0x00-0xffff
- ./module param=RANGE0 0=int:0-500
- ./module param=RANGE0 0=lower:a-zzz
- Fuzzing a parameter by iterating over the output of an external program.
- ---------
- ./module param=PROG0 0='john -stdout -i'
- ./module param=PROG0 0='mp64.bin ?l?l?l',$(mp64.bin --combination ?l?l?l) # http://hashcat.net/wiki/doku.php?id=maskprocessor
- * Actions & Conditions
- Use the -x option to do specific actions upon receiving expected results. For example:
- To ignore responses with status code 200 *AND* a size within a specific range.
- ---------
- ./module host=10.0.0.1 user=FILE0 -x ignore:code=200,size=57-74
- To ignore responses with status code 500 *OR* containing "Internal error".
- ---------
- ./module host=10.0.0.1 user=FILE0 -x ignore:code=500 -x ignore:fgrep='Internal error'
- Remember that conditions are ANDed within the same -x option, use multiple -x options to
- specify ORed conditions.
- * Failures
- During execution, failures may happen, such as a TCP connect timeout for
- example. By definition a failure is an exception that the module does not expect,
- and as a result the exception is caught upstream by the controller.
- Such exceptions, or failures, are not immediately reported to the user, the
- controller will retry 4 more times (see --max-retries) before reporting the
- failed payload to the user with the logging level "FAIL".
- * Read carefully the following examples to get a good understanding of how patator works.
- {{{ FTP
- * Brute-force authentication. Do not report wrong passwords.
- ---------
- ftp_login host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt -x ignore:mesg='Login incorrect.'
- NB0. If you get errors like "500 OOPS: priv_sock_get_cmd", use -x ignore,reset,retry:code=500
- in order to retry the last login/password using a new TCP connection. Odd servers like vsftpd
- return this when they shut down the TCP connection (ie. max login attempts reached).
- NB1. If you get errors like "too many connections from your IP address", try decreasing the number of
- threads, the server may be enforcing a maximum number of concurrent connections.
- * Same as before, but stop testing a user after his password is found.
- ---------
- ftp_login ... -x free=user:code=0
- * Find anonymous FTP servers on a subnet.
- ---------
- ftp_login host=NET0 user=anonymous password=test@example.com 0=10.0.0.0/24
- }}}
- {{{ SSH
- * Brute-force authentication with password same as login (aka single mode). Do not report wrong passwords.
- ---------
- ssh_login host=10.0.0.1 user=FILE0 password=FILE0 0=logins.txt -x ignore:mesg='Authentication failed.'
- NB. If you get errors like "Error reading SSH protocol banner ... Connection reset by peer",
- try decreasing the number of threads, the server may be enforcing a maximum
- number of concurrent connections (eg. MaxStartups in OpenSSH).
- * Brute-force several hosts and stop testing a host after a valid password is found.
- ---------
- ssh_login host=FILE0 user=FILE1 password=FILE2 0=hosts.txt 1=logins.txt 2=passwords.txt -x free=host:code=0
- * Same as previous, but stop testing a user on a host after his password is found.
- ---------
- ssh_login host=FILE0 user=FILE1 password=FILE2 0=hosts.txt 1=logins.txt 2=passwords.txt -x free=host+user:code=0
- }}}
- {{{ Telnet
- * Brute-force authentication.
- (a) Enter login after first prompt is detected, enter password after second prompt.
- (b) The regex to detect the login and password prompts.
- (c) Reconnect when we get no login prompt back (max number of tries reached or successful login).
- ------------ (a)
- telnet_login host=10.0.0.1 inputs='FILE0\nFILE1' 0=logins.txt 1=passwords.txt
- prompt_re='tux login:|Password:' -x reset:egrep!='Login incorrect.+tux login:'
- (b) (c)
- NB. If you get errors like "telnet connection closed", try decreasing the number of threads,
- the server may be enforcing a maximum number of concurrent connections.
- }}}
- {{{ SMTP
- * Enumerate valid users using the VRFY command.
- (a) Do not report invalid recipients.
- (b) Do not report when the server shuts us down with "421 too many errors",
- reconnect and resume testing.
- --------- (a)
- smtp_vrfy host=10.0.0.1 user=FILE0 0=logins.txt -x ignore:fgrep='User unknown in local
- recipient table' -x ignore,reset,retry:code=421
- (b)
- * Use the RCPT TO command in case the VRFY command is not available.
- ---------
- smtp_rcpt host=10.0.0.1 user=FILE0@localhost 0=logins.txt helo='ehlo mx.fb.com' mail_from=root
- * Brute-force authentication.
- (a) Send a fake hostname (by default your host fqdn is sent)
- ------------ (a)
- smtp_login host=10.0.0.1 helo='ehlo its.me.com' user=FILE0@dom.com password=FILE1 0=logins.txt 1=passwords.txt
- }}}
- {{{ HTTP
- * Find hidden web resources.
- (a) Use a specific header.
- (b) Follow redirects.
- (c) Do not report 404 errors.
- (d) Retry on 500 errors.
- --------- (a)
- http_fuzz url=http://localhost/FILE0 0=words.txt header='Cookie: SESSID=A2FD8B2DA4'
- follow=1 -x ignore:code=404 -x ignore,retry:code=500
- (b) (c) (d)
- NB. You may be able to go 10 times faster using webef (http://www.hsc.fr/ressources/outils/webef/).
- It is the fastest HTTP brute-forcer I know, yet at the moment it still lacks useful features
- that will prevent you from performing the following attacks.
- * Brute-force phpMyAdmin logon.
- (a) Use POST requests.
- (b) Follow redirects using cookies sent by server.
- (c) Ignore failed authentications.
- --------- (a) (b) (b)
- http_fuzz url=http://10.0.0.1/phpmyadmin/index.php method=POST follow=1 accept_cookie=1
- body='pma_username=root&pma_password=FILE0&server=1&lang=en' 0=passwords.txt
- -x ignore:fgrep='Cannot log in to the MySQL server'
- (c)
- * Scan subnet for directory listings.
- (a) Ignore not matching reponses.
- (b) Save matching responses into directory.
- ---------
- http_fuzz url=http://NET0/FILE1 0=10.0.0.0/24 1=dirs.txt -x ignore:fgrep!='Index of'
- -l /tmp/directory_listings (a)
- (b)
- * Brute-force Basic authentication.
- (a) Single mode (login == password).
- (b) Do not report failed login attempts.
- ---------
- http_fuzz url=http://10.0.0.1/manager/html user_pass=FILE0:FILE0 0=logins.txt -x ignore:code=401
- (a) (b)
- * Find hidden virtual hosts.
- (a) Read template from file.
- (b) Fuzz both the Host and User-Agent headers.
- ---------
- echo -e 'Host: FILE0\nUser-Agent: FILE1' > headers.txt
- http_fuzz url=http://10.0.0.1/ header=@headers.txt 0=vhosts.txt 1=agents.txt
- (a) (b)
- * Brute-force logon using GET requests.
- (a) Encode everything surrounded by the two tags _@@_ in hexadecimal.
- (b) Ignore HTTP 200 responses with a content size (header+body) within given range
- and that also contain the given string.
- (c) Use a different delimiter string because the comma cannot be escaped.
- --------- (a) (a)
- http_fuzz url='http://10.0.0.1/login?username=admin&password=_@@_FILE0_@@_' -e _@@_:hex
- 0=words.txt -x ignore:'code=200|size=1500-|fgrep=Welcome, unauthenticated user' -X '|'
- (b) (c)
- * Brute-force logon that enforces two random nonces to be submitted along every POST.
- (a) First, request the page that provides the nonces as hidden input fields.
- (b) Use regular expressions to extract the nonces that are to be submitted along the main request.
- ---------
- 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
- before_urls=http://10.0.0.1/index before_egrep='_N1_:<input type="hidden" name="nonce1" value="(\w+)"|_N2_:name="nonce2" value="(\w+)"'
- (a) (b)
- * Test the OPTIONS method against a list of URLs.
- (a) Ignore URLs that only allow the HEAD and GET methods.
- (b) Header end of line is '\r\n'.
- (c) Use a different delimiter string because the comma cannot be escaped.
- ---------
- http_fuzz url=FILE0 0=urls.txt method=OPTIONS -x ignore:egrep='^Allow: HEAD, GET\r$' -X '|'
- (a) (b) (c)
- }}}
- {{{ LDAP
- * Brute-force authentication.
- (a) Do not report wrong passwords.
- (b) Talk SSL/TLS to port 636.
- ---------
- ldap_login host=10.0.0.1 binddn='cn=FILE0,dc=example,dc=com' 0=logins.txt bindpw=FILE1 1=passwords.txt
- -x ignore:mesg='ldap_bind: Invalid credentials (49)' ssl=1 port=636
- (a) (b)
- }}}
- {{{ SMB
- * Brute-force authentication.
- ---------
- smb_login host=10.0.0.1 user=FILE0 password=FILE1 0=logins.txt 1=passwords.txt -x ignore:fgrep=STATUS_LOGON_FAILURE
- NB. If you suddenly get STATUS_ACCOUNT_LOCKED_OUT errors for an account
- although it is not the first password you test on this account, then you must
- have locked it.
- * Pass-the-hash.
- (a) Test a list of hosts.
- (b) Test every user (each line := login:rid:LM hash:NT hash).
- ---------
- smb_login host=FILE0 0=hosts.txt user=COMBO10 password_hash=COMBO12:COMBO13 1=pwdump.txt -x ...
- (a) (b)
- }}}
- {{{ rlogin
- * Brute-force usernames that root might be allowed to login as with no password (eg. a ~/.rhosts file with the line "+ root").
- rlogin_login host=10.0.0.1 luser=root user=FILE0 0=logins.txt persistent=0 -x ignore:fgrep=Password:
- * Brute-force usernames that might be allowed to login as root with no password (eg. a /root/.rhosts file with the line "+ john").
- rlogin_login host=10.0.0.1 user=root luser=FILE0 0=logins.txt persistent=0 -x ignore:fgrep=Password:
- }}}
- {{{ MSSQL
- * Brute-force authentication.
- -----------
- mssql_login host=10.0.0.1 user=sa password=FILE0 0=passwords.txt -x ignore:fgrep='Login failed for user'
- }}}
- {{{ Oracle
- Beware, by default in Oracle, accounts are permanently locked out after 10 wrong passwords,
- except for the SYS account.
- * Brute-force authentication.
- ------------
- oracle_login host=10.0.0.1 user=SYS password=FILE0 0=passwords.txt sid=ORCL -x ignore:code=ORA-01017
- NB0. With Oracle 10g XE (Express Edition), you do not need to pass a SID.
- NB1. If you get ORA-12516 errors, it may be because you reached the limit of
- concurrent connections or db processes, try using "--rate-limit 0.5 -t 2" to be
- more polite. Also you can run "alter system set processes=150 scope=spfile;"
- and restart your database to get rid of this.
- * Brute-force SID.
- ------------
- oracle_login host=10.0.0.1 sid=FILE0 0=sids.txt -x ignore:code=ORA-12505
- NB. Against Oracle9, it may crash (Segmentation fault) as soon as a valid SID is
- found (cx_Oracle bug). Sometimes, the SID gets printed out before the crash,
- so try running the same command again if it did not.
- }}}
- {{{ MySQL
- * Brute-force authentication.
- -----------
- mysql_login host=10.0.0.1 user=FILE0 password=FILE0 0=logins.txt -x ignore:fgrep='Access denied for user'
- }}}
- {{{ PostgresSQL
- * Brute-force authentication.
- -----------
- pgsql_login host=10.0.0.1 user=postgres password=FILE0 0=passwords.txt -x ignore:fgrep='password authentication failed'
- }}}
- {{{ VNC
- Some VNC servers have built-in anti-bruteforce functionnality that temporarily
- blacklists the attacker IP address after too many wrong passwords.
- - RealVNC-4.1.3 or TightVNC-1.3.10 for example, allow 5 failed attempts and
- then enforce a 10 second delay. For each subsequent failed attempt that
- delay is doubled.
- - RealVNC-3.3.7 or UltraVNC allow 6 failed attempts and then enforce a 10
- second delay between each following attempt.
- * Brute-force authentication.
- (a) No need to use more than one thread.
- (b) Keep retrying the same password when we are blacklisted by the server.
- (c) Exit execution as soon as a valid password is found.
- --------- (a)
- vnc_login host=10.0.0.1 password=FILE0 0=passwords.txt --threads 1
- -x retry:fgrep!='Authentication failure' --max-retries -1 -x quit:code=0
- (b) (b) (c)
- }}}
- {{{ DNS
- * Brute-force subdomains.
- (a) Ignore NXDOMAIN responses (rcode 3).
- -----------
- dns_forward name=FILE0.google.com 0=names.txt -x ignore:code=3
- (a)
- * Brute-force domain with every possible TLDs.
- -----------
- dns_forward name=google.MOD0 0=TLD -x ignore:code=3
- * Brute-force SRV records.
- -----------
- dns_forward name=MOD0.microsoft.com 0=SRV qtype=SRV -x ignore:code=3
- * Grab the version of several hosts.
- -----------
- dns_forward server=FILE0 0=hosts.txt name=version.bind qtype=txt qclass=ch
- * Reverse lookup several networks.
- (a) Ignore names that do not contain 'google.com'.
- (b) Ignore generic PTR records.
- -----------
- 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-
- (a) (b)
- }}}
- {{{ SNMP
- * SNMPv1/2 : Find valid community names.
- ----------
- snmp_login host=10.0.0.1 community=FILE0 0=names.txt -x ignore:mesg='No SNMP response received before timeout'
- * SNMPv3 : Find valid usernames.
- ----------
- snmp_login host=10.0.0.1 version=3 user=FILE0 0=logins.txt -x ignore:mesg=unknownUserName
- * SNMPv3 : Find valid passwords.
- ----------
- snmp_login host=10.0.0.1 version=3 user=myuser auth_key=FILE0 0=passwords.txt -x ignore:mesg=wrongDigest
- NB0. If you get "notInTimeWindow" error messages, increase the retries option.
- NB1. SNMPv3 requires passphrases to be at least 8 characters long.
- }}}
- {{{ Unzip
- * Brute-force the ZIP file password (cracking older pkzip encryption used to be not supported in JtR).
- ----------
- unzip_pass zipfile=path/to/file.zip password=FILE0 0=passwords.txt -x ignore:code!=0
- }}}
- CHANGELOG
- ---------
- * v0.6 2014/08/25
- - added CSV and XML output formats
- - added module execution time column
- - improved RANGE keyword
- - new modules: rlogin_login, umbrack_crack
- - minor bug fixes/improvements in http_fuzz and smb_login
- - added more TLDs to dns_forward
- * v0.5 2013/07/05
- - new modules: mysql_query, tcp_fuzz
- - new RANGE and PROG keywords (supersedes the reading from stdin feature)
- - switched to impacket for mssql_login
- - output more intuitive
- - fixed connection cache
- - minor bug fixes
- * v0.4 2012/11/02
- - new modules: smb_lookupsid, finger_lookup, pop_login, imap_login, vmauthd_login
- - improved connection cache
- - improved usage, user can now act upon specific reponses (eg. stop brute-forcing host if down, or stop testing login if password found)
- - improved dns brute-forcing presentation
- - switched to dnspython which is not limited to the IN class (eg. can now scan for {hostname,version}.bind)
- - rewrote itertools.product to avoid memory over-consumption when using large wordlists
- - can now read wordlist from stdin
- - added timeout option to most of the network brute-forcing modules
- - added SSL and/or TLS support to a few modules
- - before_egrep now allows more than one expression (ie. useful when more than one random nonce needs to be submitted)
- - fixed numerous bugs
- * v0.3 2011/12/16
- - minor bugs fixed in http_fuzz
- - option -e better implemented
- - better warnings about missing dependencies
- * v0.2 2011/12/01
- - new smtp_login module
- - several bugs fixed
- * v0.1 2011/11/25 : Public release
- TODO
- ----
- * new option -e ns like in Medusa (not likely to be implemented due to design)
- * replace dnspython|paramiko|IPy with a better module (scapy|libssh2|netaddr... ?) // https://netaddr.readthedocs.org/en/latest/tutorial_01.html
- * use impacket/enum_lookupsids to automatically get the sid
- '''
- # }}}
- # logging {{{
- class Logger:
- def __init__(self, pipe):
- self.pipe = pipe
- self.name = multiprocessing.current_process().name
- # neat but wont work on windows
- # def __getattr__(self, action):
- # def send(*args):
- # self.pipe.send((self.name, action, args))
- # return send
- def send(self, action, *args):
- self.pipe.send((self.name, action, args))
- def quit(self):
- self.send('quit')
- def headers(self):
- self.send('headers')
- def result(self, *args):
- self.send('result', *args)
- def save(self, *args):
- self.send('save', *args)
- def setLevel(self, level):
- self.send('setLevel', level)
- def warn(self, msg):
- self.send('warn', msg)
- def info(self, msg):
- self.send('info', msg)
- def debug(self, msg):
- self.send('debug', msg)
- import logging
- class TXTFormatter(logging.Formatter):
- def __init__(self, indicatorsfmt):
- self.resultfmt = '%(asctime)s %(name)-7s %(levelname)7s - ' + ' '.join('%%(%s)%ss' % (k, v) for k, v in indicatorsfmt) + ' | %(candidate)-34s | %(num)5s | %(mesg)s'
- logging.Formatter.__init__(self, datefmt='%H:%M:%S')
- def format(self, record):
- if not record.msg or record.msg == 'headers':
- self._fmt = self.resultfmt
- if not all(True if 0x20 <= ord(c) < 0x7f else False for c in record.candidate):
- record.candidate = repr(record.candidate)
- else:
- if record.levelno == logging.DEBUG:
- self._fmt = '%(asctime)s %(name)-7s %(levelname)7s [%(pname)s] %(message)s'
- else:
- self._fmt = '%(asctime)s %(name)-7s %(levelname)7s - %(message)s'
- return logging.Formatter.format(self, record)
- class CSVFormatter(logging.Formatter):
- def __init__(self, indicatorsfmt):
- fmt = '%(asctime)s,%(levelname)s,'+','.join('%%(%s)s' % name for name, _ in indicatorsfmt)+',%(candidate)s,%(num)s,%(mesg)s'
- logging.Formatter.__init__(self, fmt, datefmt='%H:%M:%S')
- def format(self, record):
- for k in ['candidate', 'mesg']:
- record.__dict__[k] = '"%s"' % record.__dict__[k].replace('"', '""')
- return logging.Formatter.format(self, record)
- class XMLFormatter(logging.Formatter):
- def __init__(self, indicatorsfmt):
- fmt = '''<result time="%(asctime)s" level="%(levelname)s">
- ''' + '\n'.join(' <{0}>%({1})s</{0}>'.format(name.replace(':', '_'), name) for name, _ in indicatorsfmt) + '''
- <candidate>%(candidate)s</candidate>
- <num>%(num)s</num>
- <mesg>%(mesg)s</mesg>
- <target %(target)s/>
- </result>'''
- logging.Formatter.__init__(self, fmt, datefmt='%H:%M:%S')
- def format(self, record):
- for k, v in record.__dict__.iteritems():
- if isinstance(v, basestring):
- record.__dict__[k] = xmlescape(v)
- return super(XMLFormatter, self).format(record)
- class MsgFilter(logging.Filter):
- def filter(self, record):
- if record.msg:
- return 0
- else:
- return 1
- def process_logs(pipe, indicatorsfmt, argv, log_dir):
- ignore_ctrlc()
- try:
- # python3
- logging._levelToName[logging.ERROR] = 'FAIL'
- except:
- # python2
- logging._levelNames[logging.ERROR] = 'FAIL'
- handler_out = logging.StreamHandler()
- handler_out.setFormatter(TXTFormatter(indicatorsfmt))
- logger = logging.getLogger('patator')
- logger.setLevel(logging.DEBUG)
- logger.addHandler(handler_out)
- names = [name for name, _ in indicatorsfmt] + ['candidate', 'num', 'mesg']
- if log_dir:
- runtime_log = os.path.join(log_dir, 'RUNTIME.log')
- results_csv = os.path.join(log_dir, 'RESULTS.csv')
- results_xml = os.path.join(log_dir, 'RESULTS.xml')
- with open(runtime_log, 'a') as f:
- f.write('$ %s\n' % ' '.join(argv))
- if not os.path.exists(results_csv):
- with open(results_csv, 'w') as f:
- f.write('time,level,%s\n' % ','.join(names))
- if not os.path.exists(results_xml):
- with open(results_xml, 'w') as f:
- f.write('<?xml version="1.0" encoding="UTF-8"?>\n<root>\n')
- f.write('<start utc=%s local=%s/>\n' % (xmlquoteattr(strfutctime()), xmlquoteattr(strflocaltime())))
- f.write('<cmdline>%s</cmdline>\n' % xmlescape(' '.join(argv)))
- f.write('<module>%s</module>\n' % xmlescape(argv[0]))
- f.write('<options>\n')
- i = 0
- del argv[0]
- while i < len(argv):
- arg = argv[i]
- if arg[0] == '-':
- if arg in ('-d', '--debug'):
- f.write(' <option type="global" name=%s/>\n' % xmlquoteattr(arg))
- else:
- if not arg.startswith('--') and len(arg) > 2:
- name, value = arg[:2], arg[2:]
- elif '=' in arg:
- name, value = arg.split('=', 1)
- else:
- name, value = arg, argv[i+1]
- i += 1
- f.write(' <option type="global" name=%s>%s</option>\n' % (xmlquoteattr(name), xmlescape(value)))
- else:
- name, value = arg.split('=', 1)
- f.write(' <option type="module" name=%s>%s</option>\n' % (xmlquoteattr(name), xmlescape(value)))
- i += 1
- f.write('</options>\n')
- f.write('<results>\n')
- else: # remove "</results>...</root>"
- with open(results_xml, 'r+') as f:
- f.seek(f.read().find('</results>'))
- f.truncate(f.tell())
- handler_log = logging.FileHandler(runtime_log)
- handler_csv = logging.FileHandler(results_csv)
- handler_xml = logging.FileHandler(results_xml)
- handler_csv.addFilter(MsgFilter())
- handler_xml.addFilter(MsgFilter())
- handler_log.setFormatter(TXTFormatter(indicatorsfmt))
- handler_csv.setFormatter(CSVFormatter(indicatorsfmt))
- handler_xml.setFormatter(XMLFormatter(indicatorsfmt))
- logger.addHandler(handler_log)
- logger.addHandler(handler_csv)
- logger.addHandler(handler_xml)
- while True:
- pname, action, args = pipe.recv()
- if action == 'quit':
- if log_dir:
- with open(os.path.join(log_dir, 'RESULTS.xml'), 'a') as f:
- f.write('</results>\n<stop utc=%s local=%s/>\n</root>\n' % (xmlquoteattr(strfutctime()), xmlquoteattr(strflocaltime())))
- break
- elif action == 'headers':
- logger.info(' '*77)
- logger.info('headers', extra=dict((n, n) for n in names))
- logger.info('-'*77)
- elif action == 'result':
- typ, resp, candidate, num = args
- results = [(name, value) for (name, _), value in zip(indicatorsfmt, resp.indicators())]
- results += [('candidate', candidate), ('num', num), ('mesg', str(resp)), ('target', resp.str_target())]
- if typ == 'fail':
- logger.error(None, extra=dict(results))
- else:
- logger.info(None, extra=dict(results))
- elif action == 'save':
- resp, num = args
- if log_dir:
- filename = '%d_%s' % (num, '-'.join(map(str, resp.indicators())))
- with open('%s.txt' % os.path.join(log_dir, filename), 'w') as f:
- f.write(resp.dump())
- elif action == 'setLevel':
- logger.setLevel(args[0])
- else: # 'warn', 'info', 'debug'
- getattr(logger, action)(args[0], extra={'pname': pname})
- # }}}
- # imports {{{
- import re
- import os
- import sys
- from time import localtime, gmtime, strftime, sleep, time
- from platform import system
- from functools import reduce
- from select import select
- from itertools import islice
- import string
- import random
- from base64 import b64encode
- from datetime import timedelta, datetime
- from struct import unpack
- import socket
- import subprocess
- import hashlib
- from collections import defaultdict
- import multiprocessing
- import signal
- import ctypes
- from xml.sax.saxutils import escape as xmlescape, quoteattr as xmlquoteattr
- try:
- # python3+
- from queue import Empty, Full
- from urllib.parse import quote, urlencode, urlparse, urlunparse, parse_qsl, quote_plus
- from io import StringIO
- from sys import maxsize as maxint
- except ImportError:
- # python2.6+
- from Queue import Empty, Full
- from urllib import quote, urlencode, quote_plus
- from urlparse import urlparse, urlunparse, parse_qsl
- from cStringIO import StringIO
- from sys import maxint
- notfound = []
- try:
- from IPy import IP
- has_ipy = True
- except ImportError:
- has_ipy = False
- notfound.append('IPy')
- import multiprocessing.forking
- class _Popen(multiprocessing.forking.Popen):
- def __init__(self, *args, **kw):
- if hasattr(sys, 'frozen'):
- # We have to set original _MEIPASS2 value from sys._MEIPASS
- # to get --onefile mode working.
- os.putenv('_MEIPASS2', sys._MEIPASS)
- try:
- super(_Popen, self).__init__(*args, **kw)
- finally:
- if hasattr(sys, 'frozen'):
- # On some platforms (e.g. AIX) 'os.unsetenv()' is not
- # available. In those cases we cannot delete the variable
- # but only set it to the empty string. The bootloader
- # can handle this case.
- if hasattr(os, 'unsetenv'):
- os.unsetenv('_MEIPASS2')
- else:
- os.putenv('_MEIPASS2', '')
- class Process(multiprocessing.Process):
- _Popen = _Popen
- # So BaseManager.start() uses this new Process class
- multiprocessing.Process = Process
- from multiprocessing.managers import SyncManager
- # imports }}}
- # utils {{{
- def strfutctime():
- return strftime("%Y-%m-%d %H:%M:%S", gmtime())
- def strflocaltime():
- return strftime("%Y-%m-%d %H:%M:%S %Z", localtime())
- def which(program):
- def is_exe(fpath):
- return os.path.exists(fpath) and os.access(fpath, os.X_OK)
- fpath, fname = os.path.split(program)
- if on_windows() and fname[-4:] != '.exe' :
- fname += '.exe'
- if fpath:
- if is_exe(program):
- return program
- else:
- for path in os.environ["PATH"].split(os.pathsep):
- exe_file = os.path.join(path, fname)
- if is_exe(exe_file):
- return exe_file
- return None
- def build_logdir(opt_dir, opt_auto):
- if opt_auto:
- return create_time_dir(opt_dir or '/tmp/patator', opt_auto)
- elif opt_dir:
- return create_dir(opt_dir)
- else:
- return None
- def create_dir(top_path):
- top_path = os.path.abspath(top_path)
- if os.path.isdir(top_path):
- files = os.listdir(top_path)
- if files:
- if raw_input("Directory '%s' is not empty, do you want to wipe it ? [Y/n]: " % top_path) != 'n':
- for root, dirs, files in os.walk(top_path):
- if dirs:
- print("Directory '%s' contains sub-directories, safely aborting..." % root)
- sys.exit(0)
- for f in files:
- os.unlink(os.path.join(root, f))
- break
- else:
- os.mkdir(top_path)
- return top_path
- def create_time_dir(top_path, desc):
- now = localtime()
- date, time = strftime('%Y-%m-%d', now), strftime('%H%M%S', now)
- top_path = os.path.abspath(top_path)
- date_path = os.path.join(top_path, date)
- time_path = os.path.join(top_path, date, time + '_' + desc)
- if not os.path.isdir(top_path):
- os.makedirs(top_path)
- if not os.path.isdir(date_path):
- os.mkdir(date_path)
- if not os.path.isdir(time_path):
- os.mkdir(time_path)
- return time_path
- def pprint_seconds(seconds, fmt):
- return fmt % reduce(lambda x,y: divmod(x[0], y) + x[1:], [(seconds,),60,60])
- def md5hex(plain):
- return hashlib.md5(plain).hexdigest()
- def sha1hex(plain):
- return hashlib.sha1(plain).hexdigest()
- # I rewrote itertools.product to avoid memory over-consumption when using large wordlists
- def product(xs, *rest):
- if len(rest) == 0:
- for x in xs():
- yield [x]
- else:
- for head in xs():
- for tail in product(*rest):
- yield [head] + tail
- def chain(*iterables):
- def xs():
- for iterable in iterables:
- for element in iterable:
- yield element
- return xs
- class FileIter:
- def __init__(self, filename):
- self.filename = filename
- def __iter__(self):
- return open(self.filename)
- def padhex(d):
- x = '%x' % d
- return '0' * (len(x) % 2) + x
- # These are examples. You can easily write your own iterator to fit your needs.
- # Or using the PROG keyword, you can call an external program such as:
- # - seq(1) from coreutils
- # - http://hashcat.net/wiki/doku.php?id=maskprocessor
- # - john -stdout -i
- # For example:
- # $ ./dummy_test data=PROG0 0='seq 1 80'
- # $ ./dummy_test data=PROG0 0='mp64.bin ?l?l?l',$(mp64.bin --combination ?l?l?l)
- class RangeIter:
- def __init__(self, typ, rng, random=None):
- if typ in ('hex', 'int', 'float'):
- m = re.match(r'(-?.+?)-(-?.+)$', rng) # 5-50 or -5-50 or 5--50 or -5--50
- if not m:
- raise NotImplementedError("Unsupported range '%s'" % rng)
- mn = m.group(1)
- mx = m.group(2)
- if typ in ('hex', 'int'):
- mn = int(mn, 16 if '0x' in mn else 10)
- mx = int(mx, 16 if '0x' in mx else 10)
- if typ == 'hex':
- fmt = padhex
- elif typ == 'int':
- fmt = '%d'
- elif typ == 'float':
- from decimal import Decimal
- mn = Decimal(mn)
- mx = Decimal(mx)
- if mn > mx:
- step = -1
- else:
- step = 1
- elif typ == 'letters':
- charset = [c for c in string.letters]
- elif typ in ('lower', 'lowercase'):
- charset = [c for c in string.lowercase]
- elif typ in ('upper', 'uppercase'):
- charset = [c for c in string.uppercase]
- else:
- raise NotImplementedError("Incorrect type '%s'" % typ)
- def zrange(start, stop, step, fmt):
- x = start
- while x != stop+step:
- if callable(fmt):
- yield fmt(x)
- else:
- yield fmt % x
- x += step
- def letterrange(first, last, charset):
- for k in range(len(last)):
- for x in product(*[chain(charset)]*(k+1)):
- result = ''.join(x)
- if first:
- if first != result:
- continue
- else:
- first = None
- yield result
- if result == last:
- return
- if typ == 'float':
- precision = max(len(str(x).partition('.')[-1]) for x in (mn, mx))
- fmt = '%%.%df' % precision
- exp = 10**precision
- step *= Decimal(1) / exp
- self.generator = zrange, (mn, mx, step, fmt)
- self.size = int(abs(mx-mn) * exp) + 1
- def random_generator():
- while True:
- yield fmt % (Decimal(random.randint(mn*exp, mx*exp)) / exp)
- elif typ in ('hex', 'int'):
- self.generator = zrange, (mn, mx, step, fmt)
- self.size = abs(mx-mn) + 1
- def random_generator():
- while True:
- yield fmt % random.randint(mn, mx)
- else: # letters, lower, upper
- def count(f):
- total = 0
- i = 0
- for c in f[::-1]:
- z = charset.index(c) + 1
- total += (len(charset)**i)*z
- i += 1
- return total + 1
- first, last = rng.split('-')
- self.generator = letterrange, (first, last, charset)
- self.size = count(last) - count(first) + 1
- if random:
- self.generator = random_generator, ()
- self.size = maxint
- def __iter__(self):
- fn, args = self.generator
- return fn(*args)
- def __len__(self):
- return self.size
- class ProgIter:
- def __init__(self, prog):
- self.prog = prog
- def __iter__(self):
- p = subprocess.Popen(self.prog.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- return p.stdout
- class Progress:
- def __init__(self):
- self.current = ''
- self.done_count = 0
- self.hits_count = 0
- self.skip_count = 0
- self.fail_count = 0
- self.seconds = [1]*25 # avoid division by zero early bug condition
- class TimeoutError(Exception):
- pass
- def on_windows():
- return 'Win' in system()
- def ignore_ctrlc():
- if on_windows():
- ctypes.windll.kernel32.SetConsoleCtrlHandler(0, 1)
- else:
- signal.signal(signal.SIGINT, signal.SIG_IGN)
- def handle_alarm():
- if not on_windows():
- signal.signal(signal.SIGALRM, raise_timeout)
- def raise_timeout(signum, frame):
- if signum == signal.SIGALRM:
- raise TimeoutError('timed out')
- def enable_alarm(timeout):
- if not on_windows():
- signal.alarm(timeout)
- def disable_alarm():
- if not on_windows():
- signal.alarm(0)
- # SyncManager.start(initializer) only available since python2.7
- class MyManager(SyncManager):
- @classmethod
- def _run_server(cls, registry, address, authkey, serializer, writer, initializer=None, initargs=()):
- ignore_ctrlc()
- super(MyManager, cls)._run_server(registry, address, authkey, serializer, writer)
- # }}}
- # Controller {{{
- class Controller:
- builtin_actions = (
- ('ignore', 'do not report'),
- ('retry', 'try payload again'),
- ('free', 'dismiss future similar payloads'),
- ('quit', 'terminate execution now'),
- )
- available_encodings = {
- 'hex': (lambda s: s.encode('hex'), 'encode in hexadecimal'),
- 'unhex': (lambda s: s.decode('hex'), 'decode from hexadecimal'),
- 'b64': (b64encode, 'encode in base64'),
- 'md5': (md5hex, 'hash in md5'),
- 'sha1': (sha1hex, 'hash in sha1'),
- 'url': (quote_plus, 'url encode'),
- }
- def expand_key(self, arg):
- yield arg.split('=', 1)
- def find_file_keys(self, value):
- return map(int, re.findall(r'FILE(\d)', value))
- def find_net_keys(self, value):
- return map(int, re.findall(r'NET(\d)', value))
- def find_combo_keys(self, value):
- return [map(int, t) for t in re.findall(r'COMBO(\d)(\d)', value)]
- def find_module_keys(self, value):
- return map(int, re.findall(r'MOD(\d)', value))
- def find_range_keys(self, value):
- return map(int, re.findall(r'RANGE(\d)', value))
- def find_prog_keys(self, value):
- return map(int, re.findall(r'PROG(\d)', value))
- def usage_parser(self, name):
- from optparse import OptionParser
- from optparse import OptionGroup
- from optparse import IndentedHelpFormatter
- class MyHelpFormatter(IndentedHelpFormatter):
- def format_epilog(self, epilog):
- return epilog
- def format_heading(self, heading):
- if self.current_indent == 0 and heading == 'Options':
- heading = 'Global options'
- return "%*s%s:\n" % (self.current_indent, "", heading)
- def format_usage(self, usage):
- return '%s\nUsage: %s\n' % (__banner__, usage)
- available_actions = self.builtin_actions + self.module.available_actions
- available_conditions = self.module.Response.available_conditions
- usage = '''%%prog <module-options ...> [global-options ...]
- Examples:
- %s''' % '\n '.join(self.module.usage_hints)
- usage += '''
- Module options:
- %s ''' % ('\n'.join(' %-14s: %s' % (k, v) for k, v in self.module.available_options))
- epilog = '''
- Syntax:
- -x actions:conditions
- actions := action[,action]*
- action := "%s"
- conditions := condition=value[,condition=value]*
- condition := "%s"
- ''' % ('" | "'.join(k for k, v in available_actions),
- '" | "'.join(k for k, v in available_conditions))
- epilog += '''
- %s
- %s
- ''' % ('\n'.join(' %-12s: %s' % (k, v) for k, v in available_actions),
- '\n'.join(' %-12s: %s' % (k, v) for k, v in available_conditions))
- epilog += '''
- For example, to ignore all redirects to the home page:
- ... -x ignore:code=302,fgrep='Location: /home.html'
- -e tag:encoding
- tag := any unique string (eg. T@G or _@@_ or ...)
- encoding := "%s"
- %s''' % ('" | "'.join(k for k in self.available_encodings),
- '\n'.join(' %-12s: %s' % (k, v) for k, (f, v) in self.available_encodings.items()))
- epilog += '''
- For example, to encode every password in base64:
- ... host=10.0.0.1 user=admin password=_@@_FILE0_@@_ -e _@@_:b64
- Please read the README inside for more examples and usage information.
- '''
- parser = OptionParser(usage=usage, prog=name, epilog=epilog, version=__banner__, formatter=MyHelpFormatter())
- exe_grp = OptionGroup(parser, 'Execution')
- exe_grp.add_option('-x', dest='actions', action='append', default=[], metavar='arg', help='actions and conditions, see Syntax below')
- exe_grp.add_option('--start', dest='start', type='int', default=0, metavar='N', help='start from offset N in the wordlist product')
- exe_grp.add_option('--stop', dest='stop', type='int', default=None, metavar='N', help='stop at offset N')
- exe_grp.add_option('--resume', dest='resume', metavar='r1[,rN]*', help='resume previous run')
- exe_grp.add_option('-e', dest='encodings', action='append', default=[], metavar='arg', help='encode everything between two tags, see Syntax below')
- exe_grp.add_option('-C', dest='combo_delim', default=':', metavar='str', help="delimiter string in combo files (default is ':')")
- exe_grp.add_option('-X', dest='condition_delim', default=',', metavar='str', help="delimiter string in conditions (default is ',')")
- exe_grp.add_option('--allow-ignore-failures', action='store_true', default=False, dest='allow_ignore_failures', help="failures cannot be ignored with -x (this is by design to avoid false negatives) this option overrides this behavior")
- opt_grp = OptionGroup(parser, 'Optimization')
- opt_grp.add_option('--rate-limit', dest='rate_limit', type='float', default=0, metavar='N', help='wait N seconds between each test (default is 0)')
- 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)')
- 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)')
- opt_grp.add_option('-t', '--threads', dest='num_threads', type='int', default=10, metavar='N', help='number of threads (default is 10)')
- log_grp = OptionGroup(parser, 'Logging')
- log_grp.add_option('-l', dest='log_dir', metavar='DIR', help="save output and response data into DIR ")
- 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')")
- dbg_grp = OptionGroup(parser, 'Debugging')
- dbg_grp.add_option('-d', '--debug', dest='debug', action='store_true', default=False, help='enable debug messages')
- parser.option_groups.extend([exe_grp, opt_grp, log_grp, dbg_grp])
- return parser
- def parse_usage(self, argv):
- parser = self.usage_parser(argv[0])
- opts, args = parser.parse_args(argv[1:])
- if not len(args) > 0:
- parser.print_usage()
- print('ERROR: wrong usage. Please read the README inside for more information.')
- sys.exit(2)
- return opts, args
- def __init__(self, module, argv):
- self.thread_report = []
- self.thread_progress = []
- self.payload = {}
- self.iter_keys = {}
- self.enc_keys = []
- self.module = module
- opts, args = self.parse_usage(argv)
- self.combo_delim = opts.combo_delim
- self.condition_delim = opts.condition_delim
- self.rate_limit = opts.rate_limit
- self.timeout = opts.timeout
- self.max_retries = opts.max_retries
- self.num_threads = opts.num_threads
- self.start, self.stop = opts.start, opts.stop
- self.allow_ignore_failures = opts.allow_ignore_failures
- self.resume = [int(i) for i in opts.resume.split(',')] if opts.resume else None
- manager = MyManager()
- manager.start()
- self.ns = manager.Namespace()
- self.ns.actions = {}
- self.ns.free_list = []
- self.ns.paused = False
- self.ns.quit_now = False
- self.ns.start_time = 0
- self.ns.total_size = 1
- pipe = multiprocessing.Pipe(duplex=False)
- logsvc = Process(name='LogSvc', target=process_logs, args=(pipe[0], module.Response.indicatorsfmt, argv, build_logdir(opts.log_dir, opts.auto_log)))
- logsvc.daemon = True
- logsvc.start()
- global logger
- logger = Logger(pipe[1])
- if opts.debug:
- logger.setLevel(logging.DEBUG)
- else:
- logger.setLevel(logging.INFO)
- wlists = {}
- kargs = []
- for arg in args: # ('host=NET0', '0=10.0.0.0/24', 'user=COMBO10', 'password=COMBO11', '1=combos.txt', 'name=google.MOD2', '2=TLD')
- for k, v in self.expand_key(arg):
- logger.debug('k: %s, v: %s' % (k, v))
- if k.isdigit():
-