<!-- Copyright (c) 2026 defconxt. All rights reserved. -->
<!-- Licensed under AGPL-3.0 — see LICENSE file for details. -->
---
name: building-network-discovery-tools
description: >-
  Build network discovery and mapping tools using raw sockets, SNMP, ARP,
  and DNS enumeration. Covers subnet scanning, service fingerprinting,
  OS detection, and topology mapping with structured output for asset
  inventory and attack surface management.
domain: cybersecurity
subdomain: automation-scripting
tags:
  - network-discovery
  - asset-inventory
  - snmp
  - dns-enumeration
  - service-fingerprinting
  - subnet-scanning
version: "1.0"
author: defconxt
license: AGPL-3.0
metadata:
  mitre-attack: ["T1046", "T1018"]
---

# Building Network Discovery Tools

## Overview

Accurate asset inventory is the foundation of security. This skill covers
building custom network discovery tools that combine ICMP, ARP, TCP, SNMP,
and DNS techniques to map live hosts, services, and OS fingerprints across
subnets. Output feeds asset management and attack surface tracking.

Mode: `[MODE: RECON]` — Discovery with passive preference.

## Prerequisites

| Requirement | Details |
|---|---|
| Python 3.10+ with `scapy`, `python-nmap`, `dnspython` | Required |
| Root/sudo for ICMP and ARP operations | Required |
| SNMP community strings for managed device enumeration | Required |

## Key Concepts

### ICMP Ping Sweep

```python
import asyncio
import subprocess

async def ping_host(ip: str, timeout: int = 1) -> dict:
    """Ping a single host asynchronously."""
    proc = await asyncio.create_subprocess_exec(
        "ping", "-c", "1", "-W", str(timeout), ip,
        stdout=asyncio.subprocess.DEVNULL,
        stderr=asyncio.subprocess.DEVNULL,
    )
    rc = await proc.wait()
    return {"ip": ip, "alive": rc == 0}

async def ping_sweep(network: str) -> list[dict]:
    """Sweep a /24 subnet with concurrent pings."""
    import ipaddress
    hosts = [str(ip) for ip in ipaddress.IPv4Network(network, strict=False).hosts()]
    tasks = [ping_host(ip) for ip in hosts]
    results = await asyncio.gather(*tasks)
    return [r for r in results if r["alive"]]
```

### DNS Enumeration

```python
import dns.resolver
import dns.reversename

def dns_enum(domain: str, record_types: list[str] | None = None) -> dict:
    """Enumerate DNS records for a domain."""
    if record_types is None:
        record_types = ["A", "AAAA", "MX", "NS", "TXT", "CNAME", "SOA"]
    results = {}
    for rtype in record_types:
        try:
            answers = dns.resolver.resolve(domain, rtype)
            results[rtype] = [str(r) for r in answers]
        except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN, dns.resolver.NoNameservers):
            results[rtype] = []
    return {"domain": domain, "records": results}

def reverse_dns(ip: str) -> str | None:
    """Perform reverse DNS lookup."""
    try:
        rev = dns.reversename.from_address(ip)
        return str(dns.resolver.resolve(rev, "PTR")[0])
    except Exception:
        return None
```

### SNMP Device Query

```python
def snmp_query(host: str, community: str = "public", oid: str = "1.3.6.1.2.1.1.1.0") -> dict:
    """Query SNMP OID from a network device."""
    try:
        from pysnmp.hlapi import (
            CommunityData, ContextData, ObjectIdentity, ObjectType,
            SnmpEngine, UdpTransportTarget, getCmd,
        )
        iterator = getCmd(
            SnmpEngine(),
            CommunityData(community),
            UdpTransportTarget((host, 161), timeout=5),
            ContextData(),
            ObjectType(ObjectIdentity(oid)),
        )
        error_indication, error_status, _, var_binds = next(iterator)
        if error_indication or error_status:
            return {"host": host, "error": str(error_indication or error_status)}
        return {"host": host, "oid": oid, "value": str(var_binds[0][1])}
    except ImportError:
        return {"error": "pysnmp not installed — run: pip install pysnmp"}
```

## Workflow

### Step 1: Ping Sweep Subnet

```bash
node scripts/agent.js --action ping-sweep --target 192.168.1.0/24
```

### Step 2: DNS Enumeration

```bash
node scripts/agent.js --action dns-enum --target example.com
```

### Step 3: Service Fingerprint

```bash
node scripts/agent.js --action service-scan --target 192.168.1.1 --ports 22,80,443
```

## Verification

- [ ] Ping sweep discovers all live hosts on subnet
- [ ] DNS enumeration returns all record types with proper error handling
- [ ] SNMP queries return device system descriptions
- [ ] Handles unreachable hosts and timeouts gracefully
- [ ] Exit code 0 on success, 1 on partial results, 2 on error

## References

- [Scapy Documentation](https://scapy.readthedocs.io/)
- [dnspython Documentation](https://www.dnspython.org/)
- [MITRE ATT&CK T1046 — Network Service Scanning](https://attack.mitre.org/techniques/T1046/)
- [MITRE ATT&CK T1018 — Remote System Discovery](https://attack.mitre.org/techniques/T1018/)
