|
2 | 2 | import binascii |
3 | 3 | import os |
4 | 4 | import re |
| 5 | +import struct |
| 6 | +import ipaddress |
5 | 7 | from Cryptodome.Hash import MD4 |
6 | 8 |
|
7 | 9 | from impacket.smbconnection import SMBConnection, SessionError |
|
32 | 34 | from impacket.dcerpc.v5.dtypes import NULL |
33 | 35 | from impacket.dcerpc.v5.dcomrt import DCOMConnection |
34 | 36 | from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, IWbemLevel1Login |
35 | | -from impacket.smb3structs import FILE_SHARE_WRITE, FILE_SHARE_DELETE |
| 37 | +from impacket.smb3structs import FILE_SHARE_WRITE, FILE_SHARE_DELETE, SMB2_0_IOCTL_IS_FSCTL |
36 | 38 | from impacket.dcerpc.v5 import tsts as TSTS |
37 | 39 |
|
38 | 40 | from nxc.config import process_secret, host_info_colors |
@@ -1435,74 +1437,116 @@ def dir(self): |
1435 | 1437 | full_path = ntpath.join(self.args.dir, content.get_longname()) |
1436 | 1438 | self.logger.highlight(f"{'d' if content.is_directory() else 'f'}{'rw-' if content.is_readonly() > 0 else 'r--':<8}{content.get_filesize():<15}{ctime(float(content.get_mtime_epoch())):<30}{full_path:<45}") |
1437 | 1439 |
|
1438 | | - @requires_admin |
1439 | 1440 | def interfaces(self): |
1440 | 1441 | """ |
1441 | | - Retrieve the list of network interfaces info (Name, IP Address, Subnet Mask, Default Gateway) from remote Windows registry' |
1442 | | - Made by: @Sant0rryu, @NeffIsBack |
| 1442 | + Enumeratie active network interfaces via SMB |
| 1443 | + Made by Ilya Yatsenko (@fulc2um) |
1443 | 1444 | """ |
1444 | 1445 | try: |
1445 | | - remoteOps = RemoteOperations(self.conn, False) |
1446 | | - remoteOps.enableRegistry() |
1447 | | - |
1448 | | - if remoteOps._RemoteOperations__rrp: |
1449 | | - reg_handle = rrp.hOpenLocalMachine(remoteOps._RemoteOperations__rrp)["phKey"] |
1450 | | - key_handle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, reg_handle, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces")["phkResult"] |
1451 | | - sub_key_list = rrp.hBaseRegQueryInfoKey(remoteOps._RemoteOperations__rrp, key_handle)["lpcSubKeys"] |
1452 | | - sub_keys = [rrp.hBaseRegEnumKey(remoteOps._RemoteOperations__rrp, key_handle, i)["lpNameOut"][:-1] for i in range(sub_key_list)] |
1453 | | - |
1454 | | - self.logger.highlight(f"{'-Name-':<11} | {'-IP Address-':<15} | {'-SubnetMask-':<15} | {'-Gateway-':<15} | -DHCP-") |
1455 | | - for sub_key in sub_keys: |
1456 | | - interface = {} |
| 1446 | + self.logger.display("Starting network interface enumeration") |
| 1447 | + |
| 1448 | + tree_id = self.conn.connectTree("IPC$") |
| 1449 | + |
| 1450 | + FSCTL_QUERY_NETWORK_INTERFACE_INFO = 0x001401FC |
| 1451 | + |
| 1452 | + response = self.conn._SMBConnection.ioctl( |
| 1453 | + tree_id, |
| 1454 | + fileId=None, |
| 1455 | + ctlCode=FSCTL_QUERY_NETWORK_INTERFACE_INFO, |
| 1456 | + flags=SMB2_0_IOCTL_IS_FSCTL, |
| 1457 | + inputBlob=b"", |
| 1458 | + maxOutputResponse=8192 |
| 1459 | + ) |
| 1460 | + |
| 1461 | + if response: |
| 1462 | + self.logger.success("Retrieved network interface data") |
| 1463 | + |
| 1464 | + # Parse FSCTL_QUERY_NETWORK_INTERFACE_INFO response data |
| 1465 | + if not response: |
| 1466 | + self.logger.fail("No data to parse") |
| 1467 | + return |
| 1468 | + |
| 1469 | + # Parse and group interfaces |
| 1470 | + grouped_interfaces = {} |
| 1471 | + offset = 0 |
| 1472 | + |
| 1473 | + while offset < len(response) and offset + 152 <= len(response): |
1457 | 1474 | try: |
1458 | | - interface_key = f"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\{sub_key}" |
1459 | | - interface_handle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, reg_handle, interface_key)["phkResult"] |
1460 | | - |
1461 | | - # Retrieve Interace Name |
1462 | | - interface_name_key = f"SYSTEM\\ControlSet001\\Control\\Network\\{{4D36E972-E325-11CE-BFC1-08002BE10318}}\\{sub_key}\\Connection" |
1463 | | - interface_name_handle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, reg_handle, interface_name_key)["phkResult"] |
1464 | | - interface_name = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_name_handle, "Name")[1].rstrip("\x00") |
1465 | | - interface["Name"] = str(interface_name) |
1466 | | - if "Kernel" in interface_name: |
1467 | | - continue |
1468 | | - |
1469 | | - # Retrieve DHCP |
1470 | | - try: |
1471 | | - dhcp_enabled = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "EnableDHCP")[1] |
1472 | | - except DCERPCException: |
1473 | | - dhcp_enabled = False |
1474 | | - interface["DHCP"] = bool(dhcp_enabled) |
| 1475 | + # Parse NETWORK_INTERFACE_INFO structure |
| 1476 | + next_offset = struct.unpack("<L", response[offset:offset + 4])[0] |
| 1477 | + if_index = struct.unpack("<L", response[offset + 4:offset + 8])[0] |
| 1478 | + capabilities = struct.unpack("<L", response[offset + 8:offset + 12])[0] |
| 1479 | + link_speed = struct.unpack("<Q", response[offset + 16:offset + 24])[0] |
| 1480 | + |
| 1481 | + # Socket address (SockAddr_Storage at offset+24) |
| 1482 | + family = struct.unpack("<H", response[offset + 24:offset + 26])[0] |
| 1483 | + |
| 1484 | + if family == 0x0002: # IPv4 |
| 1485 | + ip_bytes = response[offset + 28:offset + 32] |
| 1486 | + ip_addr = ipaddress.IPv4Address(ip_bytes) |
| 1487 | + addr_info = f"IPv4: {ip_addr}" |
| 1488 | + elif family == 0x0017: # IPv6 |
| 1489 | + ip6_bytes = response[offset + 32:offset + 48] |
| 1490 | + ip_addr = ipaddress.IPv6Address(ip6_bytes) |
| 1491 | + addr_info = f"IPv6: {ip_addr}" |
| 1492 | + else: |
| 1493 | + addr_info = f"Unknown family: 0x{family:04x}" |
1475 | 1494 |
|
1476 | | - # Retrieve IPAddress |
1477 | | - try: |
1478 | | - ip_address = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "DhcpIPAddress" if dhcp_enabled else "IPAddress")[1].rstrip("\x00").replace("\x00", ", ") |
1479 | | - except DCERPCException: |
1480 | | - ip_address = None |
1481 | | - interface["IPAddress"] = ip_address if ip_address else None |
| 1495 | + # Group by interface index |
| 1496 | + if if_index not in grouped_interfaces: |
| 1497 | + caps = [] |
| 1498 | + if capabilities & 0x01: |
| 1499 | + caps.append("RSS") |
| 1500 | + if capabilities & 0x02: |
| 1501 | + caps.append("RDMA") |
1482 | 1502 |
|
1483 | | - # Retrieve SubnetMask |
1484 | | - try: |
1485 | | - subnetmask = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "SubnetMask")[1].rstrip("\x00").replace("\x00", ", ") |
1486 | | - except DCERPCException: |
1487 | | - subnetmask = None |
1488 | | - interface["SubnetMask"] = subnetmask if subnetmask else None |
| 1503 | + grouped_interfaces[if_index] = { |
| 1504 | + "capabilities": caps, |
| 1505 | + "link_speed": link_speed, |
| 1506 | + "addresses": [] |
| 1507 | + } |
1489 | 1508 |
|
1490 | | - # Retrieve DefaultGateway |
1491 | | - try: |
1492 | | - default_gateway = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "DhcpDefaultGateway")[1].rstrip("\x00").replace("\x00", ", ") |
1493 | | - except DCERPCException: |
1494 | | - default_gateway = None |
1495 | | - interface["DefaultGateway"] = default_gateway if default_gateway else None |
| 1509 | + grouped_interfaces[if_index]["addresses"].append(addr_info) |
1496 | 1510 |
|
1497 | | - self.logger.highlight(f"{interface['Name']:<11} | {interface['IPAddress']!s:<15} | {interface['SubnetMask']!s:<15} | {interface['DefaultGateway']!s:<15} | {interface['DHCP']}") |
| 1511 | + if next_offset == 0: |
| 1512 | + break |
1498 | 1513 |
|
1499 | | - except DCERPCException as e: |
1500 | | - self.logger.info(f"Failed to retrieve the network interface info for {sub_key}: {e!s}") |
| 1514 | + offset = next_offset if next_offset > offset else offset + next_offset |
| 1515 | + if offset >= len(response): |
| 1516 | + break |
1501 | 1517 |
|
1502 | | - with contextlib.suppress(Exception): |
1503 | | - remoteOps.finish() |
1504 | | - except DCERPCException as e: |
1505 | | - self.logger.error(f"Failed to connect to the target: {e!s}") |
| 1518 | + except (struct.error, IndexError) as e: |
| 1519 | + self.logger.fail(f"Error parsing interface at offset {offset}: {e}") |
| 1520 | + break |
| 1521 | + |
| 1522 | + # Display interfaces |
| 1523 | + if not grouped_interfaces: |
| 1524 | + self.logger.fail("No network interfaces found") |
| 1525 | + return |
| 1526 | + |
| 1527 | + self.logger.highlight(f"Found {len(grouped_interfaces)} network interface(s)") |
| 1528 | + |
| 1529 | + for i, if_index in enumerate(sorted(grouped_interfaces.keys())): |
| 1530 | + iface = grouped_interfaces[if_index] |
| 1531 | + caps_str = ", ".join(iface["capabilities"]) if iface["capabilities"] else "None" |
| 1532 | + speed_mbps = iface["link_speed"] / 1000000 |
| 1533 | + |
| 1534 | + self.logger.display(f"Interface {i + 1} (Index: {if_index}):") |
| 1535 | + self.logger.display(f" - Capabilities: {caps_str}") |
| 1536 | + self.logger.display(f" - Speed: {speed_mbps:.0f} Mbps") |
| 1537 | + self.logger.display(" - Addresses:") |
| 1538 | + |
| 1539 | + for addr in iface["addresses"]: |
| 1540 | + prefix = " -" |
| 1541 | + self.logger.display(f"{prefix} {addr}") |
| 1542 | + else: |
| 1543 | + self.logger.fail("No response data received") |
| 1544 | + |
| 1545 | + self.conn.disconnectTree(tree_id) |
| 1546 | + |
| 1547 | + except Exception as e: |
| 1548 | + self.logger.fail(f"Error during network interface enumeration: {e}") |
| 1549 | + self.logger.fail(f"Full error: {e}", exc_info=True) |
1506 | 1550 |
|
1507 | 1551 | def get_dc_ips(self): |
1508 | 1552 | dc_ips = [dc[1] for dc in self.db.get_domain_controllers(domain=self.domain)] |
|
0 commit comments