ruff autoformat to clean up all the single quotes and other bad formatting

main
Marshall Hallenbeck 2023-09-22 21:10:21 -04:00 committed by Marshall Hallenbeck
parent 9ae9d01e5a
commit 11ddfd9c79
61 changed files with 1480 additions and 1970 deletions

View File

@ -14,7 +14,8 @@ def gen_cli_args():
VERSION = importlib.metadata.version("netexec")
CODENAME = "A New Beginning"
parser = argparse.ArgumentParser(description=f"""
parser = argparse.ArgumentParser(
description=f"""
_ _ _ _____
| \ | | ___ | |_ | ____| __ __ ___ ___
| \| | / _ \ | __| | _| \ \/ / / _ \ / __|

View File

@ -45,4 +45,4 @@ if len(host_info_colors) != 4:
# this should probably be put somewhere else, but if it's in the config helpers, there is a circular import
def process_secret(text):
hidden = text[:reveal_chars_of_pwd]
return text if not audit_mode else hidden+audit_mode * 8
return text if not audit_mode else hidden + audit_mode * 8

View File

@ -25,15 +25,16 @@ user_failed_logins = {}
def gethost_addrinfo(hostname):
try:
for res in getaddrinfo( hostname, None, AF_INET6, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME):
for res in getaddrinfo(hostname, None, AF_INET6, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME):
af, socktype, proto, canonname, sa = res
host = canonname if ip_address(sa[0]).is_link_local else sa[0]
except socket.gaierror:
for res in getaddrinfo( hostname, None, AF_INET, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME):
for res in getaddrinfo(hostname, None, AF_INET, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME):
af, socktype, proto, canonname, sa = res
host = sa[0] if sa[0] else canonname
return host
def requires_admin(func):
def _decorator(self, *args, **kwargs):
if self.admin_privs is False:
@ -42,22 +43,23 @@ def requires_admin(func):
return wraps(func)(_decorator)
def dcom_FirewallChecker(iInterface, timeout):
stringBindings = iInterface.get_cinstance().get_string_bindings()
for strBinding in stringBindings:
if strBinding['wTowerId'] == 7:
if strBinding['aNetworkAddr'].find('[') >= 0:
binding, _, bindingPort = strBinding['aNetworkAddr'].partition('[')
bindingPort = '[' + bindingPort
if strBinding["wTowerId"] == 7:
if strBinding["aNetworkAddr"].find("[") >= 0:
binding, _, bindingPort = strBinding["aNetworkAddr"].partition("[")
bindingPort = "[" + bindingPort
else:
binding = strBinding['aNetworkAddr']
bindingPort = ''
binding = strBinding["aNetworkAddr"]
bindingPort = ""
if binding.upper().find(iInterface.get_target().upper()) >= 0:
stringBinding = 'ncacn_ip_tcp:' + strBinding['aNetworkAddr'][:-1]
stringBinding = "ncacn_ip_tcp:" + strBinding["aNetworkAddr"][:-1]
break
elif iInterface.is_fqdn() and binding.upper().find(iInterface.get_target().upper().partition('.')[0]) >= 0:
stringBinding = 'ncacn_ip_tcp:%s%s' % (iInterface.get_target(), bindingPort)
elif iInterface.is_fqdn() and binding.upper().find(iInterface.get_target().upper().partition(".")[0]) >= 0:
stringBinding = "ncacn_ip_tcp:%s%s" % (iInterface.get_target(), bindingPort)
if "stringBinding" not in locals():
return True, None
try:
@ -308,7 +310,7 @@ class connection(object):
# Parse usernames
for user in self.args.username:
if isfile(user):
with open(user, 'r') as user_file:
with open(user, "r") as user_file:
for line in user_file:
if "\\" in line:
domain_single, username_single = line.split("\\")
@ -340,34 +342,33 @@ class connection(object):
self.logger.error(f"{type(e).__name__}: Could not decode password file. Make sure the file only contains UTF-8 characters.")
self.logger.error("You can ignore non UTF-8 characters with the option '--ignore-pw-decoding'")
exit(1)
else:
secret.append(password)
cred_type.append('plaintext')
cred_type.append("plaintext")
# Parse NTLM-hashes
if hasattr(self.args, "hash") and self.args.hash:
for ntlm_hash in self.args.hash:
if isfile(ntlm_hash):
with open(ntlm_hash, 'r') as ntlm_hash_file:
with open(ntlm_hash, "r") as ntlm_hash_file:
for line in ntlm_hash_file:
secret.append(line.strip())
cred_type.append('hash')
cred_type.append("hash")
else:
secret.append(ntlm_hash)
cred_type.append('hash')
cred_type.append("hash")
# Parse AES keys
if self.args.aesKey:
for aesKey in self.args.aesKey:
if isfile(aesKey):
with open(aesKey, 'r') as aesKey_file:
with open(aesKey, "r") as aesKey_file:
for line in aesKey_file:
secret.append(line.strip())
cred_type.append('aesKey')
cred_type.append("aesKey")
else:
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:
@ -390,26 +391,26 @@ class connection(object):
if self.args.continue_on_success and owned:
return False
# Enforcing FQDN for SMB if not using local authentication. Related issues/PRs: #26, #28, #24, #38
if self.args.protocol == 'smb' and not self.args.local_auth and "." not in domain and not self.args.laps and secret != "" and not (self.domain.upper() == self.hostname.upper()) :
if self.args.protocol == "smb" and not self.args.local_auth and "." not in domain and not self.args.laps and secret != "" and not (self.domain.upper() == self.hostname.upper()):
self.logger.error(f"Domain {domain} for user {username.rstrip()} need to be FQDN ex:domain.local, not domain")
return False
with sem:
if cred_type == 'plaintext':
if cred_type == "plaintext":
if self.args.kerberos:
return self.kerberos_login(domain, username, secret, '', '', self.kdcHost, False)
return self.kerberos_login(domain, username, secret, "", "", self.kdcHost, False)
elif hasattr(self.args, "domain"): # Some protocolls don't use domain for login
return self.plaintext_login(domain, username, secret)
elif self.args.protocol == 'ssh':
elif self.args.protocol == "ssh":
return self.plaintext_login(username, secret, data)
else:
return self.plaintext_login(username, secret)
elif cred_type == 'hash':
elif cred_type == "hash":
if self.args.kerberos:
return self.kerberos_login(domain, username, '', secret, '', self.kdcHost, False)
return self.kerberos_login(domain, username, "", secret, "", self.kdcHost, False)
return self.hash_login(domain, username, secret)
elif cred_type == 'aesKey':
return self.kerberos_login(domain, username, '', '', secret, self.kdcHost, False)
elif cred_type == "aesKey":
return self.kerberos_login(domain, username, "", "", secret, self.kdcHost, False)
def login(self):
"""

View File

@ -152,9 +152,7 @@ else
IEX "$functions"
Command-ToExecute
}}
""".format(
command=amsi_bypass + ps_command
)
""".format(command=amsi_bypass + ps_command)
)
else:

View File

@ -34,7 +34,7 @@ class NXCAdapter(logging.LoggerAdapter):
logging.getLogger("pypykatz").disabled = True
logging.getLogger("minidump").disabled = True
logging.getLogger("lsassy").disabled = True
#logging.getLogger("impacket").disabled = True
# logging.getLogger("impacket").disabled = True
def format(self, msg, *args, **kwargs):
"""
@ -88,7 +88,7 @@ class NXCAdapter(logging.LoggerAdapter):
nxc_console.print(text, *args, **kwargs)
self.log_console_to_file(text, *args, **kwargs)
def success(self, msg, color='green', *args, **kwargs):
def success(self, msg, color="green", *args, **kwargs):
"""
Print some sort of success to the user
"""
@ -118,7 +118,7 @@ class NXCAdapter(logging.LoggerAdapter):
nxc_console.print(text, *args, **kwargs)
self.log_console_to_file(text, *args, **kwargs)
def fail(self, msg, color='red', *args, **kwargs):
def fail(self, msg, color="red", *args, **kwargs):
"""
Prints a failure (may or may not be an error) - e.g. login creds didn't work
"""
@ -181,13 +181,13 @@ class NXCAdapter(logging.LoggerAdapter):
@staticmethod
def init_log_file():
newpath = os.path.expanduser("~/.nxc") + "/logs/" + datetime.now().strftime('%Y-%m-%d')
newpath = os.path.expanduser("~/.nxc") + "/logs/" + datetime.now().strftime("%Y-%m-%d")
if not os.path.exists(newpath):
os.makedirs(newpath)
log_filename = os.path.join(
os.path.expanduser("~/.nxc"),
"logs",
datetime.now().strftime('%Y-%m-%d'),
datetime.now().strftime("%Y-%m-%d"),
f"log_{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}.log",
)
return log_filename

View File

@ -14,9 +14,9 @@ class NXCModule:
Thanks to the guys at impacket for the original code
"""
name = 'add-computer'
description = 'Adds or deletes a domain computer'
supported_protocols = ['smb']
name = "add-computer"
description = "Adds or deletes a domain computer"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = False
@ -39,26 +39,26 @@ class NXCModule:
self.__delete = False
self.noLDAPRequired = False
if 'DELETE' in module_options:
if "DELETE" in module_options:
self.__delete = True
if 'CHANGEPW' in module_options and ('NAME' not in module_options or 'PASSWORD' not in module_options):
context.log.error('NAME and PASSWORD options are required!')
elif 'CHANGEPW' in module_options:
self.__noAdd = True
if "CHANGEPW" in module_options and ("NAME" not in module_options or "PASSWORD" not in module_options):
context.log.error("NAME and PASSWORD options are required!")
elif "CHANGEPW" in module_options:
self.__noAdd = True
if 'NAME' in module_options:
self.__computerName = module_options['NAME']
if self.__computerName[-1] != '$':
self.__computerName += '$'
if "NAME" in module_options:
self.__computerName = module_options["NAME"]
if self.__computerName[-1] != "$":
self.__computerName += "$"
else:
context.log.error('NAME option is required!')
context.log.error("NAME option is required!")
exit(1)
if 'PASSWORD' in module_options:
self.__computerPassword = module_options['PASSWORD']
elif 'PASSWORD' not in module_options and not self.__delete:
context.log.error('PASSWORD option is required!')
if "PASSWORD" in module_options:
self.__computerPassword = module_options["PASSWORD"]
elif "PASSWORD" not in module_options and not self.__delete:
context.log.error("PASSWORD option is required!")
exit(1)
def on_login(self, context, connection):
@ -89,7 +89,7 @@ class NXCModule:
# If SAMR fails now try over LDAPS
if not self.noLDAPRequired:
self.do_ldaps_add(connection, context)
self.do_ldaps_add(connection, context)
else:
exit(1)
@ -113,16 +113,9 @@ class NXCModule:
rpc_transport.setRemoteHost(self.__targetIp)
rpc_transport.setRemoteName(self.__target)
if hasattr(rpc_transport, 'set_credentials'):
if hasattr(rpc_transport, "set_credentials"):
# This method exists only for selected protocol sequences.
rpc_transport.set_credentials(
self.__username,
self.__password,
self.__domain,
self.__lmhash,
self.__nthash,
self.__aesKey
)
rpc_transport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey)
rpc_transport.set_kerberos(self.__doKerberos, self.__kdcHost)
@ -130,22 +123,16 @@ class NXCModule:
dce.connect()
dce.bind(samr.MSRPC_UUID_SAMR)
samr_connect_response = samr.hSamrConnect5(
dce,
'\\\\%s\x00' % self.__target,
samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN
)
serv_handle = samr_connect_response['ServerHandle']
samr_connect_response = samr.hSamrConnect5(dce, "\\\\%s\x00" % self.__target, samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN)
serv_handle = samr_connect_response["ServerHandle"]
samr_enum_response = samr.hSamrEnumerateDomainsInSamServer(dce, serv_handle)
domains = samr_enum_response['Buffer']['Buffer']
domains_without_builtin = [
domain for domain in domains if domain['Name'].lower() != 'builtin'
]
domains = samr_enum_response["Buffer"]["Buffer"]
domains_without_builtin = [domain for domain in domains if domain["Name"].lower() != "builtin"]
if len(domains_without_builtin) > 1:
domain = list(filter(lambda x: x['Name'].lower() == self.__domainNetbios, domains))
domain = list(filter(lambda x: x["Name"].lower() == self.__domainNetbios, domains))
if len(domain) != 1:
context.log.highlight(u'{}'.format('This domain does not exist: "' + self.__domainNetbios + '"'))
context.log.highlight("{}".format('This domain does not exist: "' + self.__domainNetbios + '"'))
context.log.highlight("Available domain(s):")
for domain in domains:
context.log.highlight(f" * {domain['Name']}")
@ -155,33 +142,25 @@ class NXCModule:
else:
selected_domain = domains_without_builtin[0]["Name"]
samr_lookup_domain_response = samr.hSamrLookupDomainInSamServer(
dce, serv_handle, selected_domain
)
samr_lookup_domain_response = samr.hSamrLookupDomainInSamServer(dce, serv_handle, selected_domain)
domain_sid = samr_lookup_domain_response["DomainId"]
context.log.debug(f"Opening domain {selected_domain}...")
samr_open_domain_response = samr.hSamrOpenDomain(
dce, serv_handle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, domain_sid
)
samr_open_domain_response = samr.hSamrOpenDomain(dce, serv_handle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER, domain_sid)
domain_handle = samr_open_domain_response["DomainHandle"]
if self.__noAdd or self.__delete:
try:
check_for_user = samr.hSamrLookupNamesInDomain(
dce, domain_handle, [self.__computerName]
)
check_for_user = samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
except samr.DCERPCSessionError as e:
if e.error_code == 0xc0000073:
context.log.highlight(
f"{self.__computerName} not found in domain {selected_domain}"
)
if e.error_code == 0xC0000073:
context.log.highlight(f"{self.__computerName} not found in domain {selected_domain}")
self.noLDAPRequired = True
raise Exception()
else:
raise
user_rid = check_for_user['RelativeIds']['Element'][0]
user_rid = check_for_user["RelativeIds"]["Element"][0]
if self.__delete:
access = samr.DELETE
message = "delete"
@ -190,11 +169,10 @@ class NXCModule:
message = "set the password for"
try:
open_user = samr.hSamrOpenUser(dce, domain_handle, access, user_rid)
user_handle = open_user['UserHandle']
user_handle = open_user["UserHandle"]
except samr.DCERPCSessionError as e:
if e.error_code == 0xc0000022:
context.log.highlight(u'{}'.format(
self.__username + ' does not have the right to ' + message + " " + self.__computerName))
if e.error_code == 0xC0000022:
context.log.highlight("{}".format(self.__username + " does not have the right to " + message + " " + self.__computerName))
self.noLDAPRequired = True
raise Exception()
else:
@ -204,11 +182,10 @@ class NXCModule:
try:
samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
self.noLDAPRequired = True
context.log.highlight(u'{}'.format(
'Computer account already exists with the name: "' + self.__computerName + '"'))
context.log.highlight("{}".format('Computer account already exists with the name: "' + self.__computerName + '"'))
raise Exception()
except samr.DCERPCSessionError as e:
if e.error_code != 0xc0000073:
if e.error_code != 0xC0000073:
raise
else:
found_unused = False
@ -217,52 +194,52 @@ class NXCModule:
try:
samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
except samr.DCERPCSessionError as e:
if e.error_code == 0xc0000073:
if e.error_code == 0xC0000073:
found_unused = True
else:
raise
try:
create_user = samr.hSamrCreateUser2InDomain(dce, domain_handle, self.__computerName, samr.USER_WORKSTATION_TRUST_ACCOUNT, samr.USER_FORCE_PASSWORD_CHANGE,)
create_user = samr.hSamrCreateUser2InDomain(
dce,
domain_handle,
self.__computerName,
samr.USER_WORKSTATION_TRUST_ACCOUNT,
samr.USER_FORCE_PASSWORD_CHANGE,
)
self.noLDAPRequired = True
context.log.highlight('Successfully added the machine account: "' + self.__computerName + '" with Password: "' + self.__computerPassword + '"')
except samr.DCERPCSessionError as e:
if e.error_code == 0xc0000022:
context.log.highlight(u'{}'.format(
'The following user does not have the right to create a computer account: "' + self.__username + '"'))
if e.error_code == 0xC0000022:
context.log.highlight("{}".format('The following user does not have the right to create a computer account: "' + self.__username + '"'))
raise Exception()
elif e.error_code == 0xc00002e7:
context.log.highlight(u'{}'.format(
'The following user exceeded their machine account quota: "' + self.__username + '"'))
elif e.error_code == 0xC00002E7:
context.log.highlight("{}".format('The following user exceeded their machine account quota: "' + self.__username + '"'))
raise Exception()
else:
raise
user_handle = create_user['UserHandle']
user_handle = create_user["UserHandle"]
if self.__delete:
samr.hSamrDeleteUser(dce, user_handle)
context.log.highlight(u'{}'.format('Successfully deleted the "' + self.__computerName + '" Computer account'))
self.noLDAPRequired=True
context.log.highlight("{}".format('Successfully deleted the "' + self.__computerName + '" Computer account'))
self.noLDAPRequired = True
user_handle = None
else:
samr.hSamrSetPasswordInternal4New(dce, user_handle, self.__computerPassword)
if self.__noAdd:
context.log.highlight(u'{}'.format(
'Successfully set the password of machine "' + self.__computerName + '" with password "' + self.__computerPassword + '"'))
self.noLDAPRequired=True
context.log.highlight("{}".format('Successfully set the password of machine "' + self.__computerName + '" with password "' + self.__computerPassword + '"'))
self.noLDAPRequired = True
else:
check_for_user = samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
user_rid = check_for_user['RelativeIds']['Element'][0]
open_user = samr.hSamrOpenUser(
dce, domain_handle, access, user_rid
)
user_handle = open_user['UserHandle']
user_rid = check_for_user["RelativeIds"]["Element"][0]
open_user = samr.hSamrOpenUser(dce, domain_handle, access, user_rid)
user_handle = open_user["UserHandle"]
req = samr.SAMPR_USER_INFO_BUFFER()
req['tag'] = samr.USER_INFORMATION_CLASS.UserControlInformation
req['Control']['UserAccountControl'] = samr.USER_WORKSTATION_TRUST_ACCOUNT
req["tag"] = samr.USER_INFORMATION_CLASS.UserControlInformation
req["Control"]["UserAccountControl"] = samr.USER_WORKSTATION_TRUST_ACCOUNT
samr.hSamrSetInformationUser2(dce, user_handle, req)
if not self.noLDAPRequired:
context.log.highlight(u'{}'.format(
'Successfully added the machine account "' + self.__computerName + '" with Password: "' + self.__computerPassword + '"'))
context.log.highlight("{}".format('Successfully added the machine account "' + self.__computerName + '" with Password: "' + self.__computerPassword + '"'))
self.noLDAPRequired = True
if user_handle is not None:
@ -315,8 +292,7 @@ class NXCModule:
elif result is False and c.last_error == "insufficientAccessRights":
context.log.highlight(f'Insufficient Access Rights to delete the Computer "{self.__computerName}"')
else:
context.log.highlight(
f'Unable to delete the "{self.__computerName}" Computer account. The error was: {c.last_error}')
context.log.highlight(f'Unable to delete the "{self.__computerName}" Computer account. The error was: {c.last_error}')
else:
result = c.add(
f"cn={self.__computerName},cn=Computers,dc={ldap_domain}",
@ -324,8 +300,7 @@ class NXCModule:
ucd
)
if result:
context.log.highlight(
f'Successfully added the machine account: "{self.__computerName}" with Password: "{self.__computerPassword}"')
context.log.highlight(f'Successfully added the machine account: "{self.__computerName}" with Password: "{self.__computerPassword}"')
context.log.highlight("You can try to verify this with the nxc command:")
context.log.highlight(f"nxc ldap {connection.host} -u {connection.username} -p {connection.password} -M group-mem -o GROUP='Domain Computers'")
elif result is False and c.last_error == "entryAlreadyExists":

View File

@ -42,7 +42,7 @@ class NXCModule:
command = "powershell -c 'C:\\windows\\system32\\inetsrv\\appcmd.exe list apppool /@t:*'"
context.log.info("Checking For Hidden Credentials With Appcmd.exe")
output = connection.execute(command, True)
lines = output.splitlines()
username = None
password = None
@ -58,12 +58,12 @@ class NXCModule:
if "password:" in line:
password = line.split("password:")[1].strip().strip('"')
if apppool_name and username is not None and password is not None:
if apppool_name and username is not None and password is not None:
current_credentials = (apppool_name, username, password)
if current_credentials not in credentials_set:
credentials_set.add(current_credentials)
if username:
context.log.success(f"Credentials Found for APPPOOL: {apppool_name}")
if password == "":

View File

@ -281,11 +281,7 @@ class NXCModule:
searchBase=self.baseDN,
searchFilter="(sAMAccountName=%s)" % escape_filter_chars(_lookedup_principal),
attributes=["objectSid"],
)[0][
1
][0][
1
][0]
)[0][1][0][1][0]
)
context.log.highlight("Found principal SID to filter on: %s" % self.principal_sid)
except Exception:
@ -414,18 +410,12 @@ class NXCModule:
searchBase=self.baseDN,
searchFilter="(objectSid=%s)" % sid,
attributes=["sAMAccountName"],
)[
0
][0]
)[0][0]
samname = self.ldap_session.search(
searchBase=self.baseDN,
searchFilter="(objectSid=%s)" % sid,
attributes=["sAMAccountName"],
)[0][
1
][0][
1
][0]
)[0][1][0][1][0]
return samname
except Exception:
context.log.debug("SID not found in LDAP: %s" % sid)

View File

@ -55,7 +55,8 @@ class NXCModule:
for service in product["services"]:
try:
lsa.LsarLookupNames(dce, policyHandle, service["name"])
context.log.info(f"Detected installed service on {connection.host}: {product['name']} {service['description']}")
context.log.info(
f"Detected installed service on {connection.host}: {product['name']} {service['description']}")
if product["name"] not in results:
results[product["name"]] = {"services": []}
results[product["name"]]["services"].append(service)
@ -72,7 +73,8 @@ class NXCModule:
for i, product in enumerate(conf["products"]):
for pipe in product["pipes"]:
if pathlib.PurePath(fl).match(pipe["name"]):
context.log.debug(f"{product['name']} running claim found on {connection.host} by existing pipe {fl} (likely processes: {pipe['processes']})")
context.log.debug(
f"{product['name']} running claim found on {connection.host} by existing pipe {fl} (likely processes: {pipe['processes']})")
if product["name"] not in results:
results[product["name"]] = {}
if "pipes" not in results[product["name"]]:
@ -116,16 +118,16 @@ class LsaLookupNames:
authn = True
def __init__(
self,
domain="",
username="",
password="",
remote_name="",
k=False,
kdcHost="",
lmhash="",
nthash="",
aesKey="",
self,
domain="",
username="",
password="",
remote_name="",
k=False,
kdcHost="",
lmhash="",
nthash="",
aesKey="",
):
self.domain = domain
self.username = username
@ -157,7 +159,8 @@ class LsaLookupNames:
# Authenticate if specified
if self.authn and hasattr(rpc_transport, "set_credentials"):
# This method exists only for selected protocol sequences.
rpc_transport.set_credentials(self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey)
rpc_transport.set_credentials(self.username, self.password, self.domain, self.lmhash, self.nthash,
self.aesKey)
if self.doKerberos:
rpc_transport.set_kerberos(self.doKerberos, kdcHost=self.dcHost)
@ -357,17 +360,14 @@ conf = {
"name": "kavfsslp",
"description": "Kaspersky Security Exploit Prevention Service",
},
{
"name": "KAVFS",
"description": "Kaspersky Security Service",
},
{
"name": "KAVFSGT",
"description": "Kaspersky Security Management Service",
},
{
"name": "klnagent",
"description": "Kaspersky Security Center",
@ -378,9 +378,8 @@ conf = {
"name": "Exploit_Blocker",
"processes": ["kavfswh.exe"],
},
],
},
},
{
"name": "Trend Micro Endpoint Security",
"services": [
@ -388,30 +387,26 @@ conf = {
"name": "Trend Micro Endpoint Basecamp",
"description": "Trend Micro Endpoint Basecamp",
},
{
"name": "TMBMServer",
"description": "Trend Micro Unauthorized Change Prevention Service",
},
{
"name": "Trend Micro Web Service Communicator",
"description": "Trend Micro Web Service Communicator",
},
{
"name": "TMiACAgentSvc",
"description": "Trend Micro Application Control Service (Agent)",
},
{
{
"name": "CETASvc",
"description": "Trend Micro Cloud Endpoint Telemetry Service",
},
{
"name": "iVPAgent",
"description": "Trend Micro Vulnerability Protection Service (Agent)",
}
},
],
"pipes": [
{
@ -435,7 +430,7 @@ conf = {
"processes": ["Ntrtscan.exe"],
},
],
},
},
{
"name": "Symantec Endpoint Protection",
"services": [
@ -455,40 +450,40 @@ conf = {
"name": "Sophos Intercept X",
"services": [
{
"name": "SntpService",
"description": "Sophos Network Threat Protection"
"name": "SntpService",
"description": "Sophos Network Threat Protection"
},
{
"name": "Sophos Endpoint Defense Service",
"description": "Sophos Endpoint Defense Service"
"name": "Sophos Endpoint Defense Service",
"description": "Sophos Endpoint Defense Service"
},
{
"name": "Sophos File Scanner Service",
"description": "Sophos File Scanner Service"
"name": "Sophos File Scanner Service",
"description": "Sophos File Scanner Service"
},
{
"name": "Sophos Health Service",
"description": "Sophos Health Service"
"name": "Sophos Health Service",
"description": "Sophos Health Service"
},
{
"name": "Sophos Live Query",
"description": "Sophos Live Query"
"name": "Sophos Live Query",
"description": "Sophos Live Query"
},
{
"name": "Sophos Managed Threat Response",
"description": "Sophos Managed Threat Response"
"name": "Sophos Managed Threat Response",
"description": "Sophos Managed Threat Response"
},
{
"name": "Sophos MCS Agent",
"description": "Sophos MCS Agent"
"name": "Sophos MCS Agent",
"description": "Sophos MCS Agent"
},
{
"name": "Sophos MCS Client",
"description": "Sophos MCS Client"
"name": "Sophos MCS Client",
"description": "Sophos MCS Client"
},
{
"name": "Sophos System Protection Service",
"description": "Sophos System Protection Service"
"name": "Sophos System Protection Service",
"description": "Sophos System Protection Service"
}
],
"pipes": [
@ -528,10 +523,7 @@ conf = {
"name": "PandaAetherAgent",
"description": "Panda Endpoint Agent",
},
{
"name": "PSUAService",
"description": "Panda Product Service"
},
{"name": "PSUAService", "description": "Panda Product Service"},
{
"name": "NanoServiceMain",
"description": "Panda Cloud Antivirus Service",
@ -547,7 +539,6 @@ conf = {
"processes": ["PSUAService.exe"],
},
],
}
},
]
}

View File

@ -10,7 +10,7 @@ class NXCModule:
name = "example module"
description = "I do something"
supported_protocols = [] # Example: ['smb', 'mssql']
supported_protocols = [] # Example: ['smb', 'mssql']
opsec_safe = True # Does the module touch disk?
multiple_hosts = True # Does it make sense to run this module on multiple hosts at a time?
@ -30,21 +30,21 @@ class NXCModule:
"""
# Logging best practice
# Mostly you should use these functions to display information to the user
context.log.display("I'm doing something") # Use this for every normal message ([*] I'm doing something)
context.log.success("I'm doing something") # Use this for when something succeeds ([+] I'm doing something)
context.log.fail("I'm doing something") # Use this for when something fails ([-] I'm doing something), for example a remote registry entry is missing which is needed to proceed
context.log.highlight("I'm doing something") # Use this for when something is important and should be highlighted, printing credentials for example
context.log.display("I'm doing something") # Use this for every normal message ([*] I'm doing something)
context.log.success("I'm doing something") # Use this for when something succeeds ([+] I'm doing something)
context.log.fail("I'm doing something") # Use this for when something fails ([-] I'm doing something), for example a remote registry entry is missing which is needed to proceed
context.log.highlight("I'm doing something") # Use this for when something is important and should be highlighted, printing credentials for example
# These are for debugging purposes
context.log.info("I'm doing something") # This will only be displayed if the user has specified the --verbose flag, so add additional info that might be useful
context.log.debug("I'm doing something") # This will only be displayed if the user has specified the --debug flag, so add info that you would might need for debugging errors
context.log.info("I'm doing something") # This will only be displayed if the user has specified the --verbose flag, so add additional info that might be useful
context.log.debug("I'm doing something") # This will only be displayed if the user has specified the --debug flag, so add info that you would might need for debugging errors
# These are for more critical error handling
context.log.error("I'm doing something") # This will not be printed in the module context and should only be used for critical errors (e.g. a required python file is missing)
context.log.error("I'm doing something") # This will not be printed in the module context and should only be used for critical errors (e.g. a required python file is missing)
try:
raise Exception("Exception that might occure")
except Exception as e:
context.log.exception(f"Exception occured: {e}") # This will display an exception traceback screen after an exception was raised and should only be used for critical errors
context.log.exception(f"Exception occured: {e}") # This will display an exception traceback screen after an exception was raised and should only be used for critical errors
def on_admin_login(self, context, connection):
"""Concurrent.

View File

@ -8,12 +8,12 @@ from impacket.ldap.ldapasn1 import SearchResultEntry
class NXCModule:
"""
Module by CyberCelt: @Cyb3rC3lt
Module by CyberCelt: @Cyb3rC3lt
Initial module:
https://github.com/Cyb3rC3lt/CrackMapExec-Modules
Initial module:
https://github.com/Cyb3rC3lt/CrackMapExec-Modules
"""
name = "find-computer"
description = "Finds computers in the domain via the provided text"
supported_protocols = ["ldap"]
@ -41,11 +41,7 @@ class NXCModule:
try:
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldapConnection.search(
searchFilter=search_filter,
attributes=["dNSHostName", "operatingSystem"],
sizeLimit=0
)
resp = connection.ldapConnection.search(searchFilter=search_filter, attributes=["dNSHostName", "operatingSystem"], sizeLimit=0)
except LDAPSearchError as e:
if e.getErrorString().find("sizeLimitExceeded") >= 0:
context.log.debug("sizeLimitExceeded exception caught, giving up and processing the data received")
@ -69,7 +65,7 @@ class NXCModule:
elif str(attribute["type"]) == "operatingSystem":
operating_system = attribute["vals"][0]
if dns_host_name != "" and operating_system != "":
answers.append([dns_host_name,operating_system])
answers.append([dns_host_name, operating_system])
except Exception as e:
context.log.debug("Exception:", exc_info=True)
context.log.debug(f"Skipping item, cannot process due to error {e}")
@ -79,10 +75,10 @@ class NXCModule:
for answer in answers:
try:
ip = socket.gethostbyname(answer[0])
context.log.highlight(f'{answer[0]} ({answer[1]}) ({ip})')
context.log.highlight(f"{answer[0]} ({answer[1]}) ({ip})")
context.log.debug("IP found")
except socket.gaierror:
context.log.debug('Missing IP')
context.log.highlight(f'{answer[0]} ({answer[1]}) (No IP Found)')
context.log.debug("Missing IP")
context.log.highlight(f"{answer[0]} ({answer[1]}) (No IP Found)")
else:
context.log.success(f"Unable to find any computers with the text {self.TEXT}")

View File

@ -5,10 +5,10 @@ from impacket.ldap import ldapasn1 as ldapasn1_impacket
class NXCModule:
"""
Module by CyberCelt: @Cyb3rC3lt
Module by CyberCelt: @Cyb3rC3lt
Initial module:
https://github.com/Cyb3rC3lt/CrackMapExec-Modules
Initial module:
https://github.com/Cyb3rC3lt/CrackMapExec-Modules
"""
name = "group-mem"
@ -26,7 +26,7 @@ class NXCModule:
Usage: nxc ldap $DC-IP -u Username -p Password -M group-mem -o GROUP="domain admins"
nxc ldap $DC-IP -u Username -p Password -M group-mem -o GROUP="domain controllers"
"""
self.GROUP = ''
self.GROUP = ""
if "GROUP" in module_options:
self.GROUP = module_options["GROUP"]
@ -39,34 +39,34 @@ class NXCModule:
search_filter = "(&(objectCategory=group)(cn=" + self.GROUP + "))"
attribute = "objectSid"
search_result = doSearch(self, context, connection, search_filter, attribute)
search_result = do_search(self, context, connection, search_filter, attribute)
# If no SID for the Group is returned exit the program
if search_result is None:
context.log.success('Unable to find any members of the "' + self.GROUP + '" group')
return True
# Convert the binary SID to a primaryGroupID string to be used further
sidString = connection.sid_to_str(search_result).split("-")
self.primaryGroupID = sidString[-1]
sid_string = connection.sid_to_str(search_result).split("-")
self.primaryGroupID = sid_string[-1]
# Look up the groups DN
search_filter = "(&(objectCategory=group)(cn=" + self.GROUP + "))"
attribute = "distinguishedName"
distinguished_name = (doSearch(self, context, connection, search_filter, attribute)).decode("utf-8")
distinguished_name = (do_search(self, context, connection, search_filter, attribute)).decode("utf-8")
# Carry out the search
search_filter = "(|(memberOf="+distinguished_name+")(primaryGroupID="+self.primaryGroupID+"))"
search_filter = "(|(memberOf=" + distinguished_name + ")(primaryGroupID=" + self.primaryGroupID + "))"
attribute = "sAMAccountName"
search_result = doSearch(self, context, connection, search_filter, attribute)
search_result = do_search(self, context, connection, search_filter, attribute)
if len(self.answers) > 0:
context.log.success('Found the following members of the ' + self.GROUP + ' group:')
context.log.success("Found the following members of the " + self.GROUP + " group:")
for answer in self.answers:
context.log.highlight(u'{}'.format(answer[0]))
context.log.highlight("{}".format(answer[0]))
# Carry out an LDAP search for the Group with the supplied Group name
def doSearch(self,context, connection,searchFilter,attributeName):
def do_search(self, context, connection, searchFilter, attributeName):
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
@ -78,18 +78,18 @@ def doSearch(self,context, connection,searchFilter,attributeName):
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
attribute_value = ''
attribute_value = ""
try:
for attribute in item['attributes']:
if str(attribute['type']) == attributeName:
for attribute in item["attributes"]:
if str(attribute["type"]) == attributeName:
if attributeName == "objectSid":
attribute_value = bytes(attribute['vals'][0])
return attribute_value
attribute_value = bytes(attribute["vals"][0])
return attribute_value
elif attributeName == "distinguishedName":
attribute_value = bytes(attribute['vals'][0])
return attribute_value
attribute_value = bytes(attribute["vals"][0])
return attribute_value
else:
attribute_value = str(attribute['vals'][0])
attribute_value = str(attribute["vals"][0])
if attribute_value is not None:
self.answers.append([attribute_value])
except Exception as e:

View File

@ -151,10 +151,10 @@ class NXCModule:
def save_credentials(context, connection, domain, username, password, lmhash, nthash):
host_id = context.db.get_computers(connection.host)[0][0]
if password is not None:
credential_type = 'plaintext'
credential_type = "plaintext"
else:
credential_type = 'hash'
password = ':'.join(h for h in [lmhash, nthash] if h is not None)
credential_type = "hash"
password = ":".join(h for h in [lmhash, nthash] if h is not None)
context.db.add_credential(credential_type, domain, username, password, pillaged_from=host_id)
def options(self, context, module_options):
@ -221,23 +221,17 @@ class NXCModule:
cred["lmhash"],
cred["nthash"],
] not in credentials_unique:
credentials_unique.append([
cred["domain"],
cred["username"],
cred["password"],
cred["lmhash"],
cred["nthash"],
])
credentials_output.append(cred)
self.save_credentials(
context,
connection,
cred["domain"],
cred["username"],
cred["password"],
cred["lmhash"],
cred["nthash"]
credentials_unique.append(
[
cred["domain"],
cred["username"],
cred["password"],
cred["lmhash"],
cred["nthash"],
]
)
credentials_output.append(cred)
self.save_credentials(context, connection, cred["domain"], cred["username"], cred["password"], cred["lmhash"], cred["nthash"])
global credentials_data
credentials_data = credentials_output

File diff suppressed because one or more lines are too long

View File

@ -53,15 +53,7 @@ class NXCModule:
values = {str(attr["type"]).lower(): attr["vals"][0] for attr in computer["attributes"]}
if "mslaps-encryptedpassword" in values:
msMCSAdmPwd = values["mslaps-encryptedpassword"]
d = LAPSv2Extract(
bytes(msMCSAdmPwd),
connection.username if connection.username else "",
connection.password if connection.password else "",
connection.domain,
connection.nthash if connection.nthash else "",
connection.kerberos,
connection.kdcHost,
339)
d = LAPSv2Extract(bytes(msMCSAdmPwd), connection.username if connection.username else "", connection.password if connection.password else "", connection.domain, connection.nthash if connection.nthash else "", connection.kerberos, connection.kdcHost, 339)
try:
data = d.run()
except Exception as e:

View File

@ -13,6 +13,7 @@ from asyauth.common.credentials.kerberos import KerberosCredential
from asysocks.unicomm.common.target import UniTarget, UniProto
class NXCModule:
"""
Checks whether LDAP signing and channelbinding are required.
@ -131,7 +132,7 @@ class NXCModule:
else:
context.log.fail(str(err))
# Run trough all our code blocks to determine LDAP signing and channel binding settings.
# Run trough all our code blocks to determine LDAP signing and channel binding settings.
stype = asyauthSecret.PASS if not connection.nthash else asyauthSecret.NT
secret = connection.password if not connection.nthash else connection.nthash
if not connection.kerberos:
@ -142,15 +143,7 @@ class NXCModule:
stype=stype,
)
else:
kerberos_target = UniTarget(
connection.hostname + '.' + connection.domain,
88,
UniProto.CLIENT_TCP,
proxies=None,
dns=None,
dc_ip=connection.domain,
domain=connection.domain
)
kerberos_target = UniTarget(connection.hostname + "." + connection.domain, 88, UniProto.CLIENT_TCP, proxies=None, dns=None, dc_ip=connection.domain, domain=connection.domain)
credential = KerberosCredential(
target=kerberos_target,
secret=secret,

View File

@ -33,18 +33,18 @@ class SmbHeader(Structure):
_fields_ = [
("server_component", c_uint32),
("smb_command", c_uint8),
("error_class", c_uint8),
("reserved1", c_uint8),
("error_code", c_uint16),
("flags", c_uint8),
("flags2", c_uint16),
("process_id_high", c_uint16),
("signature", c_uint64),
("reserved2", c_uint16),
("tree_id", c_uint16),
("process_id", c_uint16),
("user_id", c_uint16),
("multiplex_id", c_uint16),
("error_class", c_uint8),
("reserved1", c_uint8),
("error_code", c_uint16),
("flags", c_uint8),
("flags2", c_uint16),
("process_id_high", c_uint16),
("signature", c_uint64),
("reserved2", c_uint16),
("tree_id", c_uint16),
("process_id", c_uint16),
("user_id", c_uint16),
("multiplex_id", c_uint16),
]
def __new__(cls, buffer=None):
@ -99,7 +99,7 @@ def negotiate_proto_request():
# Define the NetBIOS header
netbios = [
"\x00", # Message Type
"\x00\x00\x54" # Length
"\x00\x00\x54", # Length
]
# Define the SMB header
@ -115,7 +115,7 @@ def negotiate_proto_request():
"\x00\x00", # Tree ID
"\x2F\x4B", # Process ID
"\x00\x00", # User ID
"\xC5\x5E" # Multiplex ID
"\xC5\x5E", # Multiplex ID
]
# Define the negotiate_proto_request
@ -129,7 +129,7 @@ def negotiate_proto_request():
"\x02", # Requested Dialects Count
"\x4E\x54\x20\x4C\x41\x4E\x4D\x41\x4E\x20\x31\x2E\x30\x00", # Requested Dialects
"\x02", # Requested Dialects Count
"\x4E\x54\x20\x4C\x4D\x20\x30\x2E\x31\x32\x00" # Requested Dialects
"\x4E\x54\x20\x4C\x4D\x20\x30\x2E\x31\x32\x00", # Requested Dialects
]
# Return the generated SMB protocol payload
@ -140,45 +140,45 @@ def session_setup_andx_request():
"""Generate session setup andx request."""
# Define the NetBIOS bytes
netbios = [
"\x00", # length
"\x00\x00\x63" # session service
"\x00", # length
"\x00\x00\x63", # session service
]
# Define the SMB header bytes
smb_header = [
"\xFF\x53\x4D\x42", # server component
"\x73", # command
"\x00\x00\x00\x00", # NT status
"\x18", # flags
"\x01\x20", # flags2
"\x00\x00", # PID high
"\x00\x00\x00\x00\x00\x00\x00\x00", # signature
"\x00\x00", # reserved
"\x00\x00", # tid
"\x2F\x4B", # pid
"\x00\x00", # uid
"\xC5\x5E" # mid
"\xFF\x53\x4D\x42", # server component
"\x73", # command
"\x00\x00\x00\x00", # NT status
"\x18", # flags
"\x01\x20", # flags2
"\x00\x00", # PID high
"\x00\x00\x00\x00\x00\x00\x00\x00", # signature
"\x00\x00", # reserved
"\x00\x00", # tid
"\x2F\x4B", # pid
"\x00\x00", # uid
"\xC5\x5E", # mid
]
# Define the session setup andx request bytes
session_setup_andx_request = [
"\x0D", # word count
"\xFF", # andx command
"\x00", # reserved
"\x00\x00", # andx offset
"\xDF\xFF", # max buffer
"\x02\x00", # max mpx count
"\x01\x00", # VC number
"\x00\x00\x00\x00", # session key
"\x00\x00", # ANSI password length
"\x00\x00", # Unicode password length
"\x00\x00\x00\x00", # reserved
"\x40\x00\x00\x00", # capabilities
"\x26\x00", # byte count
"\x00", # account name length
"\x2e\x00", # account name offset
"\x57\x69\x6e\x64\x6f\x77\x73\x20\x32\x30\x30\x30\x20\x32\x31\x39\x35\x00", # account name
"\x57\x69\x6e\x64\x6f\x77\x73\x20\x32\x30\x30\x30\x20\x35\x2e\x30\x00" # primary domain
"\x0D", # word count
"\xFF", # andx command
"\x00", # reserved
"\x00\x00", # andx offset
"\xDF\xFF", # max buffer
"\x02\x00", # max mpx count
"\x01\x00", # VC number
"\x00\x00\x00\x00", # session key
"\x00\x00", # ANSI password length
"\x00\x00", # Unicode password length
"\x00\x00\x00\x00", # reserved
"\x40\x00\x00\x00", # capabilities
"\x26\x00", # byte count
"\x00", # account name length
"\x2e\x00", # account name offset
"\x57\x69\x6e\x64\x6f\x77\x73\x20\x32\x30\x30\x30\x20\x32\x31\x39\x35\x00", # account name
"\x57\x69\x6e\x64\x6f\x77\x73\x20\x32\x30\x30\x30\x20\x35\x2e\x30\x00", # primary domain
]
# Call the generate_smb_proto_payload function and return the result

View File

@ -162,10 +162,7 @@ class NXCModule:
try:
context.log.success("Dumping the NTDS, this could take a while so go grab a redbull...")
NTDS.dump()
context.log.success(
f"Dumped {highlight(add_ntds_hash.ntds_hashes)} NTDS hashes to {connection.output_filename}.ntds "
f"of which {highlight(add_ntds_hash.added_to_db)} were added to the database"
)
context.log.success(f"Dumped {highlight(add_ntds_hash.ntds_hashes)} NTDS hashes to {connection.output_filename}.ntds " f"of which {highlight(add_ntds_hash.added_to_db)} were added to the database")
context.log.display("To extract only enabled accounts from the output file, run the following command: ")
context.log.display(f"grep -iv disabled {connection.output_filename}.ntds | cut -d ':' -f1")
@ -176,10 +173,6 @@ class NXCModule:
if self.no_delete:
context.log.display(f"Raw NTDS dump copied to {self.dir_result}, parse it with:")
context.log.display(
f'secretsdump.py -system {self.dir_result}/registry/SYSTEM '
f'-security {self.dir_result}/registry/SECURITY '
f'-ntds "{self.dir_result}/Active Directory/ntds.dit" LOCAL'
)
context.log.display(f"secretsdump.py -system {self.dir_result}/registry/SYSTEM " f"-security {self.dir_result}/registry/SECURITY " f'-ntds "{self.dir_result}/Active Directory/ntds.dit" LOCAL')
else:
shutil.rmtree(self.dir_result)

File diff suppressed because one or more lines are too long

View File

@ -8,9 +8,9 @@ from math import fabs
class NXCModule:
"""
Created by fplazar and wanetty
Module by @gm_eduard and @ferranplaza
Based on: https://github.com/juliourena/CrackMapExec/blob/master/cme/modules/get_description.py
Created by fplazar and wanetty
Module by @gm_eduard and @ferranplaza
Based on: https://github.com/juliourena/CrackMapExec/blob/master/cme/modules/get_description.py
"""
name = "pso"
@ -18,7 +18,7 @@ class NXCModule:
supported_protocols = ["ldap"]
opsec_safe = True
multiple_hosts = True
pso_fields = [
"cn",
"msDS-PasswordReversibleEncryptionEnabled",
@ -39,20 +39,15 @@ class NXCModule:
No options available.
"""
pass
def convert_time_field(self, field, value):
time_fields = {
"msDS-LockoutObservationWindow": (60, "mins"),
"msDS-MinimumPasswordAge": (86400, "days"),
"msDS-MaximumPasswordAge": (86400, "days"),
"msDS-LockoutDuration": (60, "mins")
}
time_fields = {"msDS-LockoutObservationWindow": (60, "mins"), "msDS-MinimumPasswordAge": (86400, "days"), "msDS-MaximumPasswordAge": (86400, "days"), "msDS-LockoutDuration": (60, "mins")}
if field in time_fields.keys():
value = f"{int((fabs(float(value)) / (10000000 * time_fields[field][0])))} {time_fields[field][1]}"
return value
def on_login(self, context, connection):
"""Concurrent. Required if on_admin_login is not present. This gets called on each authenticated connection"""
# Building the search filter
@ -60,11 +55,7 @@ class NXCModule:
try:
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldapConnection.search(
searchFilter=search_filter,
attributes=self.pso_fields,
sizeLimit=0
)
resp = connection.ldapConnection.search(searchFilter=search_filter, attributes=self.pso_fields, sizeLimit=0)
except ldap_impacket.LDAPSearchError as e:
if e.getErrorString().find("sizeLimitExceeded") >= 0:
context.log.debug("sizeLimitExceeded exception caught, giving up and processing the data received")

View File

@ -44,12 +44,12 @@ class NXCModule:
exit(1)
self.action = module_options["ACTION"].lower()
if "METHOD" not in module_options:
self.method = "wmi"
else:
self.method = module_options['METHOD'].lower()
self.method = module_options["METHOD"].lower()
if context.protocol != "smb" and self.method == "smb":
context.log.fail(f"Protocol: {context.protocol} not support this method")
exit(1)
@ -58,11 +58,11 @@ class NXCModule:
self.dcom_timeout = 10
else:
try:
self.dcom_timeout = int(module_options['DCOM-TIMEOUT'])
self.dcom_timeout = int(module_options["DCOM-TIMEOUT"])
except Exception:
context.log.fail("Wrong DCOM timeout value!")
exit(1)
if "OLD" not in module_options:
self.oldSystem = False
else:
@ -85,7 +85,7 @@ class NXCModule:
wmi_rdp = RdpWmi(context, connection, self.dcom_timeout)
if hasattr(wmi_rdp, '_rdp_WMI__iWbemLevel1Login'):
if hasattr(wmi_rdp, "_rdp_WMI__iWbemLevel1Login"):
if "ram" in self.action:
# Nt version under 6 not support RAM.
try:
@ -144,7 +144,7 @@ class RdpSmb:
self.logger.success("Enable RDP via SMB(ncacn_np) successfully")
elif int(data) == 1:
self.logger.success("Disable RDP via SMB(ncacn_np) successfully")
self.firewall_cmd(action)
if action == "enable":
@ -199,7 +199,7 @@ class RdpSmb:
key_handle = ans["phkResult"]
rtype, data = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, key_handle, "PortNumber")
self.logger.success(f"RDP Port: {str(data)}")
# https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/manage/enable_rdp.rb
@ -218,15 +218,15 @@ class RdpWmi:
self.logger = context.log
self.__currentprotocol = context.protocol
# From dfscoerce.py
self.__username=connection.username
self.__password=connection.password
self.__domain=connection.domain
self.__lmhash=connection.lmhash
self.__nthash=connection.nthash
self.__target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
self.__doKerberos=connection.kerberos
self.__kdcHost=connection.kdcHost
self.__aesKey=connection.aesKey
self.__username = connection.username
self.__password = connection.password
self.__domain = connection.domain
self.__lmhash = connection.lmhash
self.__nthash = connection.nthash
self.__target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
self.__doKerberos = connection.kerberos
self.__kdcHost = connection.kdcHost
self.__aesKey = connection.aesKey
self.__timeout = timeout
try:
@ -245,13 +245,13 @@ class RdpWmi:
i_interface = self.__dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
if self.__currentprotocol == "smb":
flag, self.__stringBinding = dcom_FirewallChecker(i_interface, self.__timeout)
flag, self.__stringBinding = dcom_FirewallChecker(i_interface, self.__timeout)
if not flag or not self.__stringBinding:
error_msg = f'RDP-WMI: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the module option "DCOM-TIMEOUT=10". If it\'s still failing maybe something is blocking the RPC connection, please try to use "-o" with "METHOD=smb"'
if not self.__stringBinding:
error_msg = "RDP-WMI: Dcom initialization failed: can't get target stringbinding, maybe cause by IPv6 or any other issues, please check your target again"
self.logger.fail(error_msg) if not flag else self.logger.debug(error_msg)
# Make it force break function
self.__dcom.disconnect()
@ -265,15 +265,11 @@ class RdpWmi:
if old is False:
# According to this document: https://learn.microsoft.com/en-us/windows/win32/termserv/win32-tslogonsetting
# Authentication level must set to RPC_C_AUTHN_LEVEL_PKT_PRIVACY when accessing namespace "//./root/cimv2/TerminalServices"
i_wbem_services = self.__iWbemLevel1Login.NTLMLogin(
"//./root/cimv2/TerminalServices",
NULL,
NULL
)
i_wbem_services = self.__iWbemLevel1Login.NTLMLogin("//./root/cimv2/TerminalServices", NULL, NULL)
i_wbem_services.get_dce_rpc().set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
self.__iWbemLevel1Login.RemRelease()
i_enum_wbem_class_object = i_wbem_services.ExecQuery("SELECT * FROM Win32_TerminalServiceSetting")
i_wbem_class_object = i_enum_wbem_class_object.Next(0xffffffff, 1)[0]
i_wbem_class_object = i_enum_wbem_class_object.Next(0xFFFFFFFF, 1)[0]
if action == "enable":
self.logger.info("Enabled RDP services and setting up firewall.")
i_wbem_class_object.SetAllowTSConnections(1, 1)
@ -281,30 +277,30 @@ class RdpWmi:
self.logger.info("Disabled RDP services and setting up firewall.")
i_wbem_class_object.SetAllowTSConnections(0, 0)
else:
i_wbem_services = self.__iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
i_wbem_services = self.__iWbemLevel1Login.NTLMLogin("//./root/cimv2", NULL, NULL)
self.__iWbemLevel1Login.RemRelease()
i_enum_wbem_class_object = i_wbem_services.ExecQuery("SELECT * FROM Win32_TerminalServiceSetting")
i_wbem_class_object = i_enum_wbem_class_object.Next(0xffffffff, 1)[0]
i_wbem_class_object = i_enum_wbem_class_object.Next(0xFFFFFFFF, 1)[0]
if action == "enable":
self.logger.info("Enabling RDP services (old system not support setting up firewall)")
i_wbem_class_object.SetAllowTSConnections(1)
elif action == "disable":
self.logger.info("Disabling RDP services (old system not support setting up firewall)")
i_wbem_class_object.SetAllowTSConnections(0)
self.query_rdp_result(old)
if action == 'enable':
if action == "enable":
self.query_rdp_port()
# Need to create new iWbemServices interface in order to flush results
def query_rdp_result(self, old=False):
if old is False:
i_wbem_services = self.__iWbemLevel1Login.NTLMLogin("//./root/cimv2/TerminalServices", NULL, NULL)
i_wbem_services.get_dce_rpc().set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
self.__iWbemLevel1Login.RemRelease()
i_enum_wbem_class_object = i_wbem_services.ExecQuery("SELECT * FROM Win32_TerminalServiceSetting")
i_wbem_class_object = i_enum_wbem_class_object.Next(0xffffffff, 1)[0]
i_wbem_class_object = i_enum_wbem_class_object.Next(0xFFFFFFFF, 1)[0]
result = dict(i_wbem_class_object.getProperties())
result = result["AllowTSConnections"]["value"]
if result == 0:
@ -315,7 +311,7 @@ class RdpWmi:
i_wbem_services = self.__iWbemLevel1Login.NTLMLogin("//./root/cimv2", NULL, NULL)
self.__iWbemLevel1Login.RemRelease()
i_enum_wbem_class_object = i_wbem_services.ExecQuery("SELECT * FROM Win32_TerminalServiceSetting")
i_wbem_class_object = i_enum_wbem_class_object.Next(0xffffffff, 1)[0]
i_wbem_class_object = i_enum_wbem_class_object.Next(0xFFFFFFFF, 1)[0]
result = dict(i_wbem_class_object.getProperties())
result = result["AllowTSConnections"]["value"]
if result == 0:
@ -327,11 +323,7 @@ class RdpWmi:
i_wbem_services = self.__iWbemLevel1Login.NTLMLogin("//./root/DEFAULT", NULL, NULL)
self.__iWbemLevel1Login.RemRelease()
std_reg_prov, resp = i_wbem_services.GetObject("StdRegProv")
out = std_reg_prov.GetDWORDValue(
2147483650,
"SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp",
"PortNumber"
)
out = std_reg_prov.GetDWORDValue(2147483650, "SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp", "PortNumber")
self.logger.success(f"RDP Port: {str(out.uValue)}")
# Nt version under 6 not support RAM.
@ -341,7 +333,7 @@ class RdpWmi:
std_reg_prov, resp = i_wbem_services.GetObject("StdRegProv")
if action == "enable-ram":
self.logger.info("Enabling Restricted Admin Mode.")
std_reg_prov.SetDWORDValue(2147483650, 'System\\CurrentControlSet\\Control\\Lsa', "DisableRestrictedAdmin", 0)
std_reg_prov.SetDWORDValue(2147483650, "System\\CurrentControlSet\\Control\\Lsa", "DisableRestrictedAdmin", 0)
elif action == "disable-ram":
self.logger.info("Disabling Restricted Admin Mode (Clear).")
std_reg_prov.DeleteValue(2147483650, "System\\CurrentControlSet\\Control\\Lsa", "DisableRestrictedAdmin")

View File

@ -45,7 +45,7 @@ def get_dns_resolver(server, context):
def ldap2domain(ldap):
return re.sub(",DC=", ".", ldap[ldap.lower().find("dc="):], flags=re.I)[3:]
return re.sub(",DC=", ".", ldap[ldap.lower().find("dc=") :], flags=re.I)[3:]
def new_record(rtype, serial):
@ -162,8 +162,7 @@ class NXCModule:
"value": address.formatCanonical(),
}
)
if dr["Type"] in [a for a in RECORD_TYPE_MAPPING if
RECORD_TYPE_MAPPING[a] in ["CNAME", "NS", "PTR"]]:
if dr["Type"] in [a for a in RECORD_TYPE_MAPPING if RECORD_TYPE_MAPPING[a] in ["CNAME", "NS", "PTR"]]:
address = DNS_RPC_RECORD_NODE_NAME(dr["Data"])
if str(recordname) != "DomainDnsZones" and str(recordname) != "ForestDnsZones":
outdata.append(
@ -185,8 +184,7 @@ class NXCModule:
)
context.log.highlight("Found %d records" % len(outdata))
path = expanduser(
"~/.nxc/logs/{}_network_{}.log".format(connection.domain, datetime.now().strftime("%Y-%m-%d_%H%M%S")))
path = expanduser("~/.nxc/logs/{}_network_{}.log".format(connection.domain, datetime.now().strftime("%Y-%m-%d_%H%M%S")))
with codecs.open(path, "w", "utf-8") as outfile:
for row in outdata:
if self.showhosts:
@ -197,9 +195,7 @@ class NXCModule:
outfile.write("{}\n".format(row["value"]))
context.log.success("Dumped {} records to {}".format(len(outdata), path))
if not self.showall and not self.showhosts:
context.log.display(
"To extract CIDR from the {} ip, run the following command: cat" " your_file | mapcidr -aa -silent | mapcidr -a -silent".format(
len(outdata)))
context.log.display("To extract CIDR from the {} ip, run the following command: cat" " your_file | mapcidr -aa -silent | mapcidr -a -silent".format(len(outdata)))
class DNS_RECORD(Structure):
@ -256,8 +252,8 @@ class DNS_COUNT_NAME(Structure):
ind = 0
labels = []
for i in range(self["LabelCount"]):
nextlen = unpack("B", self["RawName"][ind: ind + 1])[0]
labels.append(self["RawName"][ind + 1: ind + 1 + nextlen].decode("utf-8"))
nextlen = unpack("B", self["RawName"][ind : ind + 1])[0]
labels.append(self["RawName"][ind + 1 : ind + 1 + nextlen].decode("utf-8"))
ind += nextlen + 1
# For the final dot
labels.append("")

File diff suppressed because it is too large Load Diff

View File

@ -99,8 +99,7 @@ class NXCModule:
)
if len([server for server in list_servers if isinstance(server, ldapasn1_impacket.SearchResultEntry)]) == 0:
if len(site_description) != 0:
context.log.highlight(
f'Site "{site_name}" (Subnet:{subnet_name}) (description:"{site_description}")')
context.log.highlight(f'Site "{site_name}" (Subnet:{subnet_name}) (description:"{site_description}")')
else:
context.log.highlight(f'Site "{site_name}" (Subnet:{subnet_name})')
else:
@ -109,14 +108,11 @@ class NXCModule:
continue
server = search_res_entry_to_dict(server)["cn"]
if len(site_description) != 0:
context.log.highlight(
f"Site: '{site_name}' (Subnet:{subnet_name}) (description:'{site_description}') (Server:'{server}')")
context.log.highlight(f"Site: '{site_name}' (Subnet:{subnet_name}) (description:'{site_description}') (Server:'{server}')")
else:
context.log.highlight(
f'Site "{site_name}" (Subnet:{subnet_name}) (Server:{server})')
context.log.highlight(f'Site "{site_name}" (Subnet:{subnet_name}) (Server:{server})')
else:
if len(site_description) != 0:
context.log.highlight(
f'Site "{site_name}" (Subnet:{subnet_name}) (description:"{site_description}")')
context.log.highlight(f'Site "{site_name}" (Subnet:{subnet_name}) (description:"{site_description}")')
else:
context.log.highlight(f'Site "{site_name}" (Subnet:{subnet_name})')

View File

@ -5,9 +5,10 @@ from impacket.ldap import ldapasn1 as ldapasn1_impacket
class NXCModule:
"""
Extract all Trust Relationships, Trusting Direction, and Trust Transitivity
Module by Brandon Fisher @shad0wcntr0ller
Extract all Trust Relationships, Trusting Direction, and Trust Transitivity
Module by Brandon Fisher @shad0wcntr0ller
"""
name = "enum_trusts"
description = "Extract all Trust Relationships, Trusting Direction, and Trust Transitivity"
supported_protocols = ["ldap"]
@ -23,22 +24,17 @@ class NXCModule:
attributes = ["flatName", "trustPartner", "trustDirection", "trustAttributes"]
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldapConnection.search(
searchBase=domain_dn,
searchFilter=search_filter,
attributes=attributes,
sizeLimit=0
)
resp = connection.ldapConnection.search(searchBase=domain_dn, searchFilter=search_filter, attributes=attributes, sizeLimit=0)
trusts = []
context.log.debug(f"Total of records returned {len(resp)}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
flat_name = ''
trust_partner = ''
trust_direction = ''
trust_transitive = []
flat_name = ""
trust_partner = ""
trust_direction = ""
trust_transitive = []
try:
for attribute in item["attributes"]:
if str(attribute["type"]) == "flatName":
@ -92,4 +88,3 @@ class NXCModule:
context.log.display("No trust relationships found")
return True

View File

@ -56,7 +56,11 @@ class NXCModule:
# Veeam v12 check
try:
ans = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, regHandle, "SOFTWARE\\Veeam\\Veeam Backup and Replication\\DatabaseConfigurations",)
ans = rrp.hBaseRegOpenKey(
remoteOps._RemoteOperations__rrp,
regHandle,
"SOFTWARE\\Veeam\\Veeam Backup and Replication\\DatabaseConfigurations",
)
keyHandle = ans["phkResult"]
database_config = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "SqlActiveConfiguration")[1].split("\x00")[:-1][0]
@ -64,16 +68,28 @@ class NXCModule:
context.log.success("Veeam v12 installation found!")
if database_config == "PostgreSql":
# Find the PostgreSql installation path containing "psql.exe"
ans = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, regHandle, "SOFTWARE\\PostgreSQL Global Development Group\\PostgreSQL",)
ans = rrp.hBaseRegOpenKey(
remoteOps._RemoteOperations__rrp,
regHandle,
"SOFTWARE\\PostgreSQL Global Development Group\\PostgreSQL",
)
keyHandle = ans["phkResult"]
PostgreSqlExec = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "Location")[1].split("\x00")[:-1][0] + "\\bin\\psql.exe"
ans = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, regHandle, "SOFTWARE\\Veeam\\Veeam Backup and Replication\\DatabaseConfigurations\\PostgreSQL",)
ans = rrp.hBaseRegOpenKey(
remoteOps._RemoteOperations__rrp,
regHandle,
"SOFTWARE\\Veeam\\Veeam Backup and Replication\\DatabaseConfigurations\\PostgreSQL",
)
keyHandle = ans["phkResult"]
PostgresUserForWindowsAuth = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "PostgresUserForWindowsAuth")[1].split("\x00")[:-1][0]
SqlDatabaseName = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "SqlDatabaseName")[1].split("\x00")[:-1][0]
elif database_config == "MsSql":
ans = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, regHandle, "SOFTWARE\\Veeam\\Veeam Backup and Replication\\DatabaseConfigurations\\MsSql",)
ans = rrp.hBaseRegOpenKey(
remoteOps._RemoteOperations__rrp,
regHandle,
"SOFTWARE\\Veeam\\Veeam Backup and Replication\\DatabaseConfigurations\\MsSql",
)
keyHandle = ans["phkResult"]
SqlDatabase = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "SqlDatabaseName")[1].split("\x00")[:-1][0]
@ -88,7 +104,11 @@ class NXCModule:
# Veeam v11 check
try:
ans = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, regHandle, "SOFTWARE\\Veeam\\Veeam Backup and Replication",)
ans = rrp.hBaseRegOpenKey(
remoteOps._RemoteOperations__rrp,
regHandle,
"SOFTWARE\\Veeam\\Veeam Backup and Replication",
)
keyHandle = ans["phkResult"]
SqlDatabase = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "SqlDatabaseName")[1].split("\x00")[:-1][0]
@ -126,7 +146,7 @@ class NXCModule:
def stripXmlOutput(self, context, output):
return output.split("CLIXML")[1].split("<Objs Version")[0]
def executePsMssql(self, context, connection, SqlDatabase, SqlInstance, SqlServer):
self.psScriptMssql = self.psScriptMssql.replace("REPLACE_ME_SqlDatabase", SqlDatabase)
self.psScriptMssql = self.psScriptMssql.replace("REPLACE_ME_SqlInstance", SqlInstance)
@ -163,8 +183,8 @@ class NXCModule:
user, password = account.split(" ", 1)
password = password.replace("WHITESPACE_ERROR", " ")
context.log.highlight(user + ":" + f"{password}")
if ' ' in password:
context.log.fail(f"Password contains whitespaces! The password for user \"{user}\" is: \"{password}\"")
if " " in password:
context.log.fail(f'Password contains whitespaces! The password for user "{user}" is: "{password}"')
def on_admin_login(self, context, connection):
self.checkVeeamInstalled(context, connection)

File diff suppressed because one or more lines are too long

View File

@ -86,7 +86,6 @@ class NXCModule:
else:
context.log.highlight(f"[{wifi_cred.auth.upper()}] {wifi_cred.ssid} - {wifi_cred.eap_type}")
except Exception:
context.log.highlight(
f"[{wifi_cred.auth.upper()}] {wifi_cred.ssid} - Passphrase: {wifi_cred.password}")
context.log.highlight(f"[{wifi_cred.auth.upper()}] {wifi_cred.ssid} - Passphrase: {wifi_cred.password}")
else:
context.log.highlight(f"[WPA-EAP] {wifi_cred.ssid} - {wifi_cred.eap_type}")

View File

@ -63,11 +63,13 @@ class NXCModule:
except DCERPCException:
self.context.log.fail("Error while connecting to host: DCERPCException, " "which means this is probably not a DC!")
def fail(msg):
nxc_logger.debug(msg)
nxc_logger.fail("This might have been caused by invalid arguments or network issues.")
sys.exit(2)
def try_zero_authenticate(rpc_con, dc_handle, dc_ip, target_computer):
# Connect to the DC's Netlogon service.

View File

@ -31,6 +31,7 @@ from sys import platform
# Increase file_limit to prevent error "Too many open files"
if platform != "win32":
import resource
file_limit = list(resource.getrlimit(resource.RLIMIT_NOFILE))
if file_limit[1] > 10000:
file_limit[0] = 10000

View File

@ -172,7 +172,7 @@ class DatabaseNavigator(cmd.Cmd):
if cred[4] == "hash":
usernames.append(cred[2])
passwords.append(cred[3])
output_list = [':'.join(combination) for combination in zip(usernames, passwords)]
output_list = [":".join(combination) for combination in zip(usernames, passwords)]
write_list(filename, output_list)
else:
print(f"[-] No such export option: {line[1]}")
@ -243,9 +243,9 @@ class DatabaseNavigator(cmd.Cmd):
formatted_shares = []
for share in shares:
user = self.db.get_users(share[2])[0]
if self.db.get_hosts(share[1]):
share_host = self.db.get_hosts(share[1])[0][2]
else:
if self.db.get_hosts(share[1]):
share_host = self.db.get_hosts(share[1])[0][2]
else:
share_host = "ERROR"
entry = (
@ -352,15 +352,7 @@ class DatabaseNavigator(cmd.Cmd):
"check",
"status",
)
csv_header_detailed = (
"id",
"ip",
"hostname",
"check",
"description",
"status",
"reasons"
)
csv_header_detailed = ("id", "ip", "hostname", "check", "description", "status", "reasons")
filename = line[2]
host_mapping = {}
check_mapping = {}
@ -370,12 +362,12 @@ class DatabaseNavigator(cmd.Cmd):
check_results = self.db.get_check_results()
rows = []
for result_id,hostid,checkid,secure,reasons in check_results:
for result_id, hostid, checkid, secure, reasons in check_results:
row = [result_id]
if hostid in host_mapping:
row.extend(host_mapping[hostid])
else:
for host_id,ip,hostname,_,_,_,_,_,_,_,_ in hosts:
for host_id, ip, hostname, _, _, _, _, _, _, _, _ in hosts:
if host_id == hostid:
row.extend([ip, hostname])
host_mapping[hostid] = [ip, hostname]
@ -389,7 +381,7 @@ class DatabaseNavigator(cmd.Cmd):
row.extend([name, description])
check_mapping[checkid] = [name, description]
break
row.append('OK' if secure else 'KO')
row.append("OK" if secure else "KO")
row.append(reasons)
rows.append(row)

View File

@ -6,38 +6,14 @@ from nxc.logger import nxc_logger
# right now we are only referencing the port numbers, not the service name, but this should be sufficient for 99% cases
protocol_dict = {
"ftp": {
"ports": [21],
"services": ["ftp"]
},
"ssh": {
"ports": [22, 2222],
"services": ["ssh"]
},
"smb": {
"ports": [139, 445],
"services": ["netbios-ssn", "microsoft-ds"]
},
"ldap": {
"ports": [389, 636],
"services": ["ldap", "ldaps"]
},
"mssql": {
"ports": [1433],
"services": ["ms-sql-s"]
},
"rdp": {
"ports": [3389],
"services": ["ms-wbt-server"]
},
"winrm": {
"ports": [5985, 5986],
"services": ["wsman"]
},
"vnc": {
"ports": [5900, 5901, 5902, 5903, 5904, 5905, 5906],
"services": ["vnc"]
},
"ftp": {"ports": [21], "services": ["ftp"]},
"ssh": {"ports": [22, 2222], "services": ["ssh"]},
"smb": {"ports": [139, 445], "services": ["netbios-ssn", "microsoft-ds"]},
"ldap": {"ports": [389, 636], "services": ["ldap", "ldaps"]},
"mssql": {"ports": [1433], "services": ["ms-sql-s"]},
"rdp": {"ports": [3389], "services": ["ms-wbt-server"]},
"winrm": {"ports": [5985, 5986], "services": ["wsman"]},
"vnc": {"ports": [5900, 5901, 5902, 5903, 5904, 5905, 5906], "services": ["vnc"]},
}

View File

@ -96,7 +96,6 @@ class ftp(connection):
return True
self.conn.close()
def list_directory_full(self):
# in the future we can use mlsd/nlst if we want, but this gives a full output like `ls -la`
# ftplib's "dir" prints directly to stdout, and "nlst" only returns the folder name, not full details

View File

@ -31,47 +31,47 @@ class database:
@staticmethod
def db_schema(db_conn):
db_conn.execute("""CREATE TABLE "credentials" (
db_conn.execute(
"""CREATE TABLE "credentials" (
"id" integer PRIMARY KEY,
"username" text,
"password" text
)""")
)"""
)
db_conn.execute("""CREATE TABLE "hosts" (
db_conn.execute(
"""CREATE TABLE "hosts" (
"id" integer PRIMARY KEY,
"host" text,
"port" integer,
"banner" text
)""")
db_conn.execute("""CREATE TABLE "loggedin_relations" (
)"""
)
db_conn.execute(
"""CREATE TABLE "loggedin_relations" (
"id" integer PRIMARY KEY,
"credid" integer,
"hostid" integer,
FOREIGN KEY(credid) REFERENCES credentials(id),
FOREIGN KEY(hostid) REFERENCES hosts(id)
)""")
db_conn.execute("""CREATE TABLE "directory_listings" (
)"""
)
db_conn.execute(
"""CREATE TABLE "directory_listings" (
"id" integer PRIMARY KEY,
"lir_id" integer,
"data" text,
FOREIGN KEY(lir_id) REFERENCES loggedin_relations(id)
)""")
)"""
)
def reflect_tables(self):
with self.db_engine.connect():
try:
self.CredentialsTable = Table(
"credentials", self.metadata, autoload_with=self.db_engine
)
self.HostsTable = Table(
"hosts", self.metadata, autoload_with=self.db_engine
)
self.LoggedinRelationsTable = Table(
"loggedin_relations", self.metadata, autoload_with=self.db_engine
)
self.DirectoryListingsTable = Table(
"directory_listings", self.metadata, autoload_with=self.db_engine
)
self.CredentialsTable = Table("credentials", self.metadata, autoload_with=self.db_engine)
self.HostsTable = Table("hosts", self.metadata, autoload_with=self.db_engine)
self.LoggedinRelationsTable = Table("loggedin_relations", self.metadata, autoload_with=self.db_engine)
self.DirectoryListingsTable = Table("directory_listings", self.metadata, autoload_with=self.db_engine)
except (NoInspectionAvailable, NoSuchTableError):
print(
f"""
@ -135,10 +135,7 @@ class database:
# TODO: find a way to abstract this away to a single Upsert call
q = Insert(self.HostsTable) # .returning(self.HostsTable.c.id)
update_columns = {col.name: col for col in q.excluded if col.name not in "id"}
q = q.on_conflict_do_update(
index_elements=self.HostsTable.primary_key,
set_=update_columns
)
q = q.on_conflict_do_update(index_elements=self.HostsTable.primary_key, set_=update_columns)
self.sess.execute(q, hosts) # .scalar()
# we only return updated IDs for now - when RETURNING clause is allowed we can return inserted
@ -152,10 +149,7 @@ class database:
"""
credentials = []
q = select(self.CredentialsTable).filter(
func.lower(self.CredentialsTable.c.username) == func.lower(username),
func.lower(self.CredentialsTable.c.password) == func.lower(password)
)
q = select(self.CredentialsTable).filter(func.lower(self.CredentialsTable.c.username) == func.lower(username), func.lower(self.CredentialsTable.c.password) == func.lower(password))
results = self.sess.execute(q).all()
# add new credential
@ -182,10 +176,7 @@ class database:
# TODO: find a way to abstract this away to a single Upsert call
q_users = Insert(self.CredentialsTable) # .returning(self.CredentialsTable.c.id)
update_columns_users = {col.name: col for col in q_users.excluded if col.name not in "id"}
q_users = q_users.on_conflict_do_update(
index_elements=self.CredentialsTable.primary_key,
set_=update_columns_users
)
q_users = q_users.on_conflict_do_update(index_elements=self.CredentialsTable.primary_key, set_=update_columns_users)
nxc_logger.debug(f"Adding credentials: {credentials}")
self.sess.execute(q_users, credentials) # .scalar()
@ -310,10 +301,7 @@ class database:
# only add one if one doesn't already exist
if not results:
relation = {
"credid": cred_id,
"hostid": host_id
}
relation = {"credid": cred_id, "hostid": host_id}
try:
nxc_logger.debug(f"Inserting loggedin_relations: {relation}")
# TODO: find a way to abstract this away to a single Upsert call

View File

@ -6,41 +6,49 @@ from nxc.nxcdb import DatabaseNavigator, print_table, print_help
class navigator(DatabaseNavigator):
def display_creds(self, creds):
data = [[
"CredID",
"Total Logins",
"Username",
"Password",
]]
data = [
[
"CredID",
"Total Logins",
"Username",
"Password",
]
]
for cred in creds:
total_users = self.db.get_loggedin_relations(cred_id=cred[0])
data.append([
cred[0],
str(len(total_users)) + " Host(s)",
cred[1],
cred[2],
])
data.append(
[
cred[0],
str(len(total_users)) + " Host(s)",
cred[1],
cred[2],
]
)
print_table(data, title="Credentials")
def display_hosts(self, hosts):
data = [[
"HostID",
"Total Users",
"Host",
"Port",
"Banner",
]]
data = [
[
"HostID",
"Total Users",
"Host",
"Port",
"Banner",
]
]
for h in hosts:
total_users = self.db.get_loggedin_relations(host_id=h[0])
data.append([
h[0],
str(len(total_users)) + " User(s)",
h[1],
h[2],
h[3],
])
data.append(
[
h[0],
str(len(total_users)) + " User(s)",
h[1],
h[2],
h[3],
]
)
print_table(data, title="Hosts")
def do_hosts(self, line):
@ -55,12 +63,7 @@ class navigator(DatabaseNavigator):
if len(hosts) > 1:
self.display_hosts(hosts)
elif len(hosts) == 1:
data = [[
"HostID",
"Host",
"Port",
"Banner"
]]
data = [["HostID", "Host", "Port", "Banner"]]
host_id_list = [h[0] for h in hosts]
for h in hosts:
@ -68,11 +71,7 @@ class navigator(DatabaseNavigator):
print_table(data, title="Host")
login_data = [[
"CredID",
"UserName",
"Password"
]]
login_data = [["CredID", "UserName", "Password"]]
for host_id in host_id_list:
login_links = self.db.get_loggedin_relations(host_id=host_id)
@ -85,7 +84,10 @@ class navigator(DatabaseNavigator):
login_data.append(cred_data)
if len(login_data) > 1:
print_table(login_data, title="Credential(s) with Logins",)
print_table(
login_data,
title="Credential(s) with Logins",
)
@staticmethod
def help_hosts(self):

View File

@ -129,6 +129,7 @@ def resolve_collection_methods(methods):
nxc_logger.error("Invalid collection method specified: %s", method)
return False
class ldap(connection):
def __init__(self, args, db, host):
self.domain = None
@ -306,8 +307,8 @@ class ldap(connection):
else:
self.logger.extra["protocol"] = "SMB" if not self.no_ntlm else "LDAP"
self.logger.extra["port"] = "445" if not self.no_ntlm else "389"
signing = colored(f"signing:{self.signing}", host_info_colors[0], attrs=['bold']) if self.signing else colored(f"signing:{self.signing}", host_info_colors[1], attrs=['bold'])
smbv1 = colored(f"SMBv1:{self.smbv1}", host_info_colors[2], attrs=['bold']) if self.smbv1 else colored(f"SMBv1:{self.smbv1}", host_info_colors[3], attrs=['bold'])
signing = colored(f"signing:{self.signing}", host_info_colors[0], attrs=["bold"]) if self.signing else colored(f"signing:{self.signing}", host_info_colors[1], attrs=["bold"])
smbv1 = colored(f"SMBv1:{self.smbv1}", host_info_colors[2], attrs=["bold"]) if self.smbv1 else colored(f"SMBv1:{self.smbv1}", host_info_colors[3], attrs=["bold"])
self.logger.display(f"{self.server_os}{f' x{self.os_arch}' if self.os_arch else ''} (name:{self.hostname}) (domain:{self.domain}) ({signing}) ({smbv1})")
self.logger.extra["protocol"] = "LDAP"
# self.logger.display(self.endpoint)
@ -741,7 +742,7 @@ class ldap(connection):
try:
if self.ldapConnection:
self.logger.debug(f"Search Filter={searchFilter}")
# Microsoft Active Directory set an hard limit of 1000 entries returned by any search
paged_search_control = ldapasn1_impacket.SimplePagedResultsControl(criticality=True, size=1000)
resp = self.ldapConnection.search(
@ -818,18 +819,17 @@ class ldap(connection):
self.logger.debug(f"Skipping item, cannot process due to error {e}")
pass
return
def dc_list(self):
# Building the search filter
search_filter = "(&(objectCategory=computer)(primaryGroupId=516))"
attributes = ["dNSHostName"]
resp = self.search(search_filter, attributes, 0)
for item in resp:
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
name = ""
try:
try:
for attribute in item["attributes"]:
if str(attribute["type"]) == "dNSHostName":
name = str(attribute["vals"][0])

View File

@ -252,16 +252,14 @@ class KerberosAttacks:
return
# Let's output the TGT enc-part/cipher in Hashcat format, in case somebody wants to use it.
if asRep['enc-part']['etype'] == 17 or asRep['enc-part']['etype'] == 18:
if asRep["enc-part"]["etype"] == 17 or asRep["enc-part"]["etype"] == 18:
hash_TGT = "$krb5asrep$%d$%s@%s:%s$%s" % (
asRep["enc-part"]["etype"], clientName, domain,
asRep["enc-part"]["etype"],
clientName,
domain,
hexlify(asRep["enc-part"]["cipher"].asOctets()[:12]).decode(),
hexlify(asRep["enc-part"]["cipher"].asOctets()[12:]).decode(),
)
else:
hash_TGT = '$krb5asrep$%d$%s@%s:%s$%s' % (
asRep['enc-part']['etype'], clientName, domain,
hexlify(asRep['enc-part']['cipher'].asOctets()[:16]).decode(),
hexlify(asRep['enc-part']['cipher'].asOctets()[16:]).decode()
)
hash_TGT = "$krb5asrep$%d$%s@%s:%s$%s" % (asRep["enc-part"]["etype"], clientName, domain, hexlify(asRep["enc-part"]["cipher"].asOctets()[:16]).decode(), hexlify(asRep["enc-part"]["cipher"].asOctets()[16:]).decode())
return hash_TGT

View File

@ -224,13 +224,7 @@ class LAPSv2Extract:
string_binding = hept_map(destHost=self.domain, remoteIf=MSRPC_UUID_GKDI, protocol="ncacn_ip_tcp")
rpc_transport = transport.DCERPCTransportFactory(string_binding)
if hasattr(rpc_transport, "set_credentials"):
rpc_transport.set_credentials(
username=self.username,
password=self.password,
domain=self.domain,
lmhash=self.lmhash,
nthash=self.nthash
)
rpc_transport.set_credentials(username=self.username, password=self.password, domain=self.domain, lmhash=self.lmhash, nthash=self.nthash)
if self.do_kerberos:
self.logger.info("Connecting using kerberos")
rpc_transport.set_kerberos(self.do_kerberos, kdcHost=self.kdcHost)
@ -253,17 +247,10 @@ class LAPSv2Extract:
self.logger.info("Successfully bound")
self.logger.info("Calling MS-GKDI GetKey")
resp = GkdiGetKey(
dce,
target_sd=target_sd,
l0=key_id["L0Index"],
l1=key_id["L1Index"],
l2=key_id["L2Index"],
root_key_id=key_id["RootKeyId"]
)
resp = GkdiGetKey(dce, target_sd=target_sd, l0=key_id["L0Index"], l1=key_id["L1Index"], l2=key_id["L2Index"], root_key_id=key_id["RootKeyId"])
self.logger.info("Decrypting password")
# Unpack GroupKeyEnvelope
gke = GroupKeyEnvelope(b''.join(resp["pbbOut"]))
gke = GroupKeyEnvelope(b"".join(resp["pbbOut"]))
kds_cache[gke["RootKeyId"]] = gke
kek = compute_kek(gke, key_id)
@ -276,4 +263,4 @@ class LAPSv2Extract:
self.logger.info("CEK:\t%s" % cek)
plaintext = decrypt_plaintext(cek, iv, remaining)
self.logger.info(plaintext[:-18].decode("utf-16le"))
return plaintext[:-18].decode("utf-16le")
return plaintext[:-18].decode("utf-16le")

View File

@ -2,19 +2,19 @@ from argparse import _StoreTrueAction
def proto_args(parser, std_parser, module_parser):
ldap_parser = parser.add_parser('ldap', help="own stuff using LDAP", parents=[std_parser, module_parser])
ldap_parser.add_argument("-H", '--hash', metavar="HASH", dest='hash', nargs='+', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
ldap_parser = parser.add_parser("ldap", help="own stuff using LDAP", parents=[std_parser, module_parser])
ldap_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
ldap_parser.add_argument("--port", type=int, choices={389, 636}, default=389, help="LDAP port (default: 389)")
no_smb_arg = ldap_parser.add_argument("--no-smb", action=get_conditional_action(_StoreTrueAction), make_required=[], help='No smb connection')
no_smb_arg = ldap_parser.add_argument("--no-smb", action=get_conditional_action(_StoreTrueAction), make_required=[], help="No smb connection")
dgroup = ldap_parser.add_mutually_exclusive_group()
domain_arg = dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, default=None, help="domain to authenticate to")
dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target')
domain_arg = dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, default=None, help="domain to authenticate to")
dgroup.add_argument("--local-auth", action="store_true", help="authenticate locally to each target")
no_smb_arg.make_required = [domain_arg]
egroup = ldap_parser.add_argument_group("Retrevie hash on the remote DC", "Options to get hashes from Kerberos")
egroup.add_argument("--asreproast", help="Get AS_REP response ready to crack with hashcat")
egroup.add_argument("--kerberoasting", help='Get TGS ticket ready to crack with hashcat')
egroup.add_argument("--kerberoasting", help="Get TGS ticket ready to crack with hashcat")
vgroup = ldap_parser.add_argument_group("Retrieve useful information on the domain", "Options to to play with Kerberos")
vgroup.add_argument("--trusted-for-delegation", action="store_true", help="Get the list of users and computers with flag TRUSTED_FOR_DELEGATION")
@ -41,7 +41,7 @@ def proto_args(parser, std_parser, module_parser):
def get_conditional_action(baseAction):
class ConditionalAction(baseAction):
def __init__(self, option_strings, dest, **kwargs):
x = kwargs.pop('make_required', [])
x = kwargs.pop("make_required", [])
super(ConditionalAction, self).__init__(option_strings, dest, **kwargs)
self.make_required = x

View File

@ -389,7 +389,7 @@ class mssql(connection):
remote_path = self.args.get_file[0]
download_path = self.args.get_file[1]
self.logger.display(f'Copying "{remote_path}" to "{download_path}"')
try:
exec_method = MSSQLEXEC(self.conn)
exec_method.get_file(self.args.get_file[0], self.args.get_file[1])

View File

@ -204,7 +204,6 @@ class database:
self.conn.execute(q)
def add_admin_user(self, credtype, domain, username, password, host, user_id=None):
if user_id:
q = select(self.UsersTable).filter(self.UsersTable.c.id == user_id)
users = self.conn.execute(q).all()

View File

@ -1,38 +1,40 @@
from argparse import _StoreTrueAction
def proto_args(parser, std_parser, module_parser):
mssql_parser = parser.add_parser('mssql', help="own stuff using MSSQL", parents=[std_parser, module_parser])
mssql_parser.add_argument("-H", '--hash', metavar="HASH", dest='hash', nargs='+', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
mssql_parser.add_argument("--port", default=1433, type=int, metavar='PORT', help='MSSQL port (default: 1433)')
mssql_parser.add_argument("-q", "--query", dest='mssql_query', metavar='QUERY', type=str, help='execute the specified query against the MSSQL DB')
no_smb_arg = mssql_parser.add_argument("--no-smb", action=get_conditional_action(_StoreTrueAction), make_required=[], help='No smb connection')
mssql_parser = parser.add_parser("mssql", help="own stuff using MSSQL", parents=[std_parser, module_parser])
mssql_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
mssql_parser.add_argument("--port", default=1433, type=int, metavar="PORT", help="MSSQL port (default: 1433)")
mssql_parser.add_argument("-q", "--query", dest="mssql_query", metavar="QUERY", type=str, help="execute the specified query against the MSSQL DB")
no_smb_arg = mssql_parser.add_argument("--no-smb", action=get_conditional_action(_StoreTrueAction), make_required=[], help="No smb connection")
dgroup = mssql_parser.add_mutually_exclusive_group()
domain_arg = dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, help="domain name")
dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target')
domain_arg = dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, help="domain name")
dgroup.add_argument("--local-auth", action="store_true", help="authenticate locally to each target")
no_smb_arg.make_required = [domain_arg]
cgroup = mssql_parser.add_argument_group("Command Execution", "options for executing commands")
cgroup.add_argument('--force-ps32', action='store_true', 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("--force-ps32", action="store_true", 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")
xgroup = cgroup.add_mutually_exclusive_group()
xgroup.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command")
xgroup.add_argument("-X", metavar="PS_COMMAND", dest='ps_execute', help='execute the specified PowerShell command')
xgroup.add_argument("-x", metavar="COMMAND", dest="execute", help="execute the specified command")
xgroup.add_argument("-X", metavar="PS_COMMAND", dest="ps_execute", help="execute the specified PowerShell command")
psgroup = mssql_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('--clear-obfscripts', action='store_true', help='Clear all cached obfuscated PowerShell scripts')
psgroup = mssql_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("--clear-obfscripts", action="store_true", help="Clear all cached obfuscated PowerShell scripts")
tgroup = mssql_parser.add_argument_group("Files", "Options for put and get remote files")
tgroup.add_argument("--put-file", nargs=2, metavar="FILE", help='Put a local file into remote target, ex: whoami.txt C:\\Windows\\Temp\\whoami.txt')
tgroup.add_argument("--get-file", nargs=2, metavar="FILE", help='Get a remote file, ex: C:\\Windows\\Temp\\whoami.txt whoami.txt')
tgroup.add_argument("--put-file", nargs=2, metavar="FILE", help="Put a local file into remote target, ex: whoami.txt C:\\Windows\\Temp\\whoami.txt")
tgroup.add_argument("--get-file", nargs=2, metavar="FILE", help="Get a remote file, ex: C:\\Windows\\Temp\\whoami.txt whoami.txt")
return parser
def get_conditional_action(baseAction):
class ConditionalAction(baseAction):
def __init__(self, option_strings, dest, **kwargs):
x = kwargs.pop('make_required', [])
x = kwargs.pop("make_required", [])
super(ConditionalAction, self).__init__(option_strings, dest, **kwargs)
self.make_required = x
@ -41,4 +43,4 @@ def get_conditional_action(baseAction):
x.required = True
super(ConditionalAction, self).__call__(parser, namespace, values, option_string)
return ConditionalAction
return ConditionalAction

View File

@ -25,6 +25,7 @@ from asyauth.common.credentials.kerberos import KerberosCredential
from asyauth.common.constants import asyauthSecret
from asysocks.unicomm.common.target import UniTarget, UniProto
class rdp(connection):
def __init__(self, args, db, host):
self.domain = None
@ -104,7 +105,7 @@ class rdp(connection):
)
def print_host_info(self):
nla = colored(f"nla:{self.nla}", host_info_colors[3], attrs=['bold']) if self.nla else colored(f"nla:{self.nla}", host_info_colors[2], attrs=['bold'])
nla = colored(f"nla:{self.nla}", host_info_colors[3], attrs=["bold"]) if self.nla else colored(f"nla:{self.nla}", host_info_colors[2], attrs=["bold"])
if self.domain is None:
self.logger.display("Probably old, doesn't not support HYBRID or HYBRID_EX" f" ({nla})")
else:
@ -220,15 +221,7 @@ class rdp(connection):
else:
stype = asyauthSecret.PASS if not nthash else asyauthSecret.NT
kerberos_target = UniTarget(
self.domain,
88,
UniProto.CLIENT_TCP,
proxies=None,
dns=None,
dc_ip=self.domain,
domain=self.domain
)
kerberos_target = UniTarget(self.domain, 88, UniProto.CLIENT_TCP, proxies=None, dns=None, dc_ip=self.domain, domain=self.domain)
self.auth = KerberosCredential(
target=kerberos_target,
secret=password,
@ -246,9 +239,7 @@ class rdp(connection):
username,
(
# Show what was used between cleartext, nthash, aesKey and ccache
" from ccache"
if useCache
else ":%s" % (process_secret(kerb_pass))
" from ccache" if useCache else ":%s" % (process_secret(kerb_pass))
),
self.mark_pwned(),
)

View File

@ -1,17 +1,17 @@
def proto_args(parser, std_parser, module_parser):
rdp_parser = parser.add_parser('rdp', help="own stuff using RDP", parents=[std_parser, module_parser])
rdp_parser.add_argument("-H", '--hash', metavar="HASH", dest='hash', nargs='+', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
rdp_parser = parser.add_parser("rdp", help="own stuff using RDP", parents=[std_parser, module_parser])
rdp_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
rdp_parser.add_argument("--port", type=int, default=3389, help="Custom RDP port")
rdp_parser.add_argument("--rdp-timeout", type=int, default=5, help="RDP timeout on socket connection, defalut is %(default)ss")
rdp_parser.add_argument("--nla-screenshot", action="store_true", help="Screenshot RDP login prompt if NLA is disabled")
dgroup = rdp_parser.add_mutually_exclusive_group()
dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, default=None, help="domain to authenticate to")
dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target')
dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, default=None, help="domain to authenticate to")
dgroup.add_argument("--local-auth", action="store_true", help="authenticate locally to each target")
egroup = rdp_parser.add_argument_group("Screenshot", "Remote Desktop Screenshot")
egroup.add_argument("--screenshot", action="store_true", help="Screenshot RDP if connection success")
egroup.add_argument('--screentime', type=int, default=10, help='Time to wait for desktop image, default is %(default)ss')
egroup.add_argument('--res', default='1024x768', help='Resolution in "WIDTHxHEIGHT" format. Default: "1024x768"')
egroup.add_argument("--screentime", type=int, default=10, help="Time to wait for desktop image, default is %(default)ss")
egroup.add_argument("--res", default="1024x768", help='Resolution in "WIDTHxHEIGHT" format. Default: "1024x768"')
return parser
return parser

View File

@ -314,15 +314,7 @@ class smb(connection):
values = {str(attr["type"]).lower(): attr["vals"][0] for attr in host["attributes"]}
if "mslaps-encryptedpassword" in values:
msMCSAdmPwd = values["mslaps-encryptedpassword"]
d = LAPSv2Extract(
bytes(msMCSAdmPwd),
username[0] if username else "",
password[0] if password else "",
domain,
ntlm_hash[0] if ntlm_hash else "",
self.args.kerberos,
self.args.kdcHost,
339)
d = LAPSv2Extract(bytes(msMCSAdmPwd), username[0] if username else "", password[0] if password else "", domain, ntlm_hash[0] if ntlm_hash else "", self.args.kerberos, self.args.kdcHost, 339)
try:
data = d.run()
except Exception as e:
@ -362,8 +354,8 @@ class smb(connection):
return True
def print_host_info(self):
signing = colored(f"signing:{self.signing}", host_info_colors[0], attrs=['bold']) if self.signing else colored(f"signing:{self.signing}", host_info_colors[1], attrs=['bold'])
smbv1 = colored(f"SMBv1:{self.smbv1}", host_info_colors[2], attrs=['bold']) if self.smbv1 else colored(f"SMBv1:{self.smbv1}", host_info_colors[3], attrs=['bold'])
signing = colored(f"signing:{self.signing}", host_info_colors[0], attrs=["bold"]) if self.signing else colored(f"signing:{self.signing}", host_info_colors[1], attrs=["bold"])
smbv1 = colored(f"SMBv1:{self.smbv1}", host_info_colors[2], attrs=["bold"]) if self.smbv1 else colored(f"SMBv1:{self.smbv1}", host_info_colors[3], attrs=["bold"])
self.logger.display(f"{self.server_os}{f' x{self.os_arch}' if self.os_arch else ''} (name:{self.hostname}) (domain:{self.domain}) ({signing}) ({smbv1})")
if self.args.laps:
return self.laps_search(self.args.username, self.args.password, self.args.hash, self.domain)
@ -402,7 +394,7 @@ class smb(connection):
kerb_pass = ""
self.logger.debug(f"Attempting to do Kerberos Login with useCache: {useCache}")
self.conn.kerberosLogin( username, password, domain, lmhash, nthash, aesKey, kdcHost, useCache=useCache)
self.conn.kerberosLogin(username, password, domain, lmhash, nthash, aesKey, kdcHost, useCache=useCache)
self.check_if_admin()
if username == "":
@ -677,28 +669,13 @@ class smb(connection):
payload = self.args.execute
if not self.args.no_output:
get_output = True
current_method = ""
for method in methods:
current_method = method
if method == "wmiexec":
try:
exec_method = WMIEXEC(
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.smb_share_name,
self.username,
self.password,
self.domain,
self.conn,
self.kerberos,
self.aesKey,
self.kdcHost,
self.hash,
self.args.share,
logger=self.logger,
timeout=self.args.dcom_timeout,
tries=self.args.get_output_tries
)
exec_method = WMIEXEC(self.host if not self.kerberos else self.hostname + "." + self.domain, self.smb_share_name, self.username, self.password, self.domain, self.conn, self.kerberos, self.aesKey, self.kdcHost, self.hash, self.args.share, logger=self.logger, timeout=self.args.dcom_timeout, tries=self.args.get_output_tries)
self.logger.info("Executed command via wmiexec")
break
except:
@ -707,19 +684,7 @@ class smb(connection):
continue
elif method == "mmcexec":
try:
exec_method = MMCEXEC(
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.smb_share_name,
self.username,
self.password,
self.domain,
self.conn,
self.args.share,
self.hash,
self.logger,
self.args.get_output_tries,
self.args.dcom_timeout
)
exec_method = MMCEXEC(self.host if not self.kerberos else self.hostname + "." + self.domain, self.smb_share_name, self.username, self.password, self.domain, self.conn, self.args.share, self.hash, self.logger, self.args.get_output_tries, self.args.dcom_timeout)
self.logger.info("Executed command via mmcexec")
break
except:
@ -728,20 +693,7 @@ class smb(connection):
continue
elif method == "atexec":
try:
exec_method = TSCH_EXEC(
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.smb_share_name,
self.username,
self.password,
self.domain,
self.kerberos,
self.aesKey,
self.kdcHost,
self.hash,
self.logger,
self.args.get_output_tries,
self.args.share
)
exec_method = TSCH_EXEC(self.host if not self.kerberos else self.hostname + "." + self.domain, self.smb_share_name, self.username, self.password, self.domain, self.kerberos, self.aesKey, self.kdcHost, self.hash, self.logger, self.args.get_output_tries, self.args.share)
self.logger.info("Executed command via atexec")
break
except:
@ -750,23 +702,7 @@ class smb(connection):
continue
elif method == "smbexec":
try:
exec_method = SMBEXEC(
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.smb_share_name,
self.conn,
self.args.port,
self.username,
self.password,
self.domain,
self.kerberos,
self.aesKey,
self.kdcHost,
self.hash,
self.args.share,
self.args.port,
self.logger,
self.args.get_output_tries
)
exec_method = SMBEXEC(self.host if not self.kerberos else self.hostname + "." + self.domain, self.smb_share_name, self.conn, self.args.port, self.username, self.password, self.domain, self.kerberos, self.aesKey, self.kdcHost, self.hash, self.args.share, self.args.port, self.logger, self.args.get_output_tries)
self.logger.info("Executed command via smbexec")
break
except:
@ -776,7 +712,7 @@ class smb(connection):
if hasattr(self, "server"):
self.server.track_host(self.host)
if "exec_method" in locals():
output = exec_method.execute(payload, get_output)
try:
@ -798,7 +734,7 @@ class smb(connection):
else:
self.logger.fail(f"Execute command failed with {current_method}")
return False
@requires_admin
def ps_execute(
self,
@ -1014,9 +950,7 @@ class smb(connection):
group_id = self.db.get_groups(
group_name=self.args.local_groups,
group_domain=domain,
)[
0
][0]
)[0][0]
except IndexError:
group_id = self.db.add_group(
domain,
@ -1093,9 +1027,7 @@ class smb(connection):
group_id = self.db.get_groups(
group_name=self.args.groups,
group_domain=group.groupdomain,
)[
0
][0]
)[0][0]
except IndexError:
group_id = self.db.add_group(
group.groupdomain,
@ -1219,54 +1151,43 @@ class smb(connection):
def wmi(self, wmi_query=None, namespace=None):
records = []
if not wmi_query:
wmi_query = self.args.wmi.strip('\n')
wmi_query = self.args.wmi.strip("\n")
if not namespace:
namespace = self.args.wmi_namespace
try:
dcom = DCOMConnection(
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.username,
self.password,
self.domain,
self.lmhash,
self.nthash,
oxidResolver=True,
doKerberos=self.kerberos,
kdcHost=self.kdcHost,
aesKey=self.aesKey
)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login,IID_IWbemLevel1Login)
flag, stringBinding = dcom_FirewallChecker(iInterface, self.args.dcom_timeout)
dcom = DCOMConnection(self.host if not self.kerberos else self.hostname + "." + self.domain, self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.kerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
flag, stringBinding = dcom_FirewallChecker(iInterface, self.args.dcom_timeout)
if not flag or not stringBinding:
error_msg = f'WMI Query: Dcom initialization failed on connection with stringbinding: "{stringBinding}", please increase the timeout with the option "--dcom-timeout". If it\'s still failing maybe something is blocking the RPC connection, try another exec method'
if not stringBinding:
error_msg = "WMI Query: Dcom initialization failed: can't get target stringbinding, maybe cause by IPv6 or any other issues, please check your target again"
self.logger.fail(error_msg) if not flag else self.logger.debug(error_msg)
# Make it force break function
dcom.disconnect()
iWbemLevel1Login = IWbemLevel1Login(iInterface)
iWbemServices= iWbemLevel1Login.NTLMLogin(namespace , NULL, NULL)
iWbemServices = iWbemLevel1Login.NTLMLogin(namespace, NULL, NULL)
iWbemLevel1Login.RemRelease()
iEnumWbemClassObject = iWbemServices.ExecQuery(wmi_query)
except Exception as e:
self.logger.fail('Execute WQL error: {}'.format(e))
self.logger.fail("Execute WQL error: {}".format(e))
if "iWbemLevel1Login" in locals():
dcom.disconnect()
else:
self.logger.info(f"Executing WQL syntax: {wmi_query}")
while True:
try:
wmi_results = iEnumWbemClassObject.Next(0xffffffff, 1)[0]
wmi_results = iEnumWbemClassObject.Next(0xFFFFFFFF, 1)[0]
record = wmi_results.getProperties()
records.append(record)
for k,v in record.items():
for k, v in record.items():
self.logger.highlight(f"{k} => {v['value']}")
except Exception as e:
if str(e).find('S_FALSE') < 0:
if str(e).find("S_FALSE") < 0:
raise e
else:
break
@ -1486,7 +1407,7 @@ class smb(connection):
SAM.finish()
except SessionError as e:
if "STATUS_ACCESS_DENIED" in e.getErrorString():
self.logger.fail("Error \"STATUS_ACCESS_DENIED\" while dumping SAM. This is likely due to an endpoint protection.")
self.logger.fail('Error "STATUS_ACCESS_DENIED" while dumping SAM. This is likely due to an endpoint protection.')
except Exception as e:
self.logger.exception(str(e))
@ -1658,7 +1579,7 @@ class smb(connection):
if dump_cookies:
self.logger.display("Start Dumping Cookies")
for cookie in cookies:
if cookie.cookie_value != '':
if cookie.cookie_value != "":
self.logger.highlight(f"[{credential.winuser}][{cookie.browser.upper()}] {cookie.host}{cookie.path} - {cookie.cookie_name}:{cookie.cookie_value}")
self.logger.display("End Dumping Cookies")
@ -1744,7 +1665,7 @@ class smb(connection):
LSA.finish()
except SessionError as e:
if "STATUS_ACCESS_DENIED" in e.getErrorString():
self.logger.fail("Error \"STATUS_ACCESS_DENIED\" while dumping LSA. This is likely due to an endpoint protection.")
self.logger.fail('Error "STATUS_ACCESS_DENIED" while dumping LSA. This is likely due to an endpoint protection.')
except Exception as e:
self.logger.exception(str(e))

View File

@ -10,21 +10,7 @@ from time import sleep
class TSCH_EXEC:
def __init__(
self,
target,
share_name,
username,
password,
domain,
doKerberos=False,
aesKey=None,
kdcHost=None,
hashes=None,
logger=None,
tries=None,
share=None
):
def __init__(self, target, share_name, username, password, domain, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None):
self.__target = target
self.__username = username
self.__password = password
@ -144,7 +130,7 @@ class TSCH_EXEC:
dce.set_credentials(*self.__rpctransport.get_credentials())
dce.connect()
# dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
tmpName = gen_random_string(8)
xml = self.gen_xml(command, fileless)
@ -207,7 +193,7 @@ class TSCH_EXEC:
if tries >= self.__tries:
self.logger.fail("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 :
if str(e).find("STATUS_BAD_NETWORK_NAME") > 0:
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:

View File

@ -457,7 +457,6 @@ class database:
return results
def get_credential(self, cred_type, domain, username, password):
q = select(self.UsersTable).filter(
self.UsersTable.c.domain == domain,
self.UsersTable.c.username == username,
@ -899,11 +898,11 @@ class database:
def get_checks(self):
q = select(self.ConfChecksTable)
return self.conn.execute(q).all()
def get_check_results(self):
q = select(self.ConfChecksResultsTable)
return self.conn.execute(q).all()
def insert_data(self, table, select_results=[], **new_row):
"""
Insert a new row in the given table.
@ -919,20 +918,20 @@ class database:
else:
for row in select_results:
row_data = row._asdict()
for column,value in new_row.items():
for column, value in new_row.items():
row_data[column] = value
# Only add data to be updated if it has changed
if row_data not in results:
results.append(row_data)
updated_ids.append(row_data['id'])
updated_ids.append(row_data["id"])
nxc_logger.debug(f'Update data: {results}')
nxc_logger.debug(f"Update data: {results}")
# TODO: find a way to abstract this away to a single Upsert call
q = Insert(table) # .returning(table.c.id)
update_column = {col.name: col for col in q.excluded if col.name not in 'id'}
q = Insert(table) # .returning(table.c.id)
update_column = {col.name: col for col in q.excluded if col.name not in "id"}
q = q.on_conflict_do_update(index_elements=table.primary_key, set_=update_column)
self.conn.execute(q, results) # .scalar()
self.conn.execute(q, results) # .scalar()
# we only return updated IDs for now - when RETURNING clause is allowed we can return inserted
return updated_ids
@ -943,7 +942,7 @@ class database:
q = select(self.ConfChecksTable).filter(self.ConfChecksTable.c.name == name)
select_results = self.conn.execute(q).all()
context = locals()
new_row = dict(((column, context[column]) for column in ('name', 'description')))
new_row = dict(((column, context[column]) for column in ("name", "description")))
updated_ids = self.insert_data(self.ConfChecksTable, select_results, **new_row)
if updated_ids:
@ -957,7 +956,7 @@ class database:
q = select(self.ConfChecksResultsTable).filter(self.ConfChecksResultsTable.c.host_id == host_id, self.ConfChecksResultsTable.c.check_id == check_id)
select_results = self.conn.execute(q).all()
context = locals()
new_row = dict(((column, context[column]) for column in ('host_id', 'check_id', 'secure', 'reasons')))
new_row = dict(((column, context[column]) for column in ("host_id", "check_id", "secure", "reasons")))
updated_ids = self.insert_data(self.ConfChecksResultsTable, select_results, **new_row)
if updated_ids:

View File

@ -6,8 +6,9 @@ from nxc.nxcdb import DatabaseNavigator, print_table, print_help
from termcolor import colored
import functools
help_header = functools.partial(colored, color='cyan', attrs=['bold'])
help_kw = functools.partial(colored, color='green', attrs=['bold'])
help_header = functools.partial(colored, color="cyan", attrs=["bold"])
help_kw = functools.partial(colored, color="green", attrs=["bold"])
class navigator(DatabaseNavigator):
def display_creds(self, creds):
@ -361,35 +362,21 @@ class navigator(DatabaseNavigator):
print_table(data, title="Credential(s) with Admin Access")
def do_wcc(self, line):
valid_columns = {
'ip':'IP',
'hostname':'Hostname',
'check':'Check',
'description':'Description',
'status':'Status',
'reasons':'Reasons'
}
valid_columns = {"ip": "IP", "hostname": "Hostname", "check": "Check", "description": "Description", "status": "Status", "reasons": "Reasons"}
line = line.strip()
if line.lower() == 'full':
if line.lower() == "full":
columns_to_display = list(valid_columns.values())
else:
requested_columns = line.split(' ')
requested_columns = line.split(" ")
columns_to_display = list(valid_columns[column.lower()] for column in requested_columns if column.lower() in valid_columns)
results = self.db.get_check_results()
self.display_wcc_results(results, columns_to_display)
def display_wcc_results(self, results, columns_to_display=None):
data = [
[
"IP",
"Hostname",
"Check",
"Status"
]
]
data = [["IP", "Hostname", "Check", "Status"]]
if columns_to_display:
data = [columns_to_display]
@ -397,25 +384,25 @@ class navigator(DatabaseNavigator):
checks_dict = {}
for check in checks:
check = check._asdict()
checks_dict[check['id']] = check
checks_dict[check["id"]] = check
for (result_id, host_id, check_id, secure, reasons) in results:
status = 'OK' if secure else 'KO'
for result_id, host_id, check_id, secure, reasons in results:
status = "OK" if secure else "KO"
host = self.db.get_hosts(host_id)[0]._asdict()
check = checks_dict[check_id]
row = []
for column in data[0]:
if column == 'IP':
row.append(host['ip'])
if column == 'Hostname':
row.append(host['hostname'])
if column == 'Check':
row.append(check['name'])
if column == 'Description':
row.append(check['description'])
if column == 'Status':
if column == "IP":
row.append(host["ip"])
if column == "Hostname":
row.append(host["hostname"])
if column == "Check":
row.append(check["name"])
if column == "Description":
row.append(check["description"])
if column == "Status":
row.append(status)
if column == 'Reasons':
if column == "Reasons":
row.append(reasons)
data.append(row)

View File

@ -103,13 +103,13 @@ class MMCEXEC:
except:
# Make it force break function
self.__dcom.disconnect()
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout)
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout)
if not flag or not self.__stringBinding:
error_msg = f'MMCEXEC: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the option "--dcom-timeout". If it\'s still failing maybe something is blocking the RPC connection, try another exec method'
if not self.__stringBinding:
error_msg = "MMCEXEC: Dcom initialization failed: can't get target stringbinding, maybe cause by IPv6 or any other issues, please check your target again"
self.logger.fail(error_msg) if not flag else self.logger.debug(error_msg)
# Make it force break function
self.__dcom.disconnect()
@ -254,7 +254,7 @@ class MMCEXEC:
if tries >= self.__tries:
self.logger.fail("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 :
if str(e).find("STATUS_BAD_NETWORK_NAME") > 0:
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:
@ -263,7 +263,7 @@ class MMCEXEC:
tries += 1
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)
self.__smbconnection.deleteFile(self.__share, self.__output)

View File

@ -1,101 +1,77 @@
def proto_args(parser, std_parser, module_parser):
smb_parser = parser.add_parser("smb", help="own stuff using SMB", parents=[std_parser, module_parser])
smb_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[],
help="NTLM hash(es) or file(s) containing NTLM hashes")
dgroup = smb_parser.add_mutually_exclusive_group()
dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, help="domain to authenticate to")
dgroup.add_argument("--local-auth", action="store_true", help="authenticate locally to each target")
smb_parser.add_argument("--port", type=int, choices={445, 139}, default=445, help="SMB port (default: 445)")
smb_parser.add_argument("--share", metavar="SHARE", default="C$", help="specify a share (default: C$)")
smb_parser.add_argument("--smb-server-port", default="445", help="specify a server port for SMB", type=int)
smb_parser.add_argument("--gen-relay-list", metavar="OUTPUT_FILE",
help="outputs all hosts that don't require SMB signing to the specified file")
smb_parser.add_argument("--smb-timeout", help="SMB connection timeout, default 2 secondes", type=int, default=2)
smb_parser.add_argument("--laps", dest="laps", metavar="LAPS", type=str, help="LAPS authentification",
nargs="?", const="administrator")
smb_parser = parser.add_parser("smb", help="own stuff using SMB", parents=[std_parser, module_parser])
smb_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
dgroup = smb_parser.add_mutually_exclusive_group()
dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, help="domain to authenticate to")
dgroup.add_argument("--local-auth", action="store_true", help="authenticate locally to each target")
smb_parser.add_argument("--port", type=int, choices={445, 139}, default=445, help="SMB port (default: 445)")
smb_parser.add_argument("--share", metavar="SHARE", default="C$", help="specify a share (default: C$)")
smb_parser.add_argument("--smb-server-port", default="445", help="specify a server port for SMB", type=int)
smb_parser.add_argument("--gen-relay-list", metavar="OUTPUT_FILE", help="outputs all hosts that don't require SMB signing to the specified file")
smb_parser.add_argument("--smb-timeout", help="SMB connection timeout, default 2 secondes", type=int, default=2)
smb_parser.add_argument("--laps", dest="laps", metavar="LAPS", type=str, help="LAPS authentification", nargs="?", const="administrator")
cgroup = smb_parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
cgroup.add_argument("--sam", action="store_true", help="dump SAM hashes from target systems")
cgroup.add_argument("--lsa", action="store_true", help="dump LSA secrets from target systems")
cgroup.add_argument("--ntds", choices={"vss", "drsuapi"}, nargs="?", const="drsuapi",
help="dump the NTDS.dit from target DCs using the specifed method\n(default: drsuapi)")
cgroup.add_argument("--dpapi", choices={"cookies","nosystem"}, nargs="*",
help="dump DPAPI secrets from target systems, can dump cookies if you add \"cookies\", will not dump SYSTEM dpapi if you add nosystem\n")
# cgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history')
# cgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account')
cgroup = smb_parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
cgroup.add_argument("--sam", action="store_true", help="dump SAM hashes from target systems")
cgroup.add_argument("--lsa", action="store_true", help="dump LSA secrets from target systems")
cgroup.add_argument("--ntds", choices={"vss", "drsuapi"}, nargs="?", const="drsuapi", help="dump the NTDS.dit from target DCs using the specifed method\n(default: drsuapi)")
cgroup.add_argument("--dpapi", choices={"cookies", "nosystem"}, nargs="*", help='dump DPAPI secrets from target systems, can dump cookies if you add "cookies", will not dump SYSTEM dpapi if you add nosystem\n')
# cgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history')
# cgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account')
ngroup = smb_parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
ngroup.add_argument("--mkfile", action="store",
help="DPAPI option. File with masterkeys in form of {GUID}:SHA1")
ngroup.add_argument("--pvk", action="store", help="DPAPI option. File with domain backupkey")
ngroup.add_argument("--enabled", action="store_true", help="Only dump enabled targets from DC")
ngroup.add_argument("--user", dest="userntds", type=str, help="Dump selected user from DC")
ngroup = smb_parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
ngroup.add_argument("--mkfile", action="store", help="DPAPI option. File with masterkeys in form of {GUID}:SHA1")
ngroup.add_argument("--pvk", action="store", help="DPAPI option. File with domain backupkey")
ngroup.add_argument("--enabled", action="store_true", help="Only dump enabled targets from DC")
ngroup.add_argument("--user", dest="userntds", type=str, help="Dump selected user from DC")
egroup = smb_parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
egroup.add_argument("--shares", action="store_true", help="enumerate shares and access")
egroup.add_argument("--no-write-check", action="store_true", help="Skip write check on shares (avoid leaving traces when missing delete permissions)")
egroup = smb_parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
egroup.add_argument("--shares", action="store_true", help="enumerate shares and access")
egroup.add_argument("--no-write-check", action="store_true", help="Skip write check on shares (avoid leaving traces when missing delete permissions)")
egroup.add_argument("--filter-shares", nargs="+",
help="Filter share by access, option 'read' 'write' or 'read,write'")
egroup.add_argument("--sessions", action="store_true", help="enumerate active sessions")
egroup.add_argument("--disks", action="store_true", help="enumerate disks")
egroup.add_argument("--loggedon-users-filter", action="store",
help="only search for specific user, works with regex")
egroup.add_argument("--loggedon-users", action="store_true", help="enumerate logged on users")
egroup.add_argument("--users", nargs="?", const="", metavar="USER",
help="enumerate domain users, if a user is specified than only its information is queried.")
egroup.add_argument("--groups", nargs="?", const="", metavar="GROUP",
help="enumerate domain groups, if a group is specified than its members are enumerated")
egroup.add_argument("--computers", nargs="?", const="", metavar="COMPUTER", help="enumerate computer users")
egroup.add_argument("--local-groups", nargs="?", const="", metavar="GROUP",
help="enumerate local groups, if a group is specified then its members are enumerated")
egroup.add_argument("--pass-pol", action="store_true", help="dump password policy")
egroup.add_argument("--rid-brute", nargs="?", type=int, const=4000, metavar="MAX_RID",
help="enumerate users by bruteforcing RID's (default: 4000)")
egroup.add_argument("--wmi", metavar="QUERY", type=str, help="issues the specified WMI query")
egroup.add_argument("--wmi-namespace", metavar="NAMESPACE", default="root\\cimv2",
help="WMI Namespace (default: root\\cimv2)")
egroup.add_argument("--filter-shares", nargs="+", help="Filter share by access, option 'read' 'write' or 'read,write'")
egroup.add_argument("--sessions", action="store_true", help="enumerate active sessions")
egroup.add_argument("--disks", action="store_true", help="enumerate disks")
egroup.add_argument("--loggedon-users-filter", action="store", help="only search for specific user, works with regex")
egroup.add_argument("--loggedon-users", action="store_true", help="enumerate logged on users")
egroup.add_argument("--users", nargs="?", const="", metavar="USER", help="enumerate domain users, if a user is specified than only its information is queried.")
egroup.add_argument("--groups", nargs="?", const="", metavar="GROUP", help="enumerate domain groups, if a group is specified than its members are enumerated")
egroup.add_argument("--computers", nargs="?", const="", metavar="COMPUTER", help="enumerate computer users")
egroup.add_argument("--local-groups", nargs="?", const="", metavar="GROUP", help="enumerate local groups, if a group is specified then its members are enumerated")
egroup.add_argument("--pass-pol", action="store_true", help="dump password policy")
egroup.add_argument("--rid-brute", nargs="?", type=int, const=4000, metavar="MAX_RID", help="enumerate users by bruteforcing RID's (default: 4000)")
egroup.add_argument("--wmi", metavar="QUERY", type=str, help="issues the specified WMI query")
egroup.add_argument("--wmi-namespace", metavar="NAMESPACE", default="root\\cimv2", help="WMI Namespace (default: root\\cimv2)")
sgroup = smb_parser.add_argument_group("Spidering", 'Options for spidering shares')
sgroup.add_argument("--spider", metavar="SHARE", type=str, help="share to spider")
sgroup.add_argument("--spider-folder", metavar="FOLDER", default=".", type=str,
help="folder to spider (default: root share directory)")
sgroup.add_argument("--content", action="store_true", help="enable file content searching")
sgroup.add_argument("--exclude-dirs", type=str, metavar="DIR_LIST", default="",
help="directories to exclude from spidering")
segroup = sgroup.add_mutually_exclusive_group()
segroup.add_argument("--pattern", nargs="+",
help="pattern(s) to search for in folders, filenames and file content")
segroup.add_argument("--regex", nargs="+", help="regex(s) to search for in folders, filenames and file content")
sgroup.add_argument("--depth", type=int, default=None,
help="max spider recursion depth (default: infinity & beyond)")
sgroup.add_argument("--only-files", action="store_true", help="only spider files")
sgroup = smb_parser.add_argument_group("Spidering", "Options for spidering shares")
sgroup.add_argument("--spider", metavar="SHARE", type=str, help="share to spider")
sgroup.add_argument("--spider-folder", metavar="FOLDER", default=".", type=str, help="folder to spider (default: root share directory)")
sgroup.add_argument("--content", action="store_true", help="enable file content searching")
sgroup.add_argument("--exclude-dirs", type=str, metavar="DIR_LIST", default="", help="directories to exclude from spidering")
segroup = sgroup.add_mutually_exclusive_group()
segroup.add_argument("--pattern", nargs="+", help="pattern(s) to search for in folders, filenames and file content")
segroup.add_argument("--regex", nargs="+", help="regex(s) to search for in folders, filenames and file content")
sgroup.add_argument("--depth", type=int, default=None, help="max spider recursion depth (default: infinity & beyond)")
sgroup.add_argument("--only-files", action="store_true", help="only spider files")
tgroup = smb_parser.add_argument_group("Files", "Options for put and get remote files")
tgroup.add_argument("--put-file", nargs=2, metavar="FILE", help="Put a local file into remote target, ex: whoami.txt \\\\Windows\\\\Temp\\\\whoami.txt")
tgroup.add_argument("--get-file", nargs=2, metavar="FILE", help="Get a remote file, ex: \\\\Windows\\\\Temp\\\\whoami.txt whoami.txt")
tgroup.add_argument("--append-host", action="store_true", help="append the host to the get-file filename")
tgroup = smb_parser.add_argument_group("Files", "Options for put and get remote files")
tgroup.add_argument("--put-file", nargs=2, metavar="FILE", help="Put a local file into remote target, ex: whoami.txt \\\\Windows\\\\Temp\\\\whoami.txt")
tgroup.add_argument("--get-file", nargs=2, metavar="FILE", help="Get a remote file, ex: \\\\Windows\\\\Temp\\\\whoami.txt whoami.txt")
tgroup.add_argument("--append-host", action="store_true", help="append the host to the get-file filename")
cgroup = smb_parser.add_argument_group("Command Execution", "Options for executing commands")
cgroup.add_argument("--exec-method", choices={"wmiexec", "mmcexec", "smbexec", "atexec"}, default=None,
help="method to execute the command. Ignored if in MSSQL mode (default: wmiexec)")
cgroup.add_argument("--dcom-timeout", help="DCOM connection timeout, default is 5 secondes", type=int, default=5)
cgroup.add_argument("--get-output-tries", help="Number of times atexec/smbexec/mmcexec tries to get results, default is 5", type=int, default=5)
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, "
"map the result with "
"https://docs.python.org/3/library/codecs.html#standard-encodings and then execute "
"again with --codec and the corresponding codec")
cgroup.add_argument("--force-ps32", action="store_true",
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 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")
psgroup.add_argument('--amsi-bypass', nargs=1, metavar="FILE", help='File with a custom AMSI bypass')
psgroup.add_argument("--clear-obfscripts", action="store_true", help="Clear all cached obfuscated PowerShell scripts")
cgroup = smb_parser.add_argument_group("Command Execution", "Options for executing commands")
cgroup.add_argument("--exec-method", choices={"wmiexec", "mmcexec", "smbexec", "atexec"}, default=None, help="method to execute the command. Ignored if in MSSQL mode (default: wmiexec)")
cgroup.add_argument("--dcom-timeout", help="DCOM connection timeout, default is 5 secondes", type=int, default=5)
cgroup.add_argument("--get-output-tries", help="Number of times atexec/smbexec/mmcexec tries to get results, default is 5", type=int, default=5)
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, ' "map the result with " "https://docs.python.org/3/library/codecs.html#standard-encodings and then execute " "again with --codec and the corresponding codec")
cgroup.add_argument("--force-ps32", action="store_true", 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 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")
psgroup.add_argument("--amsi-bypass", nargs=1, metavar="FILE", help="File with a custom AMSI bypass")
psgroup.add_argument("--clear-obfscripts", action="store_true", help="Clear all cached obfuscated PowerShell scripts")
return parser
return parser

View File

@ -46,16 +46,7 @@ class SamrFunc:
kerberos=self.doKerberos,
aesKey=self.aesKey,
)
self.lsa_query = LSAQuery(
username=self.username,
password=self.password,
domain=self.domain,
remote_name=self.addr,
remote_host=self.addr,
kerberos=self.doKerberos,
aesKey=self.aesKey,
logger=self.logger
)
self.lsa_query = LSAQuery(username=self.username, password=self.password, domain=self.domain, remote_name=self.addr, remote_host=self.addr, kerberos=self.doKerberos, aesKey=self.aesKey, logger=self.logger)
def get_builtin_groups(self):
domains = self.samr_query.get_domains()
@ -204,18 +195,7 @@ class SAMRQuery:
class LSAQuery:
def __init__(
self,
username="",
password="",
domain="",
port=445,
remote_name="",
remote_host="",
aesKey="",
kerberos=None,
logger=None
):
def __init__(self, username="", password="", domain="", port=445, remote_name="", remote_host="", aesKey="", kerberos=None, logger=None):
self.__username = username
self.__password = password
self.__domain = domain

View File

@ -10,24 +10,7 @@ from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
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
):
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):
self.__host = host
self.__share_name = "C$"
self.__port = port
@ -127,7 +110,7 @@ class SMBEXEC:
self.logger.debug("Command to execute: " + command)
self.logger.debug(f"Remote service {self.__serviceName} created.")
try:
resp = scmr.hRCreateServiceW(
self.__scmr,
@ -143,7 +126,7 @@ class SMBEXEC:
self.logger.fail("SMBEXEC: Create services got blocked.")
else:
self.logger.fail(str(e))
return self.__outputBuffer
try:
@ -181,7 +164,7 @@ class SMBEXEC:
tries += 1
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)

View File

@ -12,23 +12,7 @@ from impacket.dcerpc.v5.dtypes import NULL
class WMIEXEC:
def __init__(
self,
target,
share_name,
username,
password,
domain,
smbconnection,
doKerberos=False,
aesKey=None,
kdcHost=None,
hashes=None,
share=None,
logger=None,
timeout=None,
tries=None
):
def __init__(self, target, share_name, username, password, domain, smbconnection, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, share=None, logger=None, timeout=None, tries=None):
self.__target = target
self.__username = username
self.__password = password
@ -73,13 +57,13 @@ class WMIEXEC:
kdcHost=self.__kdcHost,
)
iInterface = self.__dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout)
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout)
if not flag or not self.__stringBinding:
error_msg = f'WMIEXEC: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the option "--dcom-timeout". If it\'s still failing maybe something is blocking the RPC connection, try another exec method'
if not self.__stringBinding:
error_msg = "WMIEXEC: Dcom initialization failed: can't get target stringbinding, maybe cause by IPv6 or any other issues, please check your target again"
self.logger.fail(error_msg) if not flag else self.logger.debug(error_msg)
# Make it force break function
self.__dcom.disconnect()
@ -156,7 +140,7 @@ class WMIEXEC:
if self.__retOutput is False:
self.__outputBuffer = ""
return
tries = 1
while True:
try:
@ -167,7 +151,7 @@ class WMIEXEC:
if tries >= self.__tries:
self.logger.fail("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 :
if str(e).find("STATUS_BAD_NETWORK_NAME") > 0:
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:
@ -179,4 +163,4 @@ class WMIEXEC:
if self.__outputBuffer:
self.logger.debug(f"Deleting file {self.__share}\\{self.__output}")
self.__smbconnection.deleteFile(self.__share, self.__output)
self.__smbconnection.deleteFile(self.__share, self.__output)

View File

@ -42,41 +42,51 @@ class database:
@staticmethod
def db_schema(db_conn):
db_conn.execute("""CREATE TABLE "credentials" (
db_conn.execute(
"""CREATE TABLE "credentials" (
"id" integer PRIMARY KEY,
"username" text,
"password" text,
"credtype" text
)""")
db_conn.execute("""CREATE TABLE "hosts" (
)"""
)
db_conn.execute(
"""CREATE TABLE "hosts" (
"id" integer PRIMARY KEY,
"host" text,
"port" integer,
"banner" text,
"os" text
)""")
db_conn.execute("""CREATE TABLE "loggedin_relations" (
)"""
)
db_conn.execute(
"""CREATE TABLE "loggedin_relations" (
"id" integer PRIMARY KEY,
"credid" integer,
"hostid" integer,
"shell" boolean,
FOREIGN KEY(credid) REFERENCES credentials(id),
FOREIGN KEY(hostid) REFERENCES hosts(id)
)""")
)"""
)
# "admin" access with SSH means we have root access, which implies shell access since we run commands to check
db_conn.execute("""CREATE TABLE "admin_relations" (
db_conn.execute(
"""CREATE TABLE "admin_relations" (
"id" integer PRIMARY KEY,
"credid" integer,
"hostid" integer,
FOREIGN KEY(credid) REFERENCES credentials(id),
FOREIGN KEY(hostid) REFERENCES hosts(id)
)""")
db_conn.execute("""CREATE TABLE "keys" (
)"""
)
db_conn.execute(
"""CREATE TABLE "keys" (
"id" integer PRIMARY KEY,
"credid" integer,
"data" text,
FOREIGN KEY(credid) REFERENCES credentials(id)
)""")
)"""
)
def reflect_tables(self):
with self.db_engine.connect():

View File

@ -18,6 +18,7 @@ from nxc.helpers.bloodhound import add_user_bh
from nxc.protocols.ldap.laps import LDAPConnect, LAPSv2Extract
from nxc.logger import NXCAdapter
class winrm(connection):
def __init__(self, args, db, host):
self.domain = None
@ -141,22 +142,16 @@ class winrm(connection):
values = {str(attr["type"]).lower(): attr["vals"][0] for attr in host["attributes"]}
if "mslaps-encryptedpassword" in values:
from json import loads
msMCSAdmPwd = values["mslaps-encryptedpassword"]
d = LAPSv2Extract(
bytes(msMCSAdmPwd),
username[0] if username else "",
password[0] if password else "",
domain,
ntlm_hash[0] if ntlm_hash else "",
self.args.kerberos,
self.args.kdcHost,
339)
d = LAPSv2Extract(bytes(msMCSAdmPwd), username[0] if username else "", password[0] if password else "", domain, ntlm_hash[0] if ntlm_hash else "", self.args.kerberos, self.args.kdcHost, 339)
data = d.run()
r = loads(data)
msMCSAdmPwd = r["p"]
username_laps = r["n"]
elif "mslaps-password" in values:
from json import loads
r = loads(str(values["mslaps-password"]))
msMCSAdmPwd = r["p"]
username_laps = r["n"]
@ -224,7 +219,6 @@ class winrm(connection):
def plaintext_login(self, domain, username, password):
try:
# log.addFilter(SuppressFilter())
if not self.args.laps:
self.password = password
@ -331,7 +325,6 @@ class winrm(connection):
for line in buf:
self.logger.highlight(line.strip())
def ps_execute(self, payload=None, get_output=False):
r = self.conn.execute_ps(self.args.ps_execute)
self.logger.success("Executed command")

View File

@ -1,5 +1,6 @@
from argparse import _StoreTrueAction
def proto_args(parser, std_parser, module_parser):
winrm_parser = parser.add_parser("winrm", help="own stuff using WINRM", parents=[std_parser, module_parser])
winrm_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
@ -8,7 +9,7 @@ def proto_args(parser, std_parser, module_parser):
winrm_parser.add_argument("--ignore-ssl-cert", action="store_true", help="Ignore Certificate Verification")
winrm_parser.add_argument("--laps", dest="laps", metavar="LAPS", type=str, help="LAPS authentification", nargs="?", const="administrator")
winrm_parser.add_argument("--http-timeout", dest="http_timeout", type=int, default=10, help="HTTP timeout for WinRM connections")
no_smb_arg = winrm_parser.add_argument("--no-smb", action=get_conditional_action(_StoreTrueAction), make_required=[], help='No smb connection')
no_smb_arg = winrm_parser.add_argument("--no-smb", action=get_conditional_action(_StoreTrueAction), make_required=[], help="No smb connection")
dgroup = winrm_parser.add_mutually_exclusive_group()
domain_arg = dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, default=None, help="domain to authenticate to")
@ -21,22 +22,18 @@ def proto_args(parser, std_parser, module_parser):
cegroup.add_argument("--lsa", action="store_true", help="dump LSA secrets from target systems")
cgroup = winrm_parser.add_argument_group("Command Execution", "Options for executing commands")
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, "
"map the result with "
"https://docs.python.org/3/library/codecs.html#standard-encodings and then execute "
"again with --codec and the corresponding codec")
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, ' "map the result with " "https://docs.python.org/3/library/codecs.html#standard-encodings and then execute " "again with --codec and the corresponding codec")
cgroup.add_argument("--no-output", action="store_true", help="do not retrieve command output")
cgroup.add_argument("-x", metavar="COMMAND", dest="execute", help="execute the specified command")
cgroup.add_argument("-X", metavar="PS_COMMAND", dest="ps_execute", help="execute the specified PowerShell command")
return parser
def get_conditional_action(baseAction):
class ConditionalAction(baseAction):
def __init__(self, option_strings, dest, **kwargs):
x = kwargs.pop('make_required', [])
x = kwargs.pop("make_required", [])
super(ConditionalAction, self).__init__(option_strings, dest, **kwargs)
self.make_required = x
@ -45,4 +42,4 @@ def get_conditional_action(baseAction):
x.required = True
super(ConditionalAction, self).__call__(parser, namespace, values, option_string)
return ConditionalAction
return ConditionalAction

View File

@ -17,50 +17,33 @@ from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_
from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, IWbemLevel1Login
MSRPC_UUID_PORTMAP = uuidtup_to_bin(('E1AF8308-5D1F-11C9-91A4-08002B14A0FA', '3.0'))
MSRPC_UUID_PORTMAP = uuidtup_to_bin(("E1AF8308-5D1F-11C9-91A4-08002B14A0FA", "3.0"))
class wmi(connection):
def __init__(self, args, db, host):
self.domain = None
self.hash = ''
self.lmhash = ''
self.nthash = ''
self.fqdn = ''
self.remoteName = ''
self.hash = ""
self.lmhash = ""
self.nthash = ""
self.fqdn = ""
self.remoteName = ""
self.server_os = None
self.doKerberos = False
self.stringBinding = None
# From: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d
self.rpc_error_status = {
"0000052F" : "STATUS_ACCOUNT_RESTRICTION",
"00000533" : "STATUS_ACCOUNT_DISABLED",
"00000775" : "STATUS_ACCOUNT_LOCKED_OUT",
"00000701" : "STATUS_ACCOUNT_EXPIRED",
"00000532" : "STATUS_PASSWORD_EXPIRED",
"00000530" : "STATUS_INVALID_LOGON_HOURS",
"00000531" : "STATUS_INVALID_WORKSTATION",
"00000569" : "STATUS_LOGON_TYPE_NOT_GRANTED",
"00000773" : "STATUS_PASSWORD_MUST_CHANGE",
"00000005" : "STATUS_ACCESS_DENIED",
"0000052E" : "STATUS_LOGON_FAILURE",
"0000052B" : "STATUS_WRONG_PASSWORD",
"00000721" : "RPC_S_SEC_PKG_ERROR"
}
self.rpc_error_status = {"0000052F": "STATUS_ACCOUNT_RESTRICTION", "00000533": "STATUS_ACCOUNT_DISABLED", "00000775": "STATUS_ACCOUNT_LOCKED_OUT", "00000701": "STATUS_ACCOUNT_EXPIRED", "00000532": "STATUS_PASSWORD_EXPIRED", "00000530": "STATUS_INVALID_LOGON_HOURS", "00000531": "STATUS_INVALID_WORKSTATION", "00000569": "STATUS_LOGON_TYPE_NOT_GRANTED", "00000773": "STATUS_PASSWORD_MUST_CHANGE", "00000005": "STATUS_ACCESS_DENIED", "0000052E": "STATUS_LOGON_FAILURE", "0000052B": "STATUS_WRONG_PASSWORD", "00000721": "RPC_S_SEC_PKG_ERROR"}
connection.__init__(self, args, db, host)
def proto_logger(self):
self.logger = NXCAdapter(extra={'protocol': 'WMI',
'host': self.host,
'port': self.args.port,
'hostname': self.hostname})
self.logger = NXCAdapter(extra={"protocol": "WMI", "host": self.host, "port": self.args.port, "hostname": self.hostname})
def create_conn_obj(self):
if self.remoteName == '':
if self.remoteName == "":
self.remoteName = self.host
try:
rpctansport = transport.DCERPCTransportFactory(r'ncacn_ip_tcp:{0}[{1}]'.format(self.remoteName, str(self.args.port)))
rpctansport = transport.DCERPCTransportFactory(r"ncacn_ip_tcp:{0}[{1}]".format(self.remoteName, str(self.args.port)))
rpctansport.set_credentials(username="", password="", domain="", lmhash="", nthash="", aesKey="")
rpctansport.setRemoteHost(self.host)
rpctansport.set_connect_timeout(self.args.rpc_timeout)
@ -75,36 +58,36 @@ class wmi(connection):
else:
self.conn = rpctansport
return True
def enum_host_info(self):
# All code pick from DumpNTLNInfo.py
# https://github.com/fortra/impacket/blob/master/examples/DumpNTLMInfo.py
ntlmChallenge = None
bind = MSRPCBind()
item = CtxItem()
item['AbstractSyntax'] = epm.MSRPC_UUID_PORTMAP
item['TransferSyntax'] = uuidtup_to_bin(('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0'))
item['ContextID'] = 0
item['TransItems'] = 1
item["AbstractSyntax"] = epm.MSRPC_UUID_PORTMAP
item["TransferSyntax"] = uuidtup_to_bin(("8a885d04-1ceb-11c9-9fe8-08002b104860", "2.0"))
item["ContextID"] = 0
item["TransItems"] = 1
bind.addCtxItem(item)
packet = MSRPCHeader()
packet['type'] = MSRPC_BIND
packet['pduData'] = bind.getData()
packet['call_id'] = 1
packet["type"] = MSRPC_BIND
packet["pduData"] = bind.getData()
packet["call_id"] = 1
auth = ntlm.getNTLMSSPType1('', '', signingRequired=True, use_ntlmv2=True)
auth = ntlm.getNTLMSSPType1("", "", signingRequired=True, use_ntlmv2=True)
sec_trailer = SEC_TRAILER()
sec_trailer['auth_type'] = RPC_C_AUTHN_WINNT
sec_trailer['auth_level'] = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
sec_trailer['auth_ctx_id'] = 0 + 79231
sec_trailer["auth_type"] = RPC_C_AUTHN_WINNT
sec_trailer["auth_level"] = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
sec_trailer["auth_ctx_id"] = 0 + 79231
pad = (4 - (len(packet.get_packet()) % 4)) % 4
if pad != 0:
packet['pduData'] += b'\xFF'*pad
sec_trailer['auth_pad_len']=pad
packet['sec_trailer'] = sec_trailer
packet['auth_data'] = auth
packet["pduData"] += b"\xFF" * pad
sec_trailer["auth_pad_len"] = pad
packet["sec_trailer"] = sec_trailer
packet["auth_data"] = auth
try:
self.conn.connect()
@ -117,29 +100,29 @@ class wmi(connection):
response = MSRPCHeader(buffer)
bindResp = MSRPCBindAck(response.getData())
ntlmChallenge = ntlm.NTLMAuthChallenge(bindResp['auth_data'])
ntlmChallenge = ntlm.NTLMAuthChallenge(bindResp["auth_data"])
if ntlmChallenge['TargetInfoFields_len'] > 0:
av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']])
if ntlmChallenge["TargetInfoFields_len"] > 0:
av_pairs = ntlm.AV_PAIRS(ntlmChallenge["TargetInfoFields"][: ntlmChallenge["TargetInfoFields_len"]])
if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1] is not None:
try:
self.hostname = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')
self.hostname = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode("utf-16le")
except:
self.hostname = self.host
if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1] is not None:
try:
self.domain = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le')
self.domain = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode("utf-16le")
except:
self.domain = self.args.domain
if av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME][1] is not None:
try:
self.fqdn = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME][1].decode('utf-16le')
self.fqdn = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME][1].decode("utf-16le")
except:
pass
if 'Version' in ntlmChallenge.fields:
version = ntlmChallenge['Version']
if "Version" in ntlmChallenge.fields:
version = ntlmChallenge["Version"]
if len(version) >= 4:
self.server_os = "Windows NT %d.%d Build %d" % (indexbytes(version,0), indexbytes(version,1), struct.unpack('<H',version[2:4])[0])
self.server_os = "Windows NT %d.%d Build %d" % (indexbytes(version, 0), indexbytes(version, 1), struct.unpack("<H", version[2:4])[0])
else:
self.hostname = self.host
@ -154,16 +137,14 @@ class wmi(connection):
self.output_filename = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-"))
def print_host_info(self):
self.logger.extra['protocol'] = "RPC"
self.logger.extra['port'] = "135"
self.logger.display(u"{} (name:{}) (domain:{})".format(self.server_os,
self.hostname,
self.domain))
self.logger.extra["protocol"] = "RPC"
self.logger.extra["port"] = "135"
self.logger.display("{} (name:{}) (domain:{})".format(self.server_os, self.hostname, self.domain))
return True
def check_if_admin(self):
try:
dcom = DCOMConnection(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos ,kdcHost=self.kdcHost, aesKey=self.aesKey)
dcom = DCOMConnection(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
flag, self.stringBinding = dcom_FirewallChecker(iInterface, self.args.rpc_timeout)
except Exception as e:
@ -176,15 +157,15 @@ class wmi(connection):
if not flag or not self.stringBinding:
dcom.disconnect()
error_msg = f'Check admin error: dcom initialization failed with stringbinding: "{self.stringBinding}", please try "--rpc-timeout" option. (probably is admin)'
if not self.stringBinding:
error_msg = "Check admin error: dcom initialization failed: can't get target stringbinding, maybe cause by IPv6 or any other issues, please check your target again"
self.logger.fail(error_msg) if not flag else self.logger.debug(error_msg)
else:
try:
iWbemLevel1Login = IWbemLevel1Login(iInterface)
iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
iWbemServices = iWbemLevel1Login.NTLMLogin("//./root/cimv2", NULL, NULL)
except Exception as e:
dcom.disconnect()
@ -192,37 +173,37 @@ class wmi(connection):
self.logger.fail(str(e))
else:
dcom.disconnect()
self.logger.extra['protocol'] = "WMI"
self.logger.extra["protocol"] = "WMI"
self.admin_privs = True
return
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
logging.getLogger("impacket").disabled = True
lmhash = ''
nthash = ''
lmhash = ""
nthash = ""
self.password = password
self.username = username
self.domain = domain
self.remoteName = self.fqdn
self.create_conn_obj()
if password == "":
if ntlm_hash.find(':') != -1:
lmhash, nthash = ntlm_hash.split(':')
if ntlm_hash.find(":") != -1:
lmhash, nthash = ntlm_hash.split(":")
else:
nthash = ntlm_hash
self.nthash = nthash
self.lmhash = lmhash
if not all("" == s for s in [nthash, password, aesKey]):
kerb_pass = next(s for s in [nthash, password, aesKey] if s)
else:
kerb_pass = ""
if useCache:
if kerb_pass == "":
ccache = CCache.loadFile(os.getenv("KRB5CCNAME"))
username = ccache.credentials[0].header['client'].prettyPrint().decode().split("@")[0]
username = ccache.credentials[0].header["client"].prettyPrint().decode().split("@")[0]
self.username = username
used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}"
@ -255,12 +236,12 @@ class wmi(connection):
# Get data from rpc connection if got vaild creds
entry_handle = epm.ept_lookup_handle_t()
request = epm.ept_lookup()
request['inquiry_type'] = 0x0
request['object'] = NULL
request['Ifid'] = NULL
request['vers_option'] = 0x1
request['entry_handle'] = entry_handle
request['max_ents'] = 1
request["inquiry_type"] = 0x0
request["object"] = NULL
request["Ifid"] = NULL
request["vers_option"] = 0x1
request["entry_handle"] = entry_handle
request["max_ents"] = 1
dce.request(request)
except Exception as e:
dce.disconnect()
@ -301,14 +282,14 @@ class wmi(connection):
# Get data from rpc connection if got vaild creds
entry_handle = epm.ept_lookup_handle_t()
request = epm.ept_lookup()
request['inquiry_type'] = 0x0
request['object'] = NULL
request['Ifid'] = NULL
request['vers_option'] = 0x1
request['entry_handle'] = entry_handle
request['max_ents'] = 1
request["inquiry_type"] = 0x0
request["object"] = NULL
request["Ifid"] = NULL
request["vers_option"] = 0x1
request["entry_handle"] = entry_handle
request["max_ents"] = 1
dce.request(request)
except Exception as e:
except Exception as e:
dce.disconnect()
error_msg = str(e).lower()
self.logger.debug(error_msg)
@ -325,20 +306,20 @@ class wmi(connection):
out += "(Default allow anonymous login)"
self.logger.success(out)
return True
def hash_login(self, domain, username, ntlm_hash):
self.username = username
lmhash = ''
nthash = ''
if ntlm_hash.find(':') != -1:
self.lmhash, self.nthash = ntlm_hash.split(':')
lmhash = ""
nthash = ""
if ntlm_hash.find(":") != -1:
self.lmhash, self.nthash = ntlm_hash.split(":")
else:
lmhash = ''
lmhash = ""
nthash = ntlm_hash
self.nthash = nthash
self.lmhash = lmhash
try:
self.conn.set_credentials(username=self.username, password=self.password, domain=self.domain, lmhash=lmhash, nthash=nthash)
dce = self.conn.get_dce_rpc()
@ -356,14 +337,14 @@ class wmi(connection):
# Get data from rpc connection if got vaild creds
entry_handle = epm.ept_lookup_handle_t()
request = epm.ept_lookup()
request['inquiry_type'] = 0x0
request['object'] = NULL
request['Ifid'] = NULL
request['vers_option'] = 0x1
request['entry_handle'] = entry_handle
request['max_ents'] = 1
request["inquiry_type"] = 0x0
request["object"] = NULL
request["Ifid"] = NULL
request["vers_option"] = 0x1
request["entry_handle"] = entry_handle
request["max_ents"] = 1
dce.request(request)
except Exception as e:
except Exception as e:
dce.disconnect()
error_msg = str(e).lower()
self.logger.debug(error_msg)
@ -381,39 +362,39 @@ class wmi(connection):
self.logger.success(out)
return True
# It's very complex to use wmi from rpctansport "convert" to dcom, so let we use dcom directly.
# It's very complex to use wmi from rpctansport "convert" to dcom, so let we use dcom directly.
@requires_admin
def wmi(self, WQL=None, namespace=None):
records = []
if not WQL:
WQL = self.args.wmi.strip('\n')
WQL = self.args.wmi.strip("\n")
if not namespace:
namespace = self.args.wmi_namespace
try:
dcom = DCOMConnection(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos ,kdcHost=self.kdcHost, aesKey=self.aesKey)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login,IID_IWbemLevel1Login)
dcom = DCOMConnection(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
iWbemLevel1Login = IWbemLevel1Login(iInterface)
iWbemServices= iWbemLevel1Login.NTLMLogin(namespace , NULL, NULL)
iWbemServices = iWbemLevel1Login.NTLMLogin(namespace, NULL, NULL)
iWbemLevel1Login.RemRelease()
iEnumWbemClassObject = iWbemServices.ExecQuery(WQL)
except Exception as e:
dcom.disconnect()
self.logger.debug(str(e))
self.logger.fail('Execute WQL error: {}'.format(str(e)))
self.logger.fail("Execute WQL error: {}".format(str(e)))
return False
else:
self.logger.info(f"Executing WQL syntax: {WQL}")
while True:
try:
wmi_results = iEnumWbemClassObject.Next(0xffffffff, 1)[0]
wmi_results = iEnumWbemClassObject.Next(0xFFFFFFFF, 1)[0]
record = wmi_results.getProperties()
records.append(record)
for k,v in record.items():
for k, v in record.items():
self.logger.highlight(f"{k} => {v['value']}")
except Exception as e:
if str(e).find('S_FALSE') < 0:
if str(e).find("S_FALSE") < 0:
self.logger.debug(str(e))
else:
break
@ -434,7 +415,7 @@ class wmi(connection):
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
if self.server_os is not None and "NT 5" in self.server_os:
self.logger.fail("Execute command failed, not support current server os (version < NT 6)")
return False
@ -442,7 +423,7 @@ class wmi(connection):
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.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.exec_timeout, self.args.codec)
output = exec_method.execute(command, get_output)
@ -456,4 +437,4 @@ class wmi(connection):
buf = StringIO(output).readlines()
for line in buf:
self.logger.highlight(line.strip())
return output
return output

View File

@ -1,40 +1,31 @@
def proto_args(parser, std_parser, module_parser):
wmi_parser = parser.add_parser('wmi', help="own stuff using WMI", parents=[std_parser, module_parser], conflict_handler='resolve')
wmi_parser.add_argument("-H", '--hash', metavar="HASH", dest='hash', nargs='+', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
wmi_parser = parser.add_parser("wmi", help="own stuff using WMI", parents=[std_parser, module_parser], conflict_handler="resolve")
wmi_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
wmi_parser.add_argument("--port", type=int, choices={135}, default=135, help="WMI port (default: 135)")
wmi_parser.add_argument("--rpc-timeout", help="RPC/DCOM(WMI) connection timeout, default is %(default)s secondes", type=int, default=2)
# For domain options
dgroup = wmi_parser.add_mutually_exclusive_group()
dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', default=None, type=str, help="Domain to authenticate to")
dgroup.add_argument("--local-auth", action='store_true', help='Authenticate locally to each target')
dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", default=None, type=str, help="Domain to authenticate to")
dgroup.add_argument("--local-auth", action="store_true", help="Authenticate locally to each target")
egroup = wmi_parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
egroup.add_argument("--wmi", metavar='QUERY', dest='wmi',type=str, help='Issues the specified WMI query')
egroup.add_argument("--wmi-namespace", metavar='NAMESPACE', type=str, default='root\\cimv2', help='WMI Namespace (default: root\\cimv2)')
egroup.add_argument("--wmi", metavar="QUERY", dest="wmi", type=str, help="Issues the specified WMI query")
egroup.add_argument("--wmi-namespace", metavar="NAMESPACE", type=str, default="root\\cimv2", help="WMI Namespace (default: root\\cimv2)")
cgroup = wmi_parser.add_argument_group("Command Execution", "Options for executing commands")
cgroup.add_argument("--no-output", action="store_true", help="do not retrieve command output")
cgroup.add_argument("-x", metavar='COMMAND', dest='execute', type=str, help='Creates a new cmd process and executes the specified command with output')
cgroup.add_argument("--exec-method", choices={"wmiexec", "wmiexec-event"}, default="wmiexec",
help="method to execute the command. (default: wmiexec). "
"[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("--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, "
"map the result with "
"https://docs.python.org/3/library/codecs.html#standard-encodings and then execute "
"again with --codec and the corresponding codec")
cgroup.add_argument("-x", metavar="COMMAND", dest="execute", type=str, help="Creates a new cmd process and executes the specified command with output")
cgroup.add_argument("--exec-method", choices={"wmiexec", "wmiexec-event"}, default="wmiexec", help="method to execute the command. (default: wmiexec). " "[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("--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, ' "map the result with " "https://docs.python.org/3/library/codecs.html#standard-encodings and then execute " "again with --codec and the corresponding codec")
return parser
def get_conditional_action(baseAction):
class ConditionalAction(baseAction):
def __init__(self, option_strings, dest, **kwargs):
x = kwargs.pop('make_required', [])
x = kwargs.pop("make_required", [])
super(ConditionalAction, self).__init__(option_strings, dest, **kwargs)
self.make_required = x
@ -43,4 +34,4 @@ def get_conditional_action(baseAction):
x.required = True
super(ConditionalAction, self).__call__(parser, namespace, values, option_string)
return ConditionalAction
return ConditionalAction

View File

@ -6,17 +6,17 @@
# Link: https://github.com/XiaoliChan/wmiexec-RegOut/blob/main/wmiexec-regOut.py
# Note: windows version under NT6 not working with this command execution way
# https://github.com/XiaoliChan/wmiexec-RegOut/blob/main/wmiexec-reg-sch-UnderNT6-wip.py -- WIP
#
# Description:
#
# Description:
# For more details, please check out my repository.
# https://github.com/XiaoliChan/wmiexec-RegOut
#
# Workflow:
# Stage 1:
# cmd.exe /Q /c {command} > C:\windows\temp\{random}.txt (aka command results)
#
#
# powershell convert the command results into base64, and save it into C:\windows\temp\{random2}.txt (now the command results was base64 encoded)
#
#
# Create registry path: HKLM:\Software\Classes\hello, then add C:\windows\temp\{random2}.txt into HKLM:\Software\Classes\hello\{NewKey}
#
# Remove anythings which in C:\windows\temp\
@ -33,6 +33,7 @@ from impacket.dcerpc.v5.dtypes import NULL
from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, IWbemLevel1Login
class WMIEXEC:
def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, exec_timeout, codec):
self.__host = host
@ -50,19 +51,19 @@ class WMIEXEC:
self.__outputBuffer = ""
self.__retOutput = True
self.__shell = 'cmd.exe /Q /c '
#self.__pwsh = 'powershell.exe -NoP -NoL -sta -NonI -W Hidden -Exec Bypass -Enc '
#self.__pwsh = 'powershell.exe -Enc '
self.__pwd = str('C:\\')
self.__shell = "cmd.exe /Q /c "
# self.__pwsh = 'powershell.exe -NoP -NoL -sta -NonI -W Hidden -Exec Bypass -Enc '
# self.__pwsh = 'powershell.exe -Enc '
self.__pwd = str("C:\\")
self.__codec = codec
self.__dcom = DCOMConnection(self.__host, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos ,kdcHost=self.__kdcHost, aesKey=self.__aesKey)
self.__dcom = DCOMConnection(self.__host, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, aesKey=self.__aesKey)
iInterface = self.__dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
iWbemLevel1Login = IWbemLevel1Login(iInterface)
self.__iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
self.__iWbemServices = iWbemLevel1Login.NTLMLogin("//./root/cimv2", NULL, NULL)
iWbemLevel1Login.RemRelease()
self.__win32Process, _ = self.__iWbemServices.GetObject('Win32_Process')
self.__win32Process, _ = self.__iWbemServices.GetObject("Win32_Process")
def execute(self, command, output=False):
self.__retOutput = output
if self.__retOutput:
@ -88,7 +89,7 @@ class WMIEXEC:
keyName = str(uuid.uuid4())
self.__registry_Path = f"Software\\Classes\\{gen_random_string(6)}"
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 = rf"""{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.__exec_timeout))
@ -99,15 +100,15 @@ class WMIEXEC:
def queryRegistry(self, keyName):
try:
self.logger.debug(f"Querying registry key: HKLM\\{self.__registry_Path}")
descriptor, _ = self.__iWbemServices.GetObject('StdRegProv')
descriptor, _ = self.__iWbemServices.GetObject("StdRegProv")
descriptor = descriptor.SpawnInstance()
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:
self.logger.fail("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}")
retVal = descriptor.DeleteKey(2147483650, self.__registry_Path)
except Exception as e:
self.logger.debug(f"Target: {self.__host} removing temporary registry path error: {str(e)}")
self.logger.debug(f"Target: {self.__host} removing temporary registry path error: {str(e)}")

View File

@ -6,8 +6,8 @@
# Link: https://github.com/XiaoliChan/wmiexec-Pro
# Note: windows version under NT6 not working with this command execution way, it need Win32_ScheduledJob.
# https://github.com/XiaoliChan/wmiexec-Pro/blob/main/lib/modules/exec_command.py
#
# Description:
#
# Description:
# For more details, please check out my repository.
# https://github.com/XiaoliChan/wmiexec-Pro/blob/main/lib/modules/exec_command.py
#
@ -49,22 +49,22 @@ class WMIEXEC_EVENT:
self.__aesKey = aesKey
self.__outputBuffer = ""
self.__retOutput = True
self.logger = logger
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())}"
self.__dcom = DCOMConnection(self.__host, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos ,kdcHost=self.__kdcHost, aesKey=self.__aesKey)
self.__dcom = DCOMConnection(self.__host, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, aesKey=self.__aesKey)
iInterface = self.__dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
iWbemLevel1Login = IWbemLevel1Login(iInterface)
self.__iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/subscription', NULL, NULL)
self.__iWbemServices = iWbemLevel1Login.NTLMLogin("//./root/subscription", NULL, NULL)
iWbemLevel1Login.RemRelease()
def execute(self, command, output=False):
if "'" in command:
command = command.replace("'",r'"')
command = command.replace("'", r'"')
self.__retOutput = output
self.execute_handler(command)
@ -83,7 +83,7 @@ class WMIEXEC_EVENT:
# Generate vbsript and execute it
self.logger.debug(f"{self.__host}: Execute command via wmi event, job instance id: {self.__instanceID}, command result instance id: {self.__instanceID_StoreResult}")
self.execute_remote(command)
# Get command results
self.logger.info(f"Waiting {self.__exec_timeout}s for command completely executed.")
time.sleep(self.__exec_timeout)
@ -123,7 +123,7 @@ class WMIEXEC_EVENT:
try:
error_name = WBEMSTATUS.enumItems(call_status).name
except ValueError:
error_name = 'Unknown'
error_name = "Unknown"
self.logger.debug("{} - ERROR: {} (0x{:08x})".format(banner, error_name, call_status))
else:
self.logger.debug(f"{banner} - OK")
@ -131,21 +131,21 @@ class WMIEXEC_EVENT:
def execute_vbs(self, vbs_content):
# Copy from wmipersist.py
# Install ActiveScriptEventConsumer
active_script, _ = self.__iWbemServices.GetObject('ActiveScriptEventConsumer')
active_script, _ = self.__iWbemServices.GetObject("ActiveScriptEventConsumer")
active_script = active_script.SpawnInstance()
active_script.Name = self.__instanceID
active_script.ScriptingEngine = 'VBScript'
active_script.ScriptingEngine = "VBScript"
active_script.CreatorSID = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
active_script.ScriptText = vbs_content
# Don't output impacket default verbose
current=sys.stdout
current = sys.stdout
sys.stdout = StringIO()
resp = self.__iWbemServices.PutInstance(active_script.marshalMe())
sys.stdout = current
self.check_error(f'Adding ActiveScriptEventConsumer.Name="{self.__instanceID}"', resp.GetCallStatus(0) & 0xffffffff)
self.check_error(f'Adding ActiveScriptEventConsumer.Name="{self.__instanceID}"', resp.GetCallStatus(0) & 0xFFFFFFFF)
# Timer means the amount of milliseconds after the script will be triggered, hard coding to 1 second it in this case.
wmi_timer, _ = self.__iWbemServices.GetObject('__IntervalTimerInstruction')
wmi_timer, _ = self.__iWbemServices.GetObject("__IntervalTimerInstruction")
wmi_timer = wmi_timer.SpawnInstance()
wmi_timer.TimerId = self.__instanceID
wmi_timer.IntervalBetweenEvents = 1000
@ -155,57 +155,57 @@ class WMIEXEC_EVENT:
sys.stdout = StringIO()
resp = self.__iWbemServices.PutInstance(wmi_timer.marshalMe())
sys.stdout = current
self.check_error(f'Adding IntervalTimerInstruction.TimerId="{self.__instanceID}"', resp.GetCallStatus(0) & 0xffffffff)
self.check_error(f'Adding IntervalTimerInstruction.TimerId="{self.__instanceID}"', resp.GetCallStatus(0) & 0xFFFFFFFF)
# EventFilter
event_filter, _ = self.__iWbemServices.GetObject('__EventFilter')
event_filter, _ = self.__iWbemServices.GetObject("__EventFilter")
event_filter = event_filter.SpawnInstance()
event_filter.Name = self.__instanceID
event_filter.CreatorSID = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
event_filter.CreatorSID = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
event_filter.Query = f'select * from __TimerEvent where TimerID = "{self.__instanceID}" '
event_filter.QueryLanguage = 'WQL'
event_filter.EventNamespace = r'root\subscription'
event_filter.QueryLanguage = "WQL"
event_filter.EventNamespace = r"root\subscription"
# Don't output verbose
current=sys.stdout
current = sys.stdout
sys.stdout = StringIO()
resp = self.__iWbemServices.PutInstance(event_filter.marshalMe())
sys.stdout = current
self.check_error(f'Adding EventFilter.Name={self.__instanceID}"', resp.GetCallStatus(0) & 0xffffffff)
self.check_error(f'Adding EventFilter.Name={self.__instanceID}"', resp.GetCallStatus(0) & 0xFFFFFFFF)
# Binding EventFilter & EventConsumer
filter_binding, _ = self.__iWbemServices.GetObject('__FilterToConsumerBinding')
filter_binding, _ = self.__iWbemServices.GetObject("__FilterToConsumerBinding")
filter_binding = filter_binding.SpawnInstance()
filter_binding.Filter = f'__EventFilter.Name="{self.__instanceID}"'
filter_binding.Consumer = f'ActiveScriptEventConsumer.Name="{self.__instanceID}"'
filter_binding.CreatorSID = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
# Don't output verbose
current=sys.stdout
current = sys.stdout
sys.stdout = StringIO()
resp = self.__iWbemServices.PutInstance(filter_binding.marshalMe())
sys.stdout = current
self.check_error(fr'Adding FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"{self.__instanceID}\"", Filter="__EventFilter.Name=\"{self.__instanceID}\""', resp.GetCallStatus(0) & 0xffffffff)
self.check_error(rf'Adding FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"{self.__instanceID}\"", Filter="__EventFilter.Name=\"{self.__instanceID}\""', resp.GetCallStatus(0) & 0xFFFFFFFF)
def get_command_result(self):
try:
command_result_object, _ = self.__iWbemServices.GetObject(f'ActiveScriptEventConsumer.Name="{self.__instanceID_StoreResult}"')
record = dict(command_result_object.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:
self.logger.fail("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:
resp = self.__iWbemServices.DeleteInstance(f'ActiveScriptEventConsumer.Name="{self.__instanceID_StoreResult}"')
self.check_error(f'Removing ActiveScriptEventConsumer.Name="{self.__instanceID}"', resp.GetCallStatus(0) & 0xffffffff)
self.check_error(f'Removing ActiveScriptEventConsumer.Name="{self.__instanceID}"', resp.GetCallStatus(0) & 0xFFFFFFFF)
resp = self.__iWbemServices.DeleteInstance(f'ActiveScriptEventConsumer.Name="{self.__instanceID}"')
self.check_error(f'Removing ActiveScriptEventConsumer.Name="{self.__instanceID}"', resp.GetCallStatus(0) & 0xffffffff)
self.check_error(f'Removing ActiveScriptEventConsumer.Name="{self.__instanceID}"', resp.GetCallStatus(0) & 0xFFFFFFFF)
resp = self.__iWbemServices.DeleteInstance(f'__IntervalTimerInstruction.TimerId="{self.__instanceID}"')
self.check_error(f'Removing IntervalTimerInstruction.TimerId="{self.__instanceID}"', resp.GetCallStatus(0) & 0xffffffff)
self.check_error(f'Removing IntervalTimerInstruction.TimerId="{self.__instanceID}"', resp.GetCallStatus(0) & 0xFFFFFFFF)
resp = self.__iWbemServices.DeleteInstance(f'__EventFilter.Name="{self.__instanceID}"')
self.check_error(f'Removing EventFilter.Name="{self.__instanceID}"', resp.GetCallStatus(0) & 0xffffffff)
self.check_error(f'Removing EventFilter.Name="{self.__instanceID}"', resp.GetCallStatus(0) & 0xFFFFFFFF)
resp = self.__iWbemServices.DeleteInstance(fr'__FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"{self.__instanceID}\"",Filter="__EventFilter.Name=\"{self.__instanceID}\""')
self.check_error(fr'Removing FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"{self.__instanceID}\"", Filter="__EventFilter.Name=\"{self.__instanceID}\""', resp.GetCallStatus(0) & 0xffffffff)
resp = self.__iWbemServices.DeleteInstance(rf'__FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"{self.__instanceID}\"",Filter="__EventFilter.Name=\"{self.__instanceID}\""')
self.check_error(rf'Removing FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"{self.__instanceID}\"", Filter="__EventFilter.Name=\"{self.__instanceID}\""', resp.GetCallStatus(0) & 0xFFFFFFFF)

View File

@ -63,6 +63,7 @@ rich = "^13.3.5"
python-libnmap = "^0.7.3"
resource = "^0.2.1"
oscrypto = { git = "https://github.com/NeffIsBack/oscrypto" }
ruff = "^0.0.291"
[tool.poetry.group.dev.dependencies]
flake8 = "*"
@ -74,3 +75,53 @@ pytest = "^7.2.2"
[build-system]
requires = ["poetry-core>=1.2.0"]
build-backend = "poetry.core.masonry.api"
[tool.ruff]
# Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E", "F"]
ignore = [ "E501", "F405", "F841"]
# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".mypy_cache",
".nox",
".pants.d",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
]
per-file-ignores = {}
line-length = 65000
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
target-version = "py37"
[tool.ruff.flake8-quotes]
docstring-quotes = "double"
[tool.ruff.format]
quote-style = "double"
indent-style = "space"