From f36ff73c9e1fc56075b4eefc156288290298f6a7 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 21 Mar 2019 16:55:36 +0200 Subject: [PATCH] Telemetry implemented --- monkey/infection_monkey/monkey.py | 4 +++ .../transport/attack_telems/base_telem.py | 33 ++++++++++++++----- .../attack_telems/victim_host_telem.py | 21 ++++++++++++ monkey/infection_monkey/utils.py | 24 ++++++++++++++ monkey/monkey_island/cc/app.py | 2 ++ monkey/monkey_island/cc/resources/attack.py | 22 +++++++------ .../cc/services/attck/__init__.py | 1 + .../cc/services/attck/attack_results.py | 11 +++++++ 8 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 monkey/infection_monkey/transport/attack_telems/victim_host_telem.py create mode 100644 monkey/monkey_island/cc/services/attck/__init__.py create mode 100644 monkey/monkey_island/cc/services/attck/attack_results.py diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index e80e15396..4d5d8f016 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -17,6 +17,8 @@ from infection_monkey.system_info import SystemInfoCollector from infection_monkey.system_singleton import SystemSingleton from infection_monkey.windows_upgrader import WindowsUpgrader from infection_monkey.post_breach.post_breach_handler import PostBreach +from infection_monkey.transport.attack_telems.base_telem import ScanStatus +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem __author__ = 'itamar' @@ -179,9 +181,11 @@ class InfectionMonkey(object): for exploiter in [exploiter(machine) for exploiter in self._exploiters]: if self.try_exploiting(machine, exploiter): host_exploited = True + VictimHostTelem('T1210', ScanStatus.USED.value, machine=machine).send() break if not host_exploited: self._fail_exploitation_machines.add(machine) + VictimHostTelem('T1210', ScanStatus.SCANNED.value, machine=machine).send() if not self._keep_running: break diff --git a/monkey/infection_monkey/transport/attack_telems/base_telem.py b/monkey/infection_monkey/transport/attack_telems/base_telem.py index ad1908e95..f90a53256 100644 --- a/monkey/infection_monkey/transport/attack_telems/base_telem.py +++ b/monkey/infection_monkey/transport/attack_telems/base_telem.py @@ -1,9 +1,10 @@ from enum import Enum -from infection_monkey.config import WormConfiguration, GUID +from infection_monkey.config import WormConfiguration import requests import json from infection_monkey.control import ControlClient import logging +from infection_monkey.utils import get_host_info __author__ = "VakarisZ" @@ -21,21 +22,35 @@ class ScanStatus(Enum): class AttackTelem(object): - def __init__(self, technique, status, data, machine=None): + def __init__(self, technique, status, data=None, machine=False): + """ + Default ATT&CK telemetry constructor + :param technique: Technique ID. E.g. T111 + :param status: int from ScanStatus Enum + :param data: Other data relevant to the attack technique + :param machine: Boolean. Should we pass current machine's info or not + """ self.technique = technique self.result = status - self.data = {'machine': machine, 'status': status, 'monkey_guid': GUID} - self.data.update(data) + self.data = {'status': status} + if data: + self.data.update(data) + if machine: + self.data.update({'machine': get_host_info()}) def send(self): + """ + Sends telemetry to island + :return: + """ if not WormConfiguration.current_server: return try: - reply = requests.post("https://%s/api/%s" % (WormConfiguration.current_server, self.technique), - data=json.dumps(self.data), - headers={'content-type': 'application/json'}, - verify=False, - proxies=ControlClient.proxies) + requests.post("https://%s/api/attack/%s" % (WormConfiguration.current_server, self.technique), + data=json.dumps(self.data), + headers={'content-type': 'application/json'}, + verify=False, + proxies=ControlClient.proxies) except Exception as exc: LOG.warn("Error connecting to control server %s: %s", WormConfiguration.current_server, exc) diff --git a/monkey/infection_monkey/transport/attack_telems/victim_host_telem.py b/monkey/infection_monkey/transport/attack_telems/victim_host_telem.py new file mode 100644 index 000000000..e70c03c2c --- /dev/null +++ b/monkey/infection_monkey/transport/attack_telems/victim_host_telem.py @@ -0,0 +1,21 @@ +from infection_monkey.transport.attack_telems.base_telem import AttackTelem + +__author__ = "VakarisZ" + + +class VictimHostTelem(AttackTelem): + + def __init__(self, technique, status, machine, data=None): + """ + ATT&CK telemetry that parses and sends VictimHost telemetry + :param technique: Technique ID. E.g. T111 + :param status: int from ScanStatus Enum + :param machine: VictimHost obj from model/host.py + :param data: Other data relevant to the attack technique + """ + super(VictimHostTelem, self).__init__(technique, status, data, machine=False) + victim_host = {'hostname': machine.domain_name, 'ip': machine.ip_addr} + if data: + self.data.update(data) + if machine: + self.data.update({'machine': victim_host}) diff --git a/monkey/infection_monkey/utils.py b/monkey/infection_monkey/utils.py index 741d7c950..05d0cf807 100644 --- a/monkey/infection_monkey/utils.py +++ b/monkey/infection_monkey/utils.py @@ -2,9 +2,13 @@ import os import sys import shutil import struct +import socket from infection_monkey.config import WormConfiguration +LOCAL_IP = '127.0.0.1' +MOCK_IP = '10.255.255.255' + def get_monkey_log_path(): return os.path.expandvars(WormConfiguration.monkey_log_path_windows) if sys.platform == "win32" \ @@ -32,6 +36,26 @@ def is_windows_os(): return sys.platform.startswith("win") +def get_host_info(): + return {'hostname': socket.gethostname(), 'ip': get_primary_ip()} + + +def get_primary_ip(): + """ + :return: Primary (default route) IP address + """ + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + # doesn't even have to be reachable + s.connect((MOCK_IP, 1)) + ip = s.getsockname()[0] + except: + ip = LOCAL_IP + finally: + s.close() + return ip + + def utf_to_ascii(string): # Converts utf string to ascii. Safe to use even if string is already ascii. udata = string.decode("utf-8") diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index d43930206..88d7c9f5d 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -30,6 +30,7 @@ from cc.resources.telemetry_feed import TelemetryFeed from cc.resources.pba_file_download import PBAFileDownload from cc.services.config import ConfigService from cc.resources.pba_file_upload import FileUpload +from cc.resources.attack import Attack __author__ = 'Barak' @@ -123,5 +124,6 @@ def init_app(mongo_url): '/api/fileUpload/?load=', '/api/fileUpload/?restore=') api.add_resource(RemoteRun, '/api/remote-monkey', '/api/remote-monkey/') + api.add_resource(Attack, '/api/attack/') return app diff --git a/monkey/monkey_island/cc/resources/attack.py b/monkey/monkey_island/cc/resources/attack.py index 457e6bfff..681c54274 100644 --- a/monkey/monkey_island/cc/resources/attack.py +++ b/monkey/monkey_island/cc/resources/attack.py @@ -1,11 +1,8 @@ import flask_restful -from flask import request, send_from_directory, Response -from cc.services.config import ConfigService, PBA_WINDOWS_FILENAME_PATH, PBA_LINUX_FILENAME_PATH, UPLOADS_DIR -from cc.auth import jwt_required -import os -from werkzeug.utils import secure_filename +from flask import request +import json +from cc.services.attck.attack_results import set_results import logging -import copy __author__ = 'VakarisZ' @@ -14,9 +11,14 @@ LOG = logging.getLogger(__name__) class Attack(flask_restful.Resource): """ - ATT&CK endpoint used to retrieve matrix related info + ATT&CK endpoint used to retrieve matrix related info from monkey """ - @jwt_required() - def post(self, attack_type): - + def post(self, technique): + """ + Gets ATT&CK telemetry data and stores it in the database + :param technique: Technique ID, e.g. T1111 + """ + data = json.loads(request.data) + set_results(technique, data) + return {} diff --git a/monkey/monkey_island/cc/services/attck/__init__.py b/monkey/monkey_island/cc/services/attck/__init__.py new file mode 100644 index 000000000..98867ed4d --- /dev/null +++ b/monkey/monkey_island/cc/services/attck/__init__.py @@ -0,0 +1 @@ +__author__ = 'VakarisZ' diff --git a/monkey/monkey_island/cc/services/attck/attack_results.py b/monkey/monkey_island/cc/services/attck/attack_results.py new file mode 100644 index 000000000..fb8b1cd82 --- /dev/null +++ b/monkey/monkey_island/cc/services/attck/attack_results.py @@ -0,0 +1,11 @@ +import logging +from cc.database import mongo + +__author__ = "VakarisZ" + +logger = logging.getLogger(__name__) + + +def set_results(technique, data): + data.update({'technique': technique}) + mongo.db.attack_results.insert(data)