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

Commit 68a9085

Browse files
author
byt3bl33d3r
committed
Second round of refactoring for packaging
1 parent d5a7af9 commit 68a9085

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+468
-439
lines changed

.gitmodules

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
[submodule "data/PowerSploit"]
2-
path = data/PowerSploit
1+
[submodule "cme/data/PowerSploit"]
2+
path = cme/data/PowerSploit
33
url = https://github.com/PowerShellMafia/PowerSploit

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
recursive-include cme/data *
2+
recursive-include cme/modules *

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ The biggest improvements over the above tools are:
3030

3131
#Installation
3232

33-
See the [Installation](https://github.com/byt3bl33d3r/CrackMapExec/wiki/Installation) wiki page for install instructions
33+
To install run ```pip install crackmapexec```
3434

3535
#Quick Demo
3636

cmedb renamed to cme/cmedb.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ def complete_creds(self, text, line, begidx, endidx):
297297
offs = len(mline) - len(text)
298298
return [s[offs:] for s in commands if s.startswith(mline)]
299299

300-
if __name__ == '__main__':
300+
def main():
301301

302302
parser = argparse.ArgumentParser()
303303
parser.add_argument("path", nargs='?', type=str, default='data/cme.db', help="path to CME database (default: data/cme.db)")

cme/cmeserver.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from BaseHTTPServer import BaseHTTPRequestHandler
55
from logging import getLogger
66
from gevent import sleep
7-
from core.helpers import highlight
8-
from core.logger import CMEAdapter
7+
from cme.helpers import highlight
8+
from cme.logger import CMEAdapter
99

1010
class RequestHandler(BaseHTTPRequestHandler):
1111

cme/connection.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from traceback import format_exc
2-
from core.helpers import highlight
3-
from core.execmethods.mssqlexec import MSSQLEXEC
4-
from core.execmethods.wmiexec import WMIEXEC
5-
from core.execmethods.smbexec import SMBEXEC
6-
from core.execmethods.atexec import TSCH_EXEC
2+
from helpers import highlight
3+
from cme.execmethods.mssqlexec import MSSQLEXEC
4+
from cme.execmethods.wmiexec import WMIEXEC
5+
from cme.execmethods.smbexec import SMBEXEC
6+
from cme.execmethods.atexec import TSCH_EXEC
77
from impacket.dcerpc.v5 import transport, scmr
88
from impacket.dcerpc.v5.rpcrt import DCERPCException
99
from impacket.smbconnection import SessionError

cme/connector.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
from impacket.smbconnection import SMBConnection, SessionError
22
from impacket.nmb import NetBIOSError
33
from impacket import tds
4-
from core.mssql import *
4+
from cme.mssql import *
55
from impacket.dcerpc.v5.rpcrt import DCERPCException
6-
from core.connection import Connection
6+
from cme.connection import Connection
77
from logging import getLogger
8-
from core.logger import CMEAdapter
9-
from core.context import Context
10-
from core.helpers import create_ps_command
8+
from cme.logger import CMEAdapter
9+
from cme.context import Context
10+
from cme.helpers import create_ps_command
1111
from StringIO import StringIO
12-
from core.enum.shares import ShareEnum
13-
from core.enum.uac import UAC
14-
from core.enum.rpcquery import RPCQUERY
15-
from core.enum.passpol import PassPolDump
16-
from core.enum.users import SAMRDump
17-
from core.enum.wmiquery import WMIQUERY
18-
from core.enum.lookupsid import LSALookupSid
19-
from core.credentials.secretsdump import DumpSecrets
20-
from core.credentials.wdigest import WDIGEST
21-
from core.spider.smbspider import SMBSpider
12+
from cme.enum.shares import ShareEnum
13+
from cme.enum.uac import UAC
14+
from cme.enum.rpcquery import RPCQUERY
15+
from cme.enum.passpol import PassPolDump
16+
from cme.enum.users import SAMRDump
17+
from cme.enum.wmiquery import WMIQUERY
18+
from cme.enum.lookupsid import LSALookupSid
19+
from cme.credentials.secretsdump import DumpSecrets
20+
from cme.credentials.wdigest import WDIGEST
21+
from cme.spider.smbspider import SMBSpider
2222
import socket
2323

2424
def connector(target, args, db, module, context, cmeserver):

cme/crackmapexec.py

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
#!/usr/bin/env python2
2+
3+
#This must be one of the first imports or else we get threading error on completion
4+
from gevent import monkey
5+
monkey.patch_all()
6+
7+
from gevent.pool import Pool
8+
from gevent import joinall
9+
from argparse import RawTextHelpFormatter
10+
from cme.connector import connector
11+
from cme.database import CMEDatabase
12+
from cme.logger import setup_logger, setup_debug_logger, CMEAdapter
13+
from cme.helpers import highlight
14+
from cme.targetparser import parse_targets
15+
from cme.moduleloader import ModuleLoader
16+
from cme.first_run import first_run_setup
17+
import sqlite3
18+
import argparse
19+
import os
20+
import sys
21+
22+
def main():
23+
24+
VERSION = '3.1'
25+
CODENAME = '\'Duchess\''
26+
27+
parser = argparse.ArgumentParser(description="""
28+
______ .______ ___ ______ __ ___ .___ ___. ___ .______ _______ ___ ___ _______ ______
29+
/ || _ \ / \ / || |/ / | \/ | / \ | _ \ | ____|\ \ / / | ____| / |
30+
| ,----'| |_) | / ^ \ | ,----'| ' / | \ / | / ^ \ | |_) | | |__ \ V / | |__ | ,----'
31+
| | | / / /_\ \ | | | < | |\/| | / /_\ \ | ___/ | __| > < | __| | |
32+
| `----.| |\ \----. / _____ \ | `----.| . \ | | | | / _____ \ | | | |____ / . \ | |____ | `----.
33+
\______|| _| `._____|/__/ \__\ \______||__|\__\ |__| |__| /__/ \__\ | _| |_______|/__/ \__\ |_______| \______|
34+
35+
36+
Swiss army knife for pentesting Windows/Active Directory environments | @byt3bl33d3r
37+
38+
Powered by Impacket https://github.com/CoreSecurity/impacket (@agsolino)
39+
40+
Inspired by:
41+
@ShawnDEvans's smbmap https://github.com/ShawnDEvans/smbmap
42+
@gojhonny's CredCrack https://github.com/gojhonny/CredCrack
43+
@pentestgeek's smbexec https://github.com/pentestgeek/smbexec
44+
45+
{}: {}
46+
{}: {}
47+
""".format(highlight('Version', 'red'),
48+
highlight(VERSION),
49+
highlight('Codename', 'red'),
50+
highlight(CODENAME)),
51+
52+
formatter_class=RawTextHelpFormatter,
53+
version='{} - {}'.format(VERSION, CODENAME),
54+
epilog='I swear I had something for this...')
55+
56+
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")
57+
parser.add_argument("-t", type=int, dest="threads", default=100, help="Set how many concurrent threads to use (defaults to 100)")
58+
parser.add_argument('-id', metavar="CRED_ID", type=int, dest='cred_id', help='Database credential ID to use for authentication')
59+
parser.add_argument("-u", metavar="USERNAME", dest='username', nargs='*', default=[], help="Username(s) or file(s) containing usernames")
60+
parser.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, help="Domain name")
61+
msgroup = parser.add_mutually_exclusive_group()
62+
msgroup.add_argument("-p", metavar="PASSWORD", dest='password', nargs= '*', default=[], help="Password(s) or file(s) containing passwords")
63+
msgroup.add_argument("-H", metavar="HASH", dest='hash', nargs='*', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
64+
parser.add_argument("-M", "--module", metavar='MODULE', dest='module', help='Payload module to use')
65+
parser.add_argument('-o', metavar='MODULE_OPTION', nargs='*', default=[], dest='module_options', help='Payload module options')
66+
parser.add_argument('--module-info', action='store_true', dest='module_info', help='Display module info')
67+
parser.add_argument('--list-modules', action='store_true', help='List available modules')
68+
parser.add_argument("--share", metavar="SHARE", dest='share', default="C$", help="Specify a share (default: C$)")
69+
parser.add_argument("--smb-port", dest='smb_port', type=int, choices={139, 445}, default=445, help="SMB port (default: 445)")
70+
parser.add_argument("--mssql-port", dest='mssql_port', default=1433, type=int, metavar='PORT', help='MSSQL port (default: 1433)')
71+
parser.add_argument("--server", choices={'http', 'https'}, default='https', help='Use the selected server (default: https)')
72+
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)')
73+
parser.add_argument("--server-port", dest='server_port', metavar='PORT', type=int, help='Start the server on the specified port')
74+
parser.add_argument("--local-auth", dest='local_auth', action='store_true', help='Authenticate locally to each target')
75+
parser.add_argument("--timeout", default=20, type=int, help='Max timeout in seconds of each thread (default: 20)')
76+
parser.add_argument("--verbose", action='store_true', dest='verbose', help="Enable verbose output")
77+
78+
rgroup = parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
79+
rgroup.add_argument("--sam", action='store_true', help='Dump SAM hashes from target systems')
80+
rgroup.add_argument("--lsa", action='store_true', help='Dump LSA secrets from target systems')
81+
rgroup.add_argument("--ntds", choices={'vss', 'drsuapi'}, help="Dump the NTDS.dit from target DCs using the specifed method\n(drsuapi is the fastest)")
82+
rgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history')
83+
rgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account')
84+
rgroup.add_argument("--wdigest", choices={'enable', 'disable'}, help="Creates/Deletes the 'UseLogonCredential' registry key enabling WDigest cred dumping on Windows >= 8.1")
85+
86+
egroup = parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
87+
egroup.add_argument("--shares", action="store_true", dest="enum_shares", help="Enumerate shares and access")
88+
egroup.add_argument('--uac', action='store_true', help='Checks UAC status')
89+
egroup.add_argument("--sessions", action='store_true', dest='enum_sessions', help='Enumerate active sessions')
90+
egroup.add_argument('--disks', action='store_true', dest='enum_disks', help='Enumerate disks')
91+
egroup.add_argument("--users", action='store_true', dest='enum_users', help='Enumerate users')
92+
egroup.add_argument("--rid-brute", nargs='?', const=4000, metavar='MAX_RID', dest='rid_brute', help='Enumerate users by bruteforcing RID\'s (default: 4000)')
93+
egroup.add_argument("--pass-pol", action='store_true', dest='pass_pol', help='Dump password policy')
94+
egroup.add_argument("--lusers", action='store_true', dest='enum_lusers', help='Enumerate logged on users')
95+
egroup.add_argument("--wmi", metavar='QUERY', type=str, dest='wmi_query', help='Issues the specified WMI query')
96+
egroup.add_argument("--wmi-namespace", metavar='NAMESPACE', dest='wmi_namespace', default='//./root/cimv2', help='WMI Namespace (default: //./root/cimv2)')
97+
98+
sgroup = parser.add_argument_group("Spidering", "Options for spidering shares")
99+
sgroup.add_argument("--spider", metavar='FOLDER', nargs='?', const='.', type=str, help='Folder to spider (default: root directory)')
100+
sgroup.add_argument("--content", dest='search_content', action='store_true', help='Enable file content searching')
101+
sgroup.add_argument("--exclude-dirs", type=str, metavar='DIR_LIST', default='', dest='exclude_dirs', help='Directories to exclude from spidering')
102+
esgroup = sgroup.add_mutually_exclusive_group()
103+
esgroup.add_argument("--pattern", nargs='*', help='Pattern(s) to search for in folders, filenames and file content')
104+
esgroup.add_argument("--regex", nargs='*', help='Regex(s) to search for in folders, filenames and file content')
105+
sgroup.add_argument("--depth", type=int, default=10, help='Spider recursion depth (default: 10)')
106+
107+
cgroup = parser.add_argument_group("Command Execution", "Options for executing commands")
108+
cgroup.add_argument('--exec-method', choices={"wmiexec", "smbexec", "atexec"}, default=None, help="Method to execute the command. Ignored if in MSSQL mode (default: wmiexec)")
109+
cgroup.add_argument('--force-ps32', action='store_true', help='Force the PowerShell command to run in a 32-bit process')
110+
cgroup.add_argument('--no-output', action='store_true', dest='no_output', help='Do not retrieve command output')
111+
cgroup.add_argument("-x", metavar="COMMAND", dest='command', help="Execute the specified command")
112+
cgroup.add_argument("-X", metavar="PS_COMMAND", dest='pscommand', help='Execute the specified PowerShell command')
113+
114+
mgroup = parser.add_argument_group("MSSQL Interaction", "Options for interacting with MSSQL DBs")
115+
mgroup.add_argument("--mssql", action='store_true', help='Switches CME into MSSQL Mode. If credentials are provided will authenticate against all discovered MSSQL DBs')
116+
mgroup.add_argument("--mssql-query", metavar='QUERY', type=str, help='Execute the specifed query against the MSSQL DB')
117+
118+
logger = CMEAdapter(setup_logger())
119+
first_run_setup(logger)
120+
121+
if len(sys.argv) == 1:
122+
parser.print_help()
123+
sys.exit(1)
124+
125+
cme_path = os.path.expanduser('~/.cme')
126+
127+
module = None
128+
server = None
129+
context = None
130+
targets = []
131+
server_port_dict = {'http': 80, 'https': 443}
132+
133+
args = parser.parse_args()
134+
135+
if args.verbose:
136+
setup_debug_logger()
137+
138+
if not args.server_port:
139+
args.server_port = server_port_dict[args.server]
140+
141+
db_path = os.path.join(cme_path, 'cme.db')
142+
# set the database connection to autocommit w/ isolation level
143+
db_connection = sqlite3.connect(db_path, check_same_thread=False)
144+
db_connection.text_factory = str
145+
db_connection.isolation_level = None
146+
db = CMEDatabase(db_connection)
147+
148+
if args.cred_id:
149+
try:
150+
c_id, credtype, domain, username, password = db.get_credentials(filterTerm=args.cred_id)[0]
151+
args.username = [username]
152+
153+
if not args.domain:
154+
args.domain = domain
155+
if credtype == 'hash':
156+
args.hash = [password]
157+
elif credtype == 'plaintext':
158+
args.password = [password]
159+
except IndexError:
160+
logger.error("Invalid database credential ID!")
161+
sys.exit(1)
162+
else:
163+
for user in args.username:
164+
if os.path.exists(user):
165+
args.username.remove(user)
166+
args.username.append(open(user, 'r'))
167+
168+
if args.password:
169+
for passw in args.password:
170+
if os.path.exists(passw):
171+
args.password.remove(passw)
172+
args.password.append(open(passw, 'r'))
173+
174+
elif args.hash:
175+
for ntlm_hash in args.hash:
176+
if os.path.exists(ntlm_hash):
177+
args.hash.remove(ntlm_hash)
178+
args.hash.append(open(ntlm_hash, 'r'))
179+
180+
for target in args.target:
181+
if os.path.exists(target):
182+
with open(target, 'r') as target_file:
183+
for target_entry in target_file:
184+
targets.extend(parse_targets(target_entry))
185+
else:
186+
targets.extend(parse_targets(target))
187+
188+
189+
loader = ModuleLoader(args, db, logger)
190+
modules = loader.get_modules()
191+
192+
if args.list_modules:
193+
for module in modules:
194+
print module
195+
196+
elif args.modules:
197+
for module in modules.keys():
198+
if args.module.lower() == module.lower():
199+
module, context, server = loader.init_module(modules[module]['path'])
200+
break
201+
202+
try:
203+
'''
204+
Open all the greenlet (as supposed to redlet??) threads
205+
Whoever came up with that name has a fetish for traffic lights
206+
'''
207+
pool = Pool(args.threads)
208+
jobs = [pool.spawn(connector, str(target), args, db, module, context, server) for target in targets]
209+
210+
#Dumping the NTDS.DIT and/or spidering shares can take a long time, so we ignore the thread timeout
211+
if args.ntds or args.spider:
212+
joinall(jobs)
213+
elif not args.ntds:
214+
for job in jobs:
215+
job.join(timeout=args.timeout)
216+
except KeyboardInterrupt:
217+
pass
218+
219+
if server:
220+
server.shutdown()
221+
222+
logger.info('KTHXBYE!')

cme/credentials/lsa.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from core.credentials.offlineregistry import OfflineRegistry
2-
from core.credentials.cryptocommon import CryptoCommon
3-
from core.credentials.commonstructs import LSA_SECRET, LSA_SECRET_BLOB, NL_RECORD, LSA_SECRET_XP
1+
from cme.credentials.offlineregistry import OfflineRegistry
2+
from cme.credentials.cryptocommon import CryptoCommon
3+
from cme.credentials.commonstructs import LSA_SECRET, LSA_SECRET_BLOB, NL_RECORD, LSA_SECRET_XP
44
from impacket import ntlm
55
from impacket.winregistry import hexdump
66
from Crypto.Cipher import AES, DES, ARC4

cme/credentials/ntds.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
from binascii import hexlify, unhexlify
77
from struct import unpack
88
from datetime import datetime
9-
from core.credentials.cryptocommon import CryptoCommon
9+
from cme.credentials.cryptocommon import CryptoCommon
1010
from Crypto.Cipher import DES, ARC4
11-
from core.credentials.commonstructs import SAMR_RPC_SID
11+
from cme.credentials.commonstructs import SAMR_RPC_SID
1212
from impacket.ese import ESENT_DB
1313
import logging
1414
import hashlib

0 commit comments

Comments
 (0)