From 3cbeb3dbf77d196be7f10085fc4bcef8712bff36 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 13:51:20 +0530 Subject: [PATCH 01/11] island: Add attack mitigations to mongo upon registration --- .../monkey_island/cc/resources/auth/registration.py | 11 +++++++++++ monkey/monkey_island/cc/server_setup.py | 3 --- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index 121b03d71..61a04b2d8 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -1,4 +1,5 @@ import json +import logging import flask_restful from flask import make_response, request @@ -7,6 +8,9 @@ import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.password_utils as password_utils from common.utils.exceptions import InvalidRegistrationCredentialsError, RegistrationNotNeededError from monkey_island.cc.environment.user_creds import UserCreds +from monkey_island.cc.setup.mongo.database_initializer import init_collections + +logger = logging.getLogger(__name__) class Registration(flask_restful.Resource): @@ -18,9 +22,16 @@ class Registration(flask_restful.Resource): try: env_singleton.env.try_add_user(credentials) + init_collections() return make_response({"error": ""}, 200) except (InvalidRegistrationCredentialsError, RegistrationNotNeededError) as e: return make_response({"error": str(e)}, 400) + except Exception as ex: + logger.error( + "Exception raised during registration; most likely an issue with the " + f"mongo collection's initialisation. Exception: {str(ex)}." + ) + return make_response({"error": str(ex)}, 400) def _get_user_credentials_from_request(request): diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index a4e4da485..69ab0437a 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -36,7 +36,6 @@ from monkey_island.cc.setup import island_config_options_validator # noqa: E402 from monkey_island.cc.setup.gevent_hub_error_handler import GeventHubErrorHandler # noqa: E402 from monkey_island.cc.setup.island_config_options import IslandConfigOptions # noqa: E402 from monkey_island.cc.setup.mongo import mongo_setup # noqa: E402 -from monkey_island.cc.setup.mongo.database_initializer import init_collections # noqa: E402 from monkey_island.cc.setup.mongo.mongo_db_process import MongoDbProcess # noqa: E402 logger = logging.getLogger(__name__) @@ -131,8 +130,6 @@ def _start_island_server(should_setup_only, config_options: IslandConfigOptions) populate_exporter_list() app = init_app(mongo_setup.MONGO_URL) - init_collections() - if should_setup_only: logger.warning("Setup only flag passed. Exiting.") return From 194e244080ba2ac74afcfd78b6912bc63c41b6a6 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 13:52:21 +0530 Subject: [PATCH 02/11] island: On login, check if collection 'attack_mitigations' is present in DB, add if not --- monkey/monkey_island/cc/resources/auth/auth.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 064395eaf..98408c05c 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -11,6 +11,9 @@ from jwt import PyJWTError import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.password_utils as password_utils import monkey_island.cc.resources.auth.user_store as user_store +from monkey_island.cc.database import mongo +from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations +from monkey_island.cc.setup.mongo.database_initializer import init_collections logger = logging.getLogger(__name__) @@ -42,6 +45,7 @@ class Authenticate(flask_restful.Resource): if _credentials_match_registered_user(username, password): access_token = _create_access_token(username) + _check_attack_mitigations_in_mongo() return make_response({"access_token": access_token, "error": ""}, 200) else: return make_response({"error": "Invalid credentials"}, 401) @@ -74,6 +78,11 @@ def _create_access_token(username): return access_token +def _check_attack_mitigations_in_mongo(): + if AttackMitigations.COLLECTION_NAME not in mongo.db.list_collection_names(): + init_collections() + + # See https://flask-jwt-extended.readthedocs.io/en/stable/custom_decorators/ def jwt_required(fn): @wraps(fn) From 340dd1f94b1ba6572e3edd3f62fb9a49c4307bef Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 14:32:02 +0530 Subject: [PATCH 03/11] island: Drop mongo db if registration is required --- monkey/monkey_island/cc/resources/auth/registration.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index 61a04b2d8..ad4ce796a 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -7,6 +7,7 @@ from flask import make_response, request import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.password_utils as password_utils from common.utils.exceptions import InvalidRegistrationCredentialsError, RegistrationNotNeededError +from monkey_island.cc.database import mongo from monkey_island.cc.environment.user_creds import UserCreds from monkey_island.cc.setup.mongo.database_initializer import init_collections @@ -21,6 +22,8 @@ class Registration(flask_restful.Resource): credentials = _get_user_credentials_from_request(request) try: + # if new registration is required (credentials are reset), drop previous user's data + _drop_mongo_db() env_singleton.env.try_add_user(credentials) init_collections() return make_response({"error": ""}, 200) @@ -42,3 +45,7 @@ def _get_user_credentials_from_request(request): password_hash = password_utils.hash_password(password) return UserCreds(username, password_hash) + + +def _drop_mongo_db(): + mongo.db.command("dropDatabase") From 6fe4d6cb31f769bff492f0f270e994c920d41284 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Mon, 27 Sep 2021 14:36:55 +0530 Subject: [PATCH 04/11] island: Drop mongo db when registartion requirement is realised instead of when registration request is sent The issue with this whole change is that there's a long gap where nothing happens after you click on the log in or register button on the UI. But we don't need to worry about this because we plan on shipping Island's mongodb with attack mitigations already present. --- monkey/monkey_island/cc/resources/auth/registration.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index ad4ce796a..92fca24c9 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -16,14 +16,16 @@ logger = logging.getLogger(__name__) class Registration(flask_restful.Resource): def get(self): - return {"needs_registration": env_singleton.env.needs_registration()} + is_registration_needed = env_singleton.env.needs_registration() + if is_registration_needed: + # if registration is required, drop previous user's data (for credentials reset case) + _drop_mongo_db() + return {"needs_registration": is_registration_needed} def post(self): credentials = _get_user_credentials_from_request(request) try: - # if new registration is required (credentials are reset), drop previous user's data - _drop_mongo_db() env_singleton.env.try_add_user(credentials) init_collections() return make_response({"error": ""}, 200) From 1e02ab6d2b2ec2bb9e8d0839d5dd0dde65e8336c Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 28 Sep 2021 13:18:27 +0530 Subject: [PATCH 05/11] docs: Add warning that DB will be cleared if creds are reset --- docs/content/FAQ/_index.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index a02b5b32d..7d3701d65 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -56,6 +56,12 @@ When you first access the Monkey Island server, you'll be prompted to create an To reset the credentials, edit the `server_config.json` file manually (located in the [data directory](/reference/data_directory)). +{{% notice warning %}} +If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost.

+However, you can save the Monkey's exisiting configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page. +{{% /notice %}} + + In order to reset the credentials, the following edits need to be made: 1. Delete the `user` field. It will look like this: ```json From 2cbaf954e13a12e7c7ae632dfab9eeaaba5b5e58 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 28 Sep 2021 13:19:00 +0530 Subject: [PATCH 06/11] docs: Fix spelling mistake --- docs/content/FAQ/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index 7d3701d65..bec25b6b6 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -58,7 +58,7 @@ To reset the credentials, edit the `server_config.json` file manually {{% notice warning %}} If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost.

-However, you can save the Monkey's exisiting configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page. +However, you can save the Monkey's existing configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page. {{% /notice %}} From ab7872d1031e1abfd9fb1307b306f323163ccd19 Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Tue, 28 Sep 2021 13:34:58 +0530 Subject: [PATCH 07/11] CHANGELOG: Add entry for delaying mongo init --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d9457f92..beee97d9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - The name of the "Communicate as new user" post-breach action to "Communicate as backdoor user". #1410 - ATT&CK report messages (more accurate now). #1483 +- Initialise MongoDB collection of attack mitigations after registration or login (if required) + instead of on Island startup. #1495 ### Removed - Internet access check on agent start. #1402 From b73958dd5539059a01f54a62c1d46611ad5e8de6 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 29 Sep 2021 12:18:57 +0300 Subject: [PATCH 08/11] Rename the CHANGELOG.md entry about resetting login credentials to "Resetting login credentials also cleans the contents of the database. #1495" --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index beee97d9c..69408a7fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - The name of the "Communicate as new user" post-breach action to "Communicate as backdoor user". #1410 +- Resetting login credentials also cleans the contents of the database. #1495 - ATT&CK report messages (more accurate now). #1483 -- Initialise MongoDB collection of attack mitigations after registration or login (if required) - instead of on Island startup. #1495 ### Removed - Internet access check on agent start. #1402 From c211d51d8caa29de4a7a5c8d9ed98d9cafd71b2c Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 29 Sep 2021 16:41:08 +0300 Subject: [PATCH 09/11] Move database reset to happen during the registration --- monkey/monkey_island/cc/resources/auth/auth.py | 9 --------- .../cc/resources/auth/registration.py | 18 ++---------------- monkey/monkey_island/cc/services/database.py | 4 ++++ .../cc/setup/mongo/database_initializer.py | 9 ++++++--- 4 files changed, 12 insertions(+), 28 deletions(-) diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 98408c05c..064395eaf 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -11,9 +11,6 @@ from jwt import PyJWTError import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.password_utils as password_utils import monkey_island.cc.resources.auth.user_store as user_store -from monkey_island.cc.database import mongo -from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations -from monkey_island.cc.setup.mongo.database_initializer import init_collections logger = logging.getLogger(__name__) @@ -45,7 +42,6 @@ class Authenticate(flask_restful.Resource): if _credentials_match_registered_user(username, password): access_token = _create_access_token(username) - _check_attack_mitigations_in_mongo() return make_response({"access_token": access_token, "error": ""}, 200) else: return make_response({"error": "Invalid credentials"}, 401) @@ -78,11 +74,6 @@ def _create_access_token(username): return access_token -def _check_attack_mitigations_in_mongo(): - if AttackMitigations.COLLECTION_NAME not in mongo.db.list_collection_names(): - init_collections() - - # See https://flask-jwt-extended.readthedocs.io/en/stable/custom_decorators/ def jwt_required(fn): @wraps(fn) diff --git a/monkey/monkey_island/cc/resources/auth/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py index 92fca24c9..12c17d6e5 100644 --- a/monkey/monkey_island/cc/resources/auth/registration.py +++ b/monkey/monkey_island/cc/resources/auth/registration.py @@ -7,9 +7,8 @@ from flask import make_response, request import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.password_utils as password_utils from common.utils.exceptions import InvalidRegistrationCredentialsError, RegistrationNotNeededError -from monkey_island.cc.database import mongo from monkey_island.cc.environment.user_creds import UserCreds -from monkey_island.cc.setup.mongo.database_initializer import init_collections +from monkey_island.cc.setup.mongo.database_initializer import reset_database logger = logging.getLogger(__name__) @@ -17,9 +16,6 @@ logger = logging.getLogger(__name__) class Registration(flask_restful.Resource): def get(self): is_registration_needed = env_singleton.env.needs_registration() - if is_registration_needed: - # if registration is required, drop previous user's data (for credentials reset case) - _drop_mongo_db() return {"needs_registration": is_registration_needed} def post(self): @@ -27,16 +23,10 @@ class Registration(flask_restful.Resource): try: env_singleton.env.try_add_user(credentials) - init_collections() + reset_database() return make_response({"error": ""}, 200) except (InvalidRegistrationCredentialsError, RegistrationNotNeededError) as e: return make_response({"error": str(e)}, 400) - except Exception as ex: - logger.error( - "Exception raised during registration; most likely an issue with the " - f"mongo collection's initialisation. Exception: {str(ex)}." - ) - return make_response({"error": str(ex)}, 400) def _get_user_credentials_from_request(request): @@ -47,7 +37,3 @@ def _get_user_credentials_from_request(request): password_hash = password_utils.hash_password(password) return UserCreds(username, password_hash) - - -def _drop_mongo_db(): - mongo.db.command("dropDatabase") diff --git a/monkey/monkey_island/cc/services/database.py b/monkey/monkey_island/cc/services/database.py index d0656f946..afd4ecc02 100644 --- a/monkey/monkey_island/cc/services/database.py +++ b/monkey/monkey_island/cc/services/database.py @@ -37,3 +37,7 @@ class Database(object): def init_db(): if not mongo.db.collection_names(): Database.reset_db() + + @staticmethod + def is_mitigations_missing() -> bool: + return bool(AttackMitigations.COLLECTION_NAME not in mongo.db.list_collection_names()) diff --git a/monkey/monkey_island/cc/setup/mongo/database_initializer.py b/monkey/monkey_island/cc/setup/mongo/database_initializer.py index 34914c7ce..72d7bec7d 100644 --- a/monkey/monkey_island/cc/setup/mongo/database_initializer.py +++ b/monkey/monkey_island/cc/setup/mongo/database_initializer.py @@ -5,13 +5,16 @@ from pymongo import errors from monkey_island.cc.database import mongo from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations from monkey_island.cc.services.attack.mitre_api_interface import MitreApiInterface +from monkey_island.cc.services.database import Database logger = logging.getLogger(__name__) -def init_collections(): - logger.info("Setting up the Monkey Island, this might take a while...") - _try_store_mitigations_on_mongo() +def reset_database(): + Database.reset_db() + if Database.is_mitigations_missing(): + logger.info("Populating Monkey Island with ATT&CK mitigations, this might take a while...") + _try_store_mitigations_on_mongo() def _try_store_mitigations_on_mongo(): From 579ebf4a0f1c62067471c53e4e09307cd5cb17ae Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 29 Sep 2021 16:43:24 +0300 Subject: [PATCH 10/11] Alter registration page to show loading icon while registration request is being processed --- .../cc/ui/src/components/pages/RegisterPage.js | 9 ++++++++- .../monkey_island/cc/ui/src/styles/pages/AuthPage.scss | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js index 55a5fcebf..596a86f5a 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js @@ -4,11 +4,13 @@ import {Row, Col, Container, Form, Button} from 'react-bootstrap'; import AuthService from '../../services/AuthService'; import monkeyDetective from '../../images/detective-monkey.svg'; import ParticleBackground from '../ui-components/ParticleBackground'; +import LoadingIcon from '../ui-components/LoadingIcon'; class RegisterPageComponent extends React.Component { register = (event) => { event.preventDefault(); + this.setState({loading: true}) this.auth.register(this.username, this.password).then(res => { this.setState({failed: false, error: ''}); if (res['result']) { @@ -68,7 +70,12 @@ class RegisterPageComponent extends React.Component { this.updateUsername(evt)} type='text' placeholder='Username'/> this.updatePassword(evt)} type='password' placeholder='Password'/> diff --git a/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss b/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss index 80bd54507..3392fcee7 100644 --- a/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss +++ b/monkey/monkey_island/cc/ui/src/styles/pages/AuthPage.scss @@ -35,3 +35,11 @@ margin-bottom: 20px; text-align: center; } + +.auth-container .monkey-submit-button:hover .loading-icon { + color: $monkey-black; +} + +.auth-container .monkey-submit-button:focus .loading-icon { + color: $monkey-black; +} From 7939ed47393dc771cae3f047ee7fd59a0c8bcc1b Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 29 Sep 2021 17:02:34 +0300 Subject: [PATCH 11/11] Alter the log message talking about storing the mitigations: remove the part saying that it will take a while --- monkey/monkey_island/cc/setup/mongo/database_initializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/setup/mongo/database_initializer.py b/monkey/monkey_island/cc/setup/mongo/database_initializer.py index 72d7bec7d..32e3c8486 100644 --- a/monkey/monkey_island/cc/setup/mongo/database_initializer.py +++ b/monkey/monkey_island/cc/setup/mongo/database_initializer.py @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) def reset_database(): Database.reset_db() if Database.is_mitigations_missing(): - logger.info("Populating Monkey Island with ATT&CK mitigations, this might take a while...") + logger.info("Populating Monkey Island with ATT&CK mitigations.") _try_store_mitigations_on_mongo()