1use std::collections::BTreeSet;
2
3use crate::models::{MSFModule, MSFNoModuleReason};
4
5use std::sync::OnceLock;
6
7fn catalog() -> &'static Vec<MSFModule> {
11 static MODULES: OnceLock<Vec<MSFModule>> = OnceLock::new();
12 MODULES.get_or_init(|| {
13 vec![
14 MSFModule {
15 name: "exploit/multi/http/apache_path_traversal".into(),
16 cve: Some("CVE-2021-41773".into()),
17 rank: "excellent".into(),
18 disclosure_date: "2021-10-05".into(),
19 platforms: vec!["Unix".into(), "Windows".into()],
20 required_options: vec!["RHOSTS".into(), "TARGETURI".into(), "LHOST".into()],
21 },
22 MSFModule {
23 name: "exploit/multi/http/log4shell_header_injection".into(),
24 cve: Some("CVE-2021-44228".into()),
25 rank: "excellent".into(),
26 disclosure_date: "2021-12-09".into(),
27 platforms: vec!["Unix".into(), "Windows".into(), "Java".into()],
28 required_options: vec![
29 "RHOSTS".into(),
30 "TARGETURI".into(),
31 "HTTP_HEADER".into(),
32 "LHOST".into(),
33 ],
34 },
35 MSFModule {
36 name: "exploit/windows/smb/ms17_010_eternalblue".into(),
37 cve: Some("CVE-2017-0144".into()),
38 rank: "excellent".into(),
39 disclosure_date: "2017-03-14".into(),
40 platforms: vec!["Windows".into()],
41 required_options: vec!["RHOSTS".into()],
42 },
43 MSFModule {
44 name: "exploit/windows/rdp/cve_2019_0708_bluekeep".into(),
45 cve: Some("CVE-2019-0708".into()),
46 rank: "excellent".into(),
47 disclosure_date: "2019-05-14".into(),
48 platforms: vec!["Windows".into()],
49 required_options: vec!["RHOSTS".into(), "RPORT".into()],
50 },
51 MSFModule {
52 name: "exploit/multi/http/struts2_content_type_ognl".into(),
53 cve: Some("CVE-2017-5638".into()),
54 rank: "excellent".into(),
55 disclosure_date: "2017-03-07".into(),
56 platforms: vec!["Unix".into(), "Windows".into(), "Java".into()],
57 required_options: vec!["RHOSTS".into(), "TARGETURI".into()],
58 },
59 MSFModule {
60 name: "exploit/windows/netlogon/zerologon".into(),
61 cve: Some("CVE-2020-1472".into()),
62 rank: "excellent".into(),
63 disclosure_date: "2020-08-11".into(),
64 platforms: vec!["Windows".into()],
65 required_options: vec!["RHOSTS".into(), "RPORT".into()],
66 },
67 MSFModule {
68 name: "exploit/windows/driver/cve_2021_34527_printnightmare".into(),
69 cve: Some("CVE-2021-34527".into()),
70 rank: "excellent".into(),
71 disclosure_date: "2021-07-01".into(),
72 platforms: vec!["Windows".into()],
73 required_options: vec!["RHOSTS".into(), "TARGET".into(), "LHOST".into()],
74 },
75 MSFModule {
76 name: "exploit/windows/http/exchange_proxylogon_rce".into(),
77 cve: Some("CVE-2021-26855".into()),
78 rank: "excellent".into(),
79 disclosure_date: "2021-02-27".into(),
80 platforms: vec!["Windows".into()],
81 required_options: vec![
82 "RHOSTS".into(),
83 "EMAIL".into(),
84 "PASSWORD".into(),
85 "LHOST".into(),
86 ],
87 },
88 MSFModule {
89 name: "auxiliary/scanner/http/cisco_directory_traversal".into(),
90 cve: Some("CVE-2020-3452".into()),
91 rank: "good".into(),
92 disclosure_date: "2020-07-14".into(),
93 platforms: vec!["Cisco ASA".into()],
94 required_options: vec!["RHOSTS".into(), "TARGETURI".into()],
95 },
96 MSFModule {
97 name: "exploit/unix/webapp/drupal_drupalgeddon2".into(),
98 cve: Some("CVE-2018-7600".into()),
99 rank: "excellent".into(),
100 disclosure_date: "2018-03-28".into(),
101 platforms: vec!["Unix".into(), "PHP".into()],
102 required_options: vec!["RHOSTS".into(), "TARGETURI".into(), "LHOST".into()],
103 },
104 MSFModule {
105 name: "exploit/multi/http/confluence_webwork_ognl_injection".into(),
106 cve: Some("CVE-2021-26084".into()),
107 rank: "excellent".into(),
108 disclosure_date: "2021-08-25".into(),
109 platforms: vec!["Unix".into(), "Windows".into(), "Java".into()],
110 required_options: vec!["RHOSTS".into(), "TARGETURI".into(), "LHOST".into()],
111 },
112 MSFModule {
113 name: "exploit/multi/http/vmware_vcenter_uploadova_rce".into(),
114 cve: Some("CVE-2021-21972".into()),
115 rank: "excellent".into(),
116 disclosure_date: "2021-02-23".into(),
117 platforms: vec!["Unix".into(), "Windows".into()],
118 required_options: vec!["RHOSTS".into(), "TARGETURI".into(), "LHOST".into()],
119 },
120 MSFModule {
121 name: "auxiliary/scanner/http/apache_version".into(),
122 cve: None,
123 rank: "normal".into(),
124 disclosure_date: "unknown".into(),
125 platforms: vec!["Apache".into()],
126 required_options: vec!["RHOSTS".into()],
127 },
128 MSFModule {
129 name: "auxiliary/scanner/http/iis_version".into(),
130 cve: None,
131 rank: "normal".into(),
132 disclosure_date: "unknown".into(),
133 platforms: vec!["Windows / IIS".into()],
134 required_options: vec!["RHOSTS".into()],
135 },
136 MSFModule {
137 name: "auxiliary/scanner/ssh/ssh_version".into(),
138 cve: None,
139 rank: "normal".into(),
140 disclosure_date: "unknown".into(),
141 platforms: vec!["OpenSSH".into()],
142 required_options: vec!["RHOSTS".into(), "RPORT".into()],
143 },
144 MSFModule {
145 name: "auxiliary/scanner/mysql/mysql_version".into(),
146 cve: None,
147 rank: "normal".into(),
148 disclosure_date: "unknown".into(),
149 platforms: vec!["MySQL".into()],
150 required_options: vec!["RHOSTS".into(), "RPORT".into()],
151 },
152 MSFModule {
153 name: "exploit/linux/mysql/mysql_yassl_getname".into(),
154 cve: None,
155 rank: "good".into(),
156 disclosure_date: "2012-05-10".into(),
157 platforms: vec!["Linux / MySQL".into()],
158 required_options: vec!["RHOSTS".into(), "RPORT".into()],
159 },
160 MSFModule {
161 name: "auxiliary/scanner/postgres/postgres_version".into(),
162 cve: None,
163 rank: "normal".into(),
164 disclosure_date: "unknown".into(),
165 platforms: vec!["PostgreSQL".into()],
166 required_options: vec!["RHOSTS".into(), "RPORT".into()],
167 },
168 MSFModule {
169 name: "auxiliary/scanner/smb/smb_version".into(),
170 cve: None,
171 rank: "normal".into(),
172 disclosure_date: "unknown".into(),
173 platforms: vec!["SMB".into()],
174 required_options: vec!["RHOSTS".into()],
175 },
176 MSFModule {
177 name: "auxiliary/scanner/ftp/ftp_version".into(),
178 cve: None,
179 rank: "normal".into(),
180 disclosure_date: "unknown".into(),
181 platforms: vec!["FTP".into()],
182 required_options: vec!["RHOSTS".into(), "RPORT".into()],
183 },
184 MSFModule {
185 name: "auxiliary/scanner/rdp/rdp_scanner".into(),
186 cve: None,
187 rank: "normal".into(),
188 disclosure_date: "unknown".into(),
189 platforms: vec!["RDP".into()],
190 required_options: vec!["RHOSTS".into(), "RPORT".into()],
191 },
192 MSFModule {
193 name: "exploit/multi/http/tomcat_jsp_upload_bypass".into(),
194 cve: None,
195 rank: "excellent".into(),
196 disclosure_date: "2017-06-19".into(),
197 platforms: vec!["Tomcat".into()],
198 required_options: vec![
199 "RHOSTS".into(),
200 "TARGETURI".into(),
201 "TARGET".into(),
202 "LHOST".into(),
203 ],
204 },
205 MSFModule {
206 name: "exploit/multi/http/jenkins_script_console".into(),
207 cve: None,
208 rank: "excellent".into(),
209 disclosure_date: "2019-01-01".into(),
210 platforms: vec!["Jenkins".into()],
211 required_options: vec!["RHOSTS".into(), "TARGETURI".into(), "LHOST".into()],
212 },
213 MSFModule {
214 name: "auxiliary/scanner/http/wordpress_scanner".into(),
215 cve: None,
216 rank: "normal".into(),
217 disclosure_date: "unknown".into(),
218 platforms: vec!["WordPress".into()],
219 required_options: vec!["RHOSTS".into(), "TARGETURI".into()],
220 },
221 MSFModule {
222 name: "auxiliary/scanner/http/robots_txt".into(),
223 cve: None,
224 rank: "normal".into(),
225 disclosure_date: "unknown".into(),
226 platforms: vec!["HTTP".into()],
227 required_options: vec!["RHOSTS".into(), "TARGETURI".into()],
228 },
229 MSFModule {
230 name: "auxiliary/scanner/http/golang_pprof".into(),
231 cve: None,
232 rank: "normal".into(),
233 disclosure_date: "unknown".into(),
234 platforms: vec!["Go".into()],
235 required_options: vec!["RHOSTS".into(), "TARGETURI".into()],
236 },
237 ]
238 })
239}
240
241pub fn modules_for_cve(cve: &str) -> Vec<MSFModule> {
243 let needle = cve.trim().to_uppercase();
244 catalog()
245 .iter()
246 .filter(|module| {
247 module
248 .cve
249 .as_deref()
250 .map(|known| known.eq_ignore_ascii_case(&needle))
251 .unwrap_or(false)
252 })
253 .cloned()
254 .collect()
255}
256
257pub fn modules_for_technology(needles: &[String]) -> Vec<MSFModule> {
260 if needles.is_empty() {
261 return Vec::new();
262 }
263 let lowered: Vec<String> = needles.iter().map(|n| n.to_lowercase()).collect();
264 let mut found = BTreeSet::new();
265 let mut result = Vec::new();
266 for module in catalog() {
267 if module.cve.is_some() {
268 continue; }
270 let haystack = format!(
271 "{}{}{}{}",
272 module.name,
273 module.platforms.join(" "),
274 module.disclosure_date,
275 module
276 .required_options
277 .iter()
278 .map(|o| o.to_string())
279 .collect::<Vec<_>>()
280 .join(" ")
281 )
282 .to_lowercase();
283 if lowered.iter().any(|needle| haystack.contains(needle))
284 && found.insert(module.name.clone())
285 {
286 result.push(module.clone());
287 }
288 }
289 result
290}
291
292pub fn no_module_reason(cve: &str) -> MSFNoModuleReason {
295 let cve = cve.trim().to_uppercase();
296 if modules_for_cve(&cve).is_empty() {
297 MSFNoModuleReason {
298 cve,
299 reason: "No Metasploit module is known for this CVE in the offline catalog. Verify with `msfconsole -q -x \"search cve:<id>\"` against an updated Metasploit install; if none exists, check Exploit-DB and packet storm for public PoCs.".to_string(),
300 }
301 } else {
302 MSFNoModuleReason {
303 cve,
304 reason: "Module available; see Metasploit Recommendations.".to_string(),
305 }
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[test]
314 fn cve_lookup_returns_exact_module() {
315 let modules = modules_for_cve("cve-2021-44228");
316 assert_eq!(modules.len(), 1);
317 assert_eq!(
318 modules[0].name,
319 "exploit/multi/http/log4shell_header_injection"
320 );
321 assert_eq!(modules[0].rank, "excellent");
322 assert!(!modules[0].required_options.is_empty());
323 }
324
325 #[test]
326 fn unknown_cve_has_explicit_reason() {
327 let reason = no_module_reason("CVE-2099-99999");
328 assert!(reason.reason.contains("No Metasploit module"));
329 }
330
331 #[test]
332 fn technology_lookup_filters_cve_modules() {
333 let modules = modules_for_technology(&["apache".to_string()]);
334 assert!(modules
335 .iter()
336 .any(|m| m.name == "auxiliary/scanner/http/apache_version"));
337 assert!(modules.iter().all(|m| m.cve.is_none()));
338 }
339}