NetExec/nxc/modules/ms17-010.py

502 lines
16 KiB
Python
Raw Normal View History

2023-05-02 15:17:59 +00:00
# All credits to https://github.com/d4t4s3c/Win7Blue
2021-12-18 20:28:34 +00:00
# @d4t4s3c
2023-05-02 15:17:59 +00:00
# Module by @mpgn_x64
2021-12-18 20:28:34 +00:00
from ctypes import c_uint8, c_uint16, c_uint32, c_uint64, Structure
2021-12-18 20:28:34 +00:00
import socket
import struct
from nxc.logger import nxc_logger
2021-12-18 20:28:34 +00:00
class NXCModule:
2023-05-02 15:17:59 +00:00
name = "ms17-010"
description = "MS17-010 - EternalBlue - NOT TESTED OUTSIDE LAB ENVIRONMENT"
2023-05-02 15:17:59 +00:00
supported_protocols = ["smb"]
2021-12-18 20:28:34 +00:00
opsec_safe = True
multiple_hosts = True
def options(self, context, module_options):
2023-05-02 15:17:59 +00:00
""" """
2021-12-18 20:28:34 +00:00
def on_login(self, context, connection):
if check(connection.host):
context.log.highlight("VULNERABLE")
2023-05-08 18:39:36 +00:00
context.log.highlight("Next step: https://www.rapid7.com/db/modules/exploit/windows/smb/ms17_010_eternalblue/")
2023-05-02 15:17:59 +00:00
2021-12-18 20:28:34 +00:00
class SmbHeader(Structure):
2023-05-02 15:17:59 +00:00
"""SMB Header decoder."""
_pack_ = 1
_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),
2023-05-02 15:17:59 +00:00
]
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)
2023-05-02 15:17:59 +00:00
2021-12-18 20:28:34 +00:00
def generate_smb_proto_payload(*protos):
"""
Flattens a nested list and merges all bytes objects into a single bytes object.
Args:
2023-10-12 19:13:16 +00:00
----
*protos (list): The list to flatten and merge.
Returns:
2023-10-12 19:13:16 +00:00
-------
bytes: The merged bytes object.
"""
nxc_logger.debug("generate smb proto payload")
nxc_logger.debug(f"Protos: {protos}")
hex_data = b""
2021-12-18 20:28:34 +00:00
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
2021-12-18 20:28:34 +00:00
def calculate_doublepulsar_xor_key(self, s):
"""
Calculate Doublepulsar Xor Key.
Args:
2023-10-12 19:13:16 +00:00
----
s (int): The input value.
Returns:
2023-10-12 19:13:16 +00:00
-------
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
2021-12-18 20:28:34 +00:00
def negotiate_proto_request():
2023-05-02 15:17:59 +00:00
"""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
2021-12-18 20:28:34 +00:00
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
2021-12-18 20:28:34 +00:00
]
# Define the negotiate_proto_request
2021-12-18 20:28:34 +00:00
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
2021-12-18 20:28:34 +00:00
]
# Return the generated SMB protocol payload
2021-12-18 20:28:34 +00:00
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
]
2021-12-18 20:28:34 +00:00
# Define the SMB header bytes
2021-12-18 20:28:34 +00:00
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
2021-12-18 20:28:34 +00:00
]
# Define the session setup andx request bytes
2021-12-18 20:28:34 +00:00
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
2021-12-18 20:28:34 +00:00
]
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:
2023-10-12 19:13:16 +00:00
----
ip (str): The IP address.
userid (str): The user ID.
2021-12-18 20:28:34 +00:00
Returns:
2023-10-12 19:13:16 +00:00
-------
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
2021-12-18 20:28:34 +00:00
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'
2021-12-18 20:28:34 +00:00
]
# Create the IPC string
ipc = f"\\\\{ip}\IPC$\x00"
nxc_logger.debug(f"Connecting to {ip} with UID: {userid.hex()}")
2021-12-18 20:28:34 +00:00
# Initialize the tree connect andx request
2021-12-18 20:28:34 +00:00
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
2021-12-18 20:28:34 +00:00
]
# 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}")
2021-12-18 20:28:34 +00:00
# Update the length in the netbios header
2021-12-18 20:28:34 +00:00
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
2021-12-18 20:28:34 +00:00
return generate_smb_proto_payload(netbios, smb_header, tree_connect_andx_request)
def peeknamedpipe_request(treeid, processid, userid, multiplex_id):
"""
Generate tran2 request.
Args:
2023-10-12 19:13:16 +00:00
----
treeid (str): The tree ID.
processid (str): The process ID.
userid (str): The user ID.
multiplex_id (str): The multiplex ID.
Returns:
2023-10-12 19:13:16 +00:00
-------
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
]
2021-12-18 20:28:34 +00:00
# Set the values for the SMB header
2021-12-18 20:28:34 +00:00
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
2021-12-18 20:28:34 +00:00
]
# Set the values for the transaction request
2021-12-18 20:28:34 +00:00
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\
2021-12-18 20:28:34 +00:00
]
return generate_smb_proto_payload(netbios, smb_header, tran_request)
def trans2_request(treeid, processid, userid, multiplex_id):
"""Generate trans2 request.
Args:
2023-10-12 19:13:16 +00:00
----
treeid: The treeid parameter.
processid: The processid parameter.
userid: The userid parameter.
multiplex_id: The multiplex_id parameter.
2021-12-18 20:28:34 +00:00
Returns:
2023-10-12 19:13:16 +00:00
-------
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"
]
2021-12-18 20:28:34 +00:00
# Define the SMB header section of the SMB request
2021-12-18 20:28:34 +00:00
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",
2023-05-02 15:17:59 +00:00
treeid,
processid,
userid,
multiplex_id,
2021-12-18 20:28:34 +00:00
]
# Define the trans2 request section of the SMB request
2021-12-18 20:28:34 +00:00
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,
2021-12-18 20:28:34 +00:00
]
return generate_smb_proto_payload(netbios, smb_header, trans2_request)
2023-05-02 15:17:59 +00:00
2021-12-18 20:28:34 +00:00
def check(ip, port=445):
"""Check if MS17_010 SMB Vulnerability exists.
Args:
2023-10-12 19:13:16 +00:00
----
ip (str): The IP address of the target machine.
port (int, optional): The port number to connect to. Defaults to 445.
Returns:
2023-10-12 19:13:16 +00:00
-------
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("<H", smb.user_id)
# parse native_os from Session Setup Andx Response
session_setup_andx_response = tcp_response[36:]
native_os = session_setup_andx_response[9:].split(b"\x00")[0]
# SMB - Tree Connect AndX Request
raw_proto = tree_connect_andx_request(ip, user_id)
nxc_logger.debug(f"Raw proto after tree connect andx request: {raw_proto}")
nxc_logger.debug(f"Raw proto length: {len(raw_proto)}")
client.send(raw_proto)
tcp_response = client.recv(buffersize)
nxc_logger.debug(f"TCP response after tree connect andx request: {tcp_response}")
netbios = tcp_response[:4]
smb_header = tcp_response[4:36] # SMB Header: 32 bytes
smb = SmbHeader(smb_header)
tree_id = struct.pack("<H", smb.tree_id)
process_id = struct.pack("<H", smb.process_id)
user_id = struct.pack("<H", smb.user_id)
multiplex_id = struct.pack("<H", smb.multiplex_id)
# SMB - PeekNamedPipe Request
raw_proto = peeknamedpipe_request(tree_id, process_id, user_id, multiplex_id)
client.send(raw_proto)
tcp_response = client.recv(buffersize)
netbios = tcp_response[:4]
smb_header = tcp_response[4:36]
smb = SmbHeader(smb_header)
# nt_status = smb_header[5:9]
nt_status = struct.pack("BBH", smb.error_class, smb.reserved1, smb.error_code)
nxc_logger.debug(f"NT Status: {nt_status}")
# 0xC0000205 - STATUS_INSUFF_SERVER_RESOURCES - vulnerable
# 0xC0000008 - STATUS_INVALID_HANDLE
# 0xC0000022 - STATUS_ACCESS_DENIED
if nt_status == b"\x05\x02\x00\xc0":
nxc_logger.highlight(f"[+] [{ip}] is likely VULNERABLE to MS17-010! ({native_os.decode()})")
# vulnerable to MS17-010, check for DoublePulsar infection
raw_proto = trans2_request(tree_id, process_id, user_id, multiplex_id)
2021-12-18 20:28:34 +00:00
client.send(raw_proto)
tcp_response = client.recv(buffersize)
netbios = tcp_response[:4]
2021-12-18 20:28:34 +00:00
smb_header = tcp_response[4:36]
smb = SmbHeader(smb_header)
2021-12-18 20:28:34 +00:00
if smb.multiplex_id == 0x0051:
key = calculate_doublepulsar_xor_key(smb.signature)
nxc_logger.highlight(f"Host is likely INFECTED with DoublePulsar! - XOR Key: {key.decode()}")
elif nt_status in (b"\x08\x00\x00\xc0", b"\x22\x00\x00\xc0"):
nxc_logger.fail(f"[-] [{ip}] does NOT appear vulnerable")
else:
nxc_logger.fail(f"[-] [{ip}] Unable to detect if this host is vulnerable")
2021-12-18 20:28:34 +00:00
client.close()