From 17d91766df9e3fe16c4e7c68f1e233e08ea963cc Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Thu, 1 Oct 2020 15:07:32 +0300
Subject: [PATCH] Added AWS keys to config

---
 monkey/common/cloud/scoutsuite_consts.py      |  9 +++
 monkey/infection_monkey/config.py             |  4 ++
 .../scoutsuite_collector.py                   | 32 ++++++----
 monkey/monkey_island/cc/services/config.py    | 36 ++++++-----
 .../cc/services/config_schema/internal.py     | 17 +++++
 .../scoutsuite/scoutsuite_auth_service.py     | 63 +++++++++++++++++++
 .../configuration-components/UiSchema.js      |  3 +
 7 files changed, 135 insertions(+), 29 deletions(-)
 create mode 100644 monkey/common/cloud/scoutsuite_consts.py
 create mode 100644 monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py

diff --git a/monkey/common/cloud/scoutsuite_consts.py b/monkey/common/cloud/scoutsuite_consts.py
new file mode 100644
index 000000000..86411e179
--- /dev/null
+++ b/monkey/common/cloud/scoutsuite_consts.py
@@ -0,0 +1,9 @@
+from enum import Enum
+
+
+class PROVIDERS(Enum):
+    AWS = 'aws'
+    AZURE = 'azure'
+    GCP = 'gcp'
+    ALIBABA = 'aliyun'
+    ORACLE = 'oci'
diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py
index 2917524c5..7597e3de3 100644
--- a/monkey/infection_monkey/config.py
+++ b/monkey/infection_monkey/config.py
@@ -246,6 +246,10 @@ class Configuration(object):
     exploit_ntlm_hash_list = []
     exploit_ssh_keys = []
 
+    access_key_id = ''
+    secret_access_key = ''
+    session_token = ''
+
     # smb/wmi exploiter
     smb_download_timeout = 300  # timeout in seconds
     smb_service_name = "InfectionMonkey"
diff --git a/monkey/infection_monkey/system_info/collectors/scoutsuite_collector/scoutsuite_collector.py b/monkey/infection_monkey/system_info/collectors/scoutsuite_collector/scoutsuite_collector.py
index f7d6b7ec5..6965e53c7 100644
--- a/monkey/infection_monkey/system_info/collectors/scoutsuite_collector/scoutsuite_collector.py
+++ b/monkey/infection_monkey/system_info/collectors/scoutsuite_collector/scoutsuite_collector.py
@@ -1,22 +1,28 @@
+import logging
+
 import infection_monkey.system_info.collectors.scoutsuite_collector.scoutsuite_api as scoutsuite_api
+from common.cloud.scoutsuite_consts import PROVIDERS
 from infection_monkey.telemetry.scoutsuite_telem import ScoutSuiteTelem
+from infection_monkey.config import WormConfiguration
+
+logger = logging.getLogger(__name__)
 
 
-class CLOUD_TYPES:
-    AWS = 'aws'
-    AZURE = 'azure'
-    GCP = 'gcp'
-    ALIBABA = 'aliyun'
-    ORACLE = 'oci'
+def scan_cloud_security(cloud_type: PROVIDERS):
+    try:
+        results = run_scoutsuite(cloud_type.value)
+        if 'error' in results and results['error']:
+            raise Exception(results['error'])
+        send_results(results)
+    except Exception as e:
+        logger.error(f"ScoutSuite didn't scan {cloud_type.value} security because: {e}")
 
 
-def scan_cloud_security(cloud_type: CLOUD_TYPES):
-    results = run_scoutsuite(cloud_type)
-    send_results(results)
-
-
-def run_scoutsuite(cloud_type):
-    return scoutsuite_api.run(provider=cloud_type)
+def run_scoutsuite(cloud_type: str):
+    return scoutsuite_api.run(provider=cloud_type,
+                              aws_access_key_id=WormConfiguration.access_key_id,
+                              aws_secret_access_key=WormConfiguration.secret_access_key,
+                              aws_session_token=WormConfiguration.session_token)
 
 
 def send_results(results):
diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py
index e67abc1f3..e071366fb 100644
--- a/monkey/monkey_island/cc/services/config.py
+++ b/monkey/monkey_island/cc/services/config.py
@@ -21,12 +21,15 @@ from monkey_island.cc.services.config_schema.config_value_paths import STARTED_O
 logger = logging.getLogger(__name__)
 
 # This should be used for config values of array type (array of strings only)
-ENCRYPTED_CONFIG_ARRAYS = \
+ENCRYPTED_CONFIG_VALUES = \
     [
-        ['basic', 'credentials', 'exploit_password_list'],
-        ['internal', 'exploits', 'exploit_lm_hash_list'],
-        ['internal', 'exploits', 'exploit_ntlm_hash_list'],
-        ['internal', 'exploits', 'exploit_ssh_keys']
+        PASSWORD_LIST_PATH,
+        LM_HASH_LIST_PATH,
+        NTLM_HASH_LIST_PATH,
+        SSH_KEYS_PATH,
+        AWS_KEYS_PATH + ['access_key_id'],
+        AWS_KEYS_PATH + ['secret_access_key'],
+        AWS_KEYS_PATH + ['session_token']
     ]
 
 
@@ -69,8 +72,11 @@ class ConfigService:
         for config_key_part in config_key_as_arr:
             config = config[config_key_part]
         if should_decrypt:
-            if config_key_as_arr in ENCRYPTED_CONFIG_ARRAYS:
-                config = [encryptor.dec(x) for x in config]
+            if config_key_as_arr in ENCRYPTED_CONFIG_VALUES:
+                if isinstance(config, str):
+                    config = encryptor.dec(config)
+                elif isinstance(config, list):
+                    config = [encryptor.dec(x) for x in config]
         return config
 
     @staticmethod
@@ -79,12 +85,6 @@ class ConfigService:
         mongo.db.config.update({'name': 'newconfig'},
                                {"$set": {mongo_key: value}})
 
-    @staticmethod
-    def append_to_config_array(config_key_as_arr, value):
-        mongo_key = ".".join(config_key_as_arr)
-        mongo.db.config.update({'name': 'newconfig'},
-                               {"$push": {mongo_key: value}})
-
     @staticmethod
     def get_flat_config(is_initial_config=False, should_decrypt=True):
         config_json = ConfigService.get_config(is_initial_config, should_decrypt)
@@ -92,7 +92,11 @@ class ConfigService:
         for i in config_json:
             for j in config_json[i]:
                 for k in config_json[i][j]:
-                    flat_config_json[k] = config_json[i][j][k]
+                    if isinstance(config_json[i][j][k], dict):
+                        for key, value in config_json[i][j][k].items():
+                            flat_config_json[key] = value
+                    else:
+                        flat_config_json[k] = config_json[i][j][k]
 
         return flat_config_json
 
@@ -101,8 +105,8 @@ class ConfigService:
         return SCHEMA
 
     @staticmethod
-    def add_item_to_config_set_if_dont_exist(item_key, item_value, should_encrypt):
-        item_path_array = item_key.split('.')
+    def add_item_to_config_set_if_dont_exist(item_path_array, item_value, should_encrypt):
+        item_key = '.'.join(item_path_array)
         items_from_config = ConfigService.get_config_value(item_path_array, False, should_encrypt)
         if item_value in items_from_config:
             return
diff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py
index bdbae2461..b571d31ab 100644
--- a/monkey/monkey_island/cc/services/config_schema/internal.py
+++ b/monkey/monkey_island/cc/services/config_schema/internal.py
@@ -94,6 +94,23 @@ INTERNAL = {
                     "type": "boolean",
                     "default": True,
                     "description": "Is the monkey alive"
+                },
+                "aws_keys": {
+                    "type": "object",
+                    "properties": {
+                        "access_key_id": {
+                            "type": "string",
+                            "default": ""
+                        },
+                        "secret_access_key": {
+                            "type": "string",
+                            "default": ""
+                        },
+                        "session_token": {
+                            "type": "string",
+                            "default": ""
+                        }
+                    }
                 }
             }
         },
diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py
new file mode 100644
index 000000000..5cd29d423
--- /dev/null
+++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py
@@ -0,0 +1,63 @@
+import pkgutil
+import sys
+from pathlib import PurePath
+from typing import Tuple
+
+from common.cloud.scoutsuite_consts import PROVIDERS
+from common.utils.exceptions import InvalidAWSKeys
+from monkey_island.cc.encryptor import encryptor
+from monkey_island.cc.services.config import ConfigService
+from monkey_island.cc.services.config_schema.config_value_paths import AWS_KEYS_PATH
+
+_scoutsuite_api_package = pkgutil.get_loader('common.cloud.scoutsuite.ScoutSuite.__main__')
+
+
+def _add_scoutsuite_to_python_path():
+    scoutsuite_path = PurePath(_scoutsuite_api_package.path).parent.parent.__str__()
+    sys.path.append(scoutsuite_path)
+
+
+_add_scoutsuite_to_python_path()
+
+
+def is_cloud_authentication_setup(provider: PROVIDERS) -> Tuple[bool, str]:
+    if provider == PROVIDERS.AWS.value:
+        if is_aws_keys_setup():
+            return True, "AWS keys already setup. Run monkey on Island to scan."
+
+        import common.cloud.scoutsuite.ScoutSuite.providers.aws.authentication_strategy as auth_strategy
+        try:
+            profile = auth_strategy.AWSAuthenticationStrategy().authenticate()
+            return True, f" Profile \"{profile.session.profile_name}\" is already setup. Run monkey on Island to scan."
+        except Exception:
+            return False, ""
+
+
+def is_aws_keys_setup():
+    return (ConfigService.get_config_value(AWS_KEYS_PATH + ['access_key_id']) and
+            ConfigService.get_config_value(AWS_KEYS_PATH + ['secret_access_key']))
+
+
+def set_aws_keys(access_key_id: str, secret_access_key: str, session_token: str):
+    if not access_key_id or not secret_access_key:
+        raise InvalidAWSKeys("Missing some of the following fields: access key ID, secret access key.")
+    _set_aws_key('access_key_id', access_key_id)
+    _set_aws_key('secret_access_key', secret_access_key)
+    _set_aws_key('session_token', session_token)
+
+
+def _set_aws_key(key_type: str, key_value: str):
+    path_to_keys = AWS_KEYS_PATH
+    encrypted_key = encryptor.enc(key_value)
+    ConfigService.set_config_value(path_to_keys + [key_type], encrypted_key)
+
+
+def get_aws_keys():
+    return {'access_key_id': _get_aws_key('access_key_id'),
+            'secret_access_key': _get_aws_key('secret_access_key'),
+            'session_token': _get_aws_key('session_token')}
+
+
+def _get_aws_key(key_type: str):
+    path_to_keys = AWS_KEYS_PATH
+    return ConfigService.get_config_value(config_key_as_arr=path_to_keys + [key_type])
diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js
index aab3f1899..ac9104817 100644
--- a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js
+++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js
@@ -84,6 +84,9 @@ export default function UiSchema(props) {
       monkey: {
         alive: {
           classNames: 'config-field-hidden'
+        },
+        aws_keys: {
+          classNames: 'config-field-hidden'
         }
       }
     }