(self)
| 26 | friendly_name = "Path Traversal" |
| 27 | |
| 28 | async def fuzz(self): |
| 29 | cookies = self.event.data.get("assigned_cookies", {}) |
| 30 | probe_value = self.incoming_probe_value(populate_empty=False) |
| 31 | if not probe_value: |
| 32 | self.debug( |
| 33 | f"Path Traversal detection requires original value, aborting [{self.event.data['type']}] [{self.event.data['name']}]" |
| 34 | ) |
| 35 | return |
| 36 | |
| 37 | # Single dot traversal tolerance test |
| 38 | path_techniques = { |
| 39 | "single-dot traversal tolerance (no-encoding)": { |
| 40 | "singledot_payload": f"./a/../{probe_value}", |
| 41 | "doubledot_payload": f"../a/../{probe_value}", |
| 42 | }, |
| 43 | "single-dot traversal tolerance (no-encoding, leading slash)": { |
| 44 | "singledot_payload": f"/./a/../{probe_value}", |
| 45 | "doubledot_payload": f"/../a/../{probe_value}", |
| 46 | }, |
| 47 | "single-dot traversal tolerance (url-encoding)": { |
| 48 | "singledot_payload": quote(f"./a/../{probe_value}".encode(), safe=""), |
| 49 | "doubledot_payload": quote(f"../a/../{probe_value}".encode(), safe=""), |
| 50 | }, |
| 51 | "single-dot traversal tolerance (url-encoding, leading slash)": { |
| 52 | "singledot_payload": quote(f"/./a/../{probe_value}".encode(), safe=""), |
| 53 | "doubledot_payload": quote(f"/../a/../{probe_value}".encode(), safe=""), |
| 54 | }, |
| 55 | "single-dot traversal tolerance (non-recursive stripping)": { |
| 56 | "singledot_payload": f"...//a/....//{probe_value}", |
| 57 | "doubledot_payload": f"....//a/....//{probe_value}", |
| 58 | }, |
| 59 | "single-dot traversal tolerance (non-recursive stripping, leading slash)": { |
| 60 | "singledot_payload": f"/...//a/....//{probe_value}", |
| 61 | "doubledot_payload": f"/....//a/....//{probe_value}", |
| 62 | }, |
| 63 | "single-dot traversal tolerance (double url-encoding)": { |
| 64 | "singledot_payload": f".%252fa%252f..%252f{probe_value}", |
| 65 | "doubledot_payload": f"..%252fa%252f..%252f{probe_value}", |
| 66 | }, |
| 67 | "single-dot traversal tolerance (double url-encoding, leading slash)": { |
| 68 | "singledot_payload": f"%252f.%252fa%252f..%252f{probe_value}", |
| 69 | "doubledot_payload": f"%252f..%252fa%252f..%252f{probe_value}", |
| 70 | }, |
| 71 | } |
| 72 | |
| 73 | compiled_regex = self.lightfuzz.helpers.re.compile(r"/(?:[\w-]+/)*[\w-]+\.\w+") |
| 74 | linux_path_regex = await self.lightfuzz.helpers.re.match(compiled_regex, probe_value) |
| 75 | if linux_path_regex is not None: |
| 76 | original_path_only = "/".join(probe_value.split("/")[:-1]) |
| 77 | original_filename_only = probe_value.split("/")[-1] |
| 78 | # Some servers validate the start of the path, so we construct our payload with the original path and filename |
| 79 | path_techniques["single-dot traversal tolerance (start of path validation)"] = { |
| 80 | "singledot_payload": f"{original_path_only}/./{original_filename_only}", |
| 81 | "doubledot_payload": f"{original_path_only}/../{original_filename_only}", |
| 82 | } |
| 83 | |
| 84 | for path_technique, payloads in path_techniques.items(): |
| 85 | iterations = 5 # one failed detection is tolerated, as long as its not the first run |
nothing calls this directly
no test coverage detected