From 1ab1dbedb1d8248ddd89e76509498b9f50d8d9dd Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Tue, 9 Jan 2018 19:16:18 +0200 Subject: [PATCH 1/3] Add TelemetryFeed entrypoint + all logic --- monkey_island/cc/app.py | 2 + monkey_island/cc/resources/telemetry_feed.py | 85 ++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 monkey_island/cc/resources/telemetry_feed.py diff --git a/monkey_island/cc/app.py b/monkey_island/cc/app.py index 6cfea1502..3990d1003 100644 --- a/monkey_island/cc/app.py +++ b/monkey_island/cc/app.py @@ -17,6 +17,7 @@ from cc.resources.edge import Edge from cc.resources.node import Node from cc.resources.root import Root +from cc.resources.telemetry_feed import TelemetryFeed from cc.services.config import ConfigService __author__ = 'Barak' @@ -88,5 +89,6 @@ def init_app(mongo_url): api.add_resource(NetMap, '/api/netmap', '/api/netmap/') api.add_resource(Edge, '/api/netmap/edge', '/api/netmap/edge/') api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/') + api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/') return app diff --git a/monkey_island/cc/resources/telemetry_feed.py b/monkey_island/cc/resources/telemetry_feed.py new file mode 100644 index 000000000..4c5e3b354 --- /dev/null +++ b/monkey_island/cc/resources/telemetry_feed.py @@ -0,0 +1,85 @@ +from datetime import datetime + +import dateutil +import flask_restful +from flask import request + +from cc.database import mongo +from cc.services.node import NodeService + +__author__ = 'itay.mizeretz' + + +class TelemetryFeed(flask_restful.Resource): + def get(self, **kw): + timestamp = request.args.get('timestamp') + if "null" == timestamp or timestamp is None: # special case to avoid ugly JS code... + timestamp = datetime(2010,1,1).isoformat() + + telemetries = mongo.db.telemetry.find({'timestamp': {'$gt': dateutil.parser.parse(timestamp)}}) + + return \ + { + 'telemetries': [TelemetryFeed.get_displayed_telemetry(telem) for telem in telemetries], + 'timestamp': datetime.now().isoformat() + } + + @staticmethod + def get_displayed_telemetry(telem): + return \ + { + 'id': telem['_id'], + 'timestamp': telem['timestamp'].strftime('%d/%m/%Y %H:%M:%S'), + 'hostname': NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname'], + 'brief': TELEM_PROCESS_DICT[telem['telem_type']](telem) + } + + @staticmethod + def get_tunnel_telem_brief(telem): + tunnel = telem['data']['proxy'] + if tunnel is None: + return 'No tunnel is used.' + else: + tunnel_host_ip = tunnel.split(":")[-2].replace("//", "") + tunnel_host = NodeService.get_monkey_by_ip(tunnel_host_ip)['hostname'] + return 'Tunnel set up to machine: %s.' % tunnel_host + + @staticmethod + def get_state_telem_brief(telem): + if telem['data']['done']: + return 'Monkey died.' + else: + return 'Monkey started.' + + @staticmethod + def get_exploit_telem_brief(telem): + target = telem['data']['machine']['ip_addr'] + exploiter = telem['data']['exploiter'] + result = telem['data']['result'] + if result: + return 'Monkey successfully exploited %s using the %s exploiter.' % (target, exploiter) + else: + return 'Monkey failed exploiting %s using the %s exploiter.' % (target, exploiter) + + @staticmethod + def get_scan_telem_brief(telem): + return 'Monkey discovered machine %s.' % telem['data']['machine']['ip_addr'] + + @staticmethod + def get_systeminfo_telem_brief(telem): + return 'Monkey collected system information.' + + @staticmethod + def get_trace_telem_brief(telem): + return 'Monkey reached max depth.' + + +TELEM_PROCESS_DICT = \ + { + 'tunnel': TelemetryFeed.get_tunnel_telem_brief, + 'state': TelemetryFeed.get_state_telem_brief, + 'exploit': TelemetryFeed.get_exploit_telem_brief, + 'scan': TelemetryFeed.get_scan_telem_brief, + 'system_info_collection': TelemetryFeed.get_systeminfo_telem_brief, + 'trace': TelemetryFeed.get_trace_telem_brief + } From 50c674a2af02d6da8e8350a5362f98832697ed4a Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Tue, 9 Jan 2018 19:19:16 +0200 Subject: [PATCH 2/3] Add telemetry console frontend --- .../cc/ui/src/components/pages/MapPage.js | 58 ++++++++++++++----- monkey_island/cc/ui/src/styles/App.css | 3 +- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/monkey_island/cc/ui/src/components/pages/MapPage.js b/monkey_island/cc/ui/src/components/pages/MapPage.js index 81f3baf8a..bb9aa0d74 100644 --- a/monkey_island/cc/ui/src/components/pages/MapPage.js +++ b/monkey_island/cc/ui/src/components/pages/MapPage.js @@ -53,7 +53,9 @@ class MapPageComponent extends React.Component { selected: null, selectedType: null, killPressed: false, - showKillDialog: false + showKillDialog: false, + telemetry: [], + telemetryLastTimestamp: null }; } @@ -77,13 +79,18 @@ class MapPageComponent extends React.Component { componentDidMount() { this.updateMapFromServer(); - this.interval = setInterval(this.updateMapFromServer, 1000); + this.interval = setInterval(this.timedEvents, 1000); } componentWillUnmount() { clearInterval(this.interval); } + timedEvents = () => { + this.updateMapFromServer(); + this.updateTelemetryFromServer(); + }; + updateMapFromServer = () => { fetch('/api/netmap') .then(res => res.json()) @@ -96,6 +103,21 @@ class MapPageComponent extends React.Component { }); }; + updateTelemetryFromServer = () => { + fetch('/api/telemetry-feed?timestamp='+this.state.telemetryLastTimestamp) + .then(res => res.json()) + .then(res => { + let newTelem = this.state.telemetry.concat(res['telemetries']); + + this.setState( + { + telemetry: newTelem, + telemetryLastTimestamp: res['timestamp'] + }); + this.props.onStatusChange(); + }); + }; + selectionChanged(event) { if (event.nodes.length === 1) { fetch('/api/netmap/node?id=' + event.nodes[0]) @@ -156,6 +178,26 @@ class MapPageComponent extends React.Component { ) }; + renderTelemetryEntry(telemetry) { + return ( +
+ {telemetry.timestamp} + {telemetry.hostname}: + {telemetry.brief} +
+ ); + } + + renderTelemetryConsole() { + return ( +
+ { + this.state.telemetry.map(this.renderTelemetryEntry) + } +
+ ); + } + render() { return (
@@ -174,17 +216,7 @@ class MapPageComponent extends React.Component { | Island Communication
- { - /* -
-
- 2017-10-16 16:00:05 - monkey-elastic - bla bla -
-
- */ - } + { this.renderTelemetryConsole() }
diff --git a/monkey_island/cc/ui/src/styles/App.css b/monkey_island/cc/ui/src/styles/App.css index 9ecf08cbb..9a0d248a5 100644 --- a/monkey_island/cc/ui/src/styles/App.css +++ b/monkey_island/cc/ui/src/styles/App.css @@ -276,13 +276,14 @@ body { bottom: 0; left: 0; right: 0; - height: 70px; + height: 130px; background: rgba(0,0,0,0.7); border-radius: 5px; border: 3px solid #aaa; padding: 0.5em; color: white; font-family: Consolas, "Courier New", monospace; + overflow: auto; } .telemetry-console .date { From 1935d2d6a191cd11ca249cfd1f292c979ba12220 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Tue, 9 Jan 2018 19:25:18 +0200 Subject: [PATCH 3/3] Fix temporary hack --- monkey_island/cc/resources/telemetry_feed.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey_island/cc/resources/telemetry_feed.py b/monkey_island/cc/resources/telemetry_feed.py index 4c5e3b354..24e0dcc51 100644 --- a/monkey_island/cc/resources/telemetry_feed.py +++ b/monkey_island/cc/resources/telemetry_feed.py @@ -14,9 +14,9 @@ class TelemetryFeed(flask_restful.Resource): def get(self, **kw): timestamp = request.args.get('timestamp') if "null" == timestamp or timestamp is None: # special case to avoid ugly JS code... - timestamp = datetime(2010,1,1).isoformat() - - telemetries = mongo.db.telemetry.find({'timestamp': {'$gt': dateutil.parser.parse(timestamp)}}) + telemetries = mongo.db.telemetry.find({}) + else: + telemetries = mongo.db.telemetry.find({'timestamp': {'$gt': dateutil.parser.parse(timestamp)}}) return \ {