From bc1be8e452686f3a392fd29167a78916130b9be1 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Fri, 28 Jun 2019 11:02:40 +0300
Subject: [PATCH] Implemented file deletion attack technique

---
 monkey/infection_monkey/monkey.py             |  9 +++-
 .../telemetry/attack/t1107_telem.py           | 19 ++++++++
 monkey/infection_monkey/utils.py              |  7 ++-
 .../cc/services/attack/attack_report.py       |  7 +--
 .../cc/services/attack/attack_schema.py       |  9 ++++
 .../attack/technique_reports/T1107.py         | 32 +++++++++++++
 .../attack/technique_reports/__init__.py      | 12 ++---
 .../components/attack/techniques/Helpers.js   |  6 ++-
 .../src/components/attack/techniques/T1107.js | 47 +++++++++++++++++++
 .../report-components/AttackReport.js         |  4 +-
 10 files changed, 139 insertions(+), 13 deletions(-)
 create mode 100644 monkey/infection_monkey/telemetry/attack/t1107_telem.py
 create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/T1107.py
 create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/techniques/T1107.js

diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py
index 06e0f267b..c6c5ee8b5 100644
--- a/monkey/infection_monkey/monkey.py
+++ b/monkey/infection_monkey/monkey.py
@@ -16,6 +16,7 @@ from infection_monkey.network.network_scanner import NetworkScanner
 from infection_monkey.system_info import SystemInfoCollector
 from infection_monkey.system_singleton import SystemSingleton
 from infection_monkey.telemetry.attack.victim_host_telem import VictimHostTelem
+from infection_monkey.telemetry.attack.t1107_telem import T1107Telem
 from infection_monkey.windows_upgrader import WindowsUpgrader
 from infection_monkey.post_breach.post_breach_handler import PostBreach
 from common.utils.attack_utils import ScanStatus
@@ -230,7 +231,6 @@ class InfectionMonkey(object):
                 self.send_log()
             self._singleton.unlock()
 
-        utils.remove_monkey_dir()
         InfectionMonkey.self_delete()
         LOG.info("Monkey is shutting down")
 
@@ -243,6 +243,11 @@ class InfectionMonkey(object):
 
     @staticmethod
     def self_delete():
+        if utils.remove_monkey_dir():
+            T1107Telem(ScanStatus.USED, utils.get_monkey_dir_path()).send()
+        else:
+            T1107Telem(ScanStatus.SCANNED, utils.get_monkey_dir_path()).send()
+
         if WormConfiguration.self_delete_in_cleanup \
                 and -1 == sys.executable.find('python'):
             try:
@@ -256,8 +261,10 @@ class InfectionMonkey(object):
                                      close_fds=True, startupinfo=startupinfo)
                 else:
                     os.remove(sys.executable)
+                    T1107Telem(ScanStatus.USED, sys.executable).send()
             except Exception as exc:
                 LOG.error("Exception in self delete: %s", exc)
+                T1107Telem(ScanStatus.SCANNED, sys.executable).send()
 
     def send_log(self):
         monkey_log_path = utils.get_monkey_log_path()
diff --git a/monkey/infection_monkey/telemetry/attack/t1107_telem.py b/monkey/infection_monkey/telemetry/attack/t1107_telem.py
new file mode 100644
index 000000000..ffb69b698
--- /dev/null
+++ b/monkey/infection_monkey/telemetry/attack/t1107_telem.py
@@ -0,0 +1,19 @@
+from infection_monkey.telemetry.attack.attack_telem import AttackTelem
+
+
+class T1107Telem(AttackTelem):
+    def __init__(self, status, path):
+        """
+        T1107 telemetry.
+        :param status: ScanStatus of technique
+        :param path: Path of deleted dir/file
+        """
+        super(T1107Telem, self).__init__('T1107', status)
+        self.path = path
+
+    def get_data(self):
+        data = super(T1107Telem, self).get_data()
+        data.update({
+            'path': self.path
+        })
+        return data
diff --git a/monkey/infection_monkey/utils.py b/monkey/infection_monkey/utils.py
index 6eb3aefb5..f8b5cc56a 100644
--- a/monkey/infection_monkey/utils.py
+++ b/monkey/infection_monkey/utils.py
@@ -49,8 +49,13 @@ def create_monkey_dir():
 def remove_monkey_dir():
     """
     Removes monkey's root directory
+    :return True if removed without errors and False otherwise
     """
-    shutil.rmtree(get_monkey_dir_path(), ignore_errors=True)
+    try:
+        shutil.rmtree(get_monkey_dir_path())
+        return True
+    except Exception:
+        return False
 
 
 def get_monkey_dir_path():
diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py
index 7bec85a32..ff038c092 100644
--- a/monkey/monkey_island/cc/services/attack/attack_report.py
+++ b/monkey/monkey_island/cc/services/attack/attack_report.py
@@ -1,6 +1,6 @@
 import logging
 from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059, T1086, T1082
-from monkey_island.cc.services.attack.technique_reports import T1145
+from monkey_island.cc.services.attack.technique_reports import T1145, T1107
 from monkey_island.cc.services.attack.attack_config import AttackConfig
 from monkey_island.cc.database import mongo
 
@@ -17,7 +17,8 @@ TECHNIQUES = {'T1210': T1210.T1210,
               'T1059': T1059.T1059,
               'T1086': T1086.T1086,
               'T1082': T1082.T1082,
-              'T1145': T1145.T1145}
+              'T1145': T1145.T1145,
+              'T1107': T1107.T1107}
 
 REPORT_NAME = 'new_report'
 
@@ -57,12 +58,12 @@ class AttackReportService:
         Gets latest report (by retrieving it from db or generating a new one).
         :return: report dict.
         """
+        return AttackReportService.generate_new_report()
         if AttackReportService.is_report_generated():
             telem_time = AttackReportService.get_latest_attack_telem_time()
             latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME})
             if telem_time and latest_report['latest_telem_time'] and telem_time == latest_report['latest_telem_time']:
                 return latest_report
-        return AttackReportService.generate_new_report()
 
     @staticmethod
     def is_report_generated():
diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py
index 00d3e9536..7a8cc7727 100644
--- a/monkey/monkey_island/cc/services/attack/attack_schema.py
+++ b/monkey/monkey_island/cc/services/attack/attack_schema.py
@@ -91,6 +91,15 @@ SCHEMA = {
                     "necessary": True,
                     "description": "Adversaries may abuse BITS to download, execute, "
                                    "and even clean up after running malicious code."
+                },
+                "T1107": {
+                    "title": "T1107 File Deletion",
+                    "type": "bool",
+                    "value": True,
+                    "necessary": True,
+                    "description": "Adversaries may remove files over the course of an intrusion "
+                                   "to keep their footprint low or remove them at the end as part "
+                                   "of the post-intrusion cleanup process."
                 }
             }
         },
diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1107.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1107.py
new file mode 100644
index 000000000..fd9f1ad10
--- /dev/null
+++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1107.py
@@ -0,0 +1,32 @@
+from monkey_island.cc.database import mongo
+from monkey_island.cc.services.attack.technique_reports import AttackTechnique
+
+__author__ = "VakarisZ"
+
+
+class T1107(AttackTechnique):
+    tech_id = "T1107"
+    unscanned_msg = ""
+    scanned_msg = "Monkey tried to delete files on a system in the network but failed."
+    used_msg = "Monkey successfully deleted files on systems in the network."
+
+    query = [{'$match': {'telem_category': 'attack',
+                         'data.technique': 'T1107'}},
+             {'$lookup': {'from': 'monkey',
+                          'localField': 'monkey_guid',
+                          'foreignField': 'guid',
+                          'as': 'monkey'}},
+             {'$project': {'monkey': {'$arrayElemAt': ['$monkey', 0]},
+                           'status': '$data.status',
+                           'path': '$data.path'}},
+             {'$addFields': {'_id': 0,
+                             'machine': {'hostname': '$monkey.hostname', 'ips': '$monkey.ip_addresses'},
+                             'monkey': 0}},
+             {'$group': {'_id': {'machine': '$machine', 'status': '$status', 'path': '$path'}}}]
+
+    @staticmethod
+    def get_report_data():
+        data = T1107.get_tech_base_data()
+        deleted_files = list(mongo.db.telemetry.aggregate(T1107.query))
+        data.update({'deleted_files': deleted_files})
+        return data
diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py
index edd180d50..81b7dd6bf 100644
--- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py
+++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py
@@ -52,13 +52,13 @@ class AttackTechnique(object):
         Gets the status of a certain attack technique.
         :return: ScanStatus Enum object
         """
-        if mongo.db.attack_results.find_one({'telem_category': 'attack',
-                                             'status': ScanStatus.USED.value,
-                                             'technique': cls.tech_id}):
+        if mongo.db.telemetry.find_one({'telem_category': 'attack',
+                                        'data.status': ScanStatus.USED.value,
+                                        'data.technique': cls.tech_id}):
             return ScanStatus.USED
-        elif mongo.db.attack_results.find_one({'telem_category': 'attack',
-                                               'status': ScanStatus.SCANNED.value,
-                                               'technique': cls.tech_id}):
+        elif mongo.db.telemetry.find_one({'telem_category': 'attack',
+                                          'data.status': ScanStatus.SCANNED.value,
+                                          'data.technique': cls.tech_id}):
             return ScanStatus.SCANNED
         else:
             return ScanStatus.UNSCANNED
diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js
index 9885219ad..1060f4b2d 100644
--- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js
+++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js
@@ -11,7 +11,11 @@ export function renderMachine(val){
 export function renderMachineFromSystemData(data) {
     let machineStr = data['hostname'] + " ( ";
     data['ips'].forEach(function(ipInfo){
-      machineStr += ipInfo['addr'] + " ";
+      if(typeof ipInfo === "object"){
+        machineStr += ipInfo['addr'] + " ";
+      } else {
+         machineStr += ipInfo + " ";
+      }
     });
     return machineStr + ")"
 }
diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1107.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1107.js
new file mode 100644
index 000000000..acf513c2f
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1107.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import '../../../styles/Collapse.scss'
+import ReactTable from "react-table";
+import { renderMachineFromSystemData } from "./Helpers"
+
+
+class T1107 extends React.Component {
+
+  constructor(props) {
+    super(props);
+  }
+
+  static renderDelete(status){
+    if(status === 2){
+      return <span>Yes</span>
+    } else {
+      return <span>No</span>
+    }
+  }
+
+  static getDeletedFileColumns() {
+    return ([{
+      columns: [
+        {Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x._id.machine), style: { 'whiteSpace': 'unset' }},
+        {Header: 'Path', id: 'path', accessor: x => x._id.path, style: { 'whiteSpace': 'unset' }},
+        {Header: 'Deleted?', id: 'deleted', accessor: x => this.renderDelete(x._id.status),
+          style: { 'whiteSpace': 'unset' }, width: 160}]
+    }])};
+
+  render() {
+    return (
+      <div>
+        <div>{this.props.data.message}</div>
+        <br/>
+        {this.props.data.deleted_files.length !== 0 ?
+          <ReactTable
+              columns={T1107.getDeletedFileColumns()}
+              data={this.props.data.deleted_files}
+              showPagination={false}
+              defaultPageSize={this.props.data.deleted_files.length}
+          /> : ""}
+      </div>
+    );
+  }
+}
+
+export default T1107;
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js
index 348510175..0e9931f2d 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js
@@ -14,6 +14,7 @@ import T1059 from "../attack/techniques/T1059";
 import T1086 from "../attack/techniques/T1086";
 import T1082 from "../attack/techniques/T1082";
 import T1145 from "../attack/techniques/T1145";
+import T1107 from "../attack/techniques/T1107";
 
 const tech_components = {
   'T1210': T1210,
@@ -24,7 +25,8 @@ const tech_components = {
   'T1059': T1059,
   'T1086': T1086,
   'T1082': T1082,
-  'T1145': T1145
+  'T1145': T1145,
+  'T1107': T1107
 };
 
 const classNames = require('classnames');