Skip to content

Commit 1395a28

Browse files
committed
fixed nmap lists
1 parent ee06c14 commit 1395a28

File tree

7 files changed

+367
-144
lines changed

7 files changed

+367
-144
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ base64 = "0.21"
2424
chrono = "0.4"
2525
ipnetwork = "0.20"
2626
trust-dns-resolver = { version = "0.22", features = ["tokio-runtime"] }
27+
lazy_static = "1.4"

src/main.rs

Lines changed: 30 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,41 +18,36 @@ use base64::Engine;
1818
#[derive(Parser, Debug)]
1919
#[command(author, version, about, long_about = None)]
2020
struct Args {
21-
#[arg(short, long)]
22-
target: String,
21+
#[arg(short, long, required = true)]
22+
targets: Vec<String>,
23+
24+
#[arg(short, long, default_value = "1000-2000")]
25+
ports: String,
2326

2427
#[arg(short, long, default_value = "1000")]
2528
concurrency: usize,
2629

2730
#[arg(short, long, default_value = "1000")]
2831
timeout: u64,
2932

30-
#[arg(short, long, default_value = "1-1024")]
31-
ports: String,
32-
33-
#[arg(short, long)]
33+
#[arg(long)]
3434
service_detection: bool,
35-
36-
#[arg(short, long)]
37-
subnet: bool,
3835
}
3936

40-
fn parse_port_range(ports: &str) -> Result<(u16, u16)> {
37+
fn parse_port_range(ports: &str) -> Result<std::ops::RangeInclusive<u16>> {
4138
let parts: Vec<&str> = ports.split('-').collect();
4239
if parts.len() != 2 {
43-
return Err(anyhow::anyhow!("Invalid port range format. Use start-end"));
40+
return Err(anyhow::anyhow!("Invalid port range format"));
4441
}
4542

46-
let start = parts[0].parse::<u16>()
47-
.with_context(|| format!("Invalid start port: {}", parts[0]))?;
48-
let end = parts[1].parse::<u16>()
49-
.with_context(|| format!("Invalid end port: {}", parts[1]))?;
43+
let start = parts[0].parse::<u16>()?;
44+
let end = parts[1].parse::<u16>()?;
5045

5146
if start > end {
52-
return Err(anyhow::anyhow!("Start port cannot be greater than end port"));
47+
return Err(anyhow::anyhow!("Start port must be less than or equal to end port"));
5348
}
5449

55-
Ok((start, end))
50+
Ok(start..=end)
5651
}
5752

5853
fn resolve_target(target: &str, subnet: bool) -> Result<Vec<IpAddr>> {
@@ -125,50 +120,37 @@ fn generate_issue_template(result: &ScanResult) -> String {
125120
#[tokio::main]
126121
async fn main() -> Result<()> {
127122
let args = Args::parse();
128-
let (start_port, end_port) = parse_port_range(&args.ports)?;
129-
let targets = resolve_target(&args.target, args.subnet)?;
130-
131-
println!("Starting scan on {} targets...", targets.len());
123+
let port_range = parse_port_range(&args.ports)?;
132124

133125
let scanner = Scanner::new(
134-
targets,
135-
start_port..=end_port,
126+
args.targets,
127+
port_range,
136128
args.concurrency,
137129
args.timeout,
138130
args.service_detection,
139131
);
140132

141-
let results = scanner.run().await;
142-
let mut service_stats: HashMap<String, u32> = HashMap::new();
133+
let results = scanner.run().await?;
143134

144-
for result in &results {
145-
if let Some(service) = &result.service {
146-
*service_stats.entry(service.name.clone()).or_insert(0) += 1;
135+
if !results.is_empty() {
136+
println!("\nScan Results:");
137+
for result in &results {
138+
println!("{}", format_scan_result(result));
147139
}
148-
}
149140

150-
println!("\nScan Results:");
151-
for result in results {
152-
if let Some(service) = result.service {
153-
println!("[+] {}:{} is open", result.ip, result.port);
154-
println!(" Service: {}", service.name);
155-
if let Some(version) = service.version {
156-
println!(" Version: {}", version);
157-
}
158-
if let Some(product) = service.product {
159-
println!(" Product: {}", product);
160-
}
161-
if let Some(os) = service.os_type {
162-
println!(" OS: {}", os);
141+
let mut service_stats: HashMap<String, u32> = HashMap::new();
142+
for result in &results {
143+
if let Some(service) = &result.service {
144+
*service_stats.entry(service.name.clone()).or_insert(0) += 1;
163145
}
164-
} else {
165-
println!("[+] {}:{} is open", result.ip, result.port);
166146
}
167-
}
168147

169-
println!("\nService Statistics:");
170-
for (service, count) in service_stats {
171-
println!(" {}: {}", service, count);
148+
println!("\nService Statistics:");
149+
for (service, count) in service_stats {
150+
println!(" {}: {}", service, count);
151+
}
152+
} else {
153+
println!("No open ports found");
172154
}
173155

174156
Ok(())

src/patterns.rs

Lines changed: 200 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,108 @@ use regex::Regex;
22
use std::fs;
33
use std::path::Path;
44
use anyhow::Result;
5-
use crate::types::{ServicePattern, NmapService, NmapProbe, NmapMatch};
5+
use std::collections::HashMap;
6+
use crate::types::{ServicePattern, NmapService, NmapProbe, NmapMatch, Protocol, MacPrefix, RpcInfo};
7+
8+
lazy_static::lazy_static! {
9+
static ref PROTOCOLS: HashMap<String, Protocol> = {
10+
let mut map = HashMap::new();
11+
if let Ok(protocols) = load_protocols("src/assets/nmap-protocols") {
12+
for protocol in protocols {
13+
map.insert(protocol.name.clone(), protocol);
14+
}
15+
}
16+
map
17+
};
18+
19+
static ref MAC_PREFIXES: HashMap<String, String> = {
20+
let mut map = HashMap::new();
21+
if let Ok(prefixes) = load_mac_prefixes("src/assets/nmap-mac-prefixes") {
22+
for prefix in prefixes {
23+
map.insert(prefix.prefix, prefix.vendor);
24+
}
25+
}
26+
map
27+
};
28+
29+
static ref RPC_INFO: HashMap<(String, String), RpcInfo> = {
30+
let mut map = HashMap::new();
31+
if let Ok(info) = load_rpc_info("src/assets/nmap-rpc") {
32+
for rpc in info {
33+
map.insert((rpc.program.clone(), rpc.version.clone()), rpc);
34+
}
35+
}
36+
map
37+
};
38+
39+
static ref NMAP_SERVICES: HashMap<u16, Vec<NmapService>> = {
40+
let mut map = HashMap::new();
41+
if let Ok(services) = load_nmap_services("src/assets/nmap-services") {
42+
for service in services {
43+
map.entry(service.port).or_insert_with(Vec::new).push(service);
44+
}
45+
}
46+
map
47+
};
48+
49+
static ref SERVICE_PATTERNS: Vec<ServicePattern> = {
50+
let mut patterns = Vec::with_capacity(1000);
51+
52+
patterns.extend(get_ssh_patterns());
53+
patterns.extend(get_http_patterns());
54+
patterns.extend(get_ftp_patterns());
55+
patterns.extend(get_mysql_patterns());
56+
patterns.extend(get_redis_patterns());
57+
58+
if let Ok(probes) = load_nmap_probes("src/assets/nmap-service-probes") {
59+
for probe in probes {
60+
for nmap_match in probe.matches {
61+
if nmap_match.pattern.contains("**") || nmap_match.pattern.contains("\\") || nmap_match.pattern.contains("^") {
62+
continue;
63+
}
64+
65+
if let Ok(regex) = Regex::new(&nmap_match.pattern) {
66+
let pattern = ServicePattern {
67+
name: nmap_match.service.clone(),
68+
regex,
69+
probe: probe.probe_string.clone(),
70+
version_regex: nmap_match.version_info.as_ref().and_then(|v| Regex::new(v).ok()),
71+
product_regex: nmap_match.product_info.as_ref().and_then(|p| Regex::new(p).ok()),
72+
os_regex: nmap_match.os_info.as_ref().and_then(|o| Regex::new(o).ok()),
73+
extra_info_regex: nmap_match.extra_info.as_ref().and_then(|i| Regex::new(i).ok()),
74+
cpe_regex: nmap_match.cpe.as_ref().and_then(|c| Regex::new(c).ok()),
75+
vulnerability_patterns: vec![],
76+
total_wait_ms: probe.total_wait_ms,
77+
tcp_wrapped_ms: probe.tcp_wrapped_ms,
78+
};
79+
patterns.push(pattern);
80+
}
81+
}
82+
}
83+
}
84+
patterns
85+
};
86+
}
87+
88+
pub fn get_all_patterns() -> Vec<ServicePattern> {
89+
SERVICE_PATTERNS.clone()
90+
}
91+
92+
pub fn get_protocol(name: &str) -> Option<&Protocol> {
93+
PROTOCOLS.get(name)
94+
}
95+
96+
pub fn get_mac_vendor(prefix: &str) -> Option<&String> {
97+
MAC_PREFIXES.get(prefix)
98+
}
99+
100+
pub fn get_rpc_info<'a>(program: &'a str, version: &'a str) -> Option<&'static RpcInfo> {
101+
RPC_INFO.get(&(program.to_string(), version.to_string()))
102+
}
103+
104+
pub fn get_services_by_port(port: u16) -> Option<&'static Vec<NmapService>> {
105+
NMAP_SERVICES.get(&port)
106+
}
6107

7108
pub fn get_ssh_patterns() -> Vec<ServicePattern> {
8109
vec![
@@ -230,41 +331,106 @@ pub fn load_nmap_probes(file_path: &str) -> Result<Vec<NmapProbe>> {
230331
Ok(probes)
231332
}
232333

233-
pub fn get_all_patterns() -> Vec<ServicePattern> {
234-
let mut patterns = Vec::new();
235-
236-
patterns.extend(get_ssh_patterns());
237-
patterns.extend(get_http_patterns());
238-
patterns.extend(get_ftp_patterns());
239-
patterns.extend(get_mysql_patterns());
240-
patterns.extend(get_redis_patterns());
241-
242-
if let Ok(probes) = load_nmap_probes("src/assets/nmap-service-probes") {
243-
for probe in probes {
244-
for nmap_match in probe.matches {
245-
if nmap_match.pattern.contains("**") || nmap_match.pattern.contains("\\") || nmap_match.pattern.contains("^") {
246-
continue;
247-
}
334+
pub fn load_protocols(file_path: &str) -> Result<Vec<Protocol>> {
335+
let path = Path::new(file_path);
336+
if !path.exists() {
337+
return Ok(Vec::new());
338+
}
248339

249-
if let Ok(regex) = Regex::new(&nmap_match.pattern) {
250-
let pattern = ServicePattern {
251-
name: nmap_match.service.clone(),
252-
regex,
253-
probe: probe.probe_string.clone(),
254-
version_regex: nmap_match.version_info.as_ref().and_then(|v| Regex::new(v).ok()),
255-
product_regex: nmap_match.product_info.as_ref().and_then(|p| Regex::new(p).ok()),
256-
os_regex: nmap_match.os_info.as_ref().and_then(|o| Regex::new(o).ok()),
257-
extra_info_regex: nmap_match.extra_info.as_ref().and_then(|i| Regex::new(i).ok()),
258-
cpe_regex: nmap_match.cpe.as_ref().and_then(|c| Regex::new(c).ok()),
259-
vulnerability_patterns: vec![],
260-
total_wait_ms: probe.total_wait_ms,
261-
tcp_wrapped_ms: probe.tcp_wrapped_ms,
262-
};
263-
patterns.push(pattern);
264-
}
340+
let content = fs::read_to_string(path)?;
341+
let mut protocols = Vec::new();
342+
343+
for line in content.lines() {
344+
if line.starts_with('#') || line.trim().is_empty() {
345+
continue;
346+
}
347+
348+
let parts: Vec<&str> = line.split_whitespace().collect();
349+
if parts.len() >= 2 {
350+
let name = parts[0].to_string();
351+
if let Ok(number) = parts[1].parse::<u8>() {
352+
let aliases = if parts.len() > 2 {
353+
parts[2..].iter().map(|&s| s.to_string()).collect()
354+
} else {
355+
Vec::new()
356+
};
357+
358+
protocols.push(Protocol {
359+
name,
360+
number,
361+
aliases,
362+
});
265363
}
266364
}
267365
}
268-
269-
patterns
366+
367+
Ok(protocols)
368+
}
369+
370+
pub fn load_mac_prefixes(file_path: &str) -> Result<Vec<MacPrefix>> {
371+
let path = Path::new(file_path);
372+
if !path.exists() {
373+
return Ok(Vec::new());
374+
}
375+
376+
let content = fs::read_to_string(path)?;
377+
let mut prefixes = Vec::new();
378+
379+
for line in content.lines() {
380+
if line.starts_with('#') || line.trim().is_empty() {
381+
continue;
382+
}
383+
384+
let parts: Vec<&str> = line.split_whitespace().collect();
385+
if parts.len() >= 2 {
386+
let prefix = parts[0].to_string();
387+
let vendor = parts[1..].join(" ");
388+
389+
prefixes.push(MacPrefix {
390+
prefix,
391+
vendor,
392+
});
393+
}
394+
}
395+
396+
Ok(prefixes)
397+
}
398+
399+
pub fn load_rpc_info(file_path: &str) -> Result<Vec<RpcInfo>> {
400+
let path = Path::new(file_path);
401+
if !path.exists() {
402+
return Ok(Vec::new());
403+
}
404+
405+
let content = fs::read_to_string(path)?;
406+
let mut rpc_info = Vec::new();
407+
408+
for line in content.lines() {
409+
if line.starts_with('#') || line.trim().is_empty() {
410+
continue;
411+
}
412+
413+
let parts: Vec<&str> = line.split_whitespace().collect();
414+
if parts.len() >= 3 {
415+
let program = parts[0].to_string();
416+
let version = parts[1].to_string();
417+
let protocol = parts[2].to_string();
418+
let port = parts.get(3).and_then(|p| p.parse::<u16>().ok());
419+
let description = if parts.len() > 4 {
420+
Some(parts[4..].join(" "))
421+
} else {
422+
None
423+
};
424+
425+
rpc_info.push(RpcInfo {
426+
program,
427+
version,
428+
protocol,
429+
port,
430+
description,
431+
});
432+
}
433+
}
434+
435+
Ok(rpc_info)
270436
}

0 commit comments

Comments
 (0)