ploit_malper/engine/
matcher.rs1use crate::models::ModuleSuggestion;
2
3const BANNER_CATALOG: &[(&str, &[&str])] = &[
4 (
5 "apache",
6 &[
7 "exploit/multi/http/apache_mod_cgi_bash_env_exec",
8 "auxiliary/scanner/http/apache_version",
9 ],
10 ),
11 ("iis", &["auxiliary/scanner/http/iis_version"]),
12 ("openssh", &["auxiliary/scanner/ssh/ssh_version"]),
13 (
14 "mysql",
15 &[
16 "auxiliary/scanner/mysql/mysql_version",
17 "exploit/linux/mysql/mysql_yassl_getname",
18 ],
19 ),
20 (
21 "postgresql",
22 &["auxiliary/scanner/postgres/postgres_version"],
23 ),
24 (
25 "smb",
26 &[
27 "auxiliary/scanner/smb/smb_version",
28 "exploit/windows/smb/ms17_010_eternalblue",
29 ],
30 ),
31 ("ftp", &["auxiliary/scanner/ftp/ftp_version"]),
32 (
33 "rdp",
34 &[
35 "auxiliary/scanner/rdp/rdp_scanner",
36 "exploit/windows/rdp/cve_2019_0708_bluekeep",
37 ],
38 ),
39 ("tomcat", &["exploit/multi/http/tomcat_jsp_upload_bypass"]),
40 ("jenkins", &["exploit/multi/http/jenkins_script_console"]),
41 ("wordpress", &["auxiliary/scanner/http/wordpress_scanner"]),
42 ("drupal", &["exploit/unix/webapp/drupal_drupalgeddon2"]),
43 (
44 "wp-admin",
45 &[
46 "auxiliary/scanner/http/wordpress_scanner",
47 "auxiliary/scanner/http/wordpress_login_enum",
48 ],
49 ),
50 ("robots.txt", &["auxiliary/scanner/http/robots_txt"]),
51 ("pprof", &["auxiliary/scanner/http/golang_pprof"]),
52 (
53 "debug",
54 &[
55 "auxiliary/scanner/http/debug_page",
56 "auxiliary/scanner/http/http_version",
57 ],
58 ),
59];
60
61fn banner_matches(banner_lower: &str, keyword: &str) -> bool {
62 if keyword.len() <= 3 {
63 let pattern = format!(
64 r"(?i)(^|[\s/_.\-:;]){}([\s/_.\-:;]|$)",
65 regex::escape(keyword)
66 );
67 regex::Regex::new(&pattern)
68 .expect("valid regex")
69 .is_match(banner_lower)
70 } else {
71 banner_lower.contains(keyword)
72 }
73}
74
75pub fn suggest_modules(service_banner: &str) -> Vec<ModuleSuggestion> {
76 let banner_lower = service_banner.to_lowercase();
77 let mut suggestions = Vec::new();
78
79 for (keyword, modules) in BANNER_CATALOG {
80 if banner_matches(&banner_lower, keyword) {
81 for module in *modules {
82 suggestions.push(ModuleSuggestion {
83 service_banner: service_banner.to_string(),
84 suggested_module: (*module).to_string(),
85 confidence: "high".to_string(),
86 });
87 }
88 }
89 }
90
91 suggestions
92}
93
94pub fn suggest_from_title(title: &str, details: &str) -> Vec<ModuleSuggestion> {
95 let combined = format!("{} {}", title.to_lowercase(), details.to_lowercase());
96 let mut suggestions = Vec::new();
97
98 for (keyword, modules) in BANNER_CATALOG {
99 if banner_matches(&combined, keyword) {
100 for module in *modules {
101 suggestions.push(ModuleSuggestion {
102 service_banner: format!("title/details match: {}", keyword),
103 suggested_module: (*module).to_string(),
104 confidence: "medium".to_string(),
105 });
106 }
107 }
108 }
109
110 suggestions
111}
112
113pub fn get_all_known_banners() -> Vec<String> {
114 let mut banners = BANNER_CATALOG
115 .iter()
116 .map(|(keyword, _)| (*keyword).to_string())
117 .collect::<Vec<_>>();
118 banners.sort();
119 banners
120}