Merge branch 'main' into wmi-improv-admin-check
commit
e3837d986a
|
@ -0,0 +1,6 @@
|
||||||
|
##########################################
|
||||||
|
# code ownership
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
# default ownership:
|
||||||
|
* @zblurx @Marshall-Hallenbeck @NeffIsBack
|
|
@ -1,11 +1,8 @@
|
||||||
name: CrackMapExec Tests
|
name: CrackMapExec Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
pull_request_review:
|
||||||
push:
|
types: [submitted]
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
name: CrackMapExec Tests & Build
|
name: CrackMapExec Build Binaries
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
branches: [ main ]
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
43
README.md
43
README.md
|
@ -1,58 +1,37 @@
|
||||||
![Supported Python versions](https://img.shields.io/badge/python-3.7+-blue.svg)
|
![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/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/_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)
|
[![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
|
🚩 This is the open source repository of NetExec maintained by a community of passionate people
|
||||||
# CrackMapExec
|
# NetExec - The Network Execution Tool
|
||||||
|
|
||||||
<p align="center">
|
<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>
|
</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.
|
- 🚧 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)
|
||||||
You are on the **latest up-to-date** repository of the project CrackMapExec ! 🎉
|
- 💬 If you want to discuss, open a [Discussion](https://github.com/Pennyw0rth/NetExec/discussions)
|
||||||
|
|
||||||
- 🚧 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)
|
|
||||||
|
|
||||||
# Acknowledgments
|
# 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
|
# 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
|
# 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
|
# Code Contributors
|
||||||
|
Awesome code contributors of NetExec:
|
||||||
Awesome code contributors of CME:
|
|
||||||
|
|
||||||
[![](https://github.com/mpgn.png?size=50)](https://github.com/mpgn)
|
[![](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/Marshall-Hallenbeck.png?size=50)](https://github.com/Marshall-Hallenbeck)
|
||||||
[![](https://github.com/zblurx.png?size=50)](https://github.com/zblurx)
|
[![](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/NeffIsBack.png?size=50)](https://github.com/NeffIsBack)
|
||||||
[![](https://github.com/Hackndo.png?size=50)](https://github.com/Hackndo)
|
[![](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~~
|
|
||||||
|
|
|
@ -24,7 +24,8 @@ def gen_cli_args():
|
||||||
\______|| _| `._____|/__/ \__\ \______||__|\__\ |__| |__| /__/ \__\ | _| |_______|/__/ \__\ |_______| \______|
|
\______|| _| `._____|/__/ \__\ \______||__|\__\ |__| |__| /__/ \__\ | _| |_______|/__/ \__\ |_______| \______|
|
||||||
|
|
||||||
A swiss army knife for pentesting networks
|
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('Version', 'red')} : {highlight(VERSION)}
|
||||||
{highlight('Codename', 'red')}: {highlight(CODENAME)}
|
{highlight('Codename', 'red')}: {highlight(CODENAME)}
|
||||||
|
|
|
@ -341,6 +341,12 @@ class connection(object):
|
||||||
secret.append(aesKey)
|
secret.append(aesKey)
|
||||||
cred_type.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)
|
return domain, username, owned, secret, cred_type, [None] * len(secret)
|
||||||
|
|
||||||
def try_credentials(self, domain, username, owned, secret, cred_type, data=None):
|
def try_credentials(self, domain, username, owned, secret, cred_type, data=None):
|
||||||
|
|
|
@ -144,7 +144,7 @@ class CMEModule:
|
||||||
return connection.execute("powershell.exe -e {} -OutputFormat Text".format(psScipt_b64), True)
|
return connection.execute("powershell.exe -e {} -OutputFormat Text".format(psScipt_b64), True)
|
||||||
|
|
||||||
def printCreds(self, context, output):
|
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:
|
if "CLIXML" in output:
|
||||||
output = self.stripXmlOutput(context, output)
|
output = self.stripXmlOutput(context, output)
|
||||||
|
|
||||||
|
|
|
@ -734,8 +734,9 @@ class smb(connection):
|
||||||
self.kdcHost,
|
self.kdcHost,
|
||||||
self.hash,
|
self.hash,
|
||||||
self.logger,
|
self.logger,
|
||||||
self.args.get_output_tries
|
self.args.get_output_tries,
|
||||||
) # self.args.share)
|
self.args.share
|
||||||
|
)
|
||||||
self.logger.info("Executed command via atexec")
|
self.logger.info("Executed command via atexec")
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
|
|
|
@ -38,6 +38,8 @@ class TSCH_EXEC:
|
||||||
self.__doKerberos = doKerberos
|
self.__doKerberos = doKerberos
|
||||||
self.__kdcHost = kdcHost
|
self.__kdcHost = kdcHost
|
||||||
self.__tries = tries
|
self.__tries = tries
|
||||||
|
self.__output_filename = None
|
||||||
|
self.__share = share
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
|
||||||
if hashes is not None:
|
if hashes is not None:
|
||||||
|
@ -73,7 +75,7 @@ class TSCH_EXEC:
|
||||||
def output_callback(self, data):
|
def output_callback(self, data):
|
||||||
self.__outputBuffer = 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"?>
|
xml = """<?xml version="1.0" encoding="UTF-16"?>
|
||||||
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
||||||
<Triggers>
|
<Triggers>
|
||||||
|
@ -114,11 +116,12 @@ class TSCH_EXEC:
|
||||||
<Command>cmd.exe</Command>
|
<Command>cmd.exe</Command>
|
||||||
"""
|
"""
|
||||||
if self.__retOutput:
|
if self.__retOutput:
|
||||||
|
self.__output_filename = "\\Windows\\Temp\\" + gen_random_string(6)
|
||||||
if fileless:
|
if fileless:
|
||||||
local_ip = self.__rpctransport.get_socket().getsockname()[0]
|
local_ip = self.__rpctransport.get_socket().getsockname()[0]
|
||||||
argument_xml = f" <Arguments>/C {command} > \\\\{local_ip}\\{self.__share_name}\\{tmpFileName} 2>&1</Arguments>"
|
argument_xml = f" <Arguments>/C {command} > \\\\{local_ip}\\{self.__share_name}\\{self.__output_filename} 2>&1</Arguments>"
|
||||||
else:
|
else:
|
||||||
argument_xml = f" <Arguments>/C {command} > %windir%\\Temp\\{tmpFileName} 2>&1</Arguments>"
|
argument_xml = f" <Arguments>/C {command} > {self.__output_filename} 2>&1</Arguments>"
|
||||||
|
|
||||||
elif self.__retOutput is False:
|
elif self.__retOutput is False:
|
||||||
argument_xml = f" <Arguments>/C {command}</Arguments>"
|
argument_xml = f" <Arguments>/C {command}</Arguments>"
|
||||||
|
@ -143,9 +146,8 @@ class TSCH_EXEC:
|
||||||
# dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
|
# dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
|
||||||
|
|
||||||
tmpName = gen_random_string(8)
|
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}")
|
self.logger.info(f"Task XML: {xml}")
|
||||||
taskCreated = False
|
taskCreated = False
|
||||||
|
@ -187,7 +189,7 @@ class TSCH_EXEC:
|
||||||
if fileless:
|
if fileless:
|
||||||
while True:
|
while True:
|
||||||
try:
|
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())
|
self.output_callback(output.read())
|
||||||
break
|
break
|
||||||
except IOError:
|
except IOError:
|
||||||
|
@ -198,15 +200,15 @@ class TSCH_EXEC:
|
||||||
tries = 1
|
tries = 1
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
self.logger.info(f"Attempting to read ADMIN$\\Temp\\{tmpFileName}")
|
self.logger.info(f"Attempting to read {self.__share}\\{self.__output_filename}")
|
||||||
smbConnection.getFile("ADMIN$", f"Temp\\{tmpFileName}", self.output_callback)
|
smbConnection.getFile(self.__share, self.__output_filename, self.output_callback)
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if tries >= self.__tries:
|
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
|
break
|
||||||
if str(e).find("STATUS_BAD_NETWORK_NAME") >0 :
|
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
|
break
|
||||||
if str(e).find("SHARING") > 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
|
if str(e).find("SHARING") > 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
|
||||||
sleep(3)
|
sleep(3)
|
||||||
|
@ -215,7 +217,7 @@ class TSCH_EXEC:
|
||||||
self.logger.debug(str(e))
|
self.logger.debug(str(e))
|
||||||
|
|
||||||
if self.__outputBuffer:
|
if self.__outputBuffer:
|
||||||
self.logger.debug(f"Deleting file ADMIN$\\Temp\\{tmpFileName}")
|
self.logger.debug(f"Deleting file {self.__share}\\{self.__output_filename}")
|
||||||
smbConnection.deleteFile("ADMIN$", f"Temp\\{tmpFileName}")
|
smbConnection.deleteFile(self.__share, self.__output_filename)
|
||||||
|
|
||||||
dce.disconnect()
|
dce.disconnect()
|
||||||
|
|
|
@ -252,10 +252,10 @@ class MMCEXEC:
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if tries >= self.__tries:
|
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
|
break
|
||||||
if str(e).find("STATUS_BAD_NETWORK_NAME") >0 :
|
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
|
break
|
||||||
if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
|
if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
|
||||||
# Output not finished, let's wait
|
# Output not finished, let's wait
|
||||||
|
|
|
@ -91,7 +91,7 @@ def proto_args(parser, std_parser, module_parser):
|
||||||
help="force the PowerShell command to run in a 32-bit process")
|
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")
|
cgroup.add_argument("--no-output", action="store_true", help="do not retrieve command output")
|
||||||
cegroup = cgroup.add_mutually_exclusive_group()
|
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")
|
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 = smb_parser.add_argument_group("Powershell Obfuscation", "Options for PowerShell script obfuscation")
|
||||||
psgroup.add_argument("--obfs", action="store_true", help="Obfuscate PowerShell scripts")
|
psgroup.add_argument("--obfs", action="store_true", help="Obfuscate PowerShell scripts")
|
||||||
|
|
|
@ -170,10 +170,10 @@ class SMBEXEC:
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if tries >= self.__tries:
|
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
|
break
|
||||||
if str(e).find("STATUS_BAD_NETWORK_NAME") >0 :
|
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
|
break
|
||||||
if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
|
if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
|
||||||
# Output not finished, let's wait
|
# Output not finished, let's wait
|
||||||
|
|
|
@ -166,10 +166,10 @@ class WMIEXEC:
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if tries >= self.__tries:
|
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
|
break
|
||||||
if str(e).find("STATUS_BAD_NETWORK_NAME") >0 :
|
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
|
break
|
||||||
if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
|
if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
|
||||||
sleep(2)
|
sleep(2)
|
||||||
|
|
|
@ -231,30 +231,13 @@ class winrm(connection):
|
||||||
self.password = password
|
self.password = password
|
||||||
self.username = username
|
self.username = username
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
if self.args.ssl and self.args.ignore_ssl_cert:
|
|
||||||
self.conn = Client(
|
self.conn = Client(
|
||||||
self.host,
|
self.host,
|
||||||
auth="ntlm",
|
auth="ntlm",
|
||||||
username=f"{domain}\\{self.username}",
|
username=f"{domain}\\{self.username}",
|
||||||
password=self.password,
|
password=self.password,
|
||||||
ssl=True,
|
ssl=True if self.args.ssl else False,
|
||||||
cert_validation=False,
|
cert_validation=False if self.args.ignore_ssl_cert else True,
|
||||||
)
|
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# TO DO: right now we're just running the hostname command to make the winrm library auth to the server
|
# 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
|
nthash = self.hash
|
||||||
|
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
if self.args.ssl and self.args.ignore_ssl_cert:
|
|
||||||
self.conn = Client(
|
self.conn = Client(
|
||||||
self.host,
|
self.host,
|
||||||
auth="ntlm",
|
auth="ntlm",
|
||||||
username=f"{self.domain}\\{self.username}",
|
username=f"{self.domain}\\{self.username}",
|
||||||
password=lmhash + nthash,
|
password=lmhash + nthash,
|
||||||
ssl=True,
|
ssl=True if self.args.ssl else False,
|
||||||
cert_validation=False,
|
cert_validation=False if self.args.ignore_ssl_cert else True,
|
||||||
)
|
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# TO DO: right now we're just running the hostname command to make the winrm library auth to the server
|
# TO DO: right now we're just running the hostname command to make the winrm library auth to the server
|
||||||
|
|
|
@ -432,7 +432,7 @@ class wmi(connection):
|
||||||
if not self.args.no_output:
|
if not self.args.no_output:
|
||||||
get_output = True
|
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")
|
self.logger.fail("Execute 'systeminfo' must set the interval time higher than 10 seconds")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -441,11 +441,11 @@ class wmi(connection):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.args.exec_method == "wmiexec":
|
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)
|
output = exec_method.execute(command, get_output)
|
||||||
|
|
||||||
elif self.args.exec_method == "wmiexec-event":
|
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)
|
output = exec_method.execute(command, get_output)
|
||||||
|
|
||||||
self.conn.disconnect()
|
self.conn.disconnect()
|
||||||
|
|
|
@ -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 (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, "
|
"[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).")
|
"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",
|
cgroup.add_argument("--codec", default="utf-8",
|
||||||
help="Set encoding used (codec) from the target's output (default "
|
help="Set encoding used (codec) from the target's output (default "
|
||||||
"\"utf-8\"). If errors are detected, run chcp.com at the target, "
|
"\"utf-8\"). If errors are detected, run chcp.com at the target, "
|
||||||
|
|
|
@ -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
|
from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, WBEM_FLAG_FORWARD_ONLY, IWbemLevel1Login
|
||||||
|
|
||||||
class WMIEXEC:
|
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.__host = host
|
||||||
self.__username = username
|
self.__username = username
|
||||||
self.__password = password
|
self.__password = password
|
||||||
|
@ -45,7 +45,7 @@ class WMIEXEC:
|
||||||
self.__kdcHost = kdcHost
|
self.__kdcHost = kdcHost
|
||||||
self.__aesKey = aesKey
|
self.__aesKey = aesKey
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.__interval_time = interval_time
|
self.__exec_timeout = exec_timeout
|
||||||
self.__registry_Path = ""
|
self.__registry_Path = ""
|
||||||
self.__outputBuffer = ""
|
self.__outputBuffer = ""
|
||||||
self.__retOutput = True
|
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}'''
|
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.execute_remote(command)
|
||||||
self.logger.info("Waiting {}s for command completely executed.".format(self.__interval_time))
|
self.logger.info("Waiting {}s for command completely executed.".format(self.__exec_timeout))
|
||||||
time.sleep(self.__interval_time)
|
time.sleep(self.__exec_timeout)
|
||||||
|
|
||||||
self.queryRegistry(keyName)
|
self.queryRegistry(keyName)
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ class WMIEXEC:
|
||||||
retVal = descriptor.GetStringValue(2147483650, self.__registry_Path, keyName)
|
retVal = descriptor.GetStringValue(2147483650, self.__registry_Path, keyName)
|
||||||
self.__outputBuffer = base64.b64decode(retVal.sValue).decode(self.__codec, errors='replace').rstrip('\r\n')
|
self.__outputBuffer = base64.b64decode(retVal.sValue).decode(self.__codec, errors='replace').rstrip('\r\n')
|
||||||
except Exception as e:
|
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:
|
try:
|
||||||
self.logger.debug(f"Removing temporary registry path: HKLM\\{self.__registry_Path}")
|
self.logger.debug(f"Removing temporary registry path: HKLM\\{self.__registry_Path}")
|
||||||
|
|
|
@ -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
|
from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, WBEM_FLAG_FORWARD_ONLY, IWbemLevel1Login, WBEMSTATUS
|
||||||
|
|
||||||
class WMIEXEC_EVENT:
|
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.__host = host
|
||||||
self.__username = username
|
self.__username = username
|
||||||
self.__password = password
|
self.__password = password
|
||||||
|
@ -51,7 +51,7 @@ class WMIEXEC_EVENT:
|
||||||
self.__retOutput = True
|
self.__retOutput = True
|
||||||
|
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.__interval_time = interval_time
|
self.__exec_timeout = exec_timeout
|
||||||
self.__codec = codec
|
self.__codec = codec
|
||||||
self.__instanceID = f"windows-object-{str(uuid.uuid4())}"
|
self.__instanceID = f"windows-object-{str(uuid.uuid4())}"
|
||||||
self.__instanceID_StoreResult = 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)
|
self.execute_remote(command)
|
||||||
|
|
||||||
# Get command results
|
# Get command results
|
||||||
self.logger.info("Waiting {}s for command completely executed.".format(self.__interval_time))
|
self.logger.info("Waiting {}s for command completely executed.".format(self.__exec_timeout))
|
||||||
time.sleep(self.__interval_time)
|
time.sleep(self.__exec_timeout)
|
||||||
|
|
||||||
if self.__retOutput:
|
if self.__retOutput:
|
||||||
self.get_CommandResult()
|
self.get_CommandResult()
|
||||||
|
@ -190,7 +190,7 @@ class WMIEXEC_EVENT:
|
||||||
record = dict(command_ResultObject.getProperties())
|
record = dict(command_ResultObject.getProperties())
|
||||||
self.__outputBuffer = base64.b64decode(record['ScriptText']['value']).decode(self.__codec, errors='replace')
|
self.__outputBuffer = base64.b64decode(record['ScriptText']['value']).decode(self.__codec, errors='replace')
|
||||||
except Exception as e:
|
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):
|
def remove_Instance(self):
|
||||||
if self.__retOutput:
|
if self.__retOutput:
|
||||||
|
|
Loading…
Reference in New Issue