|
| 1 | +# Copyright (c) 2025-Present MatrixEditor |
| 2 | +# |
| 3 | +# Permission is hereby granted, free of charge, to any person obtaining a copy |
| 4 | +# of this software and associated documentation files (the "Software"), to deal |
| 5 | +# in the Software without restriction, including without limitation the rights |
| 6 | +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 7 | +# copies of the Software, and to permit persons to whom the Software is |
| 8 | +# furnished to do so, subject to the following conditions: |
| 9 | +# |
| 10 | +# The above copyright notice and this permission notice shall be included in all |
| 11 | +# copies or substantial portions of the Software. |
| 12 | +# |
| 13 | +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 14 | +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 15 | +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 16 | +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 17 | +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 18 | +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 19 | +# SOFTWARE. |
| 20 | +"""Remote Mailslot Protocol implementation per [MS-MAIL]. |
| 21 | +
|
| 22 | +This module implements the Remote Mailslot Protocol as specified in |
| 23 | +[MS-MAIL] (Remote Mailslot Protocol). Mailslots provide a one-way |
| 24 | +interprocess communication mechanism for connectionless, unreliable |
| 25 | +message delivery over SMB. |
| 26 | +
|
| 27 | +The protocol is used extensively in Windows networking for service |
| 28 | +discovery, domain controller location, and browser service announcements. |
| 29 | +""" |
| 30 | + |
| 31 | +from impacket.smb import SMB |
| 32 | +from scapy.layers import netbios, smb |
| 33 | + |
| 34 | + |
| 35 | +# =========================================================================== |
| 36 | +# Constants |
| 37 | +# =========================================================================== |
| 38 | +# Mailslot operation codes per [MS-MAIL] § 2.2.1 |
| 39 | +MAILSLOT_WRITE = 0x0001 # Write to mailslot |
| 40 | + |
| 41 | +# Mailslot class values per [MS-MAIL] § 2.2.1 |
| 42 | +MAILSLOT_CLASS_UNRELIABLE = 0x0002 # Unreliable (connectionless) |
| 43 | + |
| 44 | +# Common mailslot names per [MS-MAIL] § 1.3 |
| 45 | +MAILSLOT_NETLOGON = "\\MAILSLOT\\NET\\NETLOGON" |
| 46 | +MAILSLOT_BROWSE = "\\MAILSLOT\\BROWSE" |
| 47 | +MAILSLOT_LANMAN = "\\MAILSLOT\\LANMAN" |
| 48 | + |
| 49 | +# NBT datagram types per [RFC1002] § 4.4.1 |
| 50 | +NBT_DIRECT_UNIQUE = 0x10 # Direct unique datagram |
| 51 | +NBT_DIRECT_GROUP = 0x11 # Direct group datagram |
| 52 | +NBT_BROADCAST = 0x12 # Broadcast datagram |
| 53 | + |
| 54 | +# NBT datagram flags per [RFC1002] § 4.4.1 |
| 55 | +# SNT (Source Node Type) field values: |
| 56 | +NBT_SNT_B_NODE = 0x00 # B-node (broadcast) |
| 57 | +NBT_SNT_P_NODE = 0x01 # P-node (point-to-point) |
| 58 | +NBT_SNT_M_NODE = 0x02 # M-node (mixed) |
| 59 | +NBT_SNT_H_NODE = 0x03 # H-node (hybrid) |
| 60 | + |
| 61 | +# Flag bits: |
| 62 | +NBT_FLAG_FIRST = 0x02 # First packet |
| 63 | +NBT_FLAG_MORE = 0x01 # More fragments follow |
| 64 | + |
| 65 | + |
| 66 | +# =========================================================================== |
| 67 | +# Mailslot Message Construction |
| 68 | +# =========================================================================== |
| 69 | +def build_mailslot_write( |
| 70 | + mailslot_name: str, |
| 71 | + data: bytes, |
| 72 | + mailslot_class: int = MAILSLOT_CLASS_UNRELIABLE, |
| 73 | + priority: int = 0, |
| 74 | +) -> smb.SMBMailslot_Write: |
| 75 | + r""" |
| 76 | + Build an SMB mailslot write transaction per [MS-MAIL] § 2.2.1. |
| 77 | +
|
| 78 | + Constructs an SMB_COM_TRANSACTION request with mailslot-specific |
| 79 | + setup words and parameters. The transaction writes data to a named |
| 80 | + mailslot on the target system. |
| 81 | +
|
| 82 | + Per [MS-MAIL] § 2.2.1, the SMB_COM_TRANSACTION request contains: |
| 83 | + - Setup words: [MailSlotOpcode, Priority, Class] |
| 84 | + - Name: Mailslot name (null-terminated ASCII) |
| 85 | + - Data: Message payload |
| 86 | +
|
| 87 | + Product Behavior Notes (per [MS-MAIL] § 2.2.1): |
| 88 | + - <3> Windows sets SMB_Header.Flags to 0x00 |
| 89 | + - <4> Windows sets SMB_Header.Flags2 to 0x00 |
| 90 | + - <5> Windows sets SMB_Header.PIDLow to 0x0000 |
| 91 | +
|
| 92 | + :param mailslot_name: Name of the mailslot (e.g., "\\\\MAILSLOT\\\\NET\\\\NETLOGON") |
| 93 | + :type mailslot_name: str |
| 94 | + :param data: Message payload to write to the mailslot |
| 95 | + :type data: bytes |
| 96 | + :param mailslot_class: Mailslot class (1=reliable, 2=unreliable), defaults to 2 |
| 97 | + :type mailslot_class: int |
| 98 | + :param priority: Message priority (0-9), defaults to 0 |
| 99 | + :type priority: int |
| 100 | + :return: SMB mailslot write transaction |
| 101 | + :rtype: smb.SMBMailslot_Write |
| 102 | +
|
| 103 | + Example: |
| 104 | + >>> from dementor.protocols import mailslot |
| 105 | + >>> msg = mailslot.build_mailslot_write( |
| 106 | + ... mailslot.MAILSLOT_NETLOGON, |
| 107 | + ... b"\\x07\\x00...", # NETLOGON message |
| 108 | + ... ) |
| 109 | + >>> bytes(msg) |
| 110 | + b'\\x11\\x00...' |
| 111 | +
|
| 112 | + """ |
| 113 | + # Setup words per [MS-MAIL] § 2.2.1: |
| 114 | + # - Setup[0]: MailSlotOpcode (0x0001 = Write) |
| 115 | + # - Setup[1]: Priority (0-9) |
| 116 | + # - Setup[2]: Class (1=reliable, 2=unreliable) |
| 117 | + setup_words = [MAILSLOT_WRITE, priority, mailslot_class] |
| 118 | + |
| 119 | + # Mailslot name must be null-terminated ASCII per [MS-MAIL] § 2.2.1 |
| 120 | + mailslot_name_bytes = mailslot_name.encode("ascii") + b"\x00" |
| 121 | + |
| 122 | + # Build the SMB_COM_TRANSACTION mailslot write |
| 123 | + # Per [MS-MAIL] § 2.2.1, the Buffer field contains: |
| 124 | + # - ("Parameter", b"") - Empty parameter block |
| 125 | + # - ("Data", data) - Message payload |
| 126 | + return smb.SMBMailslot_Write( |
| 127 | + SetupCount=3, |
| 128 | + Setup=setup_words, |
| 129 | + Name=mailslot_name_bytes, |
| 130 | + Buffer=[ |
| 131 | + ("Parameter", b""), |
| 132 | + ("Data", data), |
| 133 | + ], |
| 134 | + ) |
| 135 | + |
| 136 | + |
| 137 | +def build_smb_header( |
| 138 | + command: int = SMB.SMB_COM_TRANSACTION, |
| 139 | + flags: int = 0x00, |
| 140 | + flags2: int = 0x00, |
| 141 | + tid: int = 0x0000, |
| 142 | + pid_low: int = 0xFEFF, |
| 143 | + uid: int = 0x0000, |
| 144 | + mid: int = 0x0000, |
| 145 | +) -> smb.SMB_Header: |
| 146 | + """ |
| 147 | + Build an SMB header for mailslot transactions per [MS-MAIL] § 2.2.1. |
| 148 | +
|
| 149 | + Per [MS-MAIL] § 2.2.1 product behavior notes: |
| 150 | + - <3> Windows sets Flags to 0x00 |
| 151 | + - <4> Windows sets Flags2 to 0x00 |
| 152 | + - <5> Windows sets PIDLow to 0xFEFF |
| 153 | +
|
| 154 | + :param command: SMB command code, defaults to 0x25 (SMB_COM_TRANSACTION) |
| 155 | + :type command: int |
| 156 | + :param flags: SMB flags, defaults to 0x00 |
| 157 | + :type flags: int |
| 158 | + :param flags2: SMB flags2, defaults to 0x00 |
| 159 | + :type flags2: int |
| 160 | + :param tid: Tree ID, defaults to 0x0000 |
| 161 | + :type tid: int |
| 162 | + :param pid_low: Process ID (low word), defaults to 0xFEFF |
| 163 | + :type pid_low: int |
| 164 | + :param uid: User ID, defaults to 0x0000 |
| 165 | + :type uid: int |
| 166 | + :param mid: Multiplex ID, defaults to 0x0000 |
| 167 | + :type mid: int |
| 168 | + :return: SMB header |
| 169 | + :rtype: smb.SMB_Header |
| 170 | + """ |
| 171 | + return smb.SMB_Header( |
| 172 | + Command=command, |
| 173 | + Flags=flags, |
| 174 | + Flags2=flags2, |
| 175 | + TID=tid, |
| 176 | + PIDLow=pid_low, |
| 177 | + UID=uid, |
| 178 | + MID=mid, |
| 179 | + ) |
| 180 | + |
| 181 | + |
| 182 | +# =========================================================================== |
| 183 | +# NBT Datagram Construction |
| 184 | +# =========================================================================== |
| 185 | +def build_nbt_datagram( |
| 186 | + source_name: str, |
| 187 | + destination_name: str, |
| 188 | + source_ip: str, |
| 189 | + smb_packet: smb.SMB_Header | smb.SMBMailslot_Write, |
| 190 | + datagram_type: int = NBT_DIRECT_GROUP, |
| 191 | + source_port: int = 138, |
| 192 | + node_type: int = NBT_SNT_H_NODE, |
| 193 | + first_packet: bool = True, |
| 194 | + more_fragments: bool = False, |
| 195 | +) -> netbios.NBTDatagram: |
| 196 | + """ |
| 197 | + Build an NBT datagram for mailslot message delivery per [RFC1002] § 4.4.1. |
| 198 | +
|
| 199 | + NetBIOS over TCP/UDP (NBT) datagrams provide the transport layer for |
| 200 | + mailslot messages. The datagram encapsulates the SMB mailslot write |
| 201 | + transaction and delivers it to the target NetBIOS name. |
| 202 | +
|
| 203 | + Per [RFC1002] § 4.4.1, the NBT datagram header contains: |
| 204 | + - MSG_TYPE: Datagram type (DIRECT_UNIQUE, DIRECT_GROUP, BROADCAST) |
| 205 | + - FLAGS: Source node type and fragmentation flags |
| 206 | + - DGM_ID: Datagram identifier |
| 207 | + - SOURCE_IP: IP address of sender |
| 208 | + - SOURCE_PORT: UDP port of sender (typically 138) |
| 209 | + - SOURCE_NAME: NetBIOS name of sender (16 bytes, encoded) |
| 210 | + - DESTINATION_NAME: NetBIOS name of recipient (16 bytes, encoded) |
| 211 | +
|
| 212 | + Per [MS-MAIL] § 3.1.4.1 product behavior note <13>: |
| 213 | + - Windows uses DIRECT_GROUP (0x11) for mailslot broadcasts |
| 214 | + - Source node type is typically H-node (0x03) |
| 215 | + - First packet flag (F) is set to 1 |
| 216 | + - More fragments flag (M) is set to 0 for single-packet messages |
| 217 | +
|
| 218 | + :param source_name: NetBIOS name of sender (max 15 chars) |
| 219 | + :type source_name: str |
| 220 | + :param destination_name: NetBIOS name of recipient (max 15 chars) |
| 221 | + :type destination_name: str |
| 222 | + :param source_ip: IP address of sender |
| 223 | + :type source_ip: str |
| 224 | + :param smb_packet: SMB packet to encapsulate |
| 225 | + :type smb_packet: smb.SMB_Header | smb.SMBMailslot_Write |
| 226 | + :param datagram_type: NBT datagram type, defaults to NBT_DIRECT_GROUP (0x11) |
| 227 | + :type datagram_type: int |
| 228 | + :param source_port: UDP source port, defaults to 138 |
| 229 | + :type source_port: int |
| 230 | + :param node_type: Source node type (B/P/M/H-node), defaults to H-node (0x03) |
| 231 | + :type node_type: int |
| 232 | + :param first_packet: First packet flag, defaults to True |
| 233 | + :type first_packet: bool |
| 234 | + :param more_fragments: More fragments flag, defaults to False |
| 235 | + :type more_fragments: bool |
| 236 | + :return: NBT datagram |
| 237 | + :rtype: netbios.NBTDatagram |
| 238 | + """ |
| 239 | + # Encode NetBIOS names per [RFC1001] § 14.1 |
| 240 | + # NetBIOS names are 16 bytes: 15 chars + 1 byte suffix |
| 241 | + # Pad with spaces to 15 chars, append null byte (0x00) for workstation |
| 242 | + source_nb_name = (source_name.upper()[:15].ljust(15) + "\x00").encode("ascii") |
| 243 | + dest_nb_name = (destination_name.upper()[:15].ljust(15) + "\x00").encode("ascii") |
| 244 | + |
| 245 | + # Build FLAGS field per [RFC1002] § 4.4.1 |
| 246 | + # Bits 0-1: SNT (Source Node Type) |
| 247 | + # Bit 2: F (First packet) |
| 248 | + # Bit 3: M (More fragments) |
| 249 | + flags = (node_type & 0x03) << 2 |
| 250 | + if first_packet: |
| 251 | + flags |= NBT_FLAG_FIRST |
| 252 | + if more_fragments: |
| 253 | + flags |= NBT_FLAG_MORE |
| 254 | + |
| 255 | + # Build NBT datagram per [RFC1002] § 4.4.1 |
| 256 | + datagram = netbios.NBTDatagram( |
| 257 | + Type=datagram_type, |
| 258 | + Flags=flags, |
| 259 | + SourceIP=source_ip, |
| 260 | + SourcePort=source_port, |
| 261 | + SourceName=source_nb_name, |
| 262 | + DestinationName=dest_nb_name, |
| 263 | + ) |
| 264 | + |
| 265 | + # Attach SMB packet as payload |
| 266 | + return datagram / smb_packet |
| 267 | + |
| 268 | + |
| 269 | +# =========================================================================== |
| 270 | +# High-Level API |
| 271 | +# =========================================================================== |
| 272 | +def mailslot_write( |
| 273 | + mailslot_name: str, |
| 274 | + data: bytes, |
| 275 | + source_name: str, |
| 276 | + destination_name: str, |
| 277 | + source_ip: str, |
| 278 | + mailslot_class: int = MAILSLOT_CLASS_UNRELIABLE, |
| 279 | + priority: int = 0, |
| 280 | +) -> bytes: |
| 281 | + r""" |
| 282 | + Build a complete mailslot message ready for transmission. |
| 283 | +
|
| 284 | + This is a high-level convenience function that combines all the steps |
| 285 | + required to build a mailslot message: |
| 286 | + 1. Build SMB mailslot write transaction per [MS-MAIL] § 2.2.1 |
| 287 | + 2. Build SMB header per [MS-MAIL] § 2.2.1 |
| 288 | + 3. Build NBT datagram per [RFC1002] § 4.4.1 |
| 289 | +
|
| 290 | + The resulting packet can be sent via UDP to port 138 on the target system. |
| 291 | +
|
| 292 | + :param mailslot_name: Name of the mailslot (e.g., "\\\\MAILSLOT\\\\NET\\\\NETLOGON") |
| 293 | + :type mailslot_name: str |
| 294 | + :param data: Message payload |
| 295 | + :type data: bytes |
| 296 | + :param source_name: NetBIOS name of sender (max 15 chars) |
| 297 | + :type source_name: str |
| 298 | + :param destination_name: NetBIOS name of recipient (max 15 chars) |
| 299 | + :type destination_name: str |
| 300 | + :param source_ip: IP address of sender |
| 301 | + :type source_ip: str |
| 302 | + :param mailslot_class: Mailslot class (1=reliable, 2=unreliable), defaults to 2 |
| 303 | + :type mailslot_class: int |
| 304 | + :param priority: Message priority (0-9), defaults to 0 |
| 305 | + :type priority: int |
| 306 | + :return: Complete mailslot message as bytes |
| 307 | + :rtype: bytes |
| 308 | + """ |
| 309 | + # Build SMB mailslot write transaction |
| 310 | + transaction = build_mailslot_write( |
| 311 | + mailslot_name=mailslot_name, |
| 312 | + data=data, |
| 313 | + mailslot_class=mailslot_class, |
| 314 | + priority=priority, |
| 315 | + ) |
| 316 | + |
| 317 | + # Build SMB header |
| 318 | + smb_header = build_smb_header() |
| 319 | + |
| 320 | + # Combine SMB header and transaction |
| 321 | + smb_packet = smb_header / transaction |
| 322 | + |
| 323 | + # Build NBT datagram |
| 324 | + datagram = build_nbt_datagram( |
| 325 | + source_name=source_name, |
| 326 | + destination_name=destination_name, |
| 327 | + source_ip=source_ip, |
| 328 | + smb_packet=smb_packet, |
| 329 | + ) |
| 330 | + |
| 331 | + # Return as bytes |
| 332 | + return bytes(datagram) |
0 commit comments