From ab0c9c5456cf4f904cb2b0c7621bd690be30d32c Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Thu, 16 Jul 2020 11:17:07 +0000 Subject: [PATCH 1/8] fix: monkey/monkey_island/requirements.txt to reduce vulnerabilities The following vulnerabilities are fixed by pinning transitive dependencies: - https://snyk.io/vuln/SNYK-PYTHON-PYJWT-40693 --- monkey/monkey_island/requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monkey/monkey_island/requirements.txt b/monkey/monkey_island/requirements.txt index 59428bd0d..267210074 100644 --- a/monkey/monkey_island/requirements.txt +++ b/monkey/monkey_island/requirements.txt @@ -25,3 +25,5 @@ tqdm>=4.47 virtualenv>=20.0.26 werkzeug>=1.0.1 wheel>=0.34.2 + +pyjwt>=1.5.1 # not directly required, pinned by Snyk to avoid a vulnerability \ No newline at end of file From 21eef102a86d2ad4946f8b580220c29b38d7245b Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Sat, 18 Jul 2020 07:00:29 +0000 Subject: [PATCH 2/8] fix: upgrade react-tooltip-lite from 1.11.2 to 1.12.0 Snyk has created this PR to upgrade react-tooltip-lite from 1.11.2 to 1.12.0. See this package in NPM: https://www.npmjs.com/package/react-tooltip-lite See this project in Snyk: https://app.snyk.io/org/shaynehmad/project/37aecb9c-98b4-4735-95a2-83d941303b4e?utm_source=github&utm_medium=upgrade-pr --- monkey/monkey_island/cc/ui/package-lock.json | 6 +++--- monkey/monkey_island/cc/ui/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 3e8956239..db684aeb6 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -13802,9 +13802,9 @@ } }, "react-tooltip-lite": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/react-tooltip-lite/-/react-tooltip-lite-1.11.2.tgz", - "integrity": "sha512-zFVKAFta5nC1763B1XCeFhSBRYblVgLxRCnXu4ucMqaJQ7UxpZV4gFgou9gesfLWKr4Cgvx1bpy6L9msKXQqCw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/react-tooltip-lite/-/react-tooltip-lite-1.12.0.tgz", + "integrity": "sha512-QjDnmDmjtLNKvLY6bzUOG8W6ZDBTiE4UXugGzClOQEGvMvbkJn2GvZvLwRaxsN/GCx7589RgbGaESMiJAm+zWg==", "requires": { "prop-types": "^15.5.8" } diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 802f7dd41..630b239d8 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -103,7 +103,7 @@ "react-spinners": "^0.9.0", "react-table": "^6.10.3", "react-toggle": "^4.1.1", - "react-tooltip-lite": "^1.10.0", + "react-tooltip-lite": "^1.12.0", "redux": "^4.0.4", "sha3": "^2.0.7", "snyk": "^1.361.3" From 271466c6b598249835a16675169d4562b5b75087 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 21 Jul 2020 16:08:23 +0300 Subject: [PATCH 3/8] Removed core-js/fn/object/assign import --- monkey/monkey_island/cc/ui/src/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/index.js b/monkey/monkey_island/cc/ui/src/index.js index a80de72fe..06ce514e1 100644 --- a/monkey/monkey_island/cc/ui/src/index.js +++ b/monkey/monkey_island/cc/ui/src/index.js @@ -1,6 +1,5 @@ import 'core-js/stable'; import 'regenerator-runtime/runtime'; -import 'core-js/fn/object/assign'; import React from 'react'; import ReactDOM from 'react-dom'; import App from './components/Main'; From 1f26d7ffb948d0acb1e25731382179beff8df8f1 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 21 Jul 2020 17:30:21 +0300 Subject: [PATCH 4/8] replace flask-jwt with flask-jwt-extended --- monkey/monkey_island/cc/app.py | 14 ++-- .../monkey_island/cc/environment/__init__.py | 2 +- .../cc/resources/attack/attack_config.py | 4 +- .../cc/resources/attack/attack_report.py | 2 +- .../monkey_island/cc/resources/auth/auth.py | 67 +++++++++++++------ .../cc/resources/{ => auth}/registration.py | 0 .../cc/resources/island_configuration.py | 4 +- .../monkey_island/cc/resources/island_logs.py | 2 +- monkey/monkey_island/cc/resources/log.py | 2 +- .../cc/resources/monkey_configuration.py | 4 +- monkey/monkey_island/cc/resources/netmap.py | 2 +- monkey/monkey_island/cc/resources/node.py | 2 +- .../monkey_island/cc/resources/node_states.py | 2 +- .../cc/resources/pba_file_upload.py | 6 +- .../monkey_island/cc/resources/remote_run.py | 4 +- .../cc/resources/reporting/report.py | 2 +- monkey/monkey_island/cc/resources/root.py | 6 +- .../monkey_island/cc/resources/telemetry.py | 2 +- .../cc/resources/telemetry_feed.py | 4 +- .../cc/resources/test/clear_caches.py | 2 +- .../cc/resources/test/log_test.py | 2 +- .../cc/resources/test/monkey_test.py | 2 +- .../cc/resources/zero_trust/finding_event.py | 2 +- .../ui/src/components/pages/RegisterPage.js | 6 +- .../cc/ui/src/services/AuthService.js | 7 +- monkey/monkey_island/requirements.txt | 2 +- 26 files changed, 93 insertions(+), 61 deletions(-) rename monkey/monkey_island/cc/resources/{ => auth}/registration.py (100%) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 79edccffa..0249263e7 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -10,7 +10,7 @@ from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.database import database, mongo from monkey_island.cc.resources.attack.attack_config import AttackConfiguration from monkey_island.cc.resources.attack.attack_report import AttackReport -from monkey_island.cc.resources.auth.auth import init_jwt +from monkey_island.cc.resources.auth.auth import init_jwt, Authenticate from monkey_island.cc.resources.bootloader import Bootloader from monkey_island.cc.resources.client_run import ClientRun from monkey_island.cc.resources.edge import Edge @@ -31,7 +31,7 @@ from monkey_island.cc.resources.node import Node from monkey_island.cc.resources.node_states import NodeStates from monkey_island.cc.resources.pba_file_download import PBAFileDownload from monkey_island.cc.resources.pba_file_upload import FileUpload -from monkey_island.cc.resources.registration import Registration +from monkey_island.cc.resources.auth.registration import Registration from monkey_island.cc.resources.remote_run import RemoteRun from monkey_island.cc.resources.reporting.report import Report from monkey_island.cc.resources.root import Root @@ -71,9 +71,12 @@ def serve_home(): def init_app_config(app, mongo_url): app.config['MONGO_URI'] = mongo_url - app.config['SECRET_KEY'] = str(uuid.getnode()) - app.config['JWT_AUTH_URL_RULE'] = '/api/auth' - app.config['JWT_EXPIRATION_DELTA'] = env_singleton.env.get_auth_expiration_time() + + # See https://flask-jwt-extended.readthedocs.io/en/stable/options + app.config['JWT_TOKEN_LOCATION'] = ['headers'] + app.config['JWT_ACCESS_TOKEN_EXPIRES'] = env_singleton.env.get_auth_expiration_time() + # Invalidate the signature of JWTs between server resets. + app.config['JWT_SECRET_KEY'] = str(uuid.uuid4()) def init_app_services(app): @@ -96,6 +99,7 @@ def init_app_url_rules(app): def init_api_resources(api): api.add_resource(Root, '/api') api.add_resource(Registration, '/api/registration') + api.add_resource(Authenticate, '/api/auth') api.add_resource(Environment, '/api/environment') api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/') api.add_resource(Bootloader, '/api/bootloader/') diff --git a/monkey/monkey_island/cc/environment/__init__.py b/monkey/monkey_island/cc/environment/__init__.py index e35233c69..fcaa4e156 100644 --- a/monkey/monkey_island/cc/environment/__init__.py +++ b/monkey/monkey_island/cc/environment/__init__.py @@ -23,7 +23,7 @@ class Environment(object, metaclass=ABCMeta): _MONGO_URL = os.environ.get("MONKEY_MONGO_URL", "mongodb://{0}:{1}/{2}".format(_MONGO_DB_HOST, _MONGO_DB_PORT, str(_MONGO_DB_NAME))) _DEBUG_SERVER = False - _AUTH_EXPIRATION_TIME = timedelta(hours=1) + _AUTH_EXPIRATION_TIME = timedelta(minutes=30) _testing = False diff --git a/monkey/monkey_island/cc/resources/attack/attack_config.py b/monkey/monkey_island/cc/resources/attack/attack_config.py index e8889a487..532b1fb4f 100644 --- a/monkey/monkey_island/cc/resources/attack/attack_config.py +++ b/monkey/monkey_island/cc/resources/attack/attack_config.py @@ -8,7 +8,7 @@ __author__ = "VakarisZ" class AttackConfiguration(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self): return current_app.response_class(json.dumps({"configuration": AttackConfig.get_config()}, indent=None, @@ -16,7 +16,7 @@ class AttackConfiguration(flask_restful.Resource): sort_keys=False) + "\n", mimetype=current_app.config['JSONIFY_MIMETYPE']) - @jwt_required() + @jwt_required def post(self): """ Based on request content this endpoint either resets ATT&CK configuration or updates it. diff --git a/monkey/monkey_island/cc/resources/attack/attack_report.py b/monkey/monkey_island/cc/resources/attack/attack_report.py index e113dfa76..779c436c5 100644 --- a/monkey/monkey_island/cc/resources/attack/attack_report.py +++ b/monkey/monkey_island/cc/resources/attack/attack_report.py @@ -10,7 +10,7 @@ __author__ = "VakarisZ" class AttackReport(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self): response_content = {'techniques': AttackReportService.get_latest_report()['techniques'], 'schema': SCHEMA} return current_app.response_class(json.dumps(response_content, diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 24176cdf6..f86e0eeb1 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -1,40 +1,67 @@ +import json +import logging from functools import wraps -from flask import abort, current_app -from flask_jwt import JWT, JWTError, _jwt_required +import flask_restful +import flask_jwt_extended +from flask import make_response, request +from flask_jwt_extended.exceptions import JWTExtendedException +from jwt import PyJWTError from werkzeug.security import safe_str_cmp import monkey_island.cc.environment.environment_singleton as env_singleton import monkey_island.cc.resources.auth.user_store as user_store -__author__ = 'itay.mizeretz' +logger = logging.getLogger(__name__) def init_jwt(app): user_store.UserStore.set_users(env_singleton.env.get_auth_users()) + _ = flask_jwt_extended.JWTManager(app) + logger.debug("Initialized JWT with secret key that started with " + app.config["JWT_SECRET_KEY"][:4]) - def authenticate(username, secret): + +class Authenticate(flask_restful.Resource): + """ + Resource for user authentication. The user provides the username and hashed password and we give them a JWT. + See `AuthService.js` file for the frontend counterpart for this code. + """ + @staticmethod + def _authenticate(username, secret): user = user_store.UserStore.username_table.get(username, None) if user and safe_str_cmp(user.secret.encode('utf-8'), secret.encode('utf-8')): return user - def identity(payload): - user_id = payload['identity'] - return user_store.UserStore.user_id_table.get(user_id, None) - - JWT(app, authenticate, identity) + def post(self): + """ + Example request: + { + "username": "my_user", + "password": "343bb87e553b05430e5c44baf99569d4b66..." + } + """ + credentials = json.loads(request.data) + # Unpack auth info from request + username = credentials["username"] + secret = credentials["password"] + # If the user and password have been previously registered + if self._authenticate(username, secret): + access_token = flask_jwt_extended.create_access_token(identity=user_store.UserStore.username_table[username].id) + logger.debug(f"Created access token for user {username}: {access_token}") + return make_response({"access_token": access_token, "error": ""}, 200) + else: + return make_response({"error": "Invalid credentials"}, 401) -def jwt_required(realm=None): - def wrapper(fn): - @wraps(fn) - def decorator(*args, **kwargs): - try: - _jwt_required(realm or current_app.config['JWT_DEFAULT_REALM']) - return fn(*args, **kwargs) - except JWTError: - abort(401) - - return decorator +# See https://flask-jwt-extended.readthedocs.io/en/stable/custom_decorators/ +def jwt_required(fn): + @wraps(fn) + def wrapper(*args, **kwargs): + try: + flask_jwt_extended.verify_jwt_in_request() + return fn(*args, **kwargs) + # Catch authentication related errors in the verification or inside the called function. All other exceptions propagate + except (JWTExtendedException, PyJWTError) as e: + return make_response({"error": f"Authentication error: {str(e)}"}, 401) return wrapper diff --git a/monkey/monkey_island/cc/resources/registration.py b/monkey/monkey_island/cc/resources/auth/registration.py similarity index 100% rename from monkey/monkey_island/cc/resources/registration.py rename to monkey/monkey_island/cc/resources/auth/registration.py diff --git a/monkey/monkey_island/cc/resources/island_configuration.py b/monkey/monkey_island/cc/resources/island_configuration.py index deda3e251..b8a556016 100644 --- a/monkey/monkey_island/cc/resources/island_configuration.py +++ b/monkey/monkey_island/cc/resources/island_configuration.py @@ -8,12 +8,12 @@ from monkey_island.cc.services.config import ConfigService class IslandConfiguration(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self): return jsonify(schema=ConfigService.get_config_schema(), configuration=ConfigService.get_config(False, True, True)) - @jwt_required() + @jwt_required def post(self): config_json = json.loads(request.data) if 'reset' in config_json: diff --git a/monkey/monkey_island/cc/resources/island_logs.py b/monkey/monkey_island/cc/resources/island_logs.py index 5ef64789b..5d1d6d276 100644 --- a/monkey/monkey_island/cc/resources/island_logs.py +++ b/monkey/monkey_island/cc/resources/island_logs.py @@ -11,7 +11,7 @@ logger = logging.getLogger(__name__) class IslandLog(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self): try: return IslandLogService.get_log_file() diff --git a/monkey/monkey_island/cc/resources/log.py b/monkey/monkey_island/cc/resources/log.py index 67f4e5e47..0d437d174 100644 --- a/monkey/monkey_island/cc/resources/log.py +++ b/monkey/monkey_island/cc/resources/log.py @@ -14,7 +14,7 @@ __author__ = "itay.mizeretz" class Log(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self): monkey_id = request.args.get('id') exists_monkey_id = request.args.get('exists') diff --git a/monkey/monkey_island/cc/resources/monkey_configuration.py b/monkey/monkey_island/cc/resources/monkey_configuration.py index d692b8690..e6b94cf81 100644 --- a/monkey/monkey_island/cc/resources/monkey_configuration.py +++ b/monkey/monkey_island/cc/resources/monkey_configuration.py @@ -10,11 +10,11 @@ __author__ = 'Barak' class MonkeyConfiguration(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self): return jsonify(schema=ConfigService.get_config_schema(), configuration=ConfigService.get_config(False, True)) - @jwt_required() + @jwt_required def post(self): config_json = json.loads(request.data) if 'reset' in config_json: diff --git a/monkey/monkey_island/cc/resources/netmap.py b/monkey/monkey_island/cc/resources/netmap.py index d9aad5bcc..899dc478c 100644 --- a/monkey/monkey_island/cc/resources/netmap.py +++ b/monkey/monkey_island/cc/resources/netmap.py @@ -8,7 +8,7 @@ __author__ = 'Barak' class NetMap(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self, **kw): net_nodes = NetNodeService.get_all_net_nodes() net_edges = NetEdgeService.get_all_net_edges() diff --git a/monkey/monkey_island/cc/resources/node.py b/monkey/monkey_island/cc/resources/node.py index 6816e7142..ff630b9a4 100644 --- a/monkey/monkey_island/cc/resources/node.py +++ b/monkey/monkey_island/cc/resources/node.py @@ -8,7 +8,7 @@ __author__ = 'Barak' class Node(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self): node_id = request.args.get('id') if node_id: diff --git a/monkey/monkey_island/cc/resources/node_states.py b/monkey/monkey_island/cc/resources/node_states.py index a75eb3ec7..0b50ac34c 100644 --- a/monkey/monkey_island/cc/resources/node_states.py +++ b/monkey/monkey_island/cc/resources/node_states.py @@ -6,6 +6,6 @@ from monkey_island.cc.services.utils.node_states import \ class NodeStates(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self): return {'node_states': [state.value for state in NodeStateList]} diff --git a/monkey/monkey_island/cc/resources/pba_file_upload.py b/monkey/monkey_island/cc/resources/pba_file_upload.py index 1b63d3a7b..b18fd7b2f 100644 --- a/monkey/monkey_island/cc/resources/pba_file_upload.py +++ b/monkey/monkey_island/cc/resources/pba_file_upload.py @@ -27,7 +27,7 @@ class FileUpload(flask_restful.Resource): # Create all directories on the way if they don't exist UPLOADS_DIR.mkdir(parents=True, exist_ok=True) - @jwt_required() + @jwt_required def get(self, file_type): """ Sends file to filepond @@ -41,7 +41,7 @@ class FileUpload(flask_restful.Resource): filename = ConfigService.get_config_value(copy.deepcopy(PBA_WINDOWS_FILENAME_PATH)) return send_from_directory(UPLOADS_DIR, filename) - @jwt_required() + @jwt_required def post(self, file_type): """ Receives user's uploaded file from filepond @@ -55,7 +55,7 @@ class FileUpload(flask_restful.Resource): status=200, mimetype='text/plain') return response - @jwt_required() + @jwt_required def delete(self, file_type): """ Deletes file that has been deleted on the front end diff --git a/monkey/monkey_island/cc/resources/remote_run.py b/monkey/monkey_island/cc/resources/remote_run.py index fce91098a..0e80f25c0 100644 --- a/monkey/monkey_island/cc/resources/remote_run.py +++ b/monkey/monkey_island/cc/resources/remote_run.py @@ -24,7 +24,7 @@ class RemoteRun(flask_restful.Resource): island_ip = request_body.get('island_ip') return RemoteRunAwsService.run_aws_monkeys(instances, island_ip) - @jwt_required() + @jwt_required def get(self): action = request.args.get('action') if action == 'list_aws': @@ -43,7 +43,7 @@ class RemoteRun(flask_restful.Resource): return {} - @jwt_required() + @jwt_required def post(self): body = json.loads(request.data) resp = {} diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index ca1ce395f..a0ea8b0b9 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -21,7 +21,7 @@ __author__ = ["itay.mizeretz", "shay.nehmad"] class Report(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self, report_type=SECURITY_REPORT_TYPE, report_data=None): if report_type == SECURITY_REPORT_TYPE: return ReportService.get_report() diff --git a/monkey/monkey_island/cc/resources/root.py b/monkey/monkey_island/cc/resources/root.py index d3a374454..7463a9857 100644 --- a/monkey/monkey_island/cc/resources/root.py +++ b/monkey/monkey_island/cc/resources/root.py @@ -26,15 +26,15 @@ class Root(flask_restful.Resource): if not action: return self.get_server_info() elif action == "reset": - return jwt_required()(Database.reset_db)() + return jwt_required(Database.reset_db)() elif action == "killall": - return jwt_required()(InfectionLifecycle.kill_all)() + return jwt_required(InfectionLifecycle.kill_all)() elif action == "is-up": return {'is-up': True} else: return make_response(400, {'error': 'unknown action'}) - @jwt_required() + @jwt_required def get_server_info(self): return jsonify( ip_addresses=local_ip_addresses(), diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index f6c58af40..efdeb34b3 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -20,7 +20,7 @@ logger = logging.getLogger(__name__) class Telemetry(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self, **kw): monkey_guid = request.args.get('monkey_guid') telem_category = request.args.get('telem_category') diff --git a/monkey/monkey_island/cc/resources/telemetry_feed.py b/monkey/monkey_island/cc/resources/telemetry_feed.py index 3814c841a..b6152ac32 100644 --- a/monkey/monkey_island/cc/resources/telemetry_feed.py +++ b/monkey/monkey_island/cc/resources/telemetry_feed.py @@ -5,9 +5,9 @@ import dateutil import flask_pymongo import flask_restful from flask import request +from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.database import mongo -from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.services.node import NodeService logger = logging.getLogger(__name__) @@ -16,7 +16,7 @@ __author__ = 'itay.mizeretz' class TelemetryFeed(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self, **kw): timestamp = request.args.get('timestamp') if "null" == timestamp or timestamp is None: # special case to avoid ugly JS code... diff --git a/monkey/monkey_island/cc/resources/test/clear_caches.py b/monkey/monkey_island/cc/resources/test/clear_caches.py index b2f23de0a..34401b318 100644 --- a/monkey/monkey_island/cc/resources/test/clear_caches.py +++ b/monkey/monkey_island/cc/resources/test/clear_caches.py @@ -17,7 +17,7 @@ class ClearCaches(flask_restful.Resource): so we use this to clear the caches. :note: DO NOT CALL THIS IN PRODUCTION CODE as this will slow down the user experience. """ - @jwt_required() + @jwt_required def get(self, **kw): try: logger.warning("Trying to clear caches! Make sure this is not production") diff --git a/monkey/monkey_island/cc/resources/test/log_test.py b/monkey/monkey_island/cc/resources/test/log_test.py index 79f82f5c9..a9c4f8b62 100644 --- a/monkey/monkey_island/cc/resources/test/log_test.py +++ b/monkey/monkey_island/cc/resources/test/log_test.py @@ -7,7 +7,7 @@ from monkey_island.cc.resources.auth.auth import jwt_required class LogTest(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self): find_query = json_util.loads(request.args.get('find_query')) log = mongo.db.log.find_one(find_query) diff --git a/monkey/monkey_island/cc/resources/test/monkey_test.py b/monkey/monkey_island/cc/resources/test/monkey_test.py index b97589d24..da8333479 100644 --- a/monkey/monkey_island/cc/resources/test/monkey_test.py +++ b/monkey/monkey_island/cc/resources/test/monkey_test.py @@ -7,7 +7,7 @@ from monkey_island.cc.resources.auth.auth import jwt_required class MonkeyTest(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self, **kw): find_query = json_util.loads(request.args.get('find_query')) return {'results': list(mongo.db.monkey.find(find_query))} diff --git a/monkey/monkey_island/cc/resources/zero_trust/finding_event.py b/monkey/monkey_island/cc/resources/zero_trust/finding_event.py index 0725723d5..8a1879c9c 100644 --- a/monkey/monkey_island/cc/resources/zero_trust/finding_event.py +++ b/monkey/monkey_island/cc/resources/zero_trust/finding_event.py @@ -9,6 +9,6 @@ from monkey_island.cc.services.reporting.zero_trust_service import \ class ZeroTrustFindingEvent(flask_restful.Resource): - @jwt_required() + @jwt_required def get(self, finding_id: str): return {'events_json': json.dumps(ZeroTrustService.get_events_by_finding(finding_id), default=str)} 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 62ff0e170..88905d805 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js @@ -26,13 +26,13 @@ class RegisterPageComponent extends React.Component { }; setNoAuth = () => { - let options = {} + let options = {}; options['headers'] = { 'Accept': 'application/json', 'Content-Type': 'application/json' }; - options['method'] = 'PATCH' - options['body'] = JSON.stringify({'server_config': 'standard'}) + options['method'] = 'PATCH'; + options['body'] = JSON.stringify({'server_config': 'standard'}); return fetch(this.NO_AUTH_API_ENDPOINT, options) .then(res => { diff --git a/monkey/monkey_island/cc/ui/src/services/AuthService.js b/monkey/monkey_island/cc/ui/src/services/AuthService.js index d6ecb24ef..e1db4186c 100644 --- a/monkey/monkey_island/cc/ui/src/services/AuthService.js +++ b/monkey/monkey_island/cc/ui/src/services/AuthService.js @@ -83,7 +83,7 @@ export default class AuthService { }; if (this._loggedIn()) { - headers['Authorization'] = 'JWT ' + this._getToken(); + headers['Authorization'] = 'Bearer ' + this._getToken(); } if (options.hasOwnProperty('headers')) { @@ -97,6 +97,9 @@ export default class AuthService { return fetch(url, options) .then(res => { if (res.status === 401) { + res.clone().json().then(res_json => { + console.log('Got 401 from server while trying to authFetch: ' + JSON.stringify(res_json)); + }); this._removeToken(); } return res; @@ -156,6 +159,4 @@ export default class AuthService { _toHexStr(byteArr) { return byteArr.reduce((acc, x) => (acc + ('0' + x.toString(0x10)).slice(-2)), ''); } - - } diff --git a/monkey/monkey_island/requirements.txt b/monkey/monkey_island/requirements.txt index 267210074..88af6bad0 100644 --- a/monkey/monkey_island/requirements.txt +++ b/monkey/monkey_island/requirements.txt @@ -1,4 +1,4 @@ -Flask-JWT>=0.3.2 +Flask-JWT-Extended==3.24.1 Flask-Pymongo>=2.3.0 Flask-Restful>=0.3.8 PyInstaller==3.6 From 1072607c42d854f0759268f97abdaf39a7caa947 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 21 Jul 2020 18:02:17 +0300 Subject: [PATCH 5/8] Fix import order --- monkey/infection_monkey/exploit/vsftpd.py | 4 ++-- monkey/monkey_island/cc/app.py | 4 ++-- monkey/monkey_island/cc/resources/auth/auth.py | 2 +- monkey/monkey_island/cc/resources/telemetry_feed.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index 8ecac35f1..ec263411f 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -14,8 +14,8 @@ from infection_monkey.exploit.tools.helpers import (build_monkey_commandline, get_monkey_depth, get_target_monkey) from infection_monkey.exploit.tools.http_tools import HTTPTools -from infection_monkey.model import (CHMOD_MONKEY, DOWNLOAD_TIMEOUT, MONKEY_ARG, - RUN_MONKEY, WGET_HTTP_UPLOAD) +from infection_monkey.model import ( + CHMOD_MONKEY, DOWNLOAD_TIMEOUT, MONKEY_ARG, RUN_MONKEY, WGET_HTTP_UPLOAD) from infection_monkey.telemetry.attack.t1222_telem import T1222Telem LOG = getLogger(__name__) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 0249263e7..ea81c0903 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -10,7 +10,8 @@ from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.database import database, mongo from monkey_island.cc.resources.attack.attack_config import AttackConfiguration from monkey_island.cc.resources.attack.attack_report import AttackReport -from monkey_island.cc.resources.auth.auth import init_jwt, Authenticate +from monkey_island.cc.resources.auth.auth import Authenticate, init_jwt +from monkey_island.cc.resources.auth.registration import Registration from monkey_island.cc.resources.bootloader import Bootloader from monkey_island.cc.resources.client_run import ClientRun from monkey_island.cc.resources.edge import Edge @@ -31,7 +32,6 @@ from monkey_island.cc.resources.node import Node from monkey_island.cc.resources.node_states import NodeStates from monkey_island.cc.resources.pba_file_download import PBAFileDownload from monkey_island.cc.resources.pba_file_upload import FileUpload -from monkey_island.cc.resources.auth.registration import Registration from monkey_island.cc.resources.remote_run import RemoteRun from monkey_island.cc.resources.reporting.report import Report from monkey_island.cc.resources.root import Root diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index f86e0eeb1..86d0f6924 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -2,8 +2,8 @@ import json import logging from functools import wraps -import flask_restful import flask_jwt_extended +import flask_restful from flask import make_response, request from flask_jwt_extended.exceptions import JWTExtendedException from jwt import PyJWTError diff --git a/monkey/monkey_island/cc/resources/telemetry_feed.py b/monkey/monkey_island/cc/resources/telemetry_feed.py index b6152ac32..c278d2f36 100644 --- a/monkey/monkey_island/cc/resources/telemetry_feed.py +++ b/monkey/monkey_island/cc/resources/telemetry_feed.py @@ -5,9 +5,9 @@ import dateutil import flask_pymongo import flask_restful from flask import request -from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.database import mongo +from monkey_island.cc.resources.auth.auth import jwt_required from monkey_island.cc.services.node import NodeService logger = logging.getLogger(__name__) From c66fd0f2a6945fb737fe9dd1433fbeab755dea7c Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 21 Jul 2020 18:08:22 +0300 Subject: [PATCH 6/8] Update vsftpd.py --- monkey/infection_monkey/exploit/vsftpd.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index ec263411f..e66f1872d 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -14,8 +14,11 @@ from infection_monkey.exploit.tools.helpers import (build_monkey_commandline, get_monkey_depth, get_target_monkey) from infection_monkey.exploit.tools.http_tools import HTTPTools -from infection_monkey.model import ( - CHMOD_MONKEY, DOWNLOAD_TIMEOUT, MONKEY_ARG, RUN_MONKEY, WGET_HTTP_UPLOAD) +from infection_monkey.model import (CHMOD_MONKEY, + DOWNLOAD_TIMEOUT, + MONKEY_ARG, + RUN_MONKEY, + WGET_HTTP_UPLOAD) from infection_monkey.telemetry.attack.t1222_telem import T1222Telem LOG = getLogger(__name__) From dd1320418a27abc3c0e04296090c8d8078364c7a Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 21 Jul 2020 18:17:29 +0300 Subject: [PATCH 7/8] Update vsftpd.py --- monkey/infection_monkey/exploit/vsftpd.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index e66f1872d..8ecac35f1 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -14,11 +14,8 @@ from infection_monkey.exploit.tools.helpers import (build_monkey_commandline, get_monkey_depth, get_target_monkey) from infection_monkey.exploit.tools.http_tools import HTTPTools -from infection_monkey.model import (CHMOD_MONKEY, - DOWNLOAD_TIMEOUT, - MONKEY_ARG, - RUN_MONKEY, - WGET_HTTP_UPLOAD) +from infection_monkey.model import (CHMOD_MONKEY, DOWNLOAD_TIMEOUT, MONKEY_ARG, + RUN_MONKEY, WGET_HTTP_UPLOAD) from infection_monkey.telemetry.attack.t1222_telem import T1222Telem LOG = getLogger(__name__) From 2bbb2d2c2c02392e8420ed3f2b5056b6d1ceadb1 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Tue, 21 Jul 2020 18:40:08 +0300 Subject: [PATCH 8/8] Some small CR fixes - improved doc and logs --- monkey/monkey_island/cc/app.py | 4 ++-- monkey/monkey_island/cc/resources/auth/auth.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index ea81c0903..6647d4b10 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -73,9 +73,9 @@ def init_app_config(app, mongo_url): app.config['MONGO_URI'] = mongo_url # See https://flask-jwt-extended.readthedocs.io/en/stable/options - app.config['JWT_TOKEN_LOCATION'] = ['headers'] app.config['JWT_ACCESS_TOKEN_EXPIRES'] = env_singleton.env.get_auth_expiration_time() - # Invalidate the signature of JWTs between server resets. + # Invalidate the signature of JWTs if the server process restarts. This avoids the edge case of getting a JWT, + # deciding to reset credentials and then still logging in with the old JWT. app.config['JWT_SECRET_KEY'] = str(uuid.uuid4()) diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 86d0f6924..71611221c 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -47,7 +47,7 @@ class Authenticate(flask_restful.Resource): # If the user and password have been previously registered if self._authenticate(username, secret): access_token = flask_jwt_extended.create_access_token(identity=user_store.UserStore.username_table[username].id) - logger.debug(f"Created access token for user {username}: {access_token}") + logger.debug(f"Created access token for user {username} that begins with {access_token[:4]}") return make_response({"access_token": access_token, "error": ""}, 200) else: return make_response({"error": "Invalid credentials"}, 401)