forked from p15670423/monkey
Merge pull request #2053 from guardicore/2036-reset-endpoints
2036 reset endpoints
This commit is contained in:
commit
9f0b3e10e1
|
@ -16,6 +16,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- deployment_scrips/install-infection-monkey-service.sh to install an AppImage
|
- deployment_scrips/install-infection-monkey-service.sh to install an AppImage
|
||||||
as a service. #1552
|
as a service. #1552
|
||||||
- The ability to download the Monkey Island logs from the Infection Map page. #1640
|
- The ability to download the Monkey Island logs from the Infection Map page. #1640
|
||||||
|
- `/api/reset-agent-configuration` endpoint. #2036
|
||||||
|
- `/api/clear-simulation-data` endpoint. #2036
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Reset workflow. Now it's possible to delete data gathered by agents without
|
- Reset workflow. Now it's possible to delete data gathered by agents without
|
||||||
|
@ -42,6 +44,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
the current depth of the agent, not hops remaining). #2033
|
the current depth of the agent, not hops remaining). #2033
|
||||||
- Agent configuration structure. #1996, #1998, #1961, #1997, #1994, #1741,
|
- Agent configuration structure. #1996, #1998, #1961, #1997, #1994, #1741,
|
||||||
#1761, #1695, #1605, #2028
|
#1761, #1695, #1605, #2028
|
||||||
|
- `/api/island-mode` to accept and return new "unset" mode. #2036
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- VSFTPD exploiter. #1533
|
- VSFTPD exploiter. #1533
|
||||||
|
|
|
@ -10,7 +10,12 @@ from werkzeug.exceptions import NotFound
|
||||||
|
|
||||||
from common import DIContainer
|
from common import DIContainer
|
||||||
from monkey_island.cc.database import database, mongo
|
from monkey_island.cc.database import database, mongo
|
||||||
from monkey_island.cc.resources import AgentBinaries, RemoteRun
|
from monkey_island.cc.resources import (
|
||||||
|
AgentBinaries,
|
||||||
|
ClearSimulationData,
|
||||||
|
RemoteRun,
|
||||||
|
ResetAgentConfiguration,
|
||||||
|
)
|
||||||
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
||||||
from monkey_island.cc.resources.agent_configuration import AgentConfiguration
|
from monkey_island.cc.resources.agent_configuration import AgentConfiguration
|
||||||
from monkey_island.cc.resources.agent_controls import StopAgentCheck, StopAllAgents
|
from monkey_island.cc.resources.agent_controls import StopAgentCheck, StopAllAgents
|
||||||
|
@ -143,6 +148,11 @@ class FlaskDIWrapper:
|
||||||
|
|
||||||
|
|
||||||
def init_api_resources(api: FlaskDIWrapper):
|
def init_api_resources(api: FlaskDIWrapper):
|
||||||
|
init_restful_endpoints(api)
|
||||||
|
init_rpc_endpoints(api)
|
||||||
|
|
||||||
|
|
||||||
|
def init_restful_endpoints(api: FlaskDIWrapper):
|
||||||
api.add_resource(Root)
|
api.add_resource(Root)
|
||||||
api.add_resource(Registration)
|
api.add_resource(Registration)
|
||||||
api.add_resource(Authenticate)
|
api.add_resource(Authenticate)
|
||||||
|
@ -192,6 +202,11 @@ def init_api_resources(api: FlaskDIWrapper):
|
||||||
api.add_resource(TelemetryBlackboxEndpoint)
|
api.add_resource(TelemetryBlackboxEndpoint)
|
||||||
|
|
||||||
|
|
||||||
|
def init_rpc_endpoints(api: FlaskDIWrapper):
|
||||||
|
api.add_resource(ResetAgentConfiguration)
|
||||||
|
api.add_resource(ClearSimulationData)
|
||||||
|
|
||||||
|
|
||||||
def init_app(mongo_url: str, container: DIContainer):
|
def init_app(mongo_url: str, container: DIContainer):
|
||||||
"""
|
"""
|
||||||
Simple docstirng for init_app
|
Simple docstirng for init_app
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
from .remote_run import RemoteRun
|
from .remote_run import RemoteRun
|
||||||
from .agent_binaries import AgentBinaries
|
from .agent_binaries import AgentBinaries
|
||||||
|
from .clear_simulation_data import ClearSimulationData
|
||||||
|
from .reset_agent_configuration import ResetAgentConfiguration
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
from flask import make_response
|
||||||
|
|
||||||
|
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
||||||
|
from monkey_island.cc.resources.request_authentication import jwt_required
|
||||||
|
from monkey_island.cc.services.database import Database
|
||||||
|
|
||||||
|
|
||||||
|
class ClearSimulationData(AbstractResource):
|
||||||
|
urls = ["/api/clear-simulation-data"]
|
||||||
|
|
||||||
|
@jwt_required
|
||||||
|
def post(self):
|
||||||
|
"""
|
||||||
|
Clear all data collected during the simulation
|
||||||
|
"""
|
||||||
|
Database.reset_db(reset_config=False)
|
||||||
|
|
||||||
|
return make_response({}, 200)
|
|
@ -6,7 +6,7 @@ from flask import make_response, request
|
||||||
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
||||||
from monkey_island.cc.resources.request_authentication import jwt_required
|
from monkey_island.cc.resources.request_authentication import jwt_required
|
||||||
from monkey_island.cc.services.config_manipulator import update_config_on_mode_set
|
from monkey_island.cc.services.config_manipulator import update_config_on_mode_set
|
||||||
from monkey_island.cc.services.mode.island_mode_service import ModeNotSetError, get_mode, set_mode
|
from monkey_island.cc.services.mode.island_mode_service import get_mode, set_mode
|
||||||
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
|
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -34,15 +34,10 @@ class IslandMode(AbstractResource):
|
||||||
return make_response({}, 200)
|
return make_response({}, 200)
|
||||||
except (AttributeError, json.decoder.JSONDecodeError):
|
except (AttributeError, json.decoder.JSONDecodeError):
|
||||||
return make_response({}, 400)
|
return make_response({}, 400)
|
||||||
# API Spec: Check if HTTP codes make sense
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return make_response({}, 422)
|
return make_response({}, 422)
|
||||||
|
|
||||||
@jwt_required
|
@jwt_required
|
||||||
def get(self):
|
def get(self):
|
||||||
try:
|
|
||||||
island_mode = get_mode()
|
island_mode = get_mode()
|
||||||
return make_response({"mode": island_mode}, 200)
|
return make_response({"mode": island_mode}, 200)
|
||||||
|
|
||||||
except ModeNotSetError:
|
|
||||||
return make_response({"mode": None}, 200)
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
from flask import make_response
|
||||||
|
|
||||||
|
from monkey_island.cc.repository import IAgentConfigurationRepository
|
||||||
|
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
||||||
|
from monkey_island.cc.resources.request_authentication import jwt_required
|
||||||
|
|
||||||
|
|
||||||
|
class ResetAgentConfiguration(AbstractResource):
|
||||||
|
urls = ["/api/reset-agent-configuration"]
|
||||||
|
|
||||||
|
def __init__(self, agent_configuration_repository: IAgentConfigurationRepository):
|
||||||
|
self._agent_configuration_repository = agent_configuration_repository
|
||||||
|
|
||||||
|
@jwt_required
|
||||||
|
def post(self):
|
||||||
|
"""
|
||||||
|
Reset the agent configuration to its default values
|
||||||
|
"""
|
||||||
|
self._agent_configuration_repository.reset_to_default()
|
||||||
|
|
||||||
|
return make_response({}, 200)
|
|
@ -25,7 +25,8 @@ from monkey_island.cc.server_utils.encryption import (
|
||||||
)
|
)
|
||||||
from monkey_island.cc.services.config_manipulator import update_config_per_mode
|
from monkey_island.cc.services.config_manipulator import update_config_per_mode
|
||||||
from monkey_island.cc.services.config_schema.config_schema import SCHEMA
|
from monkey_island.cc.services.config_schema.config_schema import SCHEMA
|
||||||
from monkey_island.cc.services.mode.island_mode_service import ModeNotSetError, get_mode
|
from monkey_island.cc.services.mode.island_mode_service import get_mode
|
||||||
|
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
|
||||||
from monkey_island.cc.services.post_breach_files import PostBreachFilesService
|
from monkey_island.cc.services.post_breach_files import PostBreachFilesService
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -250,11 +251,13 @@ class ConfigService:
|
||||||
def reset_config():
|
def reset_config():
|
||||||
PostBreachFilesService.remove_PBA_files()
|
PostBreachFilesService.remove_PBA_files()
|
||||||
config = ConfigService.get_default_config(True)
|
config = ConfigService.get_default_config(True)
|
||||||
try:
|
|
||||||
mode = get_mode()
|
mode = get_mode()
|
||||||
update_config_per_mode(mode, config, should_encrypt=False)
|
if mode == IslandModeEnum.UNSET.value:
|
||||||
except ModeNotSetError:
|
|
||||||
ConfigService.update_config(config, should_encrypt=False)
|
ConfigService.update_config(config, should_encrypt=False)
|
||||||
|
else:
|
||||||
|
update_config_per_mode(mode, config, should_encrypt=False)
|
||||||
|
|
||||||
logger.info("Monkey config reset was called")
|
logger.info("Monkey config reset was called")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -3,4 +3,5 @@ from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
|
||||||
MANIPULATOR_PER_MODE = {
|
MANIPULATOR_PER_MODE = {
|
||||||
IslandModeEnum.ADVANCED.value: {},
|
IslandModeEnum.ADVANCED.value: {},
|
||||||
IslandModeEnum.RANSOMWARE.value: {"monkey.post_breach.post_breach_actions": []},
|
IslandModeEnum.RANSOMWARE.value: {"monkey.post_breach.post_breach_actions": []},
|
||||||
|
IslandModeEnum.UNSET.value: {},
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
|
||||||
|
|
||||||
|
|
||||||
def set_mode(mode: IslandModeEnum):
|
def set_mode(mode: IslandModeEnum):
|
||||||
|
IslandMode.drop_collection()
|
||||||
island_mode_model = IslandMode()
|
island_mode_model = IslandMode()
|
||||||
island_mode_model.mode = mode.value
|
island_mode_model.mode = mode.value
|
||||||
island_mode_model.save()
|
island_mode_model.save()
|
||||||
|
@ -13,10 +14,4 @@ def get_mode() -> str:
|
||||||
mode = IslandMode.objects[0].mode
|
mode = IslandMode.objects[0].mode
|
||||||
return mode
|
return mode
|
||||||
else:
|
else:
|
||||||
raise ModeNotSetError
|
return IslandModeEnum.UNSET.value
|
||||||
|
|
||||||
|
|
||||||
class ModeNotSetError(Exception):
|
|
||||||
"""
|
|
||||||
Throw this exception when island mode is not set.
|
|
||||||
"""
|
|
||||||
|
|
|
@ -2,5 +2,6 @@ from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class IslandModeEnum(Enum):
|
class IslandModeEnum(Enum):
|
||||||
|
UNSET = "unset"
|
||||||
RANSOMWARE = "ransomware"
|
RANSOMWARE = "ransomware"
|
||||||
ADVANCED = "advanced"
|
ADVANCED = "advanced"
|
||||||
|
|
|
@ -93,7 +93,7 @@ class AppComponent extends AuthComponent {
|
||||||
if (res) {
|
if (res) {
|
||||||
this.setMode()
|
this.setMode()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (this.state.islandMode === null) {
|
if (this.state.islandMode === "unset") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.authFetch('/api')
|
this.authFetch('/api')
|
||||||
|
@ -151,12 +151,12 @@ class AppComponent extends AuthComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
needsRedirectionToLandingPage = (route_path) => {
|
needsRedirectionToLandingPage = (route_path) => {
|
||||||
return (this.state.islandMode === null && route_path !== Routes.LandingPage)
|
return (this.state.islandMode === "unset" && route_path !== Routes.LandingPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
needsRedirectionToGettingStarted = (route_path) => {
|
needsRedirectionToGettingStarted = (route_path) => {
|
||||||
return route_path === Routes.LandingPage &&
|
return route_path === Routes.LandingPage &&
|
||||||
this.state.islandMode !== null && this.state.islandMode !== undefined
|
this.state.islandMode !== "unset" && this.state.islandMode !== undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectTo = (userPath, targetPath) => {
|
redirectTo = (userPath, targetPath) => {
|
||||||
|
|
|
@ -58,7 +58,7 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
getSectionsOrder() {
|
getSectionsOrder() {
|
||||||
let islandMode = this.props.islandMode ? this.props.islandMode : 'advanced'
|
let islandMode = this.props.islandMode !== 'unset' ? this.props.islandMode : 'advanced'
|
||||||
return CONFIGURATION_TABS_PER_MODE[islandMode];
|
return CONFIGURATION_TABS_PER_MODE[islandMode];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from tests.utils import raise_
|
||||||
from monkey_island.cc.models.island_mode_model import IslandMode
|
from monkey_island.cc.models.island_mode_model import IslandMode
|
||||||
from monkey_island.cc.resources import island_mode as island_mode_resource
|
from monkey_island.cc.resources import island_mode as island_mode_resource
|
||||||
from monkey_island.cc.resources.island_mode import IslandMode as IslandModeResource
|
from monkey_island.cc.resources.island_mode import IslandMode as IslandModeResource
|
||||||
|
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
|
@ -13,7 +14,10 @@ def uses_database():
|
||||||
IslandMode.objects().delete()
|
IslandMode.objects().delete()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ["ransomware", "advanced"])
|
@pytest.mark.parametrize(
|
||||||
|
"mode",
|
||||||
|
[IslandModeEnum.RANSOMWARE.value, IslandModeEnum.ADVANCED.value, IslandModeEnum.UNSET.value],
|
||||||
|
)
|
||||||
def test_island_mode_post(flask_client, mode, monkeypatch):
|
def test_island_mode_post(flask_client, mode, monkeypatch):
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"monkey_island.cc.resources.island_mode.update_config_on_mode_set",
|
"monkey_island.cc.resources.island_mode.update_config_on_mode_set",
|
||||||
|
@ -42,12 +46,14 @@ def test_island_mode_post__internal_server_error(monkeypatch, flask_client):
|
||||||
monkeypatch.setattr(island_mode_resource, "set_mode", lambda x: raise_(Exception()))
|
monkeypatch.setattr(island_mode_resource, "set_mode", lambda x: raise_(Exception()))
|
||||||
|
|
||||||
resp = flask_client.post(
|
resp = flask_client.post(
|
||||||
IslandModeResource.urls[0], data=json.dumps({"mode": "ransomware"}), follow_redirects=True
|
IslandModeResource.urls[0],
|
||||||
|
data=json.dumps({"mode": IslandModeEnum.RANSOMWARE.value}),
|
||||||
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
assert resp.status_code == 500
|
assert resp.status_code == 500
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ["ransomware", "advanced"])
|
@pytest.mark.parametrize("mode", [IslandModeEnum.RANSOMWARE.value, IslandModeEnum.ADVANCED.value])
|
||||||
def test_island_mode_endpoint(flask_client, uses_database, mode):
|
def test_island_mode_endpoint(flask_client, uses_database, mode):
|
||||||
flask_client.post(
|
flask_client.post(
|
||||||
IslandModeResource.urls[0], data=json.dumps({"mode": mode}), follow_redirects=True
|
IslandModeResource.urls[0], data=json.dumps({"mode": mode}), follow_redirects=True
|
||||||
|
@ -63,4 +69,4 @@ def test_island_mode_endpoint__invalid_mode(flask_client, uses_database):
|
||||||
)
|
)
|
||||||
resp_get = flask_client.get(IslandModeResource.urls[0], follow_redirects=True)
|
resp_get = flask_client.get(IslandModeResource.urls[0], follow_redirects=True)
|
||||||
assert resp_post.status_code == 422
|
assert resp_post.status_code == 422
|
||||||
assert json.loads(resp_get.data)["mode"] is None
|
assert json.loads(resp_get.data)["mode"] == IslandModeEnum.UNSET.value
|
||||||
|
|
|
@ -149,7 +149,8 @@ MONKEY_LINUX_RUNNING # unused variable (monkey/monkey_island/cc/services/utils/
|
||||||
import_status # monkey_island\cc\resources\configuration_import.py:19
|
import_status # monkey_island\cc\resources\configuration_import.py:19
|
||||||
config_schema # monkey_island\cc\resources\configuration_import.py:25
|
config_schema # monkey_island\cc\resources\configuration_import.py:25
|
||||||
exception_stream # unused attribute (monkey_island/cc/server_setup.py:104)
|
exception_stream # unused attribute (monkey_island/cc/server_setup.py:104)
|
||||||
ADVANCED # unused attribute (monkey/monkey_island/cc/services/mode/mode_enum.py:6:)
|
ADVANCED # unused attribute (monkey/monkey_island/cc/services/mode/mode_enum.py:7:)
|
||||||
|
UNSET # unused attribute (monkey/monkey_island/cc/services/mode/mode_enum.py:5:)
|
||||||
Report.overview
|
Report.overview
|
||||||
Report.recommendations
|
Report.recommendations
|
||||||
Report.glance
|
Report.glance
|
||||||
|
|
Loading…
Reference in New Issue