From 95d35fc8aa4b646fa7dc02a6cde46e29cb274af6 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 20 Sep 2017 15:55:02 +0300 Subject: [PATCH] Implement detection of monkey on island or locally Fix UI issue of cleanup Kill all monkeys works Implemented logic for V ticking --- monkey_island/cc/app.py | 2 ++ monkey_island/cc/resources/client_run.py | 22 +++++++++++++++++++ monkey_island/cc/resources/local_run.py | 9 +++++++- monkey_island/cc/resources/monkey.py | 14 ++---------- monkey_island/cc/resources/root.py | 8 ++++--- monkey_island/cc/services/node.py | 21 +++++++++++++++++- .../cc/ui/src/components/pages/MapPage.js | 15 ++++++++++++- .../ui/src/components/pages/RunMonkeyPage.js | 13 ++++++++--- .../ui/src/components/pages/StartOverPage.js | 6 +---- 9 files changed, 84 insertions(+), 26 deletions(-) create mode 100644 monkey_island/cc/resources/client_run.py diff --git a/monkey_island/cc/app.py b/monkey_island/cc/app.py index 586016341..1a2bf36ac 100644 --- a/monkey_island/cc/app.py +++ b/monkey_island/cc/app.py @@ -5,6 +5,7 @@ from flask import Flask, send_from_directory, redirect, make_response import flask_restful from cc.database import mongo +from cc.resources.client_run import ClientRun from cc.resources.monkey import Monkey from cc.resources.local_run import LocalRun from cc.resources.telemetry import Telemetry @@ -75,6 +76,7 @@ def init_app(mongo_url): api.add_resource(Root, '/api') api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/') api.add_resource(LocalRun, '/api/local-monkey', '/api/local-monkey/') + api.add_resource(ClientRun, '/api/client-monkey', '/api/client-monkey/') api.add_resource(Telemetry, '/api/telemetry', '/api/telemetry/', '/api/telemetry/') api.add_resource(MonkeyConfiguration, '/api/configuration', '/api/configuration/') api.add_resource(MonkeyDownload, '/api/monkey/download', '/api/monkey/download/', diff --git a/monkey_island/cc/resources/client_run.py b/monkey_island/cc/resources/client_run.py new file mode 100644 index 000000000..111c0d1a2 --- /dev/null +++ b/monkey_island/cc/resources/client_run.py @@ -0,0 +1,22 @@ +from flask import request, jsonify +import flask_restful + +from cc.services.node import NodeService + +__author__ = 'itay.mizeretz' + + +class ClientRun(flask_restful.Resource): + def get(self): + client_ip = request.remote_addr + if client_ip == "127.0.0.1": + monkey = NodeService.get_monkey_island_monkey() + else: + monkey = NodeService.get_monkey_by_ip(client_ip) + NodeService.update_dead_monkeys() + if monkey is not None: + is_monkey_running = not monkey["dead"] + else: + is_monkey_running = False + + return jsonify(is_running=is_monkey_running) diff --git a/monkey_island/cc/resources/local_run.py b/monkey_island/cc/resources/local_run.py index f9b5b3302..ed8949501 100644 --- a/monkey_island/cc/resources/local_run.py +++ b/monkey_island/cc/resources/local_run.py @@ -48,7 +48,14 @@ def run_local_monkey(): class LocalRun(flask_restful.Resource): def get(self): - return jsonify(is_running=(NodeService.get_monkey_island_monkey() is not None)) + NodeService.update_dead_monkeys() + island_monkey = NodeService.get_monkey_island_monkey() + if island_monkey is not None: + is_monkey_running = not island_monkey["dead"] + else: + is_monkey_running = False + + return jsonify(is_running=is_monkey_running) def post(self): body = json.loads(request.data) diff --git a/monkey_island/cc/resources/monkey.py b/monkey_island/cc/resources/monkey.py index 89be0e561..d6d89e2c8 100644 --- a/monkey_island/cc/resources/monkey.py +++ b/monkey_island/cc/resources/monkey.py @@ -1,5 +1,5 @@ import json -from datetime import datetime, timedelta +from datetime import datetime import dateutil.parser from flask import request @@ -14,19 +14,9 @@ __author__ = 'Barak' # TODO: separate logic from interface -def update_dead_monkeys(): - # Update dead monkeys only if no living monkey transmitted keepalive in the last 10 minutes - if mongo.db.monkey.find_one({'dead': {'$ne': True}, 'keepalive': {'$gte': datetime.now() - timedelta(minutes=10)}}): - return - - mongo.db.monkey.update( - {'keepalive': {'$lte': datetime.now() - timedelta(minutes=10)}, 'dead': {'$ne': True}}, - {'$set': {'dead': True, 'modifytime': datetime.now()}}, upsert=False, multi=True) - - class Monkey(flask_restful.Resource): def get(self, guid=None, **kw): - update_dead_monkeys() # refresh monkeys status + NodeService.update_dead_monkeys() # refresh monkeys status if not guid: guid = request.args.get('guid') timestamp = request.args.get('timestamp') diff --git a/monkey_island/cc/resources/root.py b/monkey_island/cc/resources/root.py index 02e152853..781926818 100644 --- a/monkey_island/cc/resources/root.py +++ b/monkey_island/cc/resources/root.py @@ -5,6 +5,7 @@ import flask_restful from cc.database import mongo from cc.services.config import ConfigService +from cc.services.node import NodeService from cc.utils import local_ip_addresses @@ -30,10 +31,11 @@ class Root(flask_restful.Resource): elif action == "killall": mongo.db.monkey.update({}, {'$set': {'config.alive': False, 'modifytime': datetime.now()}}, upsert=False, multi=True) - return 200 + return jsonify(status='OK') else: return make_response(400, {'error': 'unknown action'}) def get_completed_steps(self): - # TODO implement - return dict(run_server=True, run_monkey=False, infection_done=False) + is_any_exists = NodeService.is_any_monkey_exists() + is_any_alive = NodeService.is_any_monkey_alive() + return dict(run_server=True, run_monkey=is_any_exists, infection_done=(is_any_exists and not is_any_alive)) diff --git a/monkey_island/cc/services/node.py b/monkey_island/cc/services/node.py index 6494c5642..8666bcfca 100644 --- a/monkey_island/cc/services/node.py +++ b/monkey_island/cc/services/node.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta from bson import ObjectId from cc.database import mongo @@ -236,3 +236,22 @@ class NodeService: {"_id": node_id}, {"$set": {"exploited": True}} ) + + @staticmethod + def update_dead_monkeys(): + # Update dead monkeys only if no living monkey transmitted keepalive in the last 10 minutes + if mongo.db.monkey.find_one( + {'dead': {'$ne': True}, 'keepalive': {'$gte': datetime.now() - timedelta(minutes=10)}}): + return + + mongo.db.monkey.update( + {'keepalive': {'$lte': datetime.now() - timedelta(minutes=10)}, 'dead': {'$ne': True}}, + {'$set': {'dead': True, 'modifytime': datetime.now()}}, upsert=False, multi=True) + + @staticmethod + def is_any_monkey_alive(): + return mongo.db.monkey.find_one({'dead': False}) is not None + + @staticmethod + def is_any_monkey_exists(): + return mongo.db.monkey.find_one({}) is not None diff --git a/monkey_island/cc/ui/src/components/pages/MapPage.js b/monkey_island/cc/ui/src/components/pages/MapPage.js index b2511d8eb..c26c8288d 100644 --- a/monkey_island/cc/ui/src/components/pages/MapPage.js +++ b/monkey_island/cc/ui/src/components/pages/MapPage.js @@ -47,7 +47,8 @@ class MapPageComponent extends React.Component { this.state = { graph: {nodes: [], edges: []}, selected: null, - selectedType: null + selectedType: null, + killPressed: false }; } @@ -111,6 +112,12 @@ class MapPageComponent extends React.Component { } } + killAllMonkeys = () => { + fetch('/api?action=killall') + .then(res => res.json()) + .then(res => this.setState({killPressed: (res.status=="OK")})); + } + render() { return (
@@ -132,6 +139,12 @@ class MapPageComponent extends React.Component { Kill All Monkeys
+ {this.state.killPressed ? +
+ + Kill command sent to all monkeys +
+ : ''} diff --git a/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js b/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js index 1ed35828b..b0e6ba0b9 100644 --- a/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js +++ b/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js @@ -28,6 +28,13 @@ class RunMonkeyPageComponent extends React.Component { .then(res => this.setState({ isRunningOnIsland: res['is_running'] })); + + fetch('/api/client-monkey') + .then(res => res.json()) + .then(res => this.setState({ + isRunningLocally: res['is_running'] + })); + this.props.onStatusChange(); } @@ -99,16 +106,16 @@ class RunMonkeyPageComponent extends React.Component { className="btn btn-default" disabled={this.state.isRunningOnIsland}> Run on C&C Server - { !this.state.isRunningOnIsland ? + { this.state.isRunningOnIsland ? : ''} + style={{'marginLeft': '1em'}}> Download and run locally - { !this.state.isRunningLocally ? + { this.state.isRunningLocally ? : ''} diff --git a/monkey_island/cc/ui/src/components/pages/StartOverPage.js b/monkey_island/cc/ui/src/components/pages/StartOverPage.js index 603e72456..20271a6b4 100644 --- a/monkey_island/cc/ui/src/components/pages/StartOverPage.js +++ b/monkey_island/cc/ui/src/components/pages/StartOverPage.js @@ -41,18 +41,14 @@ class StartOverPageComponent extends React.Component { ); } - cleanup() { - // TODO: fix - /* + cleanup = () => { this.setState({ cleaned: false }); - */ fetch('/api?action=reset') .then(res => res.json()) .then(res => { if (res["status"] == "OK") { - // TODO: fix this this.setState({ cleaned: true });