Skip to content

Commit d0f4ca1

Browse files
authored
Merge pull request #1 from s-b-repo/rewrite
Rewrite
2 parents 632cab6 + 62916f4 commit d0f4ca1

File tree

10 files changed

+151
-84
lines changed

10 files changed

+151
-84
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ anyhow = "1.0.97"
1818

1919
#teminal color
2020
colored = "3.0.0"
21-
21+
rustyline = "15.0.0"
2222
#ftp brute force module
2323
async_ftp = "6.0.0"
2424

@@ -28,3 +28,5 @@ tokio-socks = "0.5.2"
2828
threadpool = "1.8.1"
2929
crossbeam-channel = "0.5.15"
3030
telnet = "0.2.3"
31+
32+
walkdir = "2.5.0"

src/commands/creds.rs

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
1-
use anyhow::Result;
2-
use crate::modules::creds;
3-
use crate::modules::creds::telnet_bruteforce;
1+
use anyhow::{Result, bail};
2+
3+
use crate::modules::creds::generic::{
4+
ftp_anonymous,
5+
ftp_bruteforce,
6+
sample_cred_check,
7+
telnet_bruteforce,
8+
};
49

510
pub async fn run_cred_check(module_name: &str, target: &str) -> Result<()> {
611
match module_name {
7-
"sample_cred_check" => {
8-
creds::sample_cred_check::run(target).await?;
9-
},
10-
"ftp_bruteforce" => {
11-
creds::ftp_bruteforce::run(target).await?;
12-
},
13-
"ftp_anonymous" => {
14-
creds::ftp_anonymous::run(target).await?;
15-
},
16-
17-
"telnet_bruteforce" => {
18-
telnet_bruteforce::run_module(target).await?;
19-
},
20-
// Add more creds modules here ...
21-
_ => {
22-
eprintln!("Creds module '{}' not found.", module_name);
23-
},
12+
"generic/sample_cred_check" => sample_cred_check::run(target).await?,
13+
"generic/ftp_bruteforce" => ftp_bruteforce::run(target).await?,
14+
"generic/ftp_anonymous" => ftp_anonymous::run(target).await?,
15+
"generic/telnet_bruteforce" => telnet_bruteforce::run(target).await?,
16+
_ => bail!("Creds module '{}' not found.", module_name),
2417
}
2518

2619
Ok(())

src/commands/mod.rs

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,96 @@
1-
// src/commands/mod.rs
2-
31
pub mod exploit;
42
pub mod scanner;
53
pub mod creds;
64

75
use anyhow::Result;
86
use crate::cli::Cli;
7+
use walkdir::WalkDir;
98

10-
9+
/// Handle CLI commands like: --command exploit --module creds/generic/ftp_anonymous --target x.x.x.x
1110
pub async fn handle_command(command: &str, cli_args: &Cli) -> Result<()> {
11+
let target = cli_args.target.clone().unwrap_or_default();
12+
let module = cli_args.module.clone().unwrap_or_default();
13+
1214
match command {
1315
"exploit" => {
14-
let target = cli_args.target.clone().unwrap_or_default();
15-
let module = cli_args.module.clone().unwrap_or_default();
16-
exploit::run_exploit(&module, &target).await?;
16+
let trimmed = module.trim_start_matches("exploits/"); // normalize
17+
exploit::run_exploit(trimmed, &target).await?;
1718
},
1819
"scanner" => {
19-
let target = cli_args.target.clone().unwrap_or_default();
20-
let module = cli_args.module.clone().unwrap_or_default();
21-
scanner::run_scan(&module, &target).await?;
20+
let trimmed = module.trim_start_matches("scanners/");
21+
scanner::run_scan(trimmed, &target).await?;
2222
},
2323
"creds" => {
24-
let target = cli_args.target.clone().unwrap_or_default();
25-
let module = cli_args.module.clone().unwrap_or_default();
26-
creds::run_cred_check(&module, &target).await?;
24+
let trimmed = module.trim_start_matches("creds/");
25+
creds::run_cred_check(trimmed, &target).await?;
2726
},
2827
_ => {
2928
eprintln!("Unknown command '{}'", command);
3029
}
3130
}
31+
3232
Ok(())
3333
}
3434

35-
// Called from the interactive shell
35+
/// Called from the interactive shell (e.g. 'use creds/generic/ftp_anonymous' + 'run')
3636
pub async fn run_module(module_path: &str, target: &str) -> Result<()> {
37-
if module_path.starts_with("exploits/") {
38-
let module_name = module_path.trim_start_matches("exploits/").to_string();
39-
exploit::run_exploit(&module_name, target).await?;
40-
} else if module_path.starts_with("scanners/") {
41-
let module_name = module_path.trim_start_matches("scanners/").to_string();
42-
scanner::run_scan(&module_name, target).await?;
43-
} else if module_path.starts_with("creds/") {
44-
let module_name = module_path.trim_start_matches("creds/").to_string();
45-
creds::run_cred_check(&module_name, target).await?;
46-
} else {
47-
eprintln!("Unknown module path '{}'", module_path);
37+
let available = discover_modules();
38+
39+
if !available.contains(&module_path.to_string()) {
40+
eprintln!("Unknown module '{}'. Available modules:", module_path);
41+
for module in available {
42+
println!(" {}", module);
43+
}
44+
return Ok(());
45+
}
46+
47+
// Split path like "creds/generic/ftp_anonymous" -> ("creds", "generic/ftp_anonymous")
48+
let mut parts = module_path.splitn(2, '/');
49+
let category = parts.next().unwrap_or("");
50+
let module_name = parts.next().unwrap_or("");
51+
52+
match category {
53+
"exploits" => {
54+
exploit::run_exploit(module_name, target).await?;
55+
},
56+
"scanners" => {
57+
scanner::run_scan(module_name, target).await?;
58+
},
59+
"creds" => {
60+
creds::run_cred_check(module_name, target).await?;
61+
},
62+
_ => {
63+
eprintln!("Category '{}' is not supported.", category);
64+
}
4865
}
4966

5067
Ok(())
5168
}
69+
70+
/// Discover modules in src/modules/{exploits,scanners,creds} recursively up to 6 levels deep
71+
pub fn discover_modules() -> Vec<String> {
72+
let mut modules = Vec::new();
73+
74+
let categories = ["exploits", "scanners", "creds"];
75+
76+
for category in categories {
77+
let base_path = format!("src/modules/{}", category);
78+
let walker = WalkDir::new(&base_path).max_depth(6);
79+
80+
for entry in walker.into_iter().filter_map(|e| e.ok()) {
81+
let path = entry.path();
82+
83+
if path.is_file() && path.extension().map(|e| e == "rs").unwrap_or(false) {
84+
if let Ok(relative) = path.strip_prefix("src/modules") {
85+
let module_path = relative
86+
.with_extension("") // remove .rs
87+
.to_string_lossy()
88+
.replace("\\", "/"); // Windows compatibility
89+
modules.push(module_path);
90+
}
91+
}
92+
}
93+
}
94+
95+
modules
96+
}
File renamed without changes.
File renamed without changes.

src/modules/creds/generic/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
pub mod sample_cred_check;
3+
pub mod ftp_bruteforce;
4+
pub mod ftp_anonymous;
5+
pub mod telnet_bruteforce;
6+
File renamed without changes.

src/modules/creds/telnet_bruteforce.rs renamed to src/modules/creds/generic/telnet_bruteforce.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use threadpool::ThreadPool;
88
use crossbeam_channel::{unbounded};
99
use telnet::Telnet;
1010

11-
pub async fn run_module(target: &str) -> Result<()> {
11+
pub async fn run(target: &str) -> Result<()> {
1212
println!("\n=== Telnet Bruteforce Module (RustSploit) ===\n");
1313

1414
let target = target.to_string();

src/modules/creds/mod.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
pub mod sample_cred_check;
2-
pub mod ftp_bruteforce;
3-
pub mod ftp_anonymous;
4-
pub mod telnet_bruteforce;
1+
pub mod generic; // <-- lowercase folder name

src/utils.rs

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,73 @@
11
use colored::*;
22
use std::fs;
3-
use std::path::Path;
3+
use std::path::{Path,};
44

5-
/// Dynamically checks if a module path exists
6-
pub fn module_exists(module_path: &str) -> bool {
7-
if let Some((category, name)) = module_path.split_once('/') {
8-
let path = format!("src/modules/{}/{}.rs", category, name);
9-
Path::new(&path).exists()
10-
} else {
11-
false
5+
/// Maximum folder depth to traverse
6+
const MAX_DEPTH: usize = 6;
7+
8+
/// Recursively list .rs files up to a certain depth
9+
fn collect_module_paths(dir: &Path, depth: usize) -> Vec<String> {
10+
let mut modules = Vec::new();
11+
12+
if depth > MAX_DEPTH || !dir.exists() {
13+
return modules;
1214
}
13-
}
1415

15-
/// Lists all available modules in exploits, scanners, and creds
16-
pub fn list_all_modules() {
17-
let categories = [
18-
("exploits", "Exploits"),
19-
("scanners", "Scanners"),
20-
("creds", "Credentials"),
21-
];
16+
if let Ok(entries) = fs::read_dir(dir) {
17+
for entry in entries.flatten() {
18+
let path = entry.path();
2219

23-
println!("{}", "Available modules:".bold().underline());
20+
if path.is_dir() {
21+
modules.extend(collect_module_paths(&path, depth + 1));
22+
} else if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) {
23+
if file_name.ends_with(".rs") && file_name != "mod.rs" {
24+
let relative_path = path
25+
.strip_prefix("src/modules")
26+
.unwrap_or(&path)
27+
.with_extension("")
28+
.to_string_lossy()
29+
.replace('\\', "/"); // For Windows compatibility
2430

25-
for (folder, display_name) in categories {
26-
let mut modules = Vec::new();
27-
let dir_path = format!("src/modules/{}", folder);
28-
29-
if let Ok(entries) = fs::read_dir(&dir_path) {
30-
for entry in entries.flatten() {
31-
if let Some(file_name) = entry.file_name().to_str() {
32-
if file_name.ends_with(".rs") && file_name != "mod.rs" {
33-
let module_name = file_name.trim_end_matches(".rs").to_string();
34-
modules.push(module_name);
35-
}
31+
modules.push(relative_path);
3632
}
3733
}
3834
}
35+
}
3936

40-
modules.sort();
37+
modules
38+
}
4139

42-
if !modules.is_empty() {
43-
println!("\n{}:", display_name.blue().bold());
44-
for module in modules {
45-
println!(" - {}", format!("{}/{}", folder, module).green());
46-
}
40+
/// Dynamically checks if a module path exists at any depth
41+
pub fn module_exists(module_path: &str) -> bool {
42+
let modules = collect_module_paths(Path::new("src/modules"), 0);
43+
modules.iter().any(|m| m == module_path)
44+
}
45+
46+
/// Lists all available modules recursively under src/modules/
47+
pub fn list_all_modules() {
48+
println!("{}", "Available modules:".bold().underline());
49+
50+
let modules = collect_module_paths(Path::new("src/modules"), 0);
51+
if modules.is_empty() {
52+
println!("{}", "No modules found.".red());
53+
return;
54+
}
55+
56+
let mut grouped = std::collections::BTreeMap::new();
57+
58+
for module in modules {
59+
let parts: Vec<&str> = module.split('/').collect();
60+
let category = parts.get(0).unwrap_or(&"Other").to_string();
61+
grouped
62+
.entry(category)
63+
.or_insert_with(Vec::new)
64+
.push(module.clone());
65+
}
66+
67+
for (category, paths) in grouped {
68+
println!("\n{}:", category.blue().bold());
69+
for path in paths {
70+
println!(" - {}", path.green());
4771
}
4872
}
4973
}

0 commit comments

Comments
 (0)