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