forked from byt3bl33d3r/CrackMapExec
-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathcrackmapexec.py
More file actions
250 lines (208 loc) · 12.9 KB
/
crackmapexec.py
File metadata and controls
250 lines (208 loc) · 12.9 KB
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
#!/usr/bin/env python2
#This must be one of the first imports or else we get threading error on completion
from gevent import monkey
monkey.patch_all()
from gevent.pool import Pool
from gevent import joinall, sleep
from core.connector import connector
from core.database import CMEDatabase
from core.cmeserver import CMEServer
from threading import Thread
from logging import getLogger
from argparse import RawTextHelpFormatter
from core.logger import setup_logger, setup_debug_logger, CMEAdapter
from core.context import Context
from core.helpers import highlight
from core.targetparser import parse_targets
import getpass
import sqlite3
import imp
import argparse
import os
import logging
import sys
VERSION = '3.0'
CODENAME = '\'So looong gay boy!\''
parser = argparse.ArgumentParser(description="""
______ .______ ___ ______ __ ___ .___ ___. ___ .______ _______ ___ ___ _______ ______
/ || _ \ / \ / || |/ / | \/ | / \ | _ \ | ____|\ \ / / | ____| / |
| ,----'| |_) | / ^ \ | ,----'| ' / | \ / | / ^ \ | |_) | | |__ \ V / | |__ | ,----'
| | | / / /_\ \ | | | < | |\/| | / /_\ \ | ___/ | __| > < | __| | |
| `----.| |\ \----. / _____ \ | `----.| . \ | | | | / _____ \ | | | |____ / . \ | |____ | `----.
\______|| _| `._____|/__/ \__\ \______||__|\__\ |__| |__| /__/ \__\ | _| |_______|/__/ \__\ |_______| \______|
Swiss army knife for pentesting Windows/Active Directory environments | @byt3bl33d3r
Powered by Impacket https://github.com/CoreSecurity/impacket (@agsolino)
Inspired by:
@ShawnDEvans's smbmap https://github.com/ShawnDEvans/smbmap
@gojhonny's CredCrack https://github.com/gojhonny/CredCrack
@pentestgeek's smbexec https://github.com/pentestgeek/smbexec
{}: {}
{}: {}
""".format(highlight('Version', 'red'),
highlight(VERSION),
highlight('Codename', 'red'),
highlight(CODENAME)),
formatter_class=RawTextHelpFormatter,
version='{} - {}'.format(VERSION, CODENAME),
epilog='HA! Made you look!')
parser.add_argument("target", nargs='*', type=str, help="The target IP(s), range(s), CIDR(s), hostname(s), FQDN(s) or file(s) containg a list of targets")
parser.add_argument("-t", type=int, dest="threads", default=100, help="Set how many concurrent threads to use (defaults to 100)")
parser.add_argument('-id', metavar="CRED_ID", type=int, dest='cred_id', help='Database credential ID to use for authentication')
parser.add_argument("-u", metavar="USERNAME", dest='username', nargs='*', default=[], help="Username(s) or file(s) containing usernames")
parser.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, help="Domain name")
msgroup = parser.add_mutually_exclusive_group()
msgroup.add_argument("-p", metavar="PASSWORD", dest='password', nargs= '*', default=[], help="Password(s) or file(s) containing passwords")
msgroup.add_argument("-H", metavar="HASH", dest='hash', nargs='*', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
parser.add_argument("-m", metavar='MODULE', dest='module', help='Payload module to use')
parser.add_argument('-o', metavar='MODULE_OPTION', nargs='*', default=[], dest='module_options', help='Payload module options')
parser.add_argument('--module-info', action='store_true', dest='module_info', help='Display module info')
parser.add_argument("--share", metavar="SHARE", dest='share', default="C$", help="Specify a share (default: C$)")
parser.add_argument("--smb-port", dest='smb_port', type=int, choices={139, 445}, default=445, help="SMB port (default: 445)")
parser.add_argument("--mssql-port", dest='mssql_port', default=1433, type=int, metavar='PORT', help='MSSQL port (default: 1433)')
parser.add_argument("--server", choices={'http', 'https'}, default='https', help='Use the selected server (default: https)')
parser.add_argument("--server-host", type=str, default='0.0.0.0', metavar='HOST', help='IP to bind the server to (default: 0.0.0.0)')
parser.add_argument("--server-port", dest='server_port', metavar='PORT', type=int, help='Start the server on the specified port')
parser.add_argument("--local-auth", dest='local_auth', action='store_true', help='Authenticate locally to each target')
parser.add_argument("--timeout", default=20, type=int, help='Max timeout in seconds of each thread (default: 20)')
parser.add_argument("--verbose", action='store_true', dest='verbose', help="Enable verbose output")
rgroup = parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
rgroup.add_argument("--sam", action='store_true', help='Dump SAM hashes from target systems')
rgroup.add_argument("--lsa", action='store_true', help='Dump LSA secrets from target systems')
rgroup.add_argument("--ntds", choices={'vss', 'drsuapi'}, help="Dump the NTDS.dit from target DCs using the specifed method\n(drsuapi is the fastest)")
rgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history')
rgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account')
rgroup.add_argument("--wdigest", choices={'enable', 'disable'}, help="Creates/Deletes the 'UseLogonCredential' registry key enabling WDigest cred dumping on Windows >= 8.1")
egroup = parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
egroup.add_argument("--shares", action="store_true", dest="enum_shares", help="Enumerate shares and access")
egroup.add_argument('--uac', action='store_true', help='Checks UAC status')
egroup.add_argument("--sessions", action='store_true', dest='enum_sessions', help='Enumerate active sessions')
egroup.add_argument('--disks', action='store_true', dest='enum_disks', help='Enumerate disks')
egroup.add_argument("--users", action='store_true', dest='enum_users', help='Enumerate users')
egroup.add_argument("--rid-brute", nargs='?', const=4000, metavar='MAX_RID', dest='rid_brute', help='Enumerate users by bruteforcing RID\'s (default: 4000)')
egroup.add_argument("--pass-pol", action='store_true', dest='pass_pol', help='Dump password policy')
egroup.add_argument("--lusers", action='store_true', dest='enum_lusers', help='Enumerate logged on users')
egroup.add_argument("--wmi", metavar='QUERY', type=str, dest='wmi_query', help='Issues the specified WMI query')
egroup.add_argument("--wmi-namespace", metavar='NAMESPACE', dest='wmi_namespace', default='//./root/cimv2', help='WMI Namespace (default: //./root/cimv2)')
sgroup = parser.add_argument_group("Spidering", "Options for spidering shares")
sgroup.add_argument("--spider", metavar='FOLDER', nargs='?', const='.', type=str, help='Folder to spider (default: root directory)')
sgroup.add_argument("--content", dest='search_content', action='store_true', help='Enable file content searching')
sgroup.add_argument("--exclude-dirs", type=str, metavar='DIR_LIST', default='', dest='exclude_dirs', help='Directories to exclude from spidering')
esgroup = sgroup.add_mutually_exclusive_group()
esgroup.add_argument("--pattern", nargs='*', help='Pattern(s) to search for in folders, filenames and file content')
esgroup.add_argument("--regex", nargs='*', help='Regex(s) to search for in folders, filenames and file content')
sgroup.add_argument("--depth", type=int, default=10, help='Spider recursion depth (default: 10)')
cgroup = parser.add_argument_group("Command Execution", "Options for executing commands")
cgroup.add_argument('--exec-method', choices={"wmiexec", "smbexec", "atexec"}, default="wmiexec", help="Method to execute the command. Ignored if in MSSQL mode (default: wmiexec)")
cgroup.add_argument('--force-ps32', action='store_true', help='Force the PowerShell command to run in a 32-bit process')
cgroup.add_argument('--no-output', action='store_true', dest='no_output', help='Do not retrieve command output')
cgroup.add_argument("-x", metavar="COMMAND", dest='command', help="Execute the specified command")
cgroup.add_argument("-X", metavar="PS_COMMAND", dest='pscommand', help='Execute the specified PowerShell command')
mgroup = parser.add_argument_group("MSSQL Interaction", "Options for interacting with MSSQL DBs")
mgroup.add_argument("--mssql", action='store_true', help='Switches CME into MSSQL Mode. If credentials are provided will authenticate against all discovered MSSQL DBs')
mgroup.add_argument("--mssql-query", metavar='QUERY', type=str, help='Execute the specifed query against the MSSQL DB')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
module = None
server = None
context = None
targets = []
server_port_dict = {'http': 80, 'https': 443}
args = parser.parse_args()
if args.verbose:
setup_debug_logger()
logger = CMEAdapter(setup_logger())
if os.geteuid() is not 0:
logger.error("I'm sorry {}, I'm afraid I can't let you do that".format(getpass.getuser()))
sys.exit(1)
if not args.server_port:
args.server_port = server_port_dict[args.server]
try:
# set the database connection to autocommit w/ isolation level
db_connection = sqlite3.connect('data/cme.db', check_same_thread=False)
db_connection.text_factory = str
db_connection.isolation_level = None
db = CMEDatabase(db_connection)
except Exception as e:
logger.error("Could not connect to CME database: {}".format(e))
sys.exit(1)
if args.cred_id:
try:
c_id, credtype, domain, username, password = db.get_credentials(filterTerm=args.cred_id)[0]
args.username = [username]
if not args.domain:
args.domain = domain
if credtype == 'hash':
args.hash = [password]
elif credtype == 'plaintext':
args.password = [password]
except IndexError:
logger.error("Invalid database credential ID!")
sys.exit(1)
else:
for user, passw, ntlm_hash in zip(args.username, args.password, args.hash):
if os.path.exists(user):
args.username.remove(user)
args.username.append(open(user, 'r'))
if os.path.exists(passw):
args.password.remove(passw)
args.password.append(open(passw, 'r'))
if os.path.exists(ntlm_hash):
args.hash.remove(ntlm_hash)
args.hash.append(open(ntlm_hash, 'r'))
if args.module:
if not os.path.exists(args.module):
logger.error('Path to module invalid!')
sys.exit(1)
else:
module = imp.load_source('payload_module', args.module).CMEModule()
if not hasattr(module, 'name'):
logger.error('Module missing the name variable!')
sys.exit(1)
elif not hasattr(module, 'options'):
logger.error('Module missing the options function!')
sys.exit(1)
elif not hasattr(module, 'on_login') and not (module, 'on_admin_login'):
logger.error('Module missing the on_login/on_admin_login function(s)!')
sys.exit(1)
if args.module_info:
logger.info('{} module description:'.format(module.name))
print module.__doc__
logger.info('{} module options:'.format(module.name))
print module.options.__doc__
sys.exit(0)
module_logger = CMEAdapter(getLogger('CME'), {'module': module.name.upper()})
context = Context(db, module_logger, args)
module_options = {}
for option in args.module_options:
key, value = option.split('=')
module_options[str(key).upper()] = value
module.options(context, module_options)
if hasattr(module, 'on_request') or hasattr(module, 'has_response'):
server = CMEServer(module, context, args.server_host, args.server_port, args.server)
server.start()
for target in args.target:
if os.path.exists(target):
with open(target, 'r') as target_file:
for target_entry in target_file:
targets.extend(parse_targets(target_entry))
else:
targets.extend(parse_targets(target))
try:
'''
Open all the greenlet (as supposed to redlet??) threads
Whoever came up with that name has a fetish for traffic lights
'''
pool = Pool(args.threads)
jobs = [pool.spawn(connector, str(target), args, db, module, context, server) for target in targets]
#Dumping the NTDS.DIT and/or spidering shares can take a long time, so we ignore the thread timeout
if args.ntds or args.spider:
joinall(jobs)
elif not args.ntds:
for job in jobs:
job.join(timeout=args.timeout)
except KeyboardInterrupt:
pass
if server:
server.shutdown()
logger.info('KTHXBYE!')