Skip to content
This repository was archived by the owner on Dec 6, 2023. It is now read-only.

Commit 11e75ac

Browse files
author
byt3bl33d3r
committed
Added --fail-limit and --gfail-limit options to limit the amount of
failed login attemptes per host and globally
1 parent 2fe0d79 commit 11e75ac

File tree

4 files changed

+55
-8
lines changed

4 files changed

+55
-8
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ optional arguments:
8383
--port {139,445} SMB port (default: 445)
8484
--server {http,https}
8585
Use the selected server (defaults to http)
86+
--fail-limit LIMIT The max number of failed login attempts allowed per host (default: None)
87+
--gfail-limit LIMIT The max number of failed login attempts allowed globally (default: None)
8688
--verbose Enable verbose output
8789
8890
Credential Gathering:

core/settings.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
def init_args(arg_namespace):
22
"""
3-
args will contain a namespace that we can modify whenever we want
4-
orig_args will contain the original namespace (duh!) and should never be modified
5-
"""
6-
global orig_args
7-
orig_args = arg_namespace
8-
3+
This is just so we can easily share argparse's namespace
4+
"""
5+
96
global args
10-
args = arg_namespace
7+
args = arg_namespace
8+
9+
global gfails
10+
gfails = 0

core/smartlogin.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,22 @@ def smart_login(host, smb, domain):
1010
'''
1111
This function should probably be called ugly_login
1212
'''
13+
fails = 0
14+
1315
if settings.args.combo_file:
1416
with open(settings.args.combo_file, 'r') as combo_file:
1517
for line in combo_file:
1618
try:
19+
20+
if settings.args.fail_limit:
21+
if settings.args.fail_limit == fails:
22+
print_status('{}:{} Reached login fail limit'.format(host, settings.args.port))
23+
raise socket.error
24+
if settings.args.gfail_limit:
25+
if settings.gfails >= settings.args.gfail_limit:
26+
print_status('{}:{} Reached global login fail limit'.format(host, settings.args.port))
27+
raise socket.error
28+
1729
line = line.strip()
1830

1931
lmhash = ''
@@ -52,6 +64,9 @@ def smart_login(host, smb, domain):
5264
return smb
5365
except SessionError as e:
5466
print_error(u"{}:{} {}\\{}:{} {}".format(host, settings.args.port, domain, user, passwd, e))
67+
if 'STATUS_LOGON_FAILURE' in e:
68+
fails += 1
69+
settings.gfails += 1
5570
continue
5671

5772
except Exception as e:
@@ -104,6 +119,16 @@ def smart_login(host, smb, domain):
104119

105120
if hashes:
106121
for ntlm_hash in hashes:
122+
123+
if settings.args.fail_limit:
124+
if settings.args.fail_limit == fails:
125+
print_status('{}:{} Reached login fail limit'.format(host, settings.args.port))
126+
raise socket.error
127+
if settings.args.gfail_limit:
128+
if settings.gfails >= settings.args.gfail_limit:
129+
print_status('{}:{} Reached global login fail limit'.format(host, settings.args.port))
130+
raise socket.error
131+
107132
ntlm_hash = ntlm_hash.strip().lower()
108133
lmhash, nthash = ntlm_hash.split(':')
109134
if user == '': user = "''"
@@ -119,10 +144,23 @@ def smart_login(host, smb, domain):
119144
return smb
120145
except SessionError as e:
121146
print_error(u"{}:{} {}\\{}:{} {}".format(host, settings.args.port, domain, user, ntlm_hash, e))
147+
if 'STATUS_LOGON_FAILURE' in str(e):
148+
fails += 1
149+
settings.gfails += 1
122150
continue
123151

124152
if passwords:
125153
for passwd in passwords:
154+
155+
if settings.args.fail_limit:
156+
if settings.args.fail_limit == fails:
157+
print_status('{}:{} Reached login fail limit'.format(host, settings.args.port))
158+
raise socket.error
159+
if settings.args.gfail_limit:
160+
if settings.gfails >= settings.args.gfail_limit:
161+
print_status('{}:{} Reached global login fail limit'.format(host, settings.args.port))
162+
raise socket.error
163+
126164
passwd = passwd.strip()
127165
if user == '': user = "''"
128166
if passwd == '': passwd = "''"
@@ -138,6 +176,9 @@ def smart_login(host, smb, domain):
138176
return smb
139177
except SessionError as e:
140178
print_error(u"{}:{} {}\\{}:{} {}".format(host, settings.args.port, domain, user, passwd, e))
179+
if 'STATUS_LOGON_FAILURE' in str(e):
180+
fails += 1
181+
settings.gfails += 1
141182
continue
142183

143184
raise socket.error #So we fail without a peep

crackmapexec.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
version='2.0 - {}'.format(CODENAME),
6060
epilog='There\'s been an awakening... have you felt it?')
6161

62+
parser.add_argument("target", nargs=1, type=str, help="The target range, CIDR identifier or file containing targets")
6263
parser.add_argument("-t", type=int, dest="threads", default=100, help="Set how many concurrent threads to use (defaults to 100)")
6364
parser.add_argument("-u", metavar="USERNAME", dest='user', type=str, default=None, help="Username(s) or file containing usernames")
6465
parser.add_argument("-p", metavar="PASSWORD", dest='passwd', type=str, default=None, help="Password(s) or file containing passwords")
@@ -72,8 +73,11 @@
7273
parser.add_argument("--port", dest='port', type=int, choices={139, 445}, default=445, help="SMB port (default: 445)")
7374
parser.add_argument("--server", choices={'http', 'https'}, default='http', help='Use the selected server (defaults to http)')
7475
#parser.add_argument("--server-port", type=int, help='Start the server on the specified port')
76+
77+
#How much fail can we limit? can we fail at failing to limit? da da da dum
78+
parser.add_argument("--fail-limit", metavar='LIMIT', type=int, default=None, help='The max number of failed login attempts allowed per host (default: None)')
79+
parser.add_argument("--gfail-limit", metavar='LIMIT', type=int, default=None, help='The max number of failed login attempts allowed globally (default: None)')
7580
parser.add_argument("--verbose", action='store_true', dest='verbose', help="Enable verbose output")
76-
parser.add_argument("target", nargs=1, type=str, help="The target range, CIDR identifier or file containing targets")
7781

7882
rgroup = parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
7983
rgroup.add_argument("--sam", action='store_true', help='Dump SAM hashes from target systems')

0 commit comments

Comments
 (0)