netscout 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,210 @@
1
+ Metadata-Version: 2.4
2
+ Name: netscout
3
+ Version: 1.0.1
4
+ Summary: Production-ready pentest recon CLI — speed first.
5
+ Author: NetScout Contributors
6
+ License: MIT
7
+ Keywords: pentest,recon,scanner,network,cli
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Information Technology
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Security
18
+ Classifier: Topic :: System :: Networking
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: rich>=13.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest; extra == "dev"
24
+ Requires-Dist: pytest-asyncio; extra == "dev"
25
+ Requires-Dist: pytest-cov; extra == "dev"
26
+
27
+ # NetScout
28
+
29
+ **Production-ready pentest recon CLI — speed first.**
30
+
31
+ NetScout is a fast, async network reconnaissance tool designed for penetration testers and security professionals. It discovers live hosts, checks ports, grabs banners, fingerprints OS, and exports results — all with minimal packets and maximum parallelism.
32
+
33
+ ---
34
+
35
+ ## Features
36
+
37
+ - **Blazing fast** — async I/O with semaphore-gated concurrency (default 150 threads)
38
+ - **Multi-CIDR input** — CIDR, single IP, ranges, hostnames, file input, or interactive prompt
39
+ - **Smart discovery** — nmap ping sweep (preferred) with asyncio TCP/ICMP fallback
40
+ - **Port scanning** — async TCP connect with banner grabbing
41
+ - **OS fingerprinting** — zero-cost TTL heuristic + optional deep nmap -O
42
+ - **Service detection** — nmap -sV with intensity 0 (single probe per port)
43
+ - **Script scanning** — nmap default scripts on demand
44
+ - **Rich terminal UI** — live progress dashboard with hacker aesthetics
45
+ - **Multi-format export** — txt, json, csv, gnmap (auto-detected from extension)
46
+ - **Graceful degradation** — works without nmap, without root, behind firewalls
47
+
48
+ ---
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ # Clone and install in editable mode
54
+ git clone https://github.com/youruser/netscout.git
55
+ cd netscout
56
+ pip install -e .
57
+
58
+ # Now available globally
59
+ netscout --version
60
+ ```
61
+
62
+ ### Requirements
63
+
64
+ - Python ≥ 3.10
65
+ - [nmap](https://nmap.org/) (optional but recommended for full functionality)
66
+
67
+ ---
68
+
69
+ ## Quick Start
70
+
71
+ ```bash
72
+ # Scan a single subnet
73
+ netscout 10.10.10.0/24
74
+
75
+ # Multiple ranges with port check
76
+ netscout 10.10.10.0/24 172.20.0.0/16 --port 22 80 443 8080
77
+
78
+ # Fast enum (OS + banner + DNS, zero extra packets)
79
+ netscout 192.168.1.0/24 --enum
80
+
81
+ # Deep scan with export
82
+ netscout 10.10.10.0/24 --deep --fast -o results.json
83
+
84
+ # Read targets from file
85
+ netscout --targets-file ranges.txt --enum -o scan.csv
86
+
87
+ # Interactive mode (no args)
88
+ netscout
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Usage
94
+
95
+ ```
96
+ netscout [OPTIONS] [TARGET ...]
97
+
98
+ Targets (positional, or interactive prompt if omitted):
99
+ 10.10.10.0/24 Single CIDR
100
+ 10.10.10.0/24 172.20.0.0/16 Multiple CIDRs
101
+ 10.10.10.1-50 Range shorthand
102
+ 10.10.10.5 Single IP
103
+ --targets-file FILE One target per line
104
+
105
+ Discovery:
106
+ --tcp TCP fallback for ICMP-dark hosts
107
+ --ports TEXT Ports for TCP fallback probe (default: 22,80,443,445,3389)
108
+ --threads INT Concurrent threads (default: 150)
109
+ --timeout FLOAT Timeout per probe in seconds (default: 1.0)
110
+ --fast Use nmap T5 + max-parallelism (fastest, noisier)
111
+
112
+ Enumeration:
113
+ --port INT [INT ...] Check if specific port(s) are open on live hosts
114
+ --os Guess OS via TTL (zero extra packets)
115
+ --services Service version detection (nmap -sV intensity 0)
116
+ --scripts Run nmap default scripts (slow, explicit only)
117
+ --enum All fast enum: OS + banner + DNS (recommended)
118
+ --deep Full nmap -O -sV on live hosts (slowest, most info)
119
+
120
+ Output:
121
+ -o, --output FILE Save results (auto-format: .txt .json .csv .gnmap)
122
+ --no-color Disable colors (for piping)
123
+ -v, --verbose Show dead hosts
124
+ -q, --quiet Only print IP:PORT, no UI chrome
125
+ --no-banner Suppress ASCII banner
126
+ --force Scan ranges > 65536 hosts
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Architecture
132
+
133
+ ```
134
+ netscout/
135
+ ├── main.py # CLI entrypoint + interactive prompt
136
+ ├── scanner.py # Async ping sweep + TCP SYN probe
137
+ ├── cidr.py # Multi-CIDR parsing, expansion, dedup
138
+ ├── enumerator.py # Port check, OS fingerprint, banner grab
139
+ ├── resolver.py # DNS forward/reverse with cache
140
+ ├── output.py # Rich terminal UI: live dashboard, summary
141
+ ├── exporter.py # txt / json / csv / gnmap output
142
+ ├── tests/
143
+ ├── pyproject.toml
144
+ └── README.md
145
+ ```
146
+
147
+ ### Speed Optimizations
148
+
149
+ 1. **Single nmap call per phase** — never loops nmap per-host
150
+ 2. **Semaphore-gated async** for all socket ops
151
+ 3. **Zero duplicate probes** — cached results, never re-probe same IP
152
+ 4. **Randomised scan order** — shuffles IPs to evade rate limiting
153
+ 5. **Connect timeout 0.5s** for TCP port checks
154
+ 6. **Batch hostname resolution** before scan starts
155
+ 7. **Progress bar at max 20fps** — rendering never slows down scanning
156
+
157
+ ### Graceful Degradation
158
+
159
+ | Condition | Behaviour |
160
+ |-----------|-----------|
161
+ | No nmap | Warns once, falls back to asyncio TCP/ICMP |
162
+ | No root/sudo | Skips raw socket features, uses connect() |
163
+ | ICMP blocked | Auto-enables TCP fallback silently |
164
+ | KeyboardInterrupt | Prints partial results + summary, clean exit |
165
+ | All errors | Go to stderr; results to stdout (pipeable) |
166
+
167
+ ---
168
+
169
+ ## Output Formats
170
+
171
+ ### Quiet mode (`-q`)
172
+ ```
173
+ 10.10.10.5:22
174
+ 10.10.10.5:80
175
+ 10.10.10.12:445
176
+ ```
177
+
178
+ ### JSON (`-o results.json`)
179
+ ```json
180
+ {
181
+ "total_hosts": 256,
182
+ "alive_hosts": 12,
183
+ "results": [
184
+ {
185
+ "ip": "10.10.10.5",
186
+ "hostname": "htb-target.local",
187
+ "os_guess": "Linux/Unix",
188
+ "open_ports": [22, 80, 443],
189
+ "ports": {
190
+ "22": {"state": "open", "service": "ssh", "banner": "SSH-2.0-OpenSSH_8.9"}
191
+ }
192
+ }
193
+ ]
194
+ }
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Development
200
+
201
+ ```bash
202
+ pip install -e ".[dev]"
203
+ pytest --cov=netscout
204
+ ```
205
+
206
+ ---
207
+
208
+ ## License
209
+
210
+ MIT
@@ -0,0 +1,184 @@
1
+ # NetScout
2
+
3
+ **Production-ready pentest recon CLI — speed first.**
4
+
5
+ NetScout is a fast, async network reconnaissance tool designed for penetration testers and security professionals. It discovers live hosts, checks ports, grabs banners, fingerprints OS, and exports results — all with minimal packets and maximum parallelism.
6
+
7
+ ---
8
+
9
+ ## Features
10
+
11
+ - **Blazing fast** — async I/O with semaphore-gated concurrency (default 150 threads)
12
+ - **Multi-CIDR input** — CIDR, single IP, ranges, hostnames, file input, or interactive prompt
13
+ - **Smart discovery** — nmap ping sweep (preferred) with asyncio TCP/ICMP fallback
14
+ - **Port scanning** — async TCP connect with banner grabbing
15
+ - **OS fingerprinting** — zero-cost TTL heuristic + optional deep nmap -O
16
+ - **Service detection** — nmap -sV with intensity 0 (single probe per port)
17
+ - **Script scanning** — nmap default scripts on demand
18
+ - **Rich terminal UI** — live progress dashboard with hacker aesthetics
19
+ - **Multi-format export** — txt, json, csv, gnmap (auto-detected from extension)
20
+ - **Graceful degradation** — works without nmap, without root, behind firewalls
21
+
22
+ ---
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ # Clone and install in editable mode
28
+ git clone https://github.com/youruser/netscout.git
29
+ cd netscout
30
+ pip install -e .
31
+
32
+ # Now available globally
33
+ netscout --version
34
+ ```
35
+
36
+ ### Requirements
37
+
38
+ - Python ≥ 3.10
39
+ - [nmap](https://nmap.org/) (optional but recommended for full functionality)
40
+
41
+ ---
42
+
43
+ ## Quick Start
44
+
45
+ ```bash
46
+ # Scan a single subnet
47
+ netscout 10.10.10.0/24
48
+
49
+ # Multiple ranges with port check
50
+ netscout 10.10.10.0/24 172.20.0.0/16 --port 22 80 443 8080
51
+
52
+ # Fast enum (OS + banner + DNS, zero extra packets)
53
+ netscout 192.168.1.0/24 --enum
54
+
55
+ # Deep scan with export
56
+ netscout 10.10.10.0/24 --deep --fast -o results.json
57
+
58
+ # Read targets from file
59
+ netscout --targets-file ranges.txt --enum -o scan.csv
60
+
61
+ # Interactive mode (no args)
62
+ netscout
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Usage
68
+
69
+ ```
70
+ netscout [OPTIONS] [TARGET ...]
71
+
72
+ Targets (positional, or interactive prompt if omitted):
73
+ 10.10.10.0/24 Single CIDR
74
+ 10.10.10.0/24 172.20.0.0/16 Multiple CIDRs
75
+ 10.10.10.1-50 Range shorthand
76
+ 10.10.10.5 Single IP
77
+ --targets-file FILE One target per line
78
+
79
+ Discovery:
80
+ --tcp TCP fallback for ICMP-dark hosts
81
+ --ports TEXT Ports for TCP fallback probe (default: 22,80,443,445,3389)
82
+ --threads INT Concurrent threads (default: 150)
83
+ --timeout FLOAT Timeout per probe in seconds (default: 1.0)
84
+ --fast Use nmap T5 + max-parallelism (fastest, noisier)
85
+
86
+ Enumeration:
87
+ --port INT [INT ...] Check if specific port(s) are open on live hosts
88
+ --os Guess OS via TTL (zero extra packets)
89
+ --services Service version detection (nmap -sV intensity 0)
90
+ --scripts Run nmap default scripts (slow, explicit only)
91
+ --enum All fast enum: OS + banner + DNS (recommended)
92
+ --deep Full nmap -O -sV on live hosts (slowest, most info)
93
+
94
+ Output:
95
+ -o, --output FILE Save results (auto-format: .txt .json .csv .gnmap)
96
+ --no-color Disable colors (for piping)
97
+ -v, --verbose Show dead hosts
98
+ -q, --quiet Only print IP:PORT, no UI chrome
99
+ --no-banner Suppress ASCII banner
100
+ --force Scan ranges > 65536 hosts
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Architecture
106
+
107
+ ```
108
+ netscout/
109
+ ├── main.py # CLI entrypoint + interactive prompt
110
+ ├── scanner.py # Async ping sweep + TCP SYN probe
111
+ ├── cidr.py # Multi-CIDR parsing, expansion, dedup
112
+ ├── enumerator.py # Port check, OS fingerprint, banner grab
113
+ ├── resolver.py # DNS forward/reverse with cache
114
+ ├── output.py # Rich terminal UI: live dashboard, summary
115
+ ├── exporter.py # txt / json / csv / gnmap output
116
+ ├── tests/
117
+ ├── pyproject.toml
118
+ └── README.md
119
+ ```
120
+
121
+ ### Speed Optimizations
122
+
123
+ 1. **Single nmap call per phase** — never loops nmap per-host
124
+ 2. **Semaphore-gated async** for all socket ops
125
+ 3. **Zero duplicate probes** — cached results, never re-probe same IP
126
+ 4. **Randomised scan order** — shuffles IPs to evade rate limiting
127
+ 5. **Connect timeout 0.5s** for TCP port checks
128
+ 6. **Batch hostname resolution** before scan starts
129
+ 7. **Progress bar at max 20fps** — rendering never slows down scanning
130
+
131
+ ### Graceful Degradation
132
+
133
+ | Condition | Behaviour |
134
+ |-----------|-----------|
135
+ | No nmap | Warns once, falls back to asyncio TCP/ICMP |
136
+ | No root/sudo | Skips raw socket features, uses connect() |
137
+ | ICMP blocked | Auto-enables TCP fallback silently |
138
+ | KeyboardInterrupt | Prints partial results + summary, clean exit |
139
+ | All errors | Go to stderr; results to stdout (pipeable) |
140
+
141
+ ---
142
+
143
+ ## Output Formats
144
+
145
+ ### Quiet mode (`-q`)
146
+ ```
147
+ 10.10.10.5:22
148
+ 10.10.10.5:80
149
+ 10.10.10.12:445
150
+ ```
151
+
152
+ ### JSON (`-o results.json`)
153
+ ```json
154
+ {
155
+ "total_hosts": 256,
156
+ "alive_hosts": 12,
157
+ "results": [
158
+ {
159
+ "ip": "10.10.10.5",
160
+ "hostname": "htb-target.local",
161
+ "os_guess": "Linux/Unix",
162
+ "open_ports": [22, 80, 443],
163
+ "ports": {
164
+ "22": {"state": "open", "service": "ssh", "banner": "SSH-2.0-OpenSSH_8.9"}
165
+ }
166
+ }
167
+ ]
168
+ }
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Development
174
+
175
+ ```bash
176
+ pip install -e ".[dev]"
177
+ pytest --cov=netscout
178
+ ```
179
+
180
+ ---
181
+
182
+ ## License
183
+
184
+ MIT
netscout-1.0.1/cidr.py ADDED
@@ -0,0 +1,195 @@
1
+ """
2
+ Multi-CIDR input parsing, expansion, deduplication, and merging.
3
+
4
+ Handles:
5
+ - CIDR notation: 10.10.10.0/24
6
+ - Single IPs: 10.10.10.5
7
+ - Ranges: 10.10.10.1-50 OR 10.10.10.1-10.10.10.254
8
+ - Hostnames: auto-resolved to IP
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import ipaddress
14
+ import re
15
+ import socket
16
+ from dataclasses import dataclass, field
17
+ from pathlib import Path
18
+ from typing import Sequence
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # Constants
22
+ # ---------------------------------------------------------------------------
23
+ MAX_HOSTS_NO_FORCE = 65_536
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # Dataclass for parsed input
27
+ # ---------------------------------------------------------------------------
28
+
29
+ @dataclass
30
+ class TargetSpec:
31
+ """Represents a parsed target specification."""
32
+ raw: str
33
+ networks: list[ipaddress.IPv4Network] = field(default_factory=list)
34
+ individual_ips: list[ipaddress.IPv4Address] = field(default_factory=list)
35
+ resolved_hostname: str = ""
36
+ error: str = ""
37
+
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # Parsing helpers
41
+ # ---------------------------------------------------------------------------
42
+
43
+ _RANGE_SHORT_RE = re.compile(
44
+ r"^(\d{1,3}\.\d{1,3}\.\d{1,3})\.(\d{1,3})-(\d{1,3})$"
45
+ )
46
+ _RANGE_FULL_RE = re.compile(
47
+ r"^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})-(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$"
48
+ )
49
+
50
+
51
+ def _parse_single(raw: str) -> TargetSpec:
52
+ """Parse a single target string into a TargetSpec."""
53
+ spec = TargetSpec(raw=raw)
54
+ text = raw.strip()
55
+ if not text:
56
+ spec.error = "empty input"
57
+ return spec
58
+
59
+ # 1) Try CIDR
60
+ if "/" in text:
61
+ try:
62
+ net = ipaddress.IPv4Network(text, strict=False)
63
+ spec.networks.append(net)
64
+ return spec
65
+ except (ipaddress.AddressValueError, ValueError) as exc:
66
+ spec.error = str(exc)
67
+ return spec
68
+
69
+ # 2) Try short range: 10.10.10.1-50
70
+ m = _RANGE_SHORT_RE.match(text)
71
+ if m:
72
+ prefix, start_s, end_s = m.group(1), int(m.group(2)), int(m.group(3))
73
+ if start_s > end_s or end_s > 255:
74
+ spec.error = f"invalid range {text}"
75
+ return spec
76
+ for octet in range(start_s, end_s + 1):
77
+ spec.individual_ips.append(ipaddress.IPv4Address(f"{prefix}.{octet}"))
78
+ return spec
79
+
80
+ # 3) Try full range: 10.10.10.1-10.10.10.254
81
+ m = _RANGE_FULL_RE.match(text)
82
+ if m:
83
+ try:
84
+ start_ip = ipaddress.IPv4Address(m.group(1))
85
+ end_ip = ipaddress.IPv4Address(m.group(2))
86
+ except ipaddress.AddressValueError as exc:
87
+ spec.error = str(exc)
88
+ return spec
89
+ if int(start_ip) > int(end_ip):
90
+ spec.error = f"start > end in range {text}"
91
+ return spec
92
+ for ip_int in range(int(start_ip), int(end_ip) + 1):
93
+ spec.individual_ips.append(ipaddress.IPv4Address(ip_int))
94
+ return spec
95
+
96
+ # 4) Try single IP
97
+ try:
98
+ addr = ipaddress.IPv4Address(text)
99
+ spec.individual_ips.append(addr)
100
+ return spec
101
+ except ipaddress.AddressValueError:
102
+ pass
103
+
104
+ # 5) Try hostname resolution
105
+ try:
106
+ resolved = socket.gethostbyname(text)
107
+ spec.individual_ips.append(ipaddress.IPv4Address(resolved))
108
+ spec.resolved_hostname = text
109
+ return spec
110
+ except (socket.gaierror, socket.herror) as exc:
111
+ spec.error = f"cannot resolve hostname '{text}': {exc}"
112
+ return spec
113
+
114
+
115
+ # ---------------------------------------------------------------------------
116
+ # Public API
117
+ # ---------------------------------------------------------------------------
118
+
119
+ def parse_targets(raw_inputs: Sequence[str]) -> tuple[list[TargetSpec], list[str]]:
120
+ """
121
+ Parse a list of raw target strings.
122
+
123
+ Returns (specs, errors) where *specs* is successfully parsed targets
124
+ and *errors* is a list of human-readable error strings.
125
+ """
126
+ specs: list[TargetSpec] = []
127
+ errors: list[str] = []
128
+ for raw in raw_inputs:
129
+ # Split on commas and whitespace for multi-target pasting
130
+ tokens = re.split(r"[,\s]+", raw.strip())
131
+ for tok in tokens:
132
+ if not tok:
133
+ continue
134
+ spec = _parse_single(tok)
135
+ if spec.error:
136
+ errors.append(f" ✗ {tok}: {spec.error}")
137
+ else:
138
+ specs.append(spec)
139
+ return specs, errors
140
+
141
+
142
+ def load_targets_file(path: str | Path) -> list[str]:
143
+ """Read one target per line from a file, stripping comments & blanks."""
144
+ p = Path(path)
145
+ if not p.exists():
146
+ raise FileNotFoundError(f"targets file not found: {p}")
147
+ lines: list[str] = []
148
+ for line in p.read_text().splitlines():
149
+ line = line.strip()
150
+ if line and not line.startswith("#"):
151
+ lines.append(line)
152
+ return lines
153
+
154
+
155
+ def deduplicate_and_merge(
156
+ specs: list[TargetSpec],
157
+ ) -> tuple[list[ipaddress.IPv4Network], int]:
158
+ """
159
+ Collapse all parsed specs into a minimal set of non-overlapping networks.
160
+
161
+ Returns (networks, total_hosts).
162
+ """
163
+ all_nets: list[ipaddress.IPv4Network] = []
164
+ for spec in specs:
165
+ all_nets.extend(spec.networks)
166
+ for ip in spec.individual_ips:
167
+ all_nets.append(ipaddress.IPv4Network(f"{ip}/32"))
168
+
169
+ collapsed = list(ipaddress.collapse_addresses(all_nets))
170
+ total = sum(net.num_addresses for net in collapsed)
171
+ return collapsed, total
172
+
173
+
174
+ def expand_hosts(networks: list[ipaddress.IPv4Network]) -> list[ipaddress.IPv4Address]:
175
+ """Expand collapsed networks into an ordered list of host addresses."""
176
+ hosts: list[ipaddress.IPv4Address] = []
177
+ for net in networks:
178
+ if net.prefixlen == 32:
179
+ hosts.append(net.network_address)
180
+ else:
181
+ hosts.extend(net.hosts())
182
+ return hosts
183
+
184
+
185
+ def check_host_count(total: int, force: bool) -> bool:
186
+ """
187
+ Return True if scan should proceed.
188
+ Raises SystemExit if total > MAX_HOSTS_NO_FORCE and --force not set.
189
+ """
190
+ if total > MAX_HOSTS_NO_FORCE and not force:
191
+ raise SystemExit(
192
+ f"[!] Target range contains {total:,} hosts (limit: {MAX_HOSTS_NO_FORCE:,}). "
193
+ f"Use --force to proceed."
194
+ )
195
+ return True