Skip to content

Commit f99e8cc

Browse files
authored
Merge branch 'main' into main
2 parents 84af17f + 05d15a9 commit f99e8cc

6 files changed

Lines changed: 202 additions & 69 deletions

File tree

nxc/modules/enum_interfaces.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/usr/bin/env python3
2+
3+
import contextlib
4+
from impacket.examples.secretsdump import RemoteOperations
5+
from impacket.dcerpc.v5 import rrp
6+
from impacket.dcerpc.v5.rpcrt import DCERPCException
7+
8+
9+
class NXCModule:
10+
"""
11+
Retrieve the list of network interfaces info (Name, IP Address, Subnet Mask, Default Gateway) from remote Windows registry'
12+
Formerly --interfaces parameter
13+
Made by: @Sant0rryu, @NeffIsBack
14+
"""
15+
16+
name = "enum_interfaces"
17+
description = "Retrieve the list of network interfaces info (Name, IP Address, Subnet Mask, Default Gateway) from remote Windows registry (formerly --interfaces)"
18+
supported_protocols = ["smb"]
19+
opsec_safe = False
20+
21+
def __init__(self):
22+
self.context = None
23+
self.module_options = {}
24+
25+
def options(self, context, module_options):
26+
"""No options available"""
27+
28+
def on_admin_login(self, context, connection):
29+
"""Execute network interface enumeration on authenticated SMB connection"""
30+
self.context = context
31+
32+
try:
33+
remoteOps = RemoteOperations(connection.conn, False)
34+
remoteOps.enableRegistry()
35+
36+
if remoteOps._RemoteOperations__rrp:
37+
reg_handle = rrp.hOpenLocalMachine(remoteOps._RemoteOperations__rrp)["phKey"]
38+
key_handle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, reg_handle, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces")["phkResult"]
39+
sub_key_list = rrp.hBaseRegQueryInfoKey(remoteOps._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
40+
sub_keys = [rrp.hBaseRegEnumKey(remoteOps._RemoteOperations__rrp, key_handle, i)["lpNameOut"][:-1] for i in range(sub_key_list)]
41+
42+
context.log.highlight(f"{'-Name-':<11} | {'-IP Address-':<15} | {'-SubnetMask-':<15} | {'-Gateway-':<15} | -DHCP-")
43+
for sub_key in sub_keys:
44+
interface = {}
45+
try:
46+
interface_key = f"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\{sub_key}"
47+
interface_handle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, reg_handle, interface_key)["phkResult"]
48+
49+
# Retrieve Interace Name
50+
interface_name_key = f"SYSTEM\\ControlSet001\\Control\\Network\\{{4D36E972-E325-11CE-BFC1-08002BE10318}}\\{sub_key}\\Connection"
51+
interface_name_handle = rrp.hBaseRegOpenKey(remoteOps._RemoteOperations__rrp, reg_handle, interface_name_key)["phkResult"]
52+
interface_name = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_name_handle, "Name")[1].rstrip("\x00")
53+
interface["Name"] = str(interface_name)
54+
if "Kernel" in interface_name:
55+
continue
56+
57+
# Retrieve DHCP
58+
try:
59+
dhcp_enabled = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "EnableDHCP")[1]
60+
except DCERPCException:
61+
dhcp_enabled = False
62+
interface["DHCP"] = bool(dhcp_enabled)
63+
64+
# Retrieve IPAddress
65+
try:
66+
ip_address = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "DhcpIPAddress" if dhcp_enabled else "IPAddress")[1].rstrip("\x00").replace("\x00", ", ")
67+
except DCERPCException:
68+
ip_address = None
69+
interface["IPAddress"] = ip_address if ip_address else None
70+
71+
# Retrieve SubnetMask
72+
try:
73+
subnetmask = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "SubnetMask")[1].rstrip("\x00").replace("\x00", ", ")
74+
except DCERPCException:
75+
subnetmask = None
76+
interface["SubnetMask"] = subnetmask if subnetmask else None
77+
78+
# Retrieve DefaultGateway
79+
try:
80+
default_gateway = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, interface_handle, "DhcpDefaultGateway")[1].rstrip("\x00").replace("\x00", ", ")
81+
except DCERPCException:
82+
default_gateway = None
83+
interface["DefaultGateway"] = default_gateway if default_gateway else None
84+
85+
context.log.highlight(f"{interface['Name']:<11} | {interface['IPAddress']!s:<15} | {interface['SubnetMask']!s:<15} | {interface['DefaultGateway']!s:<15} | {interface['DHCP']}")
86+
87+
except DCERPCException as e:
88+
context.log.info(f"Failed to retrieve the network interface info for {sub_key}: {e!s}")
89+
90+
with contextlib.suppress(Exception):
91+
remoteOps.finish()
92+
except DCERPCException as e:
93+
context.log.error(f"Failed to connect to the target: {e!s}")

nxc/modules/whoami.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def on_login(self, context, connection):
102102

103103
# Process Bad Password Count
104104
if "badPwdCount" in response:
105-
context.log.highlight(f"Bad Passwod Count: {response['badPwdCount']}")
105+
context.log.highlight(f"Bad Password Count: {response['badPwdCount']}")
106106

107107
# Process servicePrincipalName
108108
if "servicePrincipalName" in response:

nxc/netexec.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,6 @@ def main():
194194
proto_module_paths.append(modules[m]["path"])
195195
protocol_object.module_paths = proto_module_paths
196196

197-
if hasattr(args, "ntds") and args.ntds and not args.userntds:
198-
ans = input(highlight("[!] Dumping the ntds can crash the DC on Windows Server 2019. Use the option --user <user> to dump a specific user safely or the module -M ntdsutil [Y/n] ", "red"))
199-
if ans.lower() not in ["y", "yes", ""]:
200-
exit(1)
201-
202197
if args.jitter and len(targets) > 1:
203198
nxc_logger.highlight(highlight("[!] Jitter is only throttling authentications per target!", "red"))
204199

nxc/protocols/smb.py

Lines changed: 102 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import binascii
33
import os
44
import re
5+
import struct
6+
import ipaddress
57
from Cryptodome.Hash import MD4
68

79
from impacket.smbconnection import SMBConnection, SessionError
@@ -32,7 +34,7 @@
3234
from impacket.dcerpc.v5.dtypes import NULL
3335
from impacket.dcerpc.v5.dcomrt import DCOMConnection
3436
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
3638
from impacket.dcerpc.v5 import tsts as TSTS
3739

3840
from nxc.config import process_secret, host_info_colors
@@ -1435,74 +1437,116 @@ def dir(self):
14351437
full_path = ntpath.join(self.args.dir, content.get_longname())
14361438
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}")
14371439

1438-
@requires_admin
14391440
def interfaces(self):
14401441
"""
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)
14431444
"""
14441445
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):
14571474
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}"
14751494

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")
14821502

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+
}
14891508

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)
14961510

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
14981513

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
15011517

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)
15061550

15071551
def get_dc_ips(self):
15081552
dc_ips = [dc[1] for dc in self.db.get_domain_controllers(domain=self.domain)]

poetry.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/e2e_commands.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M empire_e
7979
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M enum_av
8080
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M enum_dns
8181
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M enum_dns -o DOMAIN=google.com
82+
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M enum_interfaces
8283
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M firefox
8384
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M firefox -o COOKIES=True
8485
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get_netconnections

0 commit comments

Comments
 (0)