diff --git a/monkey/common/cloud/azure/azure_instance.py b/monkey/common/cloud/azure/azure_instance.py index eb702a03d..af6c85460 100644 --- a/monkey/common/cloud/azure/azure_instance.py +++ b/monkey/common/cloud/azure/azure_instance.py @@ -4,6 +4,7 @@ import requests from common.cloud.environment_names import Environment from common.cloud.instance import CloudInstance +from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT LATEST_AZURE_METADATA_API_VERSION = "2019-04-30" AZURE_METADATA_SERVICE_URL = "http://169.254.169.254/metadata/instance?api-version=%s" % LATEST_AZURE_METADATA_API_VERSION @@ -32,7 +33,9 @@ class AzureInstance(CloudInstance): self.on_azure = False try: - response = requests.get(AZURE_METADATA_SERVICE_URL, headers={"Metadata": "true"}) + response = requests.get(AZURE_METADATA_SERVICE_URL, + headers={"Metadata": "true"}, + timeout=SHORT_REQUEST_TIMEOUT) self.on_azure = True # If not on cloud, the metadata URL is non-routable and the connection will fail. diff --git a/monkey/common/cloud/gcp/gcp_instance.py b/monkey/common/cloud/gcp/gcp_instance.py index 54f7e6d24..d81fd2186 100644 --- a/monkey/common/cloud/gcp/gcp_instance.py +++ b/monkey/common/cloud/gcp/gcp_instance.py @@ -4,6 +4,7 @@ import requests from common.cloud.environment_names import Environment from common.cloud.instance import CloudInstance +from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT logger = logging.getLogger(__name__) @@ -26,7 +27,7 @@ class GcpInstance(CloudInstance): try: # If not on GCP, this domain shouldn't resolve. - response = requests.get(GCP_METADATA_SERVICE_URL) + response = requests.get(GCP_METADATA_SERVICE_URL, timeout=SHORT_REQUEST_TIMEOUT) if response: logger.debug("Got ok metadata response: on GCP") diff --git a/monkey/common/common_consts/timeouts.py b/monkey/common/common_consts/timeouts.py new file mode 100644 index 000000000..f315e7518 --- /dev/null +++ b/monkey/common/common_consts/timeouts.py @@ -0,0 +1,3 @@ +SHORT_REQUEST_TIMEOUT = 2.5 # Seconds. Use where we expect timeout. +MEDIUM_REQUEST_TIMEOUT = 5 # Seconds. Use where we don't expect timeout. +LONG_REQUEST_TIMEOUT = 15 # Seconds. Use where we don't expect timeout and operate heavy data. diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 35922286f..9b0443791 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -9,6 +9,9 @@ from requests.exceptions import ConnectionError import infection_monkey.monkeyfs as monkeyfs import infection_monkey.tunnel as tunnel +from common.common_consts.timeouts import (LONG_REQUEST_TIMEOUT, + MEDIUM_REQUEST_TIMEOUT, + SHORT_REQUEST_TIMEOUT) from common.data.api_url_consts import T1216_PBA_FILE_DOWNLOAD_PATH from infection_monkey.config import GUID, WormConfiguration from infection_monkey.network.info import check_internet_access, local_ips @@ -121,7 +124,8 @@ class ControlClient(object): data=json.dumps(monkey), headers={'content-type': 'application/json'}, verify=False, - proxies=ControlClient.proxies) + proxies=ControlClient.proxies, + timeout=MEDIUM_REQUEST_TIMEOUT) except Exception as exc: LOG.warning("Error connecting to control server %s: %s", WormConfiguration.current_server, exc) @@ -138,7 +142,8 @@ class ControlClient(object): data=json.dumps(telemetry), headers={'content-type': 'application/json'}, verify=False, - proxies=ControlClient.proxies) + proxies=ControlClient.proxies, + timeout=MEDIUM_REQUEST_TIMEOUT) except Exception as exc: LOG.warning("Error connecting to control server %s: %s", WormConfiguration.current_server, exc) @@ -153,7 +158,8 @@ class ControlClient(object): data=json.dumps(telemetry), headers={'content-type': 'application/json'}, verify=False, - proxies=ControlClient.proxies) + proxies=ControlClient.proxies, + timeout=MEDIUM_REQUEST_TIMEOUT) except Exception as exc: LOG.warning("Error connecting to control server %s: %s", WormConfiguration.current_server, exc) @@ -165,7 +171,8 @@ class ControlClient(object): try: reply = requests.get("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID), # noqa: DUO123 verify=False, - proxies=ControlClient.proxies) + proxies=ControlClient.proxies, + timeout=MEDIUM_REQUEST_TIMEOUT) except Exception as exc: LOG.warning("Error connecting to control server %s: %s", @@ -194,7 +201,8 @@ class ControlClient(object): data=json.dumps({'config_error': True}), headers={'content-type': 'application/json'}, verify=False, - proxies=ControlClient.proxies) + proxies=ControlClient.proxies, + timeout=MEDIUM_REQUEST_TIMEOUT) except Exception as exc: LOG.warning("Error connecting to control server %s: %s", WormConfiguration.current_server, exc) return {} @@ -255,7 +263,8 @@ class ControlClient(object): download = requests.get("https://%s/api/monkey/download/%s" % # noqa: DUO123 (WormConfiguration.current_server, filename), verify=False, - proxies=ControlClient.proxies) + proxies=ControlClient.proxies, + timeout=MEDIUM_REQUEST_TIMEOUT) with monkeyfs.open(dest_file, 'wb') as file_obj: for chunk in download.iter_content(chunk_size=DOWNLOAD_CHUNK): @@ -281,7 +290,8 @@ class ControlClient(object): reply = requests.post("https://%s/api/monkey/download" % (WormConfiguration.current_server,), # noqa: DUO123 data=json.dumps(host_dict), headers={'content-type': 'application/json'}, - verify=False, proxies=ControlClient.proxies) + verify=False, proxies=ControlClient.proxies, + timeout=LONG_REQUEST_TIMEOUT) if 200 == reply.status_code: result_json = reply.json() filename = result_json.get('filename') @@ -323,7 +333,8 @@ class ControlClient(object): return requests.get(PBA_FILE_DOWNLOAD % # noqa: DUO123 (WormConfiguration.current_server, filename), verify=False, - proxies=ControlClient.proxies) + proxies=ControlClient.proxies, + timeout=LONG_REQUEST_TIMEOUT) except requests.exceptions.RequestException: return False @@ -334,7 +345,8 @@ class ControlClient(object): T1216_PBA_FILE_DOWNLOAD_PATH), verify=False, proxies=ControlClient.proxies, - stream=True) + stream=True, + timeout=MEDIUM_REQUEST_TIMEOUT) except requests.exceptions.RequestException: return False @@ -352,7 +364,7 @@ class ControlClient(object): def can_island_see_port(port): try: url = f"https://{WormConfiguration.current_server}/api/monkey_control/check_remote_port/{port}" - response = requests.get(url, verify=False) + response = requests.get(url, verify=False, timeout=SHORT_REQUEST_TIMEOUT) response = json.loads(response.content.decode()) return response['status'] == "port_visible" except requests.exceptions.RequestException: @@ -362,4 +374,5 @@ class ControlClient(object): def report_start_on_island(): requests.post(f"https://{WormConfiguration.current_server}/api/monkey_control/started_on_island", data=json.dumps({'started_on_island': True}), - verify=False) + verify=False, + timeout=MEDIUM_REQUEST_TIMEOUT) diff --git a/monkey/infection_monkey/exploit/drupal.py b/monkey/infection_monkey/exploit/drupal.py index 84919baef..5872f4703 100644 --- a/monkey/infection_monkey/exploit/drupal.py +++ b/monkey/infection_monkey/exploit/drupal.py @@ -9,6 +9,8 @@ from urllib.parse import urljoin import requests +from common.common_consts.timeouts import (LONG_REQUEST_TIMEOUT, + MEDIUM_REQUEST_TIMEOUT) from common.network.network_utils import remove_port from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.model import ID_STRING @@ -75,7 +77,8 @@ class DrupalExploiter(WebRCE): response = requests.get(f'{url}?_format=hal_json', # noqa: DUO123 json=payload, headers={"Content-Type": "application/hal+json"}, - verify=False) + verify=False, + timeout=MEDIUM_REQUEST_TIMEOUT) if is_response_cached(response): LOG.info(f'Checking if node {url} is vuln returned cache HIT, ignoring') @@ -92,7 +95,8 @@ class DrupalExploiter(WebRCE): r = requests.get(f'{url}?_format=hal_json', # noqa: DUO123 json=payload, headers={"Content-Type": "application/hal+json"}, - verify=False) + verify=False, + timeout=LONG_REQUEST_TIMEOUT) if is_response_cached(r): LOG.info(f'Exploiting {url} returned cache HIT, may have failed') @@ -136,7 +140,9 @@ def find_exploitbale_article_ids(base_url: str, lower: int = 1, upper: int = 100 articles = set() while lower < upper: node_url = urljoin(base_url, str(lower)) - response = requests.get(node_url, verify=False) # noqa: DUO123 + response = requests.get(node_url, + verify=False, + timeout=LONG_REQUEST_TIMEOUT) # noqa: DUO123 if response.status_code == 200: if is_response_cached(response): LOG.info(f'Found a cached article at: {node_url}, skipping') diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 632d968d4..eaf5d007b 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -11,6 +11,7 @@ import string import requests +from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from infection_monkey.exploit.tools.helpers import (build_monkey_commandline, get_monkey_depth) from infection_monkey.exploit.tools.http_tools import HTTPTools @@ -59,18 +60,20 @@ class HadoopExploiter(WebRCE): def exploit(self, url, command): # Get the newly created application id - resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/new-application")) + resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/new-application"), + timeout=LONG_REQUEST_TIMEOUT) resp = json.loads(resp.content) app_id = resp['application-id'] # Create a random name for our application in YARN rand_name = ID_STRING + "".join([random.choice(string.ascii_lowercase) for _ in range(self.RAN_STR_LEN)]) payload = self.build_payload(app_id, rand_name, command) - resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/"), json=payload) + resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT) return resp.status_code == 202 def check_if_exploitable(self, url): try: - resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/new-application")) + resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/new-application"), + timeout=LONG_REQUEST_TIMEOUT) except requests.ConnectionError: return False return resp.status_code == 200 diff --git a/monkey/infection_monkey/network/windowsserver_fingerprint.py b/monkey/infection_monkey/network/windowsserver_fingerprint.py index a4912d4fb..3bb1a8450 100644 --- a/monkey/infection_monkey/network/windowsserver_fingerprint.py +++ b/monkey/infection_monkey/network/windowsserver_fingerprint.py @@ -3,14 +3,11 @@ Implementation from https://github.com/SecuraBV/CVE-2020-1472 """ import logging -import subprocess import nmb.NetBIOS from impacket.dcerpc.v5 import epm, nrpc, transport -import infection_monkey.config from infection_monkey.network.HostFinger import HostFinger -from infection_monkey.utils.environment import is_windows_os LOG = logging.getLogger(__name__) diff --git a/monkey/infection_monkey/transport/http.py b/monkey/infection_monkey/transport/http.py index 5f26f4f68..57dd2450e 100644 --- a/monkey/infection_monkey/transport/http.py +++ b/monkey/infection_monkey/transport/http.py @@ -11,6 +11,7 @@ import requests import infection_monkey.control import infection_monkey.monkeyfs as monkeyfs +from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT from infection_monkey.network.tools import get_interface_to_target from infection_monkey.transport.base import (TransportProxyBase, update_last_serve_time) @@ -123,7 +124,8 @@ class HTTPConnectProxyHandler(http.server.BaseHTTPRequestHandler): r = requests.post(url=dest_path, data=post_data, verify=False, - proxies=infection_monkey.control.ControlClient.proxies) + proxies=infection_monkey.control.ControlClient.proxies, + timeout=SHORT_REQUEST_TIMEOUT) self.send_response(r.status_code) except requests.exceptions.ConnectionError as e: LOG.error("Couldn't forward request to the island: {}".format(e)) diff --git a/monkey/monkey_island/cc/bootloader_server.py b/monkey/monkey_island/cc/bootloader_server.py index 6301d7c18..fbbd32815 100644 --- a/monkey/monkey_island/cc/bootloader_server.py +++ b/monkey/monkey_island/cc/bootloader_server.py @@ -7,6 +7,7 @@ import pymongo import requests import urllib3 +from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT from monkey_island.cc.environment import Environment # Disable "unverified certificate" warnings when sending requests to island @@ -32,7 +33,10 @@ class BootloaderHTTPRequestHandler(BaseHTTPRequestHandler): # The island server doesn't always have a correct SSL cert installed # (By default it comes with a self signed one), # that's why we're not verifying the cert in this request. - r = requests.post(url=island_server_path, data=post_data, verify=False) # noqa: DUO123 + r = requests.post(url=island_server_path, + data=post_data, + verify=False, + timeout=SHORT_REQUEST_TIMEOUT) # noqa: DUO123 try: if r.status_code != 200: