diff --git a/nxc/modules/ms17-010.py b/nxc/modules/ms17-010.py index ed5abf12..a16c7856 100644 --- a/nxc/modules/ms17-010.py +++ b/nxc/modules/ms17-010.py @@ -5,22 +5,7 @@ from ctypes import c_uint8, c_uint16, c_uint32, c_uint64, Structure import socket import struct - - -class NXCModule: - name = "ms17-010" - description = "MS17-010 - EternalBlue - NOT TESTED OUTSIDE LAB ENVIRONMENT" - supported_protocols = ["smb"] - opsec_safe = True - multiple_hosts = True - - def options(self, context, module_options): - """ """ - - def on_login(self, context, connection): - if check(connection.host): - context.log.highlight("VULNERABLE") - context.log.highlight("Next step: https://www.rapid7.com/db/modules/exploit/windows/smb/ms17_010_eternalblue/") +from nxc.logger import nxc_logger class SmbHeader(Structure): @@ -45,373 +30,428 @@ class SmbHeader(Structure): ("multiplex_id", c_uint16), ] - def __new__(cls, buffer=None): - return cls.from_buffer_copy(buffer) + def __init__(self, buffer): + nxc_logger.debug("server_component : %04x" % self.server_component) + nxc_logger.debug("smb_command : %01x" % self.smb_command) + nxc_logger.debug("error_class : %01x" % self.error_class) + nxc_logger.debug("error_code : %02x" % self.error_code) + nxc_logger.debug("flags : %01x" % self.flags) + nxc_logger.debug("flags2 : %02x" % self.flags2) + nxc_logger.debug("process_id_high : %02x" % self.process_id_high) + nxc_logger.debug("signature : %08x" % self.signature) + nxc_logger.debug("reserved2 : %02x" % self.reserved2) + nxc_logger.debug("tree_id : %02x" % self.tree_id) + nxc_logger.debug("process_id : %02x" % self.process_id) + nxc_logger.debug("user_id : %02x" % self.user_id) + nxc_logger.debug("multiplex_id : %02x" % self.multiplex_id) + + def __new__(self, buffer=None): + nxc_logger.debug(f"Creating SMB_HEADER object from buffer: {buffer}") + return self.from_buffer_copy(buffer) -def generate_smb_proto_payload(*protos): - """ - Generates an SMB Protocol payload by concatenating a list of packet protos. +class NXCModule: + name = "ms17-010" + description = "MS17-010 - EternalBlue - NOT TESTED OUTSIDE LAB ENVIRONMENT" + supported_protocols = ["smb"] + opsec_safe = True + multiple_hosts = True - Args: - ---- - *protos (list): List of packet protos. + def options(self, context, module_options): + """ """ + self.logger = context.log - Returns: - ------- - str: The generated SMB Protocol payload. - """ - # Initialize an empty list to store the hex data - hex_data = [] - - # Iterate over each proto in the input list - for proto in protos: - # Extend the hex_data list with the elements of the current proto - hex_data.extend(proto) - - # Join the elements of the hex_data list into a single string and return it - return "".join(hex_data) - - -def calculate_doublepulsar_xor_key(s): - """ - Calculate Doublepulsar Xor Key. - - Args: - ---- - s (int): The input value. - - Returns: - ------- - int: The calculated xor key. - """ - # Shift the value 16 bits to the left and combine it with the value shifted 8 bits to the left - # OR the result with s shifted 16 bits to the right and combined with s masked with 0xFF0000 - temp = ((s & 0xFF00) | (s << 16)) << 8 | (((s >> 16) | s & 0xFF0000) >> 8) - - # Multiply the temp value by 2 and perform a bitwise XOR with 0xFFFFFFFF - return 2 * temp ^ 0xFFFFFFFF + def on_login(self, context, connection): + if self.check(connection.host): + context.log.highlight("VULNERABLE") + context.log.highlight("Next step: https://www.rapid7.com/db/modules/exploit/windows/smb/ms17_010_eternalblue/") -def negotiate_proto_request(): - """Generate a negotiate_proto_request packet.""" - # Define the NetBIOS header - netbios = [ - "\x00", # Message Type - "\x00\x00\x54", # Length - ] + def generate_smb_proto_payload(self, *protos): + """ + Flattens a nested list and merges all bytes objects into a single bytes object. - # Define the SMB header - smb_header = [ - "\xFF\x53\x4D\x42", # Server Component - "\x72", # SMB Command - "\x00\x00\x00\x00", # NT Status - "\x18", # Flags - "\x01\x28", # Flags2 - "\x00\x00", # Process ID High - "\x00\x00\x00\x00\x00\x00\x00\x00", # Signature - "\x00\x00", # Reserved - "\x00\x00", # Tree ID - "\x2F\x4B", # Process ID - "\x00\x00", # User ID - "\xC5\x5E", # Multiplex ID - ] + Args: + ---- + *protos (list): The list to flatten and merge. - # Define the negotiate_proto_request - negotiate_proto_request = [ - "\x00", # Word Count - "\x31\x00", # Byte Count - "\x02", # Requested Dialects Count - "\x4C\x41\x4E\x4D\x41\x4E\x31\x2E\x30\x00", # Requested Dialects - "\x02", # Requested Dialects Count - "\x4C\x4D\x31\x2E\x32\x58\x30\x30\x32\x00", # Requested Dialects - "\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 - ] + Returns: + ------- + bytes: The merged bytes object. + """ + self.logger.debug("generate smb proto payload") + self.logger.debug(f"Protos: {protos}") - # Return the generated SMB protocol payload - return generate_smb_proto_payload(netbios, smb_header, negotiate_proto_request) + hex_data = b"" + for proto in protos: + if isinstance(proto, list): + hex_data += self.generate_smb_proto_payload(*proto) + elif isinstance(proto, bytes): + hex_data += proto + + self.logger.debug(f"Packed proto data: {hex_data}") + return hex_data -def session_setup_andx_request(): - """Generate session setup andx request.""" - # Define the NetBIOS bytes - netbios = [ - "\x00", # length - "\x00\x00\x63", # session service - ] + def calculate_doublepulsar_xor_key(self, s): + """ + Calculate Doublepulsar Xor Key. - # 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 - ] + Args: + ---- + s (int): The input value. - # 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 - ] - - # Call the generate_smb_proto_payload function and return the result - return generate_smb_proto_payload(netbios, smb_header, session_setup_andx_request) + Returns: + ------- + int: The calculated xor key. + """ + nxc_logger.debug(f"Calculating Doublepulsar XOR key for: {s}") + x = (2 * s ^ (((s & 0xff00 | (s << 16)) << 8) | (((s >> 16) | s & 0xff0000) >> 8))) + return x & 0xffffffff # truncate to 32 bits -def tree_connect_andx_request(ip: str, userid: str) -> str: - """Generate tree connect andx request. - Args: - ---- - ip (str): The IP address. - userid (str): The user ID. + def negotiate_proto_request(self): + """Generate a negotiate_proto_request packet.""" + self.logger.debug("generate negotiate proto request") - Returns: - ------- - bytes: The generated tree connect andx request payload. - """ - # Initialize the netbios header - netbios = [b"\x00", b"\x00\x00\x47"] + # Define the NetBIOS header + netbios = [ + b"\x00", # Message Type + b"\x00\x00\x54", # Length + ] - # Initialize the SMB header - smb_header = [ - b"\xFF\x53\x4D\x42", - b"\x75", - b"\x00\x00\x00\x00", - b"\x18", - b"\x01\x20", - b"\x00\x00", - b"\x00\x00\x00\x00\x00\x00\x00\x00", - b"\x00\x00", - b"\x00\x00", - b"\x2F\x4B", - userid, - b"\xC5\x5E", - ] + # Define the SMB header + smb_header = [ + b"\xFF\x53\x4D\x42", # Server Component + b"\x72", # SMB Command + b"\x00\x00\x00\x00", # NT Status + b"\x18", # Flags + b"\x01\x28", # Flags2 + b"\x00\x00", # Process ID High + b"\x00\x00\x00\x00\x00\x00\x00\x00", # Signature + b"\x00\x00", # Reserved + b"\x00\x00", # Tree ID + b"\x2F\x4B", # Process ID + b"\x00\x00", # User ID + b"\xC5\x5E", # Multiplex ID + ] - # Create the IPC string - ipc = f"\\\\{ip}\\IPC$\\x00" + # Define the negotiate_proto_request + negotiate_proto_request = [ + b"\x00", # Word Count + b"\x31\x00", # Byte Count + b"\x02", # Requested Dialects Count + b"\x4C\x41\x4E\x4D\x41\x4E\x31\x2E\x30\x00", # Requested Dialects: LANMAN1.0 + b"\x02", # Requested Dialects Count + b"\x4C\x4D\x31\x2E\x32\x58\x30\x30\x32\x00", # Requested Dialects: LM1.2X002 + b"\x02", # Requested Dialects Count + b"\x4E\x54\x20\x4C\x41\x4E\x4D\x41\x4E\x20\x31\x2E\x30\x00", # Requested Dialects: NT LANMAN 1.0 + b"\x02", # Requested Dialects Count + b"\x4E\x54\x20\x4C\x4D\x20\x30\x2E\x31\x32\x00", # Requested Dialects: NT LM 0.12 + ] - # Initialize the tree connect andx request - tree_connect_andx_request = [ - b"\x04", - b"\xFF", - b"\x00", - b"\x00\x00", - b"\x00\x00", - b"\x01\x00", - b"\x1A\x00", - b"\x00", - ipc.encode(), - b"\x3f\x3f\x3f\x3f\x3f\x00", - ] - - # Calculate the length of the payload - length = len(b"".join(smb_header)) + len(b"".join(tree_connect_andx_request)) - - # Update the length in the netbios header - netbios[1] = struct.pack(">L", length)[-3:] - - # Generate the final SMB protocol payload - return generate_smb_proto_payload(netbios, smb_header, tree_connect_andx_request) + # Return the generated SMB protocol payload + return self.generate_smb_proto_payload(netbios, smb_header, negotiate_proto_request) -def peeknamedpipe_request(treeid, processid, userid, multiplex_id): - """ - Generate tran2 request. + def session_setup_andx_request(self): + """Generate session setup andx request.""" + self.logger.debug("generate session setup andx request" + ) + # Define the NetBIOS bytes + netbios = [ + b"\x00", # length + b"\x00\x00\x63", # session service + ] - Args: - ---- - treeid (str): The tree ID. - processid (str): The process ID. - userid (str): The user ID. - multiplex_id (str): The multiplex ID. + # Define the SMB header bytes + smb_header = [ + b"\xFF\x53\x4D\x42", # server component: .SMB + b"\x73", # command: Session Setup AndX + b"\x00\x00\x00\x00", # NT status + b"\x18", # flags + b"\x01\x20", # flags2 + b"\x00\x00", # PID high + b"\x00\x00\x00\x00\x00\x00\x00\x00", # signature + b"\x00\x00", # reserved + b"\x00\x00", # tree id + b"\x2F\x4B", # pid + b"\x00\x00", # uid + b"\xC5\x5E", # multiplex id + ] - Returns: - ------- - str: The generated SMB protocol payload. - """ - # Set the necessary values for the netbios header - netbios = ["\x00", "\x00\x00\x4a"] + # Define the session setup andx request bytes + session_setup_andx_request = [ + b"\x0D", # word count + b"\xFF", # andx command: no further commands + b"\x00", # reserved + b"\x00\x00", # andx offset + b"\xDF\xFF", # max buffer + b"\x02\x00", # max mpx count + b"\x01\x00", # VC number + b"\x00\x00\x00\x00", # session key + b"\x00\x00", # ANSI password length + b"\x00\x00", # Unicode password length + b"\x00\x00\x00\x00", # reserved + b"\x40\x00\x00\x00", # capabilities + b"\x26\x00", # byte count + b"\x00", # account + b"\x2e\x00", # primary domain + b"\x57\x69\x6e\x64\x6f\x77\x73\x20\x32\x30\x30\x30\x20\x32\x31\x39\x35\x00", # Native OS: Windows 2000 2195 + b"\x57\x69\x6e\x64\x6f\x77\x73\x20\x32\x30\x30\x30\x20\x35\x2e\x30\x00", # Native OS: Windows 2000 5.0 + ] - # Set the values for the SMB header - smb_header = [ - "\xFF\x53\x4D\x42", # Server Component - "\x25", # SMB Command - "\x00\x00\x00\x00", # NT Status - "\x18", # Flags2 - "\x01\x28", # Process ID High & Multiplex ID - "\x00\x00", # Tree ID - "\x00\x00\x00\x00\x00\x00\x00\x00", # NT Time - "\x00\x00", # Process ID Low - treeid, # Tree ID - processid, # Process ID - userid, # User ID - multiplex_id, # Multiplex ID - ] - - # Set the values for the transaction request - tran_request = [ - "\x10", # Word Count - "\x00\x00", # Total Parameter Count - "\x00\x00", # Total Data Count - "\xff\xff", # Max Parameter Count - "\xff\xff", # Max Data Count - "\x00", # Max Setup Count - "\x00", # Reserved - "\x00\x00", # Flags - "\x00\x00\x00\x00", # Timeout - "\x00\x00", # Reserved - "\x00\x00", # Parameter Count - "\x4a\x00", # Parameter Offset - "\x00\x00", # Data Count - "\x4a\x00", # Data Offset - "\x02", # Setup Count - "\x00", # Reserved - "\x23\x00", # Function Code - "\x00\x00", # Reserved2 - "\x07\x00", # Byte Count - "\x5c\x50\x49\x50\x45\x5c\x00", # Transaction Name - ] - - # Generate the SMB protocol payload - return generate_smb_proto_payload(netbios, smb_header, tran_request) + return self.generate_smb_proto_payload(netbios, smb_header, session_setup_andx_request) -def trans2_request(treeid: str, processid: str, userid: str, multiplex_id: str) -> str: - """Generate trans2 request. + def tree_connect_andx_request(self, ip, userid): + """Generate tree connect andx request. - Args: - ---- - treeid: The treeid parameter. - processid: The processid parameter. - userid: The userid parameter. - multiplex_id: The multiplex_id parameter. + Args: + ---- + ip (str): The IP address. + userid (str): The user ID. - Returns: - ------- - The generated SMB protocol payload. - """ - # Define the netbios section of the SMB request - netbios = ["\x00", "\x00\x00\x4f"] + Returns: + ------- + bytes: The generated tree connect andx request payload. + """ + self.logger.debug("generate tree connect andx request") - # Define the SMB header section of the SMB request - smb_header = [ - "\xFF\x53\x4D\x42", - "\x32", - "\x00\x00\x00\x00", - "\x18", - "\x07\xc0", - "\x00\x00", - "\x00\x00\x00\x00\x00\x00\x00\x00", - "\x00\x00", - treeid, - processid, - userid, - multiplex_id, - ] + # Initialize the netbios header + netbios = [ + b"\x00", # 'Message_Type' + b"\x00\x00\x47" # 'Length' + ] - # Define the trans2 request section of the SMB request - trans2_request = [ - "\x0f", - "\x0c\x00", - "\x00\x00", - "\x01\x00", - "\x00\x00", - "\x00", - "\x00", - "\x00\x00", - "\xa6\xd9\xa4\x00", - "\x00\x00", - "\x0c\x00", - "\x42\x00", - "\x00\x00", - "\x4e\x00", - "\x01", - "\x00", - "\x0e\x00", - "\x00\x00", - "\x0c\x00" + "\x00" * 12, - ] + # Initialize the SMB header + smb_header = [ + b"\xFF\x53\x4D\x42", # server_compnent: .SMB + b"\x75", # smb_command: Tree Connect AndX + b"\x00\x00\x00\x00", # 'nt_status' + b"\x18", # 'flags' + b"\x01\x20", # 'flags2' + b"\x00\x00", # 'process_id_high' + b"\x00\x00\x00\x00\x00\x00\x00\x00", # 'signature' + b"\x00\x00", # 'reserved' + b"\x00\x00", # 'tree_id' + b"\x2F\x4B", # 'process_id' + userid, # 'user_id' + b"\xC5\x5E", # 'multiplex_id' + ] - # Generate the SMB protocol payload by combining the netbios, smb_header, and trans2_request sections - return generate_smb_proto_payload(netbios, smb_header, trans2_request) + # Create the IPC string + ipc = f"\\\\{ip}\IPC$\x00" + self.logger.debug(f"Connecting to {ip} with UID: {userid.hex()}") + + # Initialize the tree connect andx request + tree_connect_andx_request = [ + b"\x04", # Word Count + b"\xFF", # AndXCommand: No further commands + b"\x00", # Reserved + b"\x00\x00", # AndXOffset + b"\x00\x00", # Flags + b"\x01\x00", # Password Length + b"\x1A\x00", # Byte Count + b"\x00", # Password + ipc.encode(), # \\xxx.xxx.xxx.xxx\IPC$ + b"\x3f\x3f\x3f\x3f\x3f\x00", # Service + ] + + # Calculate the length of the payload + length = len(b"".join(smb_header)) + len(b"".join(tree_connect_andx_request)) + self.logger.debug(f"Length of payload: {length}") + + # Update the length in the netbios header + netbios[1] = struct.pack(">L", length)[-3:] + + self.logger.debug(f"Netbios: {netbios}") + self.logger.debug(f"SMB Header: {smb_header}") + self.logger.debug(f"Tree Connect AndX Request: {tree_connect_andx_request}") + + # Generate the final SMB protocol payload + return self.generate_smb_proto_payload(netbios, smb_header, tree_connect_andx_request) -def check(ip, port=445): - """Check if MS17_010 SMB Vulnerability exists. + def peeknamedpipe_request(self, treeid, processid, userid, multiplex_id): + """ + Generate tran2 request. - Args: - ---- - ip (str): The IP address of the target machine. - port (int, optional): The port number to connect to. Defaults to 445. + Args: + ---- + treeid (str): The tree ID. + processid (str): The process ID. + userid (str): The user ID. + multiplex_id (str): The multiplex ID. - Returns: - ------- - bool: True if the vulnerability exists, False otherwise. - """ - try: + Returns: + ------- + str: The generated SMB protocol payload. + """ + self.logger.debug("generate peeknamedpipe request") + + # Set the necessary values for the netbios header + netbios = [ + b"\x00", # message type + b"\x00\x00\x4a" # length + ] + + # Set the values for the SMB header + smb_header = [ + b"\xFF\x53\x4D\x42", # Server Component: .SMB + b"\x25", # SMB Command: Trans2 + b"\x00\x00\x00\x00", # NT Status + b"\x18", # flags + b"\x01\x28", # flags2 + b"\x00\x00", # pid high + b"\x00\x00\x00\x00\x00\x00\x00\x00", # sig + b"\x00\x00", # Reserved + treeid, # Tree ID + processid, # Process ID + userid, # User ID + multiplex_id, # Multiplex ID + ] + + # Set the values for the transaction request + tran_request = [ + b"\x10", # Word Count + b"\x00\x00", # Total Parameter Count + b"\x00\x00", # Total Data Count + b"\xff\xff", # Max Parameter Count + b"\xff\xff", # Max Data Count + b"\x00", # Max Setup Count + b"\x00", # Reserved + b"\x00\x00", # Flags + b"\x00\x00\x00\x00", # Timeout + b"\x00\x00", # Reserved + b"\x00\x00", # Parameter Count + b"\x4a\x00", # Parameter Offset + b"\x00\x00", # Data Count + b"\x4a\x00", # Data Offset + b"\x02", # Setup Count + b"\x00", # Reserved + b"\x23\x00", # SMB Pipe Protocol: Function: PeekNamedPipe (0x0023) + b"\x00\x00", # SMB Pipe Protocol: FID + b"\x07\x00", + b"\x5c\x50\x49\x50\x45\x5c\x00", # \PIPE\ + ] + + return self.generate_smb_proto_payload(netbios, smb_header, tran_request) + + + def trans2_request(self, treeid, processid, userid, multiplex_id): + """Generate trans2 request. + + Args: + ---- + treeid: The treeid parameter. + processid: The processid parameter. + userid: The userid parameter. + multiplex_id: The multiplex_id parameter. + + Returns: + ------- + The generated SMB protocol payload. + """ + self.logger.debug("generate trans2 request") + + # Define the netbios section of the SMB request + netbios = [ + b"\x00", + b"\x00\x00\x4f" + ] + + # Define the SMB header section of the SMB request + smb_header = [ + b"\xFF\x53\x4D\x42", # 'server_component': .SMB + b"\x32", # 'smb_command': Trans2 + b"\x00\x00\x00\x00", + b"\x18", + b"\x07\xc0", + b"\x00\x00", + b"\x00\x00\x00\x00\x00\x00\x00\x00", + b"\x00\x00", + treeid, + processid, + userid, + multiplex_id, + ] + + # Define the trans2 request section of the SMB request + trans2_request = [ + b"\x0f", + b"\x0c\x00", + b"\x00\x00", + b"\x01\x00", + b"\x00\x00", + b"\x00", + b"\x00", + b"\x00\x00", + b"\xa6\xd9\xa4\x00", # Timeout: 3 hours, 3.622 seconds + b"\x00\x00", + b"\x0c\x00", + b"\x42\x00", + b"\x00\x00", + b"\x4e\x00", + b"\x01", + b"\x00", + b"\x0e\x00", # subcommand: SESSION_SETUP + b"\x00\x00", + b"\x0c\x00" + b"\x00" * 12, + ] + + return self.generate_smb_proto_payload(netbios, smb_header, trans2_request) + + + def check(self, ip, port=445): + """Check if MS17_010 SMB Vulnerability exists. + + Args: + ---- + ip (str): The IP address of the target machine. + port (int, optional): The port number to connect to. Defaults to 445. + + Returns: + ------- + bool: True if the vulnerability exists, False otherwise. + """ buffersize = 1024 timeout = 5.0 - # Create a socket and connect to the target IP and port + # Send smb request based on socket. client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.settimeout(timeout) client.connect((ip, port)) - # Send negotiate protocol request and receive response - raw_proto = negotiate_proto_request() + # SMB - Negotiate Protocol Request + raw_proto = self.negotiate_proto_request() client.send(raw_proto) tcp_response = client.recv(buffersize) - # Send session setup request and receive response - raw_proto = session_setup_andx_request() + # SMB - Session Setup AndX Request + raw_proto = self.session_setup_andx_request() client.send(raw_proto) tcp_response = client.recv(buffersize) + tcp_response[:4] - smb_header = tcp_response[4:36] + smb_header = tcp_response[4:36] # SMB Header: 32 bytes smb = SmbHeader(smb_header) user_id = struct.pack(" 0: self.logger.display(f"Total of records returned {len(answers):d}") - TGT = KerberosAttacks(self).get_tgt_kerberoasting() - dejavue = [] - for (_SPN, sAMAccountName, memberOf, pwdLastSet, lastLogon, _delegation) in answers: - if sAMAccountName not in dejavue: - downLevelLogonName = self.targetDomain + "\\" + sAMAccountName + TGT = KerberosAttacks(self).get_tgt_kerberoasting(self.use_kcache) + self.logger.debug(f"TGT: {TGT}") + if TGT: + dejavue = [] + for (_SPN, sAMAccountName, memberOf, pwdLastSet, lastLogon, _delegation) in answers: + if sAMAccountName not in dejavue: + downLevelLogonName = self.targetDomain + "\\" + sAMAccountName - try: - principalName = Principal() - principalName.type = constants.PrincipalNameType.NT_MS_PRINCIPAL.value - principalName.components = [downLevelLogonName] + try: + principalName = Principal() + principalName.type = constants.PrincipalNameType.NT_MS_PRINCIPAL.value + principalName.components = [downLevelLogonName] - tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS( - principalName, - self.domain, - self.kdcHost, - TGT["KDC_REP"], - TGT["cipher"], - TGT["session_key"], - ) - r = KerberosAttacks(self).output_tgs( - tgs, - oldSessionKey, - sessionKey, - sAMAccountName, - self.targetDomain + "/" + sAMAccountName, - ) - self.logger.highlight(f"sAMAccountName: {sAMAccountName} memberOf: {memberOf} pwdLastSet: {pwdLastSet} lastLogon:{lastLogon}") - self.logger.highlight(f"{r}") - with open(self.args.kerberoasting, "a+") as hash_kerberoasting: - hash_kerberoasting.write(r + "\n") - dejavue.append(sAMAccountName) - except Exception as e: - self.logger.debug("Exception:", exc_info=True) - nxc_logger.error(f"Principal: {downLevelLogonName} - {e}") - return True + tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS( + principalName, + self.domain, + self.kdcHost, + TGT["KDC_REP"], + TGT["cipher"], + TGT["sessionKey"], + ) + r = KerberosAttacks(self).output_tgs( + tgs, + oldSessionKey, + sessionKey, + sAMAccountName, + self.targetDomain + "/" + sAMAccountName, + ) + self.logger.highlight(f"sAMAccountName: {sAMAccountName} memberOf: {memberOf} pwdLastSet: {pwdLastSet} lastLogon:{lastLogon}") + self.logger.highlight(f"{r}") + if self.args.kerberoasting: + with open(self.args.kerberoasting, "a+") as hash_kerberoasting: + hash_kerberoasting.write(r + "\n") + dejavue.append(sAMAccountName) + except Exception as e: + self.logger.debug("Exception:", exc_info=True) + self.logger.fail(f"Principal: {downLevelLogonName} - {e}") + return True + else: + self.logger.fail(f"Error retrieving TGT for {self.username}\\{self.domain} from {self.kdcHost}") else: self.logger.highlight("No entries found!") self.logger.fail("Error with the LDAP account used") diff --git a/nxc/protocols/ldap/kerberos.py b/nxc/protocols/ldap/kerberos.py index 371e5f38..36d0bc02 100644 --- a/nxc/protocols/ldap/kerberos.py +++ b/nxc/protocols/ldap/kerberos.py @@ -103,22 +103,27 @@ class KerberosAttacks: return entry - def get_tgt_kerberoasting(self): - try: - ccache = CCache.loadFile(getenv("KRB5CCNAME")) - # retrieve user and domain information from CCache file if needed - domain = ccache.principal.realm["data"] if self.domain == "" else self.domain - nxc_logger.debug(f"Using Kerberos Cache: {getenv('KRB5CCNAME')}") - principal = f"krbtgt/{domain.upper()}@{domain.upper()}" - creds = ccache.getCredential(principal) - if creds is not None: - tgt = creds.toTGT() - nxc_logger.debug("Using TGT from cache") - return tgt + def get_tgt_kerberoasting(self, kcache=None): + if kcache: + if getenv("KRB5CCNAME"): + nxc_logger.debug("KRB5CCNAME environment variable exists, attempting to use that...") + try: + ccache = CCache.loadFile(getenv("KRB5CCNAME")) + # retrieve user and domain information from CCache file if needed + domain = ccache.principal.realm["data"] if self.domain == "" else self.domain + nxc_logger.debug(f"Using Kerberos Cache: {getenv('KRB5CCNAME')}") + principal = f"krbtgt/{domain.upper()}@{domain.upper()}" + creds = ccache.getCredential(principal) + if creds is not None: + tgt = creds.toTGT() + nxc_logger.debug("Using TGT from cache") + return tgt + else: + nxc_logger.debug("No valid credentials found in cache") + except Exception: + pass else: - nxc_logger.debug("No valid credentials found in cache. ") - except Exception: - pass + nxc_logger.fail("KRB5CCNAME environment variable not found, unable to use Kerberos Cache") # No TGT in cache, request it user_name = Principal(self.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) @@ -138,6 +143,12 @@ class KerberosAttacks: self.aesKey, kdcHost=self.kdcHost, ) + except OSError as e: + if e.errno == 113: + nxc_logger.fail(f"Unable to resolve KDC hostname: {e!s}") + else: + nxc_logger.fail(f"Some other OSError occured: {e!s}") + return None except Exception as e: nxc_logger.debug(f"TGT: {e!s}") tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT( @@ -149,7 +160,6 @@ class KerberosAttacks: self.aesKey, kdcHost=self.kdcHost, ) - else: tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT( user_name, @@ -160,12 +170,12 @@ class KerberosAttacks: self.aesKey, kdcHost=self.kdcHost, ) - tgt = {} - tgt["KDC_REP"] = tgt - tgt["cipher"] = cipher - tgt["session_key"] = sessionKey - - return tgt + tgt_data = {} + tgt_data["KDC_REP"] = tgt + tgt_data["cipher"] = cipher + tgt_data["sessionKey"] = sessionKey + nxc_logger.debug(f"Final TGT: {tgt_data}") + return tgt_data def get_tgt_asroast(self, userName, requestPAC=True): client_name = Principal(userName, type=constants.PrincipalNameType.NT_PRINCIPAL.value) diff --git a/nxc/protocols/ldap/proto_args.py b/nxc/protocols/ldap/proto_args.py index b4b53e8e..0feacec3 100644 --- a/nxc/protocols/ldap/proto_args.py +++ b/nxc/protocols/ldap/proto_args.py @@ -13,8 +13,8 @@ def proto_args(parser, std_parser, module_parser): 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("--asreproast", help="Output AS_REP response to crack with hashcat to file") + egroup.add_argument("--kerberoasting", help="Output TGS ticket to crack with hashcat to file") 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") diff --git a/nxc/protocols/winrm.py b/nxc/protocols/winrm.py index 688522ce..17c8bb9c 100644 --- a/nxc/protocols/winrm.py +++ b/nxc/protocols/winrm.py @@ -4,6 +4,7 @@ import os import requests import urllib3 import contextlib +import logging import xml.etree.ElementTree as ET from io import StringIO @@ -38,6 +39,8 @@ class winrm(connection): connection.__init__(self, args, db, host) def proto_logger(self): + logging.getLogger("pypsrp").disabled = True + logging.getLogger("pypsrp.wsman").disabled = True self.logger = NXCAdapter( extra={ "protocol": "WINRM", @@ -242,6 +245,7 @@ class winrm(connection): try: self.conn = Client( self.host, + port=self.port, auth="ntlm", username=f"{self.domain}\\{self.username}", password=self.password, @@ -291,6 +295,7 @@ class winrm(connection): try: self.conn = Client( self.host, + port=self.port, auth="ntlm", username=f"{self.domain}\\{self.username}", password=f"{self.lmhash}:{self.nthash}",