1use super::utils::create_payload_for_get;
2use super::*;
3use serde_json::json;
5
6pub fn change_payload(orig: &Value, path: &[String], new_val: Value) -> Value {
7 let mut change = &mut json!(null);
8 let mut ret = orig.clone();
9 for path_part in path.iter() {
10 change = &mut ret[path_part];
11 }
12 *change = new_val;
13 ret.clone()
14}
15
16
17impl<T: OAS + Serialize> ActiveScan<T> {
18 pub async fn check_min_max(&self, auth: &Authorization) -> CheckRetVal {
19 let mut ret_val = CheckRetVal::default();
20 for oas_map in self.payloads.iter() {
21 for (json_path, schema) in &oas_map.payload.map {
22 let test_vals = Vec::from([
23 schema.minimum.map(|min| ("minimum", min - 1.0)),
24 schema.maximum.map(|max| ("maximum", max + 1.0)),
25 ]);
26 for val in test_vals.into_iter().flatten() {
27 for (m, op) in oas_map
28 .path
29 .path_item
30 .get_ops()
31 .iter()
32 .filter(|(m, _)| m == &Method::POST)
33 {
34 let vec_param =
35 create_payload_for_get(&self.oas_value, op, Some("".to_string()));
36 let req = AttackRequest::builder()
37 .servers(self.oas.servers(), true)
38 .path(&oas_map.path.path)
39 .method(*m)
40 .headers(vec![])
41 .parameters(vec_param.clone())
42 .auth(auth.clone())
43 .payload(
44 &change_payload(&oas_map.payload.payload, json_path, json!(val.1))
45 .to_string(),
46 )
47 .build();
48 let response_vector = req.send_request_all_servers(self.verbosity > 0).await;
49 for response in response_vector {
50 ret_val.1.push(&req, &response, "Testing /max values".to_string());
51 ret_val.0.push((
52 ResponseData {
53 location: oas_map.path.path.clone(),
54 alert_text: format!(
55 "The {} for {:?} is not enforced by the server",
56 val.0, json_path
57 ),
58 serverity: Level::Low,
59 },
60 response,
61 ));
62 }
63 }
64 }
65 }
66 }
67 ret_val
68 }
69
70 pub async fn check_open_redirect(&self, auth: &Authorization) -> CheckRetVal {
71 let mut ret_val = CheckRetVal::default();
72 for (path, item) in &self.oas.get_paths() {
73 for (m, op) in item.get_ops().iter().filter(|(m, _)| m == &Method::GET) {
74 let vec_param = create_payload_for_get(
75 &self.oas_value,
76 op,
77 Some("http://www.google.com".to_string()),
78 );
79 for param_item in &vec_param {
80 if param_item.dm == QuePay::Query
82 && LIST_PARAM.contains(¶m_item.name.as_str())
83 {
84 let param_to_redirect = param_item.name.to_owned();
85 let req = AttackRequest::builder()
86 .servers(self.oas.servers(), true)
87 .path(path)
88 .parameters(vec_param.clone())
89 .auth(auth.clone())
90 .method(*m)
91 .headers(vec![])
92 .auth(auth.clone())
93 .build();
94 let response_vector = req.send_request_all_servers(self.verbosity > 0).await;
95 for response in response_vector {
96 ret_val.1.push(&req, &response, "Testing /max values".to_string());
97 ret_val.0.push((
98 ResponseData {
99 location: path.clone(),
100 alert_text: format!(
101 "The parameter {} seems to be vulnerable to open-redirect, location: {} "
102 , param_to_redirect, path),
103 serverity: Level::Medium,
104 },
105 response,
106 ));
107 }
108 break; }
110 }
111 }
112 }
113 ret_val
114 }
115
116 pub async fn check_string_length_max(&self, auth: &Authorization) -> CheckRetVal {
117 let mut ret_val = CheckRetVal::default();
118 for oas_map in self.payloads.iter() {
119 for (json_path, schema) in &oas_map.payload.map {
120 if let Some(max_len) = schema.max_length {
121 let new_string = iter::repeat(['B', 'L', 'S', 'T'])
122 .flatten()
123 .take(max_len.try_into().unwrap())
124 .collect::<String>();
125 for (m, op) in oas_map
126 .path
127 .path_item
128 .get_ops()
129 .iter()
130 .filter(|(m, _)| m == &Method::POST)
131 {
132 let vec_param =
133 create_payload_for_get(&self.oas_value, op, Some("".to_string()));
134
135 let url = self.oas.servers();
136
137 let req = AttackRequest::builder()
138 .servers(url, true)
139 .path(&oas_map.path.path)
140 .method(*m)
141 .headers(vec![])
142 .parameters(vec_param.clone())
143 .auth(auth.clone())
144 .headers(Vec::from([MHeader {
145 name: "Content-Type".to_string(),
146 value: "application/json".to_string(),
147 }]))
148 .payload(
149 &change_payload(
150 &oas_map.payload.payload,
151 json_path,
152 json!(new_string),
153 )
154 .to_string(),
155 )
156 .build();
157 let response_vector = req.send_request_all_servers(self.verbosity > 0).await;
158 for response in response_vector {
159 ret_val.1.push(&req, &response, "Testing /max values".to_string());
160 ret_val.0.push((
161 ResponseData {
162 location: oas_map.path.path.clone(),
163 alert_text: format!(
164 "The {} length limit for {:?} is not enforced by the server",
165 max_len,
166 json_path
167 ),
168 serverity: Level::Low,
169 },
170 response,
171 ));
172 }
173 }
174 }
175 }
176 }
177 ret_val
178 }
179
180 pub async fn check_parameter_pollution(
181 &self,
182 auth: &Authorization,
183 ) -> (CheckRetVal, Vec<String>) {
184 let mut ret_val = CheckRetVal::default();
185 let vec_polluted = vec!["blstparamtopollute".to_string()];
186 for (path, item) in &self.oas.get_paths() {
188 for (m, op) in item.get_ops() {
189 let _text = path.to_string();
190 if m == Method::GET {
191 let mut vec_param = create_payload_for_get(&self.oas_value, op, None);
192 let indices = vec_param
193 .iter()
194 .enumerate()
195 .filter(|(_, x)| x.dm == QuePay::Query)
196 .map(|(index, _)| index)
197 .collect::<Vec<_>>();
198 for i in indices {
199 let param_query_pollute = vec_param.get(i).unwrap().clone();
200 vec_param.push(param_query_pollute);
201 let req = AttackRequest::builder()
202 .servers(self.oas.servers(), true)
203 .path(path)
204 .auth(auth.clone())
205 .parameters(vec_param.clone())
206 .method(m)
207 .headers(vec![])
208 .auth(auth.clone())
209 .build();
210 let response_vector = req.send_request_all_servers(self.verbosity > 0).await;
211 for response in response_vector {
212 ret_val.1.push(&req, &response, "Testing get parameter pollution ".to_string());
213 ret_val.0.push((
214 ResponseData {
215 location: path.clone(),
216 alert_text: format!(
217 "The {} parameter in the {} endpoint seems to be vulnerable to parameter pollution"
218 , vec_param.last().unwrap().name, path),
219 serverity: Level::Medium,
220 },
221 response,
222 ));
223 }
224 vec_param.remove(vec_param.len() - 1);
225 }
226 }
227 }
228 }
229 (ret_val, vec_polluted)
230 }
231
232
233 pub async fn check_ssl(&self, auth: &Authorization) -> CheckRetVal {
234 let mut ret_val = CheckRetVal::default();
235 let req = AttackRequest::builder()
236 .servers(self.oas.servers(), false)
237 .path("")
238 .auth(auth.clone())
239 .parameters(vec![])
240 .method(Method::GET)
241 .headers(vec![])
242 .auth(auth.clone())
243 .build();
244 let response_vector = req.send_request_all_servers(self.verbosity > 0).await;
245 for (response,server) in response_vector.iter().zip(req.servers.iter()) {
246 ret_val.1.push(&req, response, "Testing SSL".to_string());
247 ret_val.0.push((
248 ResponseData {
249 location: server.base_url.clone(),
250 alert_text: format!(
251 "The server does not seem to be using SSL, status code: {}",
252 response.status
253 ),
254 serverity: Level::Low,
255 },
256 response.clone(),
257 ));
258 }
259 ret_val
260 }
261
262 pub async fn check_authentication(&self, _auth: &Authorization) -> CheckRetVal {
263 let mut ret_val = CheckRetVal::default();
264 for oas_map in self.payloads.iter() {
265 for _schema in oas_map.payload.map.values() {
266 for (m, op) in oas_map.path.path_item.get_ops().iter() {
267 let vec_param =
268 create_payload_for_get(&self.oas_value, op, Some("".to_string()));
269 if let Some(_value) = &op.security {
270 let req: AttackRequest =
271 if m == &Method::POST {
272 AttackRequest::builder() .servers(self.oas.servers(), true)
274 .path(&oas_map.path.path)
275 .method(*m)
276 .headers(vec![])
277 .parameters(vec_param.clone())
278 .payload(&oas_map.payload.payload.to_string())
280 .build()
281 } else {
282 AttackRequest::builder()
283 .servers(self.oas.servers(), true)
284 .path(&oas_map.path.path)
285 .method(*m)
286 .headers(vec![])
287 .parameters(vec_param.clone())
288 .build()
289 };
290 let response_vector = req.send_request_all_servers(self.verbosity > 0).await;
291 for response in response_vector {
292 ret_val.1.push(&req, &response, "Testing authentication".to_string());
293 ret_val.0.push((
294 ResponseData {
295 location: oas_map.path.path.clone(),
296 alert_text: format!(
297 "The {} endpoint does not seem to require authentication",
298 oas_map.path.path
299 ),
300 serverity: Level::High,
301 },
302 response,
303 ));
304 }
305 }
306 }
307 }
308 }
309 ret_val
310 }
311
312 pub async fn check_method_permissions_active(&self, auth: &Authorization) -> CheckRetVal {
313 let mut ret_val = CheckRetVal::default();
314 for (path, item) in &self.oas.get_paths() {
315 let current_method_set = item
316 .get_ops()
317 .iter()
318 .map(|(m, _)| m)
319 .cloned()
320 .collect::<HashSet<_>>();
321
322 let vec_param =
323 create_payload_for_get(&self.oas_value, item.get_ops()[0].1, Some("".to_string()));
324
325 let all_method_set = HashSet::from(LIST_METHOD);
326 for method in all_method_set.difference(¤t_method_set).cloned() {
327 let req = AttackRequest::builder()
328 .servers(self.oas.servers(), true)
329 .path(path)
330 .parameters(vec_param.clone())
331 .auth(auth.clone())
332 .method(method)
333 .headers(vec![])
334 .build();
335 let response_vector = req.send_request_all_servers(self.verbosity > 0).await;
336 for response in response_vector {
337 ret_val.1.push(&req, &response, "Testing method permissions".to_string());
338 ret_val.0.push((
339 ResponseData {
340 location: path.clone(),
341 alert_text: format!(
342 "The {} endpoint accepts {:?} although its not documented to",
343 path, method
344 ),
345 serverity: Level::High,
346 },
347 response,
348 ));
349 }
350 }
351 }
352 ret_val
353 }
354}
355
356const LIST_METHOD: [Method; 3] = [Method::GET, Method::POST, Method::PUT];
357
358const LIST_PARAM: [&str; 85] = [
359 "photoUrls",
360 "page",
361 "url",
362 "ret",
363 "r2",
364 "img",
365 "u",
366 "return",
367 "r",
368 "URL",
369 "next",
370 "redirect",
371 "redirectBack",
372 "AuthState",
373 "referer",
374 "redir",
375 "l",
376 "aspxerrorpath",
377 "image_path",
378 "ActionCodeURL",
379 "return_url",
380 "link",
381 "q",
382 "location",
383 "ReturnUrl",
384 "uri",
385 "referrer",
386 "returnUrl",
387 "forward",
388 "file",
389 "rb",
390 "end_display",
391 "urlact",
392 "from",
393 "goto",
394 "path",
395 "redirect_url",
396 "old",
397 "pathlocation",
398 "successTarget",
399 "returnURL",
400 "urlsito",
401 "newurl",
402 "Url",
403 "back",
404 "retour",
405 "odkazujuca_linka",
406 "r_link",
407 "cur_url",
408 "H_name",
409 "ref",
410 "topic",
411 "resource",
412 "returnTo",
413 "home",
414 "node",
415 "sUrl",
416 "href",
417 "linkurl",
418 "returnto",
419 "redirecturl",
420 "SL",
421 "st",
422 "errorUrl",
423 "media",
424 "destination",
425 "targeturl",
426 "return_to",
427 "cancel_url",
428 "doc",
429 "GO",
430 "ReturnTo",
431 "anything",
432 "FileName",
433 "logoutRedirectURL",
434 "list",
435 "startUrl",
436 "service",
437 "redirect_to",
438 "end_url",
439 "_next",
440 "noSuchEntryRedirect",
441 "context",
442 "returnurl",
443 "ref_url",
444];