Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 44 additions & 29 deletions scripts_staging/Win_NetworkScanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
This script performs a network scan on a given target or subnet.
It checks if the target hosts are alive, and if ports 80 (HTTP) and 443 (HTTPS) are open, and optionally performs reverse DNS lookups if specified.

Params
--hostname
Params:
--hostname Perform reverse DNS lookup
--mac Include MAC address in output

v1.1 2/2024 silversword411
v1.4 added open port checker
v1.5 5/2/2024 integrated reverse DNS lookup into the ping function with 1-second timeout
v1.6 5/31/2024 align output to columns and ports low to high
v1.7 2/18/2025 fix columns with long host names and added response time
v1.5 5/2/2024 silversword411 integrated reverse DNS lookup into the ping function with 1-second timeout
v1.6 5/31/2024 silversword411 align output to columns and ports low to high
v1.7 2/18/2025 silversword411 fix columns with long host names and added response time
v1.8 5/21/2025 silversword411 added MAC address lookup with --mac option

TODO: Make subnet get automatically detected
TODO: run on linux as well
Expand All @@ -26,7 +28,6 @@
import argparse


# Function to get the IP address of the primary network interface
def get_host_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
Expand All @@ -39,7 +40,6 @@ def get_host_ip():
return IP


# Function to ping an IP address, check if it is alive, measure response time, and optionally perform a reverse DNS lookup
def ping_ip(ip, alive_hosts, do_reverse_dns):
try:
output = subprocess.check_output(
Expand All @@ -50,9 +50,7 @@ def ping_ip(ip, alive_hosts, do_reverse_dns):
if "Reply from" in output:
alive_ip = ipaddress.ip_address(ip)
response_time = re.search(r"time[=<]\s*(\d+)ms", output)
response_time = (
int(response_time.group(1)) if response_time else -1
) # If no time found, use -1
response_time = int(response_time.group(1)) if response_time else -1

hostname = "NA"
if do_reverse_dns:
Expand All @@ -65,12 +63,22 @@ def ping_ip(ip, alive_hosts, do_reverse_dns):
finally:
s.close()

alive_hosts.append((alive_ip, hostname, response_time))
alive_hosts.append(
(alive_ip, hostname, response_time, "")
) # Placeholder for MAC
except Exception:
pass


# Function to check for open ports
def get_mac_address(ip):
try:
output = subprocess.check_output(["arp", "-a", ip], universal_newlines=True)
match = re.search(r"(\w{2}-\w{2}-\w{2}-\w{2}-\w{2}-\w{2})", output)
return match.group(1) if match else "N/A"
except Exception:
return "N/A"


def check_ports(ip, port, open_ports):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
Expand All @@ -81,18 +89,19 @@ def check_ports(ip, port, open_ports):
pass


# Parse command-line arguments
def parse_arguments():
parser = argparse.ArgumentParser(
description="Scan network subnet for alive hosts, open ports, and optionally perform reverse DNS lookup."
description="Scan network subnet for alive hosts, open ports, and optionally perform reverse DNS or get MAC address."
)
parser.add_argument(
"--hostname", help="Perform reverse DNS lookup", action="store_true"
)
parser.add_argument(
"--mac", help="Include MAC address in output", action="store_true"
)
return parser.parse_args()


# Main function to detect the subnet and scan it
def main():
args = parse_arguments()
host_ip = get_host_ip()
Expand All @@ -111,12 +120,15 @@ def main():
for t in threads:
t.join()

# Sort the alive hosts numerically
if args.mac:
for i, (ip, hostname, response_time, _) in enumerate(alive_hosts):
mac = get_mac_address(str(ip))
alive_hosts[i] = (ip, hostname, response_time, mac)

alive_hosts.sort(key=lambda x: x[0])

# Launch port checks
port_check_threads = []
for host, _, _ in alive_hosts:
for host, _, _, _ in alive_hosts:
for port in [22, 23, 25, 80, 443, 2525, 8443, 10443, 10000, 20000]:
t = threading.Thread(target=check_ports, args=(str(host), port, open_ports))
t.start()
Expand All @@ -125,28 +137,31 @@ def main():
for t in port_check_threads:
t.join()

# Determine column widths dynamically
max_hostname_length = max(
(len(hostname) for _, hostname, _ in alive_hosts), default=8
(len(hostname) for _, hostname, _, _ in alive_hosts), default=8
)
ip_column_width = 16
hostname_column_width = max(max_hostname_length, 12) + 2 # Minimum width of 12
hostname_column_width = max(max_hostname_length, 12) + 2
response_time_column_width = 8
ports_column_width = 50 # Static width for ports
mac_column_width = 20 if args.mac else 0
ports_column_width = 50

# Print header
header = f"{'IP':<{ip_column_width}}{'(ms)':<{response_time_column_width}}{'Hostname':<{hostname_column_width}}{'Open Ports':<{ports_column_width}}"
header = f"{'IP':<{ip_column_width}}{'(ms)':<{response_time_column_width}}{'Hostname':<{hostname_column_width}}"
if args.mac:
header += f"{'MAC Address':<{mac_column_width}}"
header += f"{'Open Ports':<{ports_column_width}}"
print(header)
print("-" * len(header))

# Print results
for host, hostname, response_time in alive_hosts:
for host, hostname, response_time, mac in alive_hosts:
ports = sorted(open_ports[str(host)])
ports_str = ", ".join(map(str, ports))
response_time_str = f"{response_time} ms" if response_time >= 0 else "N/A"
print(
f"{str(host):<{ip_column_width}}{response_time_str:<{response_time_column_width}}{hostname:<{hostname_column_width}}{ports_str:<{ports_column_width}}"
)
line = f"{str(host):<{ip_column_width}}{response_time_str:<{response_time_column_width}}{hostname:<{hostname_column_width}}"
if args.mac:
line += f"{mac:<{mac_column_width}}"
line += f"{ports_str:<{ports_column_width}}"
print(line)

print(f"\nTotal count of alive hosts: {len(alive_hosts)}")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
# Uses MDM features of windows to perform a Windows Reset clearing all data
<#
.SYNOPSIS
Trigger a remote wipe via MDM.

.DESCRIPTION
Invokes the 'doWipeMethod' in Windows equivalent to the Reset function in the Settings app.

.NOTES
v1.0 7/2024 bbrendon Initial version
#>

$namespaceName = "root\cimv2\mdm\dmmap"
$className = "MDM_RemoteWipe"
Expand All @@ -16,4 +25,4 @@ try {
}
catch [Exception] {
write-host $_ | out-string
}
}