forked from trustedsec/CrackHound
-
Notifications
You must be signed in to change notification settings - Fork 0
/
crackhound.py
262 lines (235 loc) · 11.1 KB
/
crackhound.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#!/usr/bin/env python
import argparse
import os
import neo4j
def banner():
print(r"""
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWN0xl:,'.........',,:oOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWN0dl:;,,,,;;;::::::;;;,''',:okKWMMMMMWWWNWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNXWMMMMMMMMMMWXkl;,,;::ccccccccccccccccccc;. .,collcccc:ccldOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNOl:dXMMMMMMMMW0o;',:cc;..':cccccccccccccccccc:. ..',;::::::;,,;o0WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOl' .lNMMMMMMMMXo'';:ccc:. ':cccccccccccccccccc:'.',:cccc::ccccccc:,,oXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMKc. .oNMMMMMMWXx;';:ccccc:,. ..';:cccccccc:ccccccccccc:;'...;cccccccc;':0MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMX: .oNMMMMWKxc'.':ccccccccc;. ..',,'..';:ccccccccc:, .;cccccccc:';OWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMx. .cONMNKkl;. .:ccccccccccc;. .,::cccccccccc;. .;cccccccc:',kWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMx. 'dkdc,. .:ccccccc:;,,,. .,cccccccccccccc' .,::cccccc:''dNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMXo. .''. .';:cccccc:,. .;ccccccccccccc:,. ...':cccc:,'cxkk0NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKxdodk0Oc'';:cccccccc:,. .ox; .;ccccc:::c:;'... .....';;'....:ONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM0;.;cccccccccc:,. 'kWMXo. .:ccccc:,.... .','......';xWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWK:.;cccccccccc:' ;0WMMM0, ..''',,,;;;,'... 'lc. .,;:;''',,'... .oXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKkl' .;:ccc:;;,,'. .oXMMMMWo .......,::::::;,'...dWWO, ':ccc;,,;;...''...;OWMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNd. .,:c;',:. .:kXWMMMMMW0l;,,;;;.............,;.'kWW0; ':cc:'. .;:;,'......oNMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNo. .....'''.;0WKx:. .:xKWMMMMMMMWWNNXd. .;o; .::.;KMMK: .,:ccc,. ';,.. .. ;KMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMNo. ':loodkXMMMMWXOo;. .:OWMMMMMMMMWk. .;d00kc. .,.:KMMXc.,:ccc:. .;:,,'. ';,..;0WMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMKc ,xXWMMMMMMMMMMMMMMNOl. '0MMMMMMMWk. ;ONMK: ,OMMNl.,:cccc, ..,;cc,..:c:,.,dKWMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMWO, .oXMMMMMMMMMMMMMMMMNo'. cXMMMMMMNx. .lXMMX: :0MMXl.,ccccc;. .;c:;;:cc::,''oNMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMKc. .xWMMMMMMMMMMMMMMMMKc. .lXMMMMMWKc. .,dNMMMNx;.',ckNMWK:.;ccccc:' .';:ccc:,... ;XMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMWo. .coxKMMMMMMMMMMMMMMk. 'xNMMMMMMX: ckONMMMMMWNNWWMWXd'.;cccccc;..;. ..';:,','. .dWMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMX: ...;0MMMMMMMMMMMMMW0doloxKWMMMMMMNo. .:KMMMMMMMWXkl'..;cccccc:'.oN0, ..';;,,',. .dWMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMWKc. .'.',.;0MMMMMMMMMMMMMMMMMMMMMMMMMMMXc ;0MMMMMXo,.',,;::::::;,.;KNk, ....,:;,;'....kMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMXl....'..'.,kWMMMMMMMMMMMMMMMMMMMMMMMMMMMXl.........'dWMMMM0:.',,,,,,,,,,'':OWXc..,,;;,;,,;,':O00NMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMWXKKXKKKKKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXXXXXXXXXXWMMMMMMNXXXXXXXXXXXXXXWMMMNXXXXXXXXXXXXXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
CRACKHOUND - jfmaes
""")
def parse_compromised_users(file):
compromised_users = []
# Trying to open file specified on command line
try:
os.path.exists(file)
f = open(file, "r")
# If file doesn't exist, exit with exception
except Exception as a:
print(f"{file} is not valid or doesn't exist!")
exit()
else:
lines = f.readlines()
for line in lines:
user_dict = {}
line = line.strip()
if ":" in line:
split = line.split(":")
compromised_user = split[0]
user_dict["password"] = split[2]
else:
compromised_user = line
user_dict["username"] = compromised_user
compromised_users.append(user_dict)
return compromised_users
def format_compromised_users(compromised_users, domain):
for user in compromised_users:
user["username"] = user["username"].upper()
if "\\" in user["username"]:
split = user["username"].split("\\")
if domain:
user["username"] = split[1] + "@" + domain
else:
user["username"] = split[1] + "@" + split[0]
else:
pass
return compromised_users
def update_database(
compromised_users, url, username, password, plaintext, verbose, add_password
):
try:
print(
"Connecting to neo4j on {} with username {} and password {} ".format(
url, username, password
)
)
try:
db_conn = neo4j.GraphDatabase.driver(
url, auth=(username, password), encrypted=False
)
except Exception:
print("Couldn't connect to database.")
else:
print("Connection success!")
for user in compromised_users:
try:
with db_conn.session() as session:
# If plaintext not specified, simply mark user as owned in BH
if not plaintext:
tx = session.run(
'match (u:User) where u.name="{0}" set u.owned=True return u.name'.format(
user["username"]
)
)
if verbose:
print(
"{0} successfully marked as owned!".format(
tx.single()[0]
)
)
# If add_password specified do the following
# Give you user plaintextpassword attribute and set it to known user password
# Set user to owned and set plaintext attribute to True
# Assuming we want all three here when specifying add_password
elif add_password:
if user["password"]:
tx = session.run(
'match (u:User) where u.name="{0}" set u.plaintextpassword="{1}"'.format(
user["username"], user["password"]
)
)
tx = session.run(
'match (u:User) where u.name="{0}" set u.owned=True set u.plaintext=True return u.name'.format(
user["username"]
)
)
if verbose:
print(
"Added plaintext password for {0} marked as owned, and marked plaintext as True.".format(
user["username"]
)
)
else:
continue
# If nothing specified, set the user to owned and say we know the plaintext password by setting plaintext=True
else:
tx = session.run(
'match (u:User) where u.name="{0}" set u.owned=True set u.plaintext=True return u.name'.format(
user["username"]
)
)
if verbose:
print(
"{0} successfully marked as owned and marked as plaintext!".format(
tx.single()[0]
)
)
except Exception as e:
print(e)
continue
session.close()
except Exception as e:
print(f"An error occured {e}")
def main():
parser = argparse.ArgumentParser(
description="Update bloodhound database with pwned users"
)
parser.add_argument(
"-f",
"--file",
required=True,
help="File with list of all users you have compromised. Supports DOMAIN.COM\\USER:NTHASH:PASS or DOMAIN.COM\\USER",
)
parser.add_argument(
"-url",
"--url",
required=False,
help="The neo4j url to auth to (defaults to bolt://localhost:7687)",
default="bolt://localhost:7687",
)
parser.add_argument(
"-u",
"--username",
required=False,
help="Username to login to neo4j (defaults to neo4j)",
default="neo4j",
)
parser.add_argument(
"-p",
"--password",
required=False,
help="Password to login to neo4j (defaults to bloodhound)",
default="bloodhound",
)
parser.add_argument(
"-plaintext",
"--plain-text",
required=False,
help="Adds plaintext property to compromised user to help with custom queries",
default=False,
action="store_true",
)
parser.add_argument(
"-addpw",
"--add-password",
action="store_true",
required=False,
default=False,
help="Adds the actual plain text password to the bloodhound data as well",
)
parser.add_argument(
"-v",
"--verbose",
required=False,
default=False,
help="verbose",
action="store_true",
)
parser.add_argument(
"-d",
"--domain",
"-fqdn",
required=False,
default="",
help="The domain name of client in case its different than netbiosname. It very likely will be different. Check your secretsdump for more info.",
)
parser.add_argument(
"-s",
"--silent",
required=False,
action="store_true",
help="Don't show ascii art ;)",
)
args = parser.parse_args()
if args.silent:
pass
else:
banner()
compromised_users = parse_compromised_users(args.file)
compromised_users = format_compromised_users(compromised_users, args.domain)
print("Updating database, could take a while...")
update_database(
compromised_users,
args.url,
args.username,
args.password,
args.plain_text,
args.verbose,
args.add_password,
)
print("All Done!")
if __name__ == "__main__":
main()