Merge branch 'main' into wmi-improv-admin-check

main
XiaoliChan 2023-09-17 01:53:37 +08:00 committed by GitHub
commit e3837d986a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 83 additions and 130 deletions

6
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,6 @@
##########################################
# code ownership
##########################################
# default ownership:
* @zblurx @Marshall-Hallenbeck @NeffIsBack

View File

@ -1,11 +1,8 @@
name: CrackMapExec Tests
on:
workflow_dispatch:
push:
branches: [ master ]
pull_request:
branches: [ master ]
pull_request_review:
types: [submitted]
jobs:
build:

View File

@ -1,12 +1,7 @@
name: CrackMapExec Tests & Build
name: CrackMapExec Build Binaries
on:
workflow_dispatch:
branches: [ main ]
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:

View File

@ -1,58 +1,37 @@
![Supported Python versions](https://img.shields.io/badge/python-3.7+-blue.svg)
[![Twitter](https://img.shields.io/twitter/follow/byt3bl33d3r?label=byt3bl33d3r&style=social)](https://twitter.com/intent/follow?screen_name=byt3bl33d3r)
[![Twitter](https://img.shields.io/twitter/follow/mpgn_x64?label=mpgn_x64&style=social)](https://twitter.com/intent/follow?screen_name=mpgn_x64)
[![Twitter](https://img.shields.io/twitter/follow/al3xn3ff?label=al3x_n3ff&style=social)](https://twitter.com/intent/follow?screen_name=al3x_n3ff)
[![Twitter](https://img.shields.io/twitter/follow/_zblurx?label=_zblurx&style=social)](https://twitter.com/intent/follow?screen_name=_zblurx)
[![Twitter](https://img.shields.io/twitter/follow/MJHallenbeck?label=MJHallenbeck&style=social)](https://twitter.com/intent/follow?screen_name=MJHallenbeck)
🚩 This is the open source repository of CrackMapExec maintained by a community of passionate people
# CrackMapExec
🚩 This is the open source repository of NetExec maintained by a community of passionate people
# NetExec - The Network Execution Tool
<p align="center">
<img src="https://cloud.githubusercontent.com/assets/5151193/17577511/d312ceb4-5f3b-11e6-8de5-8822246289fd.jpg" alt="cme"/>
<!-- placeholder for nxc logo-->
</p>
You are on the **latest up-to-date** repository of the project NetExec (nxc) ! 🎉
This project was initially created in 2015 by **@byt3bl33d3r**. In 2019 **@mpgn_x64** started maintaining the project. Five years and a lot of additions later, he retired from maintaining the project. Like many other contributer we (NeffIsBack, Marshall-Hallenbeck and zblurx) started working on new features, bugfixes and helped maintaining CME. With the end of mpgn's maintainer role, we decided to maintain the project together as an full open source project.
You are on the **latest up-to-date** repository of the project CrackMapExec ! 🎉
- 🚧 If you want to report a problem, open un [Issue](https://github.com/Pennyw0rth/CrackMapExec/issues)
- 🔀 If you want to contribute, open a [Pull Request](https://github.com/Pennyw0rth/CrackMapExec/pulls)
- 💬 If you want to discuss, open a [Discussion](https://github.com/Pennyw0rth/CrackMapExec/discussions)
- 🚧 If you want to report a problem, open un [Issue](https://github.com/Pennyw0rth/NetExec/issues)
- 🔀 If you want to contribute, open a [Pull Request](https://github.com/Pennyw0rth/NetExec/pulls)
- 💬 If you want to discuss, open a [Discussion](https://github.com/Pennyw0rth/NetExec/discussions)
# Acknowledgments
**(These are the people who did the hard stuff)**
All the hard work and development over the years from everyone in the CrackMapExec project.
This project was originally inspired by:
- [CredCrack](https://github.com/gojhonny/CredCrack)
- [smbexec](https://github.com/pentestgeek/smbexec)
- [smbmap](https://github.com/ShawnDEvans/smbmap)
Unintentional contributors:
- The [Empire](https://github.com/PowerShellEmpire/Empire) project
- @T-S-A's [smbspider](https://github.com/T-S-A/smbspider) script
- @ConsciousHacker's partial Python port of Invoke-obfuscation from the [GreatSCT](https://github.com/GreatSCT/GreatSCT) project
# Documentation, Tutorials, Examples
See the project's [wiki](https://www.crackmapexec.wiki/) for documentation and usage examples
See the project's wiki (in development) for documentation and usage examples
# Installation
Please see the installation instructions on the [official wiki](https://www.crackmapexec.wiki/getting-started/installation)
Please see the installation instructions on the wiki (in development)
# Code Contributors
Awesome code contributors of CME:
Awesome code contributors of NetExec:
[![](https://github.com/mpgn.png?size=50)](https://github.com/mpgn)
[![](https://github.com/Marshall-Hallenbeck.png?size=50)](https://github.com/Marshall-Hallenbeck)
[![](https://github.com/zblurx.png?size=50)](https://github.com/zblurx)
[![](https://github.com/NeffIsBack.png?size=50)](https://github.com/NeffIsBack)
[![](https://github.com/Hackndo.png?size=50)](https://github.com/Hackndo)
[![](https://github.com/nurfed1?size=50)](https://github.com/nurfed1)
# To do
- ~~0wn everything~~

View File

@ -24,7 +24,8 @@ def gen_cli_args():
\______|| _| `._____|/__/ \__\ \______||__|\__\ |__| |__| /__/ \__\ | _| |_______|/__/ \__\ |_______| \______|
A swiss army knife for pentesting networks
Forged by @byt3bl33d3r and @mpgn_x64 using the powah of dank memes
Forged by @byt3bl33d3r and @mpgn_x64 using the powah of dank memes.
Maintained as an open source project by @NeffIsBack, @MJHallenbeck, @_zblurx
{highlight('Version', 'red')} : {highlight(VERSION)}
{highlight('Codename', 'red')}: {highlight(CODENAME)}

View File

@ -341,6 +341,12 @@ class connection(object):
secret.append(aesKey)
cred_type.append('aesKey')
# Allow trying multiple users with a single password
if len(username) > 1 and len(secret) == 1:
secret = secret * len(username)
cred_type = cred_type * len(username)
self.args.no_bruteforce = True
return domain, username, owned, secret, cred_type, [None] * len(secret)
def try_credentials(self, domain, username, owned, secret, cred_type, data=None):

View File

@ -144,7 +144,7 @@ class CMEModule:
return connection.execute("powershell.exe -e {} -OutputFormat Text".format(psScipt_b64), True)
def printCreds(self, context, output):
# Format ouput if returned in some XML Format
# Format output if returned in some XML Format
if "CLIXML" in output:
output = self.stripXmlOutput(context, output)

View File

@ -734,8 +734,9 @@ class smb(connection):
self.kdcHost,
self.hash,
self.logger,
self.args.get_output_tries
) # self.args.share)
self.args.get_output_tries,
self.args.share
)
self.logger.info("Executed command via atexec")
break
except:

View File

@ -38,6 +38,8 @@ class TSCH_EXEC:
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__tries = tries
self.__output_filename = None
self.__share = share
self.logger = logger
if hashes is not None:
@ -73,7 +75,7 @@ class TSCH_EXEC:
def output_callback(self, data):
self.__outputBuffer = data
def gen_xml(self, command, tmpFileName, fileless=False):
def gen_xml(self, command, fileless=False):
xml = """<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<Triggers>
@ -114,11 +116,12 @@ class TSCH_EXEC:
<Command>cmd.exe</Command>
"""
if self.__retOutput:
self.__output_filename = "\\Windows\\Temp\\" + gen_random_string(6)
if fileless:
local_ip = self.__rpctransport.get_socket().getsockname()[0]
argument_xml = f" <Arguments>/C {command} &gt; \\\\{local_ip}\\{self.__share_name}\\{tmpFileName} 2&gt;&amp;1</Arguments>"
argument_xml = f" <Arguments>/C {command} &gt; \\\\{local_ip}\\{self.__share_name}\\{self.__output_filename} 2&gt;&amp;1</Arguments>"
else:
argument_xml = f" <Arguments>/C {command} &gt; %windir%\\Temp\\{tmpFileName} 2&gt;&amp;1</Arguments>"
argument_xml = f" <Arguments>/C {command} &gt; {self.__output_filename} 2&gt;&amp;1</Arguments>"
elif self.__retOutput is False:
argument_xml = f" <Arguments>/C {command}</Arguments>"
@ -143,9 +146,8 @@ class TSCH_EXEC:
# dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
tmpName = gen_random_string(8)
tmpFileName = tmpName + ".tmp"
xml = self.gen_xml(command, tmpFileName, fileless)
xml = self.gen_xml(command, fileless)
self.logger.info(f"Task XML: {xml}")
taskCreated = False
@ -187,7 +189,7 @@ class TSCH_EXEC:
if fileless:
while True:
try:
with open(os.path.join("/tmp", "cme_hosted", tmpFileName), "r") as output:
with open(os.path.join("/tmp", "cme_hosted", self.__output_filename), "r") as output:
self.output_callback(output.read())
break
except IOError:
@ -198,15 +200,15 @@ class TSCH_EXEC:
tries = 1
while True:
try:
self.logger.info(f"Attempting to read ADMIN$\\Temp\\{tmpFileName}")
smbConnection.getFile("ADMIN$", f"Temp\\{tmpFileName}", self.output_callback)
self.logger.info(f"Attempting to read {self.__share}\\{self.__output_filename}")
smbConnection.getFile(self.__share, self.__output_filename, self.output_callback)
break
except Exception as e:
if tries >= self.__tries:
self.logger.fail(f'ATEXEC: Get output file error, maybe got detected by AV software, please increase the number of tries with the option "--get-output-tries". If it\'s still failing maybe something is blocking the schedule job, try another exec method')
self.logger.fail(f"ATEXEC: Could not retrieve output file, it may have been detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it is still failing, try the 'wmi' protocol or another exec method")
break
if str(e).find("STATUS_BAD_NETWORK_NAME") >0 :
self.logger.fail(f'ATEXEC: Get ouput failed, target has blocked ADMIN$ access (maybe command executed!)')
self.logger.fail(f"ATEXEC: Getting the output file failed - target has blocked access to the share: {self.__share} (but the command may have executed!)")
break
if str(e).find("SHARING") > 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
sleep(3)
@ -215,7 +217,7 @@ class TSCH_EXEC:
self.logger.debug(str(e))
if self.__outputBuffer:
self.logger.debug(f"Deleting file ADMIN$\\Temp\\{tmpFileName}")
smbConnection.deleteFile("ADMIN$", f"Temp\\{tmpFileName}")
self.logger.debug(f"Deleting file {self.__share}\\{self.__output_filename}")
smbConnection.deleteFile(self.__share, self.__output_filename)
dce.disconnect()

View File

@ -252,10 +252,10 @@ class MMCEXEC:
break
except Exception as e:
if tries >= self.__tries:
self.logger.fail(f'MMCEXEC: Get output file error, maybe got detected by AV software, please increase the number of tries with the option "--get-output-tries". If it\'s still failing maybe something is blocking the schedule job, try another exec method')
self.logger.fail(f"MMCEXEC: Could not retrieve output file, it may have been detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it is still failing, try the 'wmi' protocol or another exec method")
break
if str(e).find("STATUS_BAD_NETWORK_NAME") >0 :
self.logger.fail(f'MMCEXEC: Get ouput failed, target has blocked {self.__share} access (maybe command executed!)')
self.logger.fail(f"MMCEXEC: Getting the output file failed - target has blocked access to the share: {self.__share} (but the command may have executed!)")
break
if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
# Output not finished, let's wait

View File

@ -91,7 +91,7 @@ def proto_args(parser, std_parser, module_parser):
help="force the PowerShell command to run in a 32-bit process")
cgroup.add_argument("--no-output", action="store_true", help="do not retrieve command output")
cegroup = cgroup.add_mutually_exclusive_group()
cegroup.add_argument("-x", metavar="COMMAND", dest="execute", help="execute the specified command")
cegroup.add_argument("-x", metavar="COMMAND", dest="execute", help="execute the specified CMD command")
cegroup.add_argument("-X", metavar="PS_COMMAND", dest="ps_execute", help="execute the specified PowerShell command")
psgroup = smb_parser.add_argument_group("Powershell Obfuscation", "Options for PowerShell script obfuscation")
psgroup.add_argument("--obfs", action="store_true", help="Obfuscate PowerShell scripts")

View File

@ -170,10 +170,10 @@ class SMBEXEC:
break
except Exception as e:
if tries >= self.__tries:
self.logger.fail(f'SMBEXEC: Get output file error, maybe got detected by AV software, please increase the number of tries with the option "--get-output-tries". If it\'s still failing maybe something is blocking the schedule job, try another exec method')
self.logger.fail(f"SMBEXEC: Could not retrieve output file, it may have been detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it is still failing, try the 'wmi' protocol or another exec method")
break
if str(e).find("STATUS_BAD_NETWORK_NAME") >0 :
self.logger.fail(f'SMBEXEC: Get ouput failed, target has blocked {self.__share} access (maybe command executed!)')
self.logger.fail(f"SMBEXEC: Getting the output file failed - target has blocked access to the share: {self.__share} (but the command may have executed!)")
break
if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
# Output not finished, let's wait

View File

@ -166,10 +166,10 @@ class WMIEXEC:
break
except Exception as e:
if tries >= self.__tries:
self.logger.fail(f'WMIEXEC: Get output file error, maybe got detected by AV software, please increase the number of tries with the option "--get-output-tries". If it\'s still failing maybe something is blocking the schedule job, try another exec method')
self.logger.fail(f"WMIEXEC: Could not retrieve output file, it may have been detected by AV. If it is still failing, try the 'wmi' protocol or another exec method")
break
if str(e).find("STATUS_BAD_NETWORK_NAME") >0 :
self.logger.fail(f'SMB connection: target has blocked {self.__share} access (maybe command executed!)')
self.logger.fail(f"SMB connection: target has blocked {self.__share} access (maybe command executed!)")
break
if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
sleep(2)

View File

@ -231,30 +231,13 @@ class winrm(connection):
self.password = password
self.username = username
self.domain = domain
if self.args.ssl and self.args.ignore_ssl_cert:
self.conn = Client(
self.host,
auth="ntlm",
username=f"{domain}\\{self.username}",
password=self.password,
ssl=True,
cert_validation=False,
)
elif self.args.ssl:
self.conn = Client(
self.host,
auth="ntlm",
username=f"{domain}\\{self.username}",
password=self.password,
ssl=True,
)
else:
self.conn = Client(
self.host,
auth="ntlm",
username=f"{domain}\\{self.username}",
password=self.password,
ssl=False,
ssl=True if self.args.ssl else False,
cert_validation=False if self.args.ignore_ssl_cert else True,
)
# TO DO: right now we're just running the hostname command to make the winrm library auth to the server
@ -308,30 +291,13 @@ class winrm(connection):
nthash = self.hash
self.domain = domain
if self.args.ssl and self.args.ignore_ssl_cert:
self.conn = Client(
self.host,
auth="ntlm",
username=f"{self.domain}\\{self.username}",
password=lmhash + nthash,
ssl=True,
cert_validation=False,
)
elif self.args.ssl:
self.conn = Client(
self.host,
auth="ntlm",
username=f"{self.domain}\\{self.username}",
password=lmhash + nthash,
ssl=True,
)
else:
self.conn = Client(
self.host,
auth="ntlm",
username=f"{self.domain}\\{self.username}",
password=lmhash + nthash,
ssl=False,
ssl=True if self.args.ssl else False,
cert_validation=False if self.args.ignore_ssl_cert else True,
)
# TO DO: right now we're just running the hostname command to make the winrm library auth to the server

View File

@ -432,7 +432,7 @@ class wmi(connection):
if not self.args.no_output:
get_output = True
if "systeminfo" in command and self.args.interval_time < 10:
if "systeminfo" in command and self.args.exec_timeout < 10:
self.logger.fail("Execute 'systeminfo' must set the interval time higher than 10 seconds")
return False
@ -441,11 +441,11 @@ class wmi(connection):
return False
if self.args.exec_method == "wmiexec":
exec_method = wmiexec.WMIEXEC(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.interval_time, self.args.codec)
exec_method = wmiexec.WMIEXEC(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
output = exec_method.execute(command, get_output)
elif self.args.exec_method == "wmiexec-event":
exec_method = wmiexec_event.WMIEXEC_EVENT(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.interval_time, self.args.codec)
exec_method = wmiexec_event.WMIEXEC_EVENT(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
output = exec_method.execute(command, get_output)
self.conn.disconnect()

View File

@ -23,7 +23,7 @@ def proto_args(parser, std_parser, module_parser):
"[wmiexec (win32_process + StdRegProv)]: get command results over registry instead of using smb connection. "
"[wmiexec-event (T1546.003)]: this method is not very stable, highly recommend use this method in single host, "
"using on multiple hosts may crash (just try again if it crashed).")
cgroup.add_argument("--interval-time", default=5 ,metavar='INTERVAL_TIME', dest='interval_time', type=int, help='Set interval time(seconds) when executing command, unrecommend set it lower than 5')
cgroup.add_argument("--exec-timeout", default=5, metavar='exec_timeout', dest='exec_timeout', type=int, help='Set timeout (in seconds) when executing a command, minimum 5 seconds is recommended. Default: %(default)s')
cgroup.add_argument("--codec", default="utf-8",
help="Set encoding used (codec) from the target's output (default "
"\"utf-8\"). If errors are detected, run chcp.com at the target, "

View File

@ -34,7 +34,7 @@ from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, WBEM_FLAG_FORWARD_ONLY, IWbemLevel1Login
class WMIEXEC:
def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, interval_time, codec):
def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, exec_timeout, codec):
self.__host = host
self.__username = username
self.__password = password
@ -45,7 +45,7 @@ class WMIEXEC:
self.__kdcHost = kdcHost
self.__aesKey = aesKey
self.logger = logger
self.__interval_time = interval_time
self.__exec_timeout = exec_timeout
self.__registry_Path = ""
self.__outputBuffer = ""
self.__retOutput = True
@ -91,8 +91,8 @@ class WMIEXEC:
command = fr'''{self.__shell} {command} 1> {result_output} 2>&1 && certutil -encodehex -f {result_output} {result_output_b64} 0x40000001 && for /F "usebackq" %G in ("{result_output_b64}") do reg add HKLM\{self.__registry_Path} /v {keyName} /t REG_SZ /d "%G" /f && del /q /f /s {result_output} {result_output_b64}'''
self.execute_remote(command)
self.logger.info("Waiting {}s for command completely executed.".format(self.__interval_time))
time.sleep(self.__interval_time)
self.logger.info("Waiting {}s for command completely executed.".format(self.__exec_timeout))
time.sleep(self.__exec_timeout)
self.queryRegistry(keyName)
@ -104,7 +104,7 @@ class WMIEXEC:
retVal = descriptor.GetStringValue(2147483650, self.__registry_Path, keyName)
self.__outputBuffer = base64.b64decode(retVal.sValue).decode(self.__codec, errors='replace').rstrip('\r\n')
except Exception as e:
self.logger.fail(f'WMIEXEC: Get output file error, maybe command not executed successfully or got detected by AV software, please increase the interval time of command execution with "--interval-time" option. If it\'s still failing maybe something is blocking the schedule job in vbscript, try another exec method')
self.logger.fail(f"WMIEXEC: Could not retrieve output file, it may have been detected by AV. Please try increasing the timeout with the '--exec-timeout' option. If it is still failing, try the 'smb' protocol or another exec method")
try:
self.logger.debug(f"Removing temporary registry path: HKLM\\{self.__registry_Path}")

View File

@ -37,7 +37,7 @@ from impacket.dcerpc.v5.dcom.wmi import WBEMSTATUS
from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, WBEM_FLAG_FORWARD_ONLY, IWbemLevel1Login, WBEMSTATUS
class WMIEXEC_EVENT:
def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, interval_time, codec):
def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, exec_timeout, codec):
self.__host = host
self.__username = username
self.__password = password
@ -51,7 +51,7 @@ class WMIEXEC_EVENT:
self.__retOutput = True
self.logger = logger
self.__interval_time = interval_time
self.__exec_timeout = exec_timeout
self.__codec = codec
self.__instanceID = f"windows-object-{str(uuid.uuid4())}"
self.__instanceID_StoreResult = f"windows-object-{str(uuid.uuid4())}"
@ -84,8 +84,8 @@ class WMIEXEC_EVENT:
self.execute_remote(command)
# Get command results
self.logger.info("Waiting {}s for command completely executed.".format(self.__interval_time))
time.sleep(self.__interval_time)
self.logger.info("Waiting {}s for command completely executed.".format(self.__exec_timeout))
time.sleep(self.__exec_timeout)
if self.__retOutput:
self.get_CommandResult()
@ -190,7 +190,7 @@ class WMIEXEC_EVENT:
record = dict(command_ResultObject.getProperties())
self.__outputBuffer = base64.b64decode(record['ScriptText']['value']).decode(self.__codec, errors='replace')
except Exception as e:
self.logger.fail(f'WMIEXEC-EVENT: Get output file error, maybe command not executed successfully or got detected by AV software, please increase the interval time of command execution with "--interval-time" option. If it\'s still failing maybe something is blocking the schedule job in vbscript, try another exec method')
self.logger.fail(f"WMIEXEC-EVENT: Could not retrieve output file, it may have been detected by AV. Please try increasing the timeout with the '--exec-timeout' option. If it is still failing, try the 'smb' protocol or another exec method")
def remove_Instance(self):
if self.__retOutput: