Module Architecture - kaotickj/NetSentinel GitHub Wiki
NetSentinel follows a modular architecture to ensure each component is reusable, testable, and easy to extend. Modules are isolated by function and interact through a shared data structure passed at runtime.
netsentinel/
βββ core/
β βββ recon.py # Network scanning, host discovery
β βββ smb_enum.py # SMB share enumeration
β βββ kerberos_enum.py # SPN & AS-REP Kerberos recon
β βββ init.py
βββ utils/
β βββ logger.py # Unified logging
β βββ config.py # Domain credentials (env or file)
β βββ ports.py # Common TCP ports list
β βββ init.py
main.py # CLI entry point
Each module receives structured data (host records) from recon.py
, and may enrich it with new keys (e.g., open ports, hostnames, shares). Output is passed down the pipeline and optionally written to a JSON file.
main.py β recon.py β [ smb_enum.py | kerberos_enum.py ] β export
All modules use the centralized Logger
class in utils/logger.py
. This provides:
- Timestamped output
- Colorized severity indicators (INFO, WARN, ERROR)
- Optional debugging verbosity
Module | Description |
---|---|
recon.py |
Performs network scanning and port enumeration |
smb_enum.py |
Discovers anonymous SMB shares on live hosts |
kerberos_enum.py |
Detects Kerberoastable SPNs and AS-REP vulnerabilities |
config.py |
Loads domain credentials from environment or file |
ports.py |
Supplies scan targets to recon.py
|
logger.py |
Provides consistent output handling |
NetSentinel is designed to be easily extended by adding modules to the core/
directory. These modules should focus on a single responsibility (e.g., SNMP scan, HTTP banner grabbing) and follow a consistent structure.
Create a new file in core/
, e.g., snmp_enum.py
:
from utils.logger import Logger
class SNMPEnumerator:
def __init__(self, logger: Logger):
self.logger = logger
def enumerate(self, hosts: list):
self.logger.info("Running SNMP scan...")
for host in hosts:
ip = host.get("ip")
# SNMP logic here
self.logger.debug(f"Queried {ip} via SNMP")
Import and call your module conditionally based on a new CLI argument:
from core.snmp_enum import SNMPEnumerator
if args.snmp_enum:
snmp = SNMPEnumerator(logger)
snmp.enumerate(results)
Add this to your argument parser:
parser.add_argument("--snmp-enum", action="store_true", help="Enumerate SNMP devices")
-
Accept a
list
of hosts from recon -
Use the shared
Logger
for output -
Return enriched results or modify the host data inline
-
Fail gracefully with try/except blocks
-
Donβt hard-code ports β use
ports.py
or inline where justified
python3 main.py --target 10.0.0.0/24 --snmp-enum
This structure ensures consistent behavior across all modules.
The recon.py
module performs initial host discovery and port scanning. It feeds structured output to all other modules. This document explains how to enhance its functionality with new enrichment methods or discovery logic.
Each discovered host is returned as a dictionary:
{
"ip": "10.0.0.5",
"mac": "00:11:22:33:44:55",
"hostname": "DC01",
"ports": [445, 135]
}
This list is passed to modules like smb_enum
and kerberos_enum
.
To extend the recon process, add new methods to the NetworkScanner
class in recon.py
.
def enrich_with_os_guessing(self):
for host in self.live_hosts:
ip = host["ip"]
ttl = self.get_ttl(ip)
host["os_guess"] = self.guess_os_from_ttl(ttl)
Call this function inside run()
or through a new CLI argument.
Feature | Output Field | Method Name |
---|---|---|
TTL fingerprinting | os_guess | enrich_with_os_guessing() |
NetBIOS hostnames | netbios_name | enrich_with_netbios() |
Clock skew via ICMP | clock_skew | enrich_with_clock_skew() |
#### Best Practices
-
Keep methods isolated and testable
-
Use
.get("ip")
defensively -
Avoid blocking calls where possible
-
Append enrichment data, never overwrite core fields
scanner = NetworkScanner(...)
scanner.run()
scanner.enrich_with_os_guessing()
This improves data passed to all downstream modules.