├── CHANGES ├── README.md └── crackqcli.py /CHANGES: -------------------------------------------------------------------------------- 1 | v0.4 2 | - Added MYSQL support for v4.1+ hashes (double SHA1 hashes) - 40 hex characters 3 | - Removed third-party modules 4 | - Fixed the WPA handshake file opening mode on non-posix systems 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Crackq Client - v0.4 2 | ==================== 3 | 4 | Crackq is an online distributed GPU-accelerated password cracker designed to 5 | help penetration testers and network auditors identify for weak passwords. It 6 | supports a number of hash types and we are actively adding new algorithms. 7 | 8 | Log in to your account (https://hashcrack.org/crackq) to get your API key. 9 | 10 | Installation 11 | ----------- 12 | 13 | Refer to (https://hashcrack.org/crackq/page?n=install) for complete 14 | installation instructions for Windows, Linux and OS X. 15 | 16 | Hash Formats 17 | ------------ 18 | 19 | Currently, the following algorithms are supported: 20 | 21 | * Password protected PDF files (v1.4 - v1.6) 22 | * NTLM 23 | * MD5 24 | * SHA1 25 | * WPA / WPA2 PSK 26 | * VPN IPSec IKE (aggressive mode) MD5 27 | * descrypt / DES(Unix) 28 | * md5crypt / FreeBSD MD5 / Cisco IOS MD5 / MD5(Unix) 29 | * PHPass MD5 (Wordpress, Joomla, phpBB3) 30 | * MYSQL 4.1+ (double SHA1) 31 | 32 | Submitting Hashes 33 | ----------------- 34 | 35 | Refer to our FAQ for detailed instructions on submitting hashes 36 | (https://hashcrack.org/crackq_faq). 37 | -------------------------------------------------------------------------------- /crackqcli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Hashcrack Crackq command line client 4 | # 5 | # Email: support[at]hashcrack.org 6 | # Web: hashcrack.org 7 | 8 | import json 9 | import sys 10 | import getopt 11 | import os 12 | import zlib 13 | import base64 14 | import re 15 | from urllib2 import Request, urlopen, URLError, HTTPError 16 | 17 | SERVER = 'https://hashcrack.org' 18 | CONFIG_PATH = None 19 | ENDPOINTS = { 20 | 'user_email' : '/crackq/v0.1/user_email', 21 | 'submit' : '/crackq/v0.1/submit', 22 | 'client_ver' : '/crackq/v0.1/client_ver' 23 | } 24 | API_KEY = None 25 | MYVER = '0.4' 26 | HASH_TYPES = ['wpa', 27 | 'descrypt', 28 | 'md5crypt', 29 | 'md5', 30 | 'ntlm', 31 | 'sha1', 32 | 'pdf', 33 | 'phpass', 34 | 'mysql'] 35 | 36 | def banner(): 37 | sys.stdout.write('Crackq client %s\n' % MYVER) 38 | sys.stdout.write('support@hashcrack.org\n\n') 39 | 40 | def usage(argv0): 41 | sys.stdout.write('%s [-t|--type hash_type] [hash|file_path]\n' % argv0) 42 | sys.stdout.write('-t --type see supported hash types below\n') 43 | sys.stdout.write('-h --help help\n\n') 44 | sys.stdout.write('Supported hash types:\n') 45 | sys.stdout.write('md5 Unsalted MD5 hashes\n') 46 | sys.stdout.write('ntlm Windows NTLM hashes\n') 47 | sys.stdout.write('sha1 Unsalted SHA1 hashes\n') 48 | sys.stdout.write('wpa WPA/WPA2 handshakes\n') 49 | sys.stdout.write('md5crypt MD5CRYPT / FreeBSD MD5 / Cisco IOS MD5 / MD5(Unix)\n') 50 | sys.stdout.write('descrypt DESCRYPT / DES(Unix)\n') 51 | sys.stdout.write('pdf PDF 1.4 - 1.6\n') 52 | sys.stdout.write('phpass PHPASS (Wordpress, Joomla and phpBB3)\n') 53 | sys.stdout.write('mysql MYSQL4.1+ (double SHA1)\n') 54 | 55 | def validate_hash(_hash, _hash_type): 56 | if _hash_type == 'descrypt': 57 | if re.match('^[\./0-9A-Za-z]{13,13}$', _hash) is None: 58 | return False 59 | elif _hash_type == 'md5crypt': 60 | if re.match('^\$1\$[\./0-9A-Za-z]{0,8}\$[\./0-9A-Za-z]{22,22}$', _hash) is None: 61 | return False 62 | elif _hash_type == 'phpass': 63 | if re.match('^\$[PH]\$[0-9A-Z][./0-9A-Za-z]{30,30}$', _hash) is None: 64 | return False 65 | elif _hash_type == 'pdf': 66 | if re.match('^\$pdf\$[0-9A-Z][./0-9A-Za-z]{30,30}$', _hash) is None: 67 | return False 68 | elif _hash_type == 'sha1' or _hash_type == 'mysql': 69 | if len(_hash) != 40: 70 | sys.stdout.write('[-] ERROR: Invalid hash\n') 71 | return False 72 | try: 73 | int(_hash, 16) 74 | except ValueError: 75 | sys.stdout.write('[-] ERROR: The hash is not in hex\n') 76 | return False 77 | else: 78 | if len(_hash) != 32: 79 | sys.stdout.write('[-] ERROR: Invalid hash\n') 80 | return False 81 | try: 82 | int(_hash, 16) 83 | except ValueError: 84 | sys.stdout.write('[-] ERROR: The hash is not in hex\n') 85 | return False 86 | return True 87 | 88 | def save_config(): 89 | global API_KEY 90 | global CONFIG_PATH 91 | 92 | sys.stdout.write('Enter your API key: ') 93 | key = sys.stdin.readline().strip() 94 | 95 | try: 96 | conf = open(CONFIG_PATH, 'w') 97 | except IOError: 98 | sys.stdout.write('[-] ERROR: Cannot write to %s\n' % CONFIG_PATH) 99 | sys.exit(-1) 100 | 101 | conf.write('key:%s\n' % key) 102 | API_KEY = key 103 | 104 | def load_config(): 105 | global API_KEY 106 | global CONFIG_PATH 107 | 108 | if os.name == 'nt': 109 | CONFIG_PATH = os.getenv('APPDATA') + '\Crackq.cfg' 110 | elif os.name == 'posix': 111 | CONFIG_PATH = os.getenv("HOME") + '/.crackq' 112 | else: 113 | sys.stdout.write('[-] ERROR: Unsupported OS\n') 114 | sys.exit(-1) 115 | try: 116 | conf = open(CONFIG_PATH, 'r') 117 | for l in conf.readlines(): 118 | k, v = l.split(':') 119 | if k == 'key': 120 | API_KEY = v.strip() 121 | if not API_KEY: 122 | sys.stdout.write('[-] ERROR: API KEY NOT FOUND\n') 123 | sys.exit(-1) 124 | except IOError: 125 | save_config() 126 | 127 | if __name__ == '__main__': 128 | _type = None 129 | banner() 130 | 131 | try: 132 | optlist, args = getopt.getopt(sys.argv[1:], 't:huq:', ['type=', 'help', 'update']) 133 | except getopt.GetoptError as err: 134 | print str(err) 135 | usage(sys.argv[0]) 136 | sys.exit(-1) 137 | 138 | for o, a in optlist: 139 | if o in ('-h', '--help'): 140 | usage(sys.argv[0]) 141 | sys.exit(0) 142 | if o in ('-u', '--update'): 143 | os.system('git pull') 144 | sys.exit(0) 145 | if o in ('-t', '--type'): 146 | _type = a 147 | 148 | try: 149 | # check for updates 150 | sys.stdout.write('[+] Checking the current client version...\n') 151 | if urlopen(SERVER + ENDPOINTS['client_ver']).read() != MYVER: 152 | sys.stdout.write('[-] WARNING: NEW CLIENT VERSION IS AVAILABLE: https://hashcrack.org/crackq/page?n=install#update\n') 153 | sys.exit(-1) 154 | 155 | if len(args) != 1: 156 | usage(sys.argv[0]) 157 | sys.exit(-1) 158 | 159 | _content = args[0] 160 | 161 | if not _type or _type not in HASH_TYPES: 162 | sys.stdout.write('[-] ERROR: INVALID HASH TYPE\n') 163 | sys.exit(-1) 164 | 165 | if (_type != 'wpa' and _type != 'pdf') and not validate_hash(_content, _type): 166 | sys.stdout.write('[-] ERROR: INVALID HASH FORMAT\n') 167 | sys.exit(-1) 168 | 169 | load_config() 170 | 171 | headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} 172 | data = {'key': API_KEY} 173 | 174 | sys.stdout.write('[+] Retrieving email...\n') 175 | req = Request(SERVER + ENDPOINTS['user_email']) 176 | req.add_header('Content-Type', 'application/json') 177 | res = urlopen(req, json.dumps(data)) 178 | data = json.load(res) 179 | sys.stdout.write('[+] Results will be emailed to: %s\n' % data['email']) 180 | sys.stdout.write('[+] Submissions left: %s\n' % data['privq_limit']) 181 | 182 | if (data['privq_limit'] > 0): 183 | sys.stdout.write('[+] Sending to the queue...\n') 184 | else: 185 | sys.stdout.write('[-] ERROR: NO QUEUE SUBMISSIONS LEFT. PURCHASE SUBMISSION QUOTA AT https://hashcrack.org/crackq_buy\n') 186 | sys.exit(-1) 187 | 188 | if _type == 'wpa': 189 | try: 190 | f = open(_content, 'rb') 191 | except IOError: 192 | sys.stdout.write('[-] ERROR: Cannot find %s\n' % _content) 193 | sys.exit(-1) 194 | 195 | _raw = f.read() 196 | if _type == 'wpa' and len(_raw) != 392: 197 | sys.stdout.write('[-] ERROR: hccap file is invalid or multiple essids detected\n') 198 | sys.exit(-1) 199 | 200 | _content = base64.b64encode(zlib.compress(_raw)) 201 | f.close() 202 | 203 | data = {'key': API_KEY, 'content': _content, 'type': _type, 'q': 'privq'} 204 | req = Request(SERVER + ENDPOINTS['submit']) 205 | req.add_header('Content-Type', 'application/json') 206 | res = urlopen(req, json.dumps(data)) 207 | sys.stdout.write('[+] Done\n') 208 | except HTTPError as e: 209 | sys.stdout.write('[-] ERROR: HTTP %d - %s\n' % (e.code, json.load(e)['msg'])) 210 | sys.exit(-1) 211 | except URLError as e: 212 | sys.stdout.write('[-] ERROR: UNREACHABLE - %s\n' % e.reason) 213 | sys.exit(-1) 214 | --------------------------------------------------------------------------------