-
Notifications
You must be signed in to change notification settings - Fork 0
/
NTLMCrack.py
executable file
·160 lines (133 loc) · 5.05 KB
/
NTLMCrack.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env python3
import re
import requests
import argparse
import sys
from time import sleep
import configparser
from os.path import expanduser, join
import sqlite3
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BLUE = '\033[94m'
RESET = '\033[0m'
VERSION = "1.0"
banner = """ _ __________ __ _________ __
/ | / /_ __/ / / |/ / ____/________ ______/ /__
/ |/ / / / / / / /|_/ / / / ___/ __ `/ ___/ //_/
/ /| / / / / /___/ / / / /___/ / / /_/ / /__/ ,< v%s
/_/ |_/ /_/ /_____/_/ /_/\\____/_/ \\__,_/\\___/_/|_| by @adamkadaban
""" % VERSION
def crack_hashes(users, hashes):
hashPhases = [hashes[i: i + 500] for i in range(0, len(hashes), 500)]
cracked = []
for crackPhase in hashPhases:
toCrack = "\n".join(crackPhase)
response = requests.post('https://ntlm.pw/api/lookup/nt', data=toCrack)
if response.status_code == 429:
rate_limit_seconds = int(response.headers['X-Rate-Limit-Reset'])
print(f"{YELLOW}[*] Too many requests. Sleeping for {rate_limit_seconds} seconds until quota fills up.{RESET}")
sleep(rate_limit_seconds)
response = requests.post('https://ntlm.pw/api/lookup/nt', data=toCrack)
elif response.status_code != 200:
print(f"{RED}[-] Error: {response.text}\nExiting{RESET}")
sys.exit(1)
r_text = response.text
cracked.extend([c.split(':')[1] for c in r_text.split("\n")[:-1]])
return len(users), dict(zip(users, cracked))
def crack_hashes_from_file(path, n):
pattern = r'(.*):\d+:[a-f0-9]{32}:([a-f0-9]{32}):::'
# build hashes to crack
users = []
hashes = []
try:
with open(path) as fin:
for idx, line in enumerate(fin, start=1):
match = re.match(pattern, line.rstrip())
if match:
users.append(match.group(1))
hashes.append(match.group(2))
else:
print(f"{RED}[-] Couldn't match on line:{RESET} {line}")
if idx == n:
break
except FileNotFoundError:
print(f'{RED}[-] File "{path}" not found')
sys.exit(1)
return crack_hashes(users, hashes)
def crack_hashes_from_nxc(workspace, n, proto='smb'):
nxc_path=expanduser('~/.nxc')
if workspace is None:
nxc_config = configparser.ConfigParser()
nxc_config.read(join(nxc_path, 'nxc.conf'))
workspace = nxc_config['nxc']['workspace']
print(f"{BLUE}[*] Getting hashes from {workspace} workspace{RESET}")
users = []
hashes = []
db_file = join(nxc_path, 'workspaces', workspace, proto + '.db')
try:
con = sqlite3.connect(db_file)
except:
print(f'{RED}[-] Workspace "{workspace}" not found: {db_file}\nExiting{RESET}')
sys.exit(1)
cur = con.cursor()
if n:
res = cur.execute(f"select domain,username,password from users where credtype='hash' limit {n}").fetchall()
else:
res = cur.execute(f"select domain,username,password from users where credtype='hash'").fetchall()
for row in res:
users.append(f'{row[0]}\\{row[1]}')
userHash = row[2]
if ':' in userHash:
hashes.append(userHash.split(':')[1])
else:
hashes.append(userHash)
return crack_hashes(users, hashes)
def main():
print(banner)
parser = argparse.ArgumentParser(description="Use ntlm.pw to automatically convert hash dumps to credentials")
group = parser.add_mutually_exclusive_group()
group.add_argument('file_path', nargs='?', help='Path to the file containing hash dumps')
group.add_argument('-X', '--nxc', action='store', nargs='?', default=False, const=None, metavar='workspace', help='Dump hashes from netexec. Will use currently selected workspace by default')
parser.add_argument('-s', '--separate-files', action='store_true', help='Output credentials in separate files')
parser.add_argument('-n', type=int, help='Limit to the first n lines of the file')
parser.add_argument('-f', '--filter-not-found', action='store_true', help="Filter hashes that can't be cracked")
args = parser.parse_args()
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
if args.filter_not_found:
print(f"{BLUE}[*] Filtering hashes that can't be cracked{RESET}")
if args.n:
print(f"{BLUE}[*] Limiting to first {args.n} hashes{RESET}")
if args.nxc == False:
toCrackLen, cracked = crack_hashes_from_file(args.file_path, args.n)
totCracked = 0
else:
toCrackLen, cracked = crack_hashes_from_nxc(args.nxc, args.n)
totCracked = 0
if args.separate_files:
userFile = open('users', 'w')
passFile = open('passwords', 'w')
for user, password in cracked.items():
if not args.filter_not_found or password != '[not found]':
userFile.write(user + "\n")
passFile.write(password + "\n")
if password != '[not found]':
totCracked += 1
userFile.close()
passFile.close()
print(f'{GREEN}[+] Wrote users/passwords to files: "users" and "passwords"{RESET}')
else:
with open('credentials', 'w') as fout:
for user, password in cracked.items():
if not args.filter_not_found or password != '[not found]':
fout.write(user + ":" + password + "\n")
if password != '[not found]':
totCracked += 1
print(f'{GREEN}[+] Wrote credentials to file: "credentials"{RESET}')
print(f'{GREEN}[+] Cracked {totCracked}/{toCrackLen} hashes{RESET}')
if __name__ == "__main__":
main()