From 6da9b46167a61a1c2e522e4aff18e0ae0bb73dde Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 2 Apr 2019 14:23:33 +0300 Subject: [PATCH 01/20] UI file structure added --- .../cc/ui/src/components/Main.js | 10 +++++++ .../src/components/pages/AttackReportPage.js | 27 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 291cfde58..0d793c7d4 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -85,6 +85,7 @@ class AppComponent extends AuthComponent { run_monkey: false, infection_done: false, report_done: false, + attack_report_done:false, isLoggedIn: undefined } }; @@ -152,6 +153,15 @@ class AppComponent extends AuthComponent { : ''} +
  • + + 5. + Security Report + {this.state.completedSteps.attack_report_done ? + + : ''} + +
  • diff --git a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js new file mode 100644 index 000000000..298ca7ab7 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js @@ -0,0 +1,27 @@ +import React from 'react'; +import {Button, Col} from 'react-bootstrap'; +import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; +import {edgeGroupToColor, options} from 'components/map/MapOptions'; +import AuthComponent from '../AuthComponent'; + +class AttackReportPageComponent extends AuthComponent { + + constructor(props) { + super(props); + this.state = { + }; + } + + render() { + return ( + +

    5.ATT&CK techniques report

    +
    + ATT&CK techniques report +
    + + ); + } +} + +export default AttackReportPageComponent; From bc09d59eb4fa3fb1c449b1d3a8174259e18ad847 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 2 Apr 2019 17:55:08 +0300 Subject: [PATCH 02/20] Report file structure created --- monkey/monkey_island/cc/app.py | 4 +- .../cc/resources/attack_report.py | 13 ++++ .../cc/services/attack/attack_report.py | 18 ++++++ .../attack/technique_reports/T1210.py | 28 +++++++++ .../attack/technique_reports/__init__.py | 0 .../technique_reports/technique_service.py | 17 ++++++ monkey/monkey_island/cc/ui/package-lock.json | 5 ++ monkey/monkey_island/cc/ui/package.json | 1 + .../cc/ui/src/components/Main.js | 4 +- .../cc/ui/src/components/attack/T1210.js | 33 ++++++++++ .../src/components/pages/AttackReportPage.js | 60 ++++++++++++++++++- .../cc/ui/src/styles/Collapse.scss | 4 ++ 12 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 monkey/monkey_island/cc/resources/attack_report.py create mode 100644 monkey/monkey_island/cc/services/attack/attack_report.py create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/T1210.py create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/__init__.py create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/T1210.js create mode 100644 monkey/monkey_island/cc/ui/src/styles/Collapse.scss diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index b7461bcca..a76774079 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -33,6 +33,7 @@ from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.resources.pba_file_upload import FileUpload from monkey_island.cc.resources.attack_telem import AttackTelem from monkey_island.cc.resources.attack_config import AttackConfiguration +from monkey_island.cc.resources.attack_report import AttackReport __author__ = 'Barak' @@ -126,7 +127,8 @@ def init_app(mongo_url): '/api/fileUpload/?load=', '/api/fileUpload/?restore=') api.add_resource(RemoteRun, '/api/remote-monkey', '/api/remote-monkey/') - api.add_resource(AttackConfiguration, '/api/attack') api.add_resource(AttackTelem, '/api/attack/') + api.add_resource(AttackReport, '/api/attack/report') + api.add_resource(AttackConfiguration, '/api/attack') return app diff --git a/monkey/monkey_island/cc/resources/attack_report.py b/monkey/monkey_island/cc/resources/attack_report.py new file mode 100644 index 000000000..82987dde9 --- /dev/null +++ b/monkey/monkey_island/cc/resources/attack_report.py @@ -0,0 +1,13 @@ +import flask_restful +from flask import jsonify +from cc.auth import jwt_required +from cc.services.attack.attack_report import AttackReportService + +__author__ = "itay.mizeretz" + + +class AttackReport(flask_restful.Resource): + + @jwt_required() + def get(self): + return jsonify(AttackReportService.get_report()) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py new file mode 100644 index 000000000..a3182545a --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -0,0 +1,18 @@ +import logging +from cc.services.attack.technique_reports import T1210 + +__author__ = "VakarisZ" + + +logger = logging.getLogger(__name__) + + +class AttackReportService: + def __init__(self): + pass + + @staticmethod + def get_report(): + report = {} + report.update({'T1210': T1210.get_report_data()}) + return report diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py new file mode 100644 index 000000000..20cdcc9f3 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -0,0 +1,28 @@ +from monkey_island.cc.services.attack.technique_reports.technique_service import technique_status, technique_title +from common.utils.attack_status_enum import ScanStatus +from cc.services.report import ReportService + +__author__ = "VakarisZ" + +TECHNIQUE = "T1210" +UNSCANNED_MSG = "Monkey didn't scan any remote services. Maybe it didn't find any machines on the network?" +SCANNED_MSG = "Monkey scanned for remote services on the network, but couldn't exploit any of them." +USED_MSG = "Monkey scanned for remote services and exploited some on the network." + + +def get_report_data(): + data = {} + status = technique_status(TECHNIQUE) + title = technique_title(TECHNIQUE) + data.update({'status': status.name, 'title': title}) + if status == ScanStatus.UNSCANNED: + data.update({'message': UNSCANNED_MSG}) + return data + elif status == ScanStatus.SCANNED: + data.update({'message': SCANNED_MSG}) + else: + data.update({'message': USED_MSG}) + data.update({'scanned_machines': ReportService.get_scanned()}) + data.update({'exploited_machines': ReportService.get_exploited()}) + 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 new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py new file mode 100644 index 000000000..d064783ed --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py @@ -0,0 +1,17 @@ +from cc.database import mongo +from common.utils.attack_status_enum import ScanStatus + +__author__ = "VakarisZ" + + +def technique_status(technique): + if mongo.db.attack_results.find_one({'status': ScanStatus.USED.value, 'technique': technique}): + return ScanStatus.USED + elif mongo.db.attack_results.find_one({'status': ScanStatus.SCANNED.value, 'technique': technique}): + return ScanStatus.SCANNED + else: + return ScanStatus.UNSCANNED + + +def technique_title(technique): + return technique+" title from SCHEMA" diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 21cd1e89c..c3dc196e2 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -221,6 +221,11 @@ } } }, + "@kunukn/react-collapse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@kunukn/react-collapse/-/react-collapse-1.0.5.tgz", + "integrity": "sha512-688uUFLgwXWf9+rtE3hvYn3f43cjG6v/LzWDgcEGjoAV0b22VGbhDe5T1BAqujmjJ89IcJXKz1L8QW+5nu0fMQ==" + }, "@webassemblyjs/ast": { "version": "1.7.8", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.8.tgz", diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index aad5ddf18..9d5277954 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -64,6 +64,7 @@ "webpack-dev-server": "^3.1.9" }, "dependencies": { + "@kunukn/react-collapse": "^1.0.5", "bootstrap": "3.3.7", "core-js": "^2.5.7", "downloadjs": "^1.4.7", diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 0d793c7d4..7af05f6cb 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -15,6 +15,7 @@ import LicensePage from 'components/pages/LicensePage'; import AuthComponent from 'components/AuthComponent'; import LoginPageComponent from 'components/pages/LoginPage'; import AttckPage from 'components/pages/AttackPage' +import AttackReportPage from 'components/pages/AttackReportPage'; import 'normalize.css/normalize.css'; import 'react-data-components/css/table-twbs.css'; @@ -156,7 +157,7 @@ class AppComponent extends AuthComponent {
  • 5. - Security Report + ATT&CK report {this.state.completedSteps.attack_report_done ? : ''} @@ -197,6 +198,7 @@ class AppComponent extends AuthComponent { {this.renderRoute('/infection/telemetry', )} {this.renderRoute('/start-over', )} {this.renderRoute('/report', )} + {this.renderRoute('/attack_report', )} {this.renderRoute('/license', )} {this.renderRoute('/attack', )} diff --git a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js b/monkey/monkey_island/cc/ui/src/components/attack/T1210.js new file mode 100644 index 000000000..d169ec069 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/T1210.js @@ -0,0 +1,33 @@ +import React from 'react'; +import ReactTable from 'react-table' +import '../../styles/Collapse.scss' +import Collapse from '@kunukn/react-collapse'; + +let renderArray = function(val) { + return
    {val.map(x =>
    {x}
    )}
    ; +}; + +let renderIpAddresses = function (val) { + return
    {renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")}
    ; +}; + +const columns = [ +]; + +const pageSize = 10; + +class T1210 extends React.Component { + constructor(props) { + super(props); + } + render() { + console.log(this.props); + return ( + +
    {this.props.data.message}
    +
    + ); + } +} + +export default T1210; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js index 298ca7ab7..08aa63635 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js @@ -3,21 +3,77 @@ import {Button, Col} from 'react-bootstrap'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {edgeGroupToColor, options} from 'components/map/MapOptions'; import AuthComponent from '../AuthComponent'; +import T1210 from '../attack/T1210' class AttackReportPageComponent extends AuthComponent { constructor(props) { super(props); this.state = { + report: {}, + allMonkeysAreDead: false, + runStarted: true }; } + componentDidMount() { + this.updateMonkeysRunning().then(res => this.getReportFromServer(res)); + } + + updateMonkeysRunning = () => { + return this.authFetch('/api') + .then(res => res.json()) + .then(res => { + // This check is used to prevent unnecessary re-rendering + this.setState({ + allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']), + runStarted: res['completed_steps']['run_monkey'] + }); + return res; + }); + }; + + getReportFromServer(res) { + if (res['completed_steps']['run_monkey']) { + this.authFetch('/api/attack/report') + .then(res => res.json()) + .then(res => { + this.setState({ + report: res + }); + }); + } + } + + generateReportContent(){ + let content = ''; + Object.keys(this.state.report).forEach((Technique) => { + content = ; + }); + return content + } + render() { + console.log(React.version); + let content; + if (Object.keys(this.state.report).length === 0) { + if (this.state.runStarted) { + content = (

    Generating Report...

    ); + } else { + content = +

    + + You have to run a monkey before generating a report! +

    ; + } + } else { + content = this.generateReportContent(); + } return ( -

    5.ATT&CK techniques report

    +

    5. ATT&CK Report

    - ATT&CK techniques report + {content}
    ); diff --git a/monkey/monkey_island/cc/ui/src/styles/Collapse.scss b/monkey/monkey_island/cc/ui/src/styles/Collapse.scss new file mode 100644 index 000000000..5f7371475 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/Collapse.scss @@ -0,0 +1,4 @@ +.collapse-css-transition { + transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1); + overflow: hidden; +} From 4d6f4037105f582b2bb17c1e9af87d9f046a047a Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 3 Apr 2019 18:16:51 +0300 Subject: [PATCH 03/20] UI improved, collapse component almost added --- .../cc/services/attack/attack_config.py | 14 ++ .../technique_reports/technique_service.py | 5 +- monkey/monkey_island/cc/ui/package-lock.json | 14 +- monkey/monkey_island/cc/ui/package.json | 1 + .../src/components/pages/AttackReportPage.js | 47 ++++++- .../cc/ui/src/styles/Collapse.scss | 132 ++++++++++++++++++ 6 files changed, 199 insertions(+), 14 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/attack_config.py b/monkey/monkey_island/cc/services/attack/attack_config.py index c153a40f7..4957fac98 100644 --- a/monkey/monkey_island/cc/services/attack/attack_config.py +++ b/monkey/monkey_island/cc/services/attack/attack_config.py @@ -14,6 +14,20 @@ def get_config(): return config +def get_technique(technique_id): + """ + Gets technique by id + :param technique_id: E.g. T1210 + :return: Technique object or false if technique is not found + """ + attack_config = get_config() + for key, attack_type in attack_config['properties'].items(): + for key, technique in attack_type['properties'].items(): + if key == technique_id: + return technique + return False + + def get_config_schema(): return SCHEMA diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py index d064783ed..3799dfde0 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py @@ -1,5 +1,6 @@ -from cc.database import mongo +from monkey_island.cc.database import mongo from common.utils.attack_status_enum import ScanStatus +from monkey_island.cc.services.attack.attack_config import get_technique __author__ = "VakarisZ" @@ -14,4 +15,4 @@ def technique_status(technique): def technique_title(technique): - return technique+" title from SCHEMA" + return get_technique(technique)['title'] diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index c3dc196e2..b266d05eb 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -2764,9 +2764,9 @@ } }, "classnames": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz", - "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=" + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" }, "clean-css": { "version": "4.1.11", @@ -13924,7 +13924,7 @@ "integrity": "sha512-xj+JfaPOvnvr3ow0aHC7Y3HaBKZNR1mm361hVxVzVX3fcdJNIrfiodbQ0m9nLBpNxiKG6FTU2lq/SbTDYT2vew==", "requires": { "@babel/runtime-corejs2": "7.1.5", - "classnames": "2.2.5", + "classnames": "2.2.6", "dom-helpers": "3.4.0", "invariant": "2.2.4", "keycode": "2.2.0", @@ -14102,7 +14102,7 @@ "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.8.3.tgz", "integrity": "sha512-h6GT3jgy90PgctleP39Yu3eK1v9vaJAW73GOA/UbN9dJ7aAN4BTZD6793eI1D5U+ukMk17qiqN/wl3diK1Z5LA==", "requires": { - "classnames": "2.2.5", + "classnames": "2.2.6", "dom-helpers": "3.4.0", "prop-types": "15.6.2", "prop-types-extra": "1.1.0", @@ -14226,7 +14226,7 @@ "resolved": "https://registry.npmjs.org/react-table/-/react-table-6.8.6.tgz", "integrity": "sha1-oK2LSDkxkFLVvvwBJgP7Fh5S7eM=", "requires": { - "classnames": "2.2.5" + "classnames": "2.2.6" } }, "react-toggle": { @@ -14234,7 +14234,7 @@ "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.0.2.tgz", "integrity": "sha512-EPTWnN7gQHgEAUEmjheanZXNzY5TPnQeyyHfEs3YshaiWZf5WNjfYDrglO5F1Hl/dNveX18i4l0grTEsYH2Ccw==", "requires": { - "classnames": "2.2.5" + "classnames": "2.2.6" } }, "react-tooltip-lite": { diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 9d5277954..d0ef4f0f3 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -66,6 +66,7 @@ "dependencies": { "@kunukn/react-collapse": "^1.0.5", "bootstrap": "3.3.7", + "classnames": "^2.2.6", "core-js": "^2.5.7", "downloadjs": "^1.4.7", "fetch": "^1.1.0", diff --git a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js index 08aa63635..0a2d8d6f0 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js @@ -3,7 +3,15 @@ import {Button, Col} from 'react-bootstrap'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {edgeGroupToColor, options} from 'components/map/MapOptions'; import AuthComponent from '../AuthComponent'; -import T1210 from '../attack/T1210' +import Collapse from '@kunukn/react-collapse'; +import T1210 from '../attack/T1210'; +import '../../styles/Collapse.scss' + +const tech_components = { + 'T1210': T1210 +}; + +const classNames = require('classnames'); class AttackReportPageComponent extends AuthComponent { @@ -45,16 +53,45 @@ class AttackReportPageComponent extends AuthComponent { } } + state = { + index: 1, + }; + + onToggle = () => { + this.setState(state => ({ isOpen: !state.isOpen })); + }; + + getTechniqueCollapse(technique){ + const TechniqueComponent = tech_components[technique]; + return ( +
    + + { + this.setState({ item1: collapseState }); + }} + onInit={({ collapseState }) => { + this.setState({ item1: collapseState }); + }}> + + +
    + ); + } + generateReportContent(){ let content = ''; - Object.keys(this.state.report).forEach((Technique) => { - content = ; + Object.keys(this.state.report).forEach((technique) => { + content = this.getTechniqueCollapse(technique) }); - return content + return
    {content}
    } render() { - console.log(React.version); let content; if (Object.keys(this.state.report).length === 0) { if (this.state.runStarted) { diff --git a/monkey/monkey_island/cc/ui/src/styles/Collapse.scss b/monkey/monkey_island/cc/ui/src/styles/Collapse.scss index 5f7371475..f01ef40c4 100644 --- a/monkey/monkey_island/cc/ui/src/styles/Collapse.scss +++ b/monkey/monkey_island/cc/ui/src/styles/Collapse.scss @@ -1,3 +1,135 @@ +$transition: 350ms cubic-bezier(0.4, 0, 0.2, 1); + +* { + box-sizing: border-box; +} + +:root { + font-size: 10px; +} + +body { + font: 1.6rem / 1.5 "Montserrat", sans-serif; + margin: 0; + min-height: 100vh; + font-size: 1.6rem; + overflow-y: scroll; +} + +p { + margin: 0; +} + +button { + font-size: inherit; + margin: 0; + padding: 1rem; + background: transparent; + border: 1px solid #ccc; + box-shadow: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +img { + vertical-align: bottom; + max-width: 100%; + height: auto; +} + +a { + color: black; + font-size: inherit; +} + +h2 { + margin: 0.5rem; +} + +.info { + margin-bottom: 3rem; + text-align: center; + padding: 1rem; +} + +.app { + min-height: 100vh; + max-width: 500px; + margin: 0 auto; + padding: 1rem; +} + +.btn { + width: 100%; + box-shadow: 0 2px 6px #ccc; + border: none; + transition: background-color $transition; + font-family: inherit; + display: flex; + justify-content: space-between; + align-items: center; + > span { + } +} + +.item { + padding: 0.5rem; + &--active { + .btn { + background-color: #f7f7f7; + } + } +} + +.collapse { + transition: height $transition; + overflow: hidden; +} + +.content { + padding: 2rem 0; + transition: transform $transition; + will-change: transform; + $offset: 10px; + + &.collapsing { + transform: translateY(-$offset); + } + &.collapsed { + transform: translateY(-$offset); + } + &.expanding { + transform: translateX(0px); + } + &.expanded { + transform: translateX(0px); + } +} + +.text { + margin-bottom: 1rem; +} + +.state { + display: inline-block; + min-width: 6em; +} + + +.image-wrapper { + position: relative; + max-width: 500px; + height: 0; + padding-bottom: 40%; + margin-bottom: 1rem; + background: #eee; + + &__img { + object-fit: cover; + width: 100%; + position: absolute; + } +} + .collapse-css-transition { transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1); overflow: hidden; From 5ff7eba12f31aac785f8c7d804e012898855a3ed Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 4 Apr 2019 17:21:02 +0300 Subject: [PATCH 04/20] ATT&CK report page UI implemented --- .../technique_reports/technique_service.py | 4 +- monkey/monkey_island/cc/ui/package-lock.json | 33 +++-- monkey/monkey_island/cc/ui/package.json | 2 +- .../cc/ui/src/components/attack/T1210.js | 1 - .../src/components/pages/AttackReportPage.js | 69 +++++++--- monkey/monkey_island/cc/ui/src/styles/App.css | 23 +++- .../cc/ui/src/styles/Collapse.scss | 120 ++++++------------ 7 files changed, 134 insertions(+), 118 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py index 3799dfde0..9533ae0b9 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py @@ -1,6 +1,6 @@ -from monkey_island.cc.database import mongo +from cc.database import mongo from common.utils.attack_status_enum import ScanStatus -from monkey_island.cc.services.attack.attack_config import get_technique +from cc.services.attack.attack_config import get_technique __author__ = "VakarisZ" diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index b266d05eb..d409681f1 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -126,18 +126,23 @@ } }, "@babel/runtime-corejs2": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.1.5.tgz", - "integrity": "sha512-WsYRwQsFhVmxkAqwypPTZyV9GpkqMEaAr2zOItOmqSX2GBFaI+eq98CN81e13o0zaUKJOQGYyjhNVqj56nnkYg==", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.4.3.tgz", + "integrity": "sha512-anTLTF7IK8Hd5f73zpPzt875I27UaaTWARJlfMGgnmQhvEe1uNHQRKBUbXL0Gc0VEYiVzsHsTPso5XdK8NGvFg==", "requires": { - "core-js": "2.5.7", - "regenerator-runtime": "0.12.1" + "core-js": "2.6.5", + "regenerator-runtime": "0.13.2" }, "dependencies": { + "core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + }, "regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" } } }, @@ -13923,7 +13928,7 @@ "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.32.4.tgz", "integrity": "sha512-xj+JfaPOvnvr3ow0aHC7Y3HaBKZNR1mm361hVxVzVX3fcdJNIrfiodbQ0m9nLBpNxiKG6FTU2lq/SbTDYT2vew==", "requires": { - "@babel/runtime-corejs2": "7.1.5", + "@babel/runtime-corejs2": "7.4.3", "classnames": "2.2.6", "dom-helpers": "3.4.0", "invariant": "2.2.4", @@ -13932,7 +13937,7 @@ "prop-types-extra": "1.1.0", "react-overlays": "0.8.3", "react-prop-types": "0.4.0", - "react-transition-group": "2.5.0", + "react-transition-group": "2.8.0", "uncontrollable": "5.1.0", "warning": "3.0.0" }, @@ -14106,7 +14111,7 @@ "dom-helpers": "3.4.0", "prop-types": "15.6.2", "prop-types-extra": "1.1.0", - "react-transition-group": "2.5.0", + "react-transition-group": "2.8.0", "warning": "3.0.0" } }, @@ -14247,9 +14252,9 @@ } }, "react-transition-group": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.0.tgz", - "integrity": "sha512-qYB3JBF+9Y4sE4/Mg/9O6WFpdoYjeeYqx0AFb64PTazVy8RPMiE3A47CG9QmM4WJ/mzDiZYslV+Uly6O1Erlgw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.8.0.tgz", + "integrity": "sha512-So23a1MPn8CGoW5WNU4l0tLiVkOFmeXSS1K4Roe+dxxqqHvI/2XBmj76jx+u96LHnQddWG7LX8QovPAainSmWQ==", "requires": { "dom-helpers": "3.4.0", "loose-envify": "1.4.0", diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index d0ef4f0f3..456fdab1f 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -65,7 +65,7 @@ }, "dependencies": { "@kunukn/react-collapse": "^1.0.5", - "bootstrap": "3.3.7", + "bootstrap": "^3.3.7", "classnames": "^2.2.6", "core-js": "^2.5.7", "downloadjs": "^1.4.7", diff --git a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js b/monkey/monkey_island/cc/ui/src/components/attack/T1210.js index d169ec069..ed36aa75d 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/T1210.js @@ -21,7 +21,6 @@ class T1210 extends React.Component { super(props); } render() { - console.log(this.props); return (
    {this.props.data.message}
    diff --git a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js index 0a2d8d6f0..b34e45dde 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js @@ -1,5 +1,5 @@ import React from 'react'; -import {Button, Col} from 'react-bootstrap'; +import {Button, Col, Row, TabContainer} from 'react-bootstrap'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {edgeGroupToColor, options} from 'components/map/MapOptions'; import AuthComponent from '../AuthComponent'; @@ -20,7 +20,8 @@ class AttackReportPageComponent extends AuthComponent { this.state = { report: {}, allMonkeysAreDead: false, - runStarted: true + runStarted: true, + index: 1 }; } @@ -53,40 +54,56 @@ class AttackReportPageComponent extends AuthComponent { } } - state = { - index: 1, - }; + onToggle = index => + this.setState(state => ({ index: state.index === index ? null : index })); - onToggle = () => { - this.setState(state => ({ isOpen: !state.isOpen })); - }; + getTechniqueCollapse(tech_id){ + switch (this.state.report[tech_id].status) { + case 'SCANNED': + var className = 'collapse-info'; + break; + case 'USED': + var className = 'collapse-danger'; + break; + default: + var className = 'collapse-default'; + } - getTechniqueCollapse(technique){ - const TechniqueComponent = tech_components[technique]; return ( -
    - { this.setState({ item1: collapseState }); }} onInit={({ collapseState }) => { this.setState({ item1: collapseState }); - }}> - - + }} + render={collapseState => this.createTechniqueContent(collapseState, tech_id)}/> +
    + ); + } + + createTechniqueContent(collapseState, technique) { + const TechniqueComponent = tech_components[technique]; + return ( +
    +
    ); } generateReportContent(){ let content = ''; - Object.keys(this.state.report).forEach((technique) => { - content = this.getTechniqueCollapse(technique) + Object.keys(this.state.report).forEach((tech_id) => { + content = this.getTechniqueCollapse(tech_id) }); return
    {content}
    } @@ -109,6 +126,20 @@ class AttackReportPageComponent extends AuthComponent { return (

    5. ATT&CK Report

    +
    {content}
    diff --git a/monkey/monkey_island/cc/ui/src/styles/App.css b/monkey/monkey_island/cc/ui/src/styles/App.css index 8b1c45d7a..83a844670 100644 --- a/monkey/monkey_island/cc/ui/src/styles/App.css +++ b/monkey/monkey_island/cc/ui/src/styles/App.css @@ -516,7 +516,28 @@ body { } -/* Attack config page */ +/* Attack pages */ .attack-matrix .messages { margin-bottom: 30px; } + +.icon-info { + color: #ade3eb !important; +} + +.icon-warning { + color: #f0ad4e !important; +} + +.icon-danger { + color: #d9acac !important; +} + +.icon-default { + color: #e0ddde !important; +} + +.attack-legend { + text-align: center; + margin-bottom: 20px; +} diff --git a/monkey/monkey_island/cc/ui/src/styles/Collapse.scss b/monkey/monkey_island/cc/ui/src/styles/Collapse.scss index f01ef40c4..e2d7d334a 100644 --- a/monkey/monkey_island/cc/ui/src/styles/Collapse.scss +++ b/monkey/monkey_island/cc/ui/src/styles/Collapse.scss @@ -1,26 +1,10 @@ -$transition: 350ms cubic-bezier(0.4, 0, 0.2, 1); +$transition: 500ms cubic-bezier(0.4, 0.1, 0.1, 0.5); -* { - box-sizing: border-box; -} +$danger-color: #d9acac; +$info-color: #ade3eb; +$default-color: #e0ddde; -:root { - font-size: 10px; -} - -body { - font: 1.6rem / 1.5 "Montserrat", sans-serif; - margin: 0; - min-height: 100vh; - font-size: 1.6rem; - overflow-y: scroll; -} - -p { - margin: 0; -} - -button { +.collapse-item button { font-size: inherit; margin: 0; padding: 1rem; @@ -30,62 +14,59 @@ button { -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } -img { - vertical-align: bottom; - max-width: 100%; - height: auto; +.collapse-item button span:first-child{ + text-align:left; } -a { - color: black; - font-size: inherit; -} - -h2 { - margin: 0.5rem; -} - -.info { - margin-bottom: 3rem; - text-align: center; - padding: 1rem; -} - -.app { - min-height: 100vh; - max-width: 500px; - margin: 0 auto; - padding: 1rem; -} - -.btn { +.collapse-item button { width: 100%; box-shadow: 0 2px 6px #ccc; border: none; transition: background-color $transition; - font-family: inherit; display: flex; - justify-content: space-between; - align-items: center; - > span { + font-family: inherit; + > span { + display: inline-block; + flex: 4; + text-align: right; + + &:nth-child(2) { + flex: 3; + } } } -.item { +.collapse-danger { + background-color: $danger-color !important; +} + +.collapse-info { + background-color: $info-color !important; +} + +.collapse-default { + background-color: $default-color !important; +} + +.collapse-item { padding: 0.5rem; &--active { - .btn { + .btn-collapse { background-color: #f7f7f7; } } } -.collapse { +.collapse-item .collapse-comp { + padding: 0 7px 7px 7px; + border: 2px solid rgb(232, 228, 228); + border-top: 0; + display:block !important; transition: height $transition; overflow: hidden; } -.content { +.collapse-item .content { padding: 2rem 0; transition: transform $transition; will-change: transform; @@ -94,7 +75,7 @@ h2 { &.collapsing { transform: translateY(-$offset); } - &.collapsed { + &.collapse-comp { transform: translateY(-$offset); } &.expanding { @@ -105,32 +86,11 @@ h2 { } } -.text { +.collapse-item .text { margin-bottom: 1rem; } -.state { +.collapse-item .state { display: inline-block; min-width: 6em; } - - -.image-wrapper { - position: relative; - max-width: 500px; - height: 0; - padding-bottom: 40%; - margin-bottom: 1rem; - background: #eee; - - &__img { - object-fit: cover; - width: 100%; - position: absolute; - } -} - -.collapse-css-transition { - transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1); - overflow: hidden; -} From c3d717a6bf0be56cb6c799034ad3b22530437bbe Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 5 Apr 2019 10:06:12 +0300 Subject: [PATCH 05/20] Report generation algorithm --- monkey/monkey_island/cc/resources/root.py | 3 +++ .../cc/services/attack/attack_report.py | 23 +++++++++++++++++-- .../cc/services/attack/attack_telem.py | 6 +++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/resources/root.py b/monkey/monkey_island/cc/resources/root.py index cd2f7e6a5..b6d2af7dc 100644 --- a/monkey/monkey_island/cc/resources/root.py +++ b/monkey/monkey_island/cc/resources/root.py @@ -10,6 +10,7 @@ from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.attack.attack_config import reset_config as reset_attack_config from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.report import ReportService +from cc.services.attack.attack_report import AttackReportService from monkey_island.cc.utils import local_ip_addresses from monkey_island.cc.services.post_breach_files import remove_PBA_files @@ -71,5 +72,7 @@ class Root(flask_restful.Resource): else: if is_any_exists: ReportService.get_report() + AttackReportService.get_report() report_done = ReportService.is_report_generated() + attack_report_done = AttackReportService.is_report_generated() return dict(run_server=True, run_monkey=is_any_exists, infection_done=infection_done, report_done=report_done) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index a3182545a..591255fa5 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,18 +1,37 @@ import logging from cc.services.attack.technique_reports import T1210 +from cc.services.attack.attack_telem import get_latest_telem +from cc.database import mongo __author__ = "VakarisZ" logger = logging.getLogger(__name__) +TECHNIQUES = {'T1210': T1210} class AttackReportService: def __init__(self): pass @staticmethod - def get_report(): - report = {} + def generate_new_report(): + report = {'techniques': {}, 'meta': {get_latest_telem()}} + for tech_id, value in report.update({'T1210': T1210.get_report_data()}) + report.update({''}) return report + + @staticmethod + def get_latest_report(): + if AttackReportService.is_report_generated(): + telem_time = get_latest_telem_time() + lates_report = mongo.db.attack_report.find_one({'name': 'new_report'}) + if telem_time == lates_report['telem_time']: + return lates_report + return AttackReportService.generate_new_report() + + @staticmethod + def is_report_generated(): + generated_report = mongo.db.attack_report.find_one({}) + return generated_report is not None diff --git a/monkey/monkey_island/cc/services/attack/attack_telem.py b/monkey/monkey_island/cc/services/attack/attack_telem.py index a4e219270..7521bbb6c 100644 --- a/monkey/monkey_island/cc/services/attack/attack_telem.py +++ b/monkey/monkey_island/cc/services/attack/attack_telem.py @@ -3,6 +3,7 @@ File that contains ATT&CK telemetry storing/retrieving logic """ import logging from monkey_island.cc.database import mongo +from time import time __author__ = "VakarisZ" @@ -17,3 +18,8 @@ def set_results(technique, data): """ data.update({'technique': technique}) mongo.db.attack_results.insert(data) + mongo.db.attack_results.update({'name': 'latest'}, {'name': 'latest', 'timestamp': time()}, upsert=True) + + +def get_latest_telem(): + return mongo.db.attack_results.find({'name': 'latest'}) From 993736a973f714e4dc17714f507b823ddc119a04 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 9 Apr 2019 10:59:53 +0300 Subject: [PATCH 06/20] T1210 implemented, T1197 started --- ...{attack_status_enum.py => attack_utils.py} | 3 + .../infection_monkey/exploit/elasticgroovy.py | 8 +++ monkey/infection_monkey/exploit/rdpgrinder.py | 3 + monkey/infection_monkey/exploit/web_rce.py | 3 + monkey/infection_monkey/monkey.py | 4 -- .../cc/resources/attack/__init__.py | 0 .../resources/{ => attack}/attack_config.py | 0 .../resources/{ => attack}/attack_report.py | 4 +- .../cc/resources/{ => attack}/attack_telem.py | 0 monkey/monkey_island/cc/resources/root.py | 6 +- .../cc/services/attack/attack_report.py | 39 +++++++++--- .../cc/services/attack/attack_telem.py | 2 +- .../attack/technique_reports/T1197.py | 16 +++++ .../attack/technique_reports/T1210.py | 23 +++---- .../technique_reports/technique_service.py | 16 ++++- .../cc/ui/src/components/attack/T1197.js | 63 +++++++++++++++++++ .../cc/ui/src/components/attack/T1210.js | 55 ++++++++++++---- .../src/components/pages/AttackReportPage.js | 49 ++++++++------- monkey/monkey_island/cc/ui/src/styles/App.css | 6 ++ 19 files changed, 232 insertions(+), 68 deletions(-) rename monkey/common/utils/{attack_status_enum.py => attack_utils.py} (72%) create mode 100644 monkey/monkey_island/cc/resources/attack/__init__.py rename monkey/monkey_island/cc/resources/{ => attack}/attack_config.py (100%) rename monkey/monkey_island/cc/resources/{ => attack}/attack_report.py (69%) rename monkey/monkey_island/cc/resources/{ => attack}/attack_telem.py (100%) create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/T1197.py create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/T1197.js diff --git a/monkey/common/utils/attack_status_enum.py b/monkey/common/utils/attack_utils.py similarity index 72% rename from monkey/common/utils/attack_status_enum.py rename to monkey/common/utils/attack_utils.py index c7d2dc62c..50271c132 100644 --- a/monkey/common/utils/attack_status_enum.py +++ b/monkey/common/utils/attack_utils.py @@ -8,3 +8,6 @@ class ScanStatus(Enum): SCANNED = 1 # Technique was attempted and succeeded USED = 2 + + +BITS_UPLOAD_STRING = "Bits job was used to upload monkey to a remote system." diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py index faa6681b4..1c530653e 100644 --- a/monkey/infection_monkey/exploit/elasticgroovy.py +++ b/monkey/infection_monkey/exploit/elasticgroovy.py @@ -11,6 +11,8 @@ from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.model import WGET_HTTP_UPLOAD, RDP_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX,\ DOWNLOAD_TIMEOUT from infection_monkey.network.elasticfinger import ES_PORT, ES_SERVICE +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING import re @@ -58,6 +60,12 @@ class ElasticGroovyExploiter(WebRCE): return False return result[0] + def upload_monkey(self, url, commands=None): + result = super(ElasticGroovyExploiter, self).upload_monkey(url, commands) + if 'windows' in self.host.os['type'] and result: + VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING) + return result + def get_results(self, response): """ Extracts the result data from our attack diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index dcef9551c..3b81f7109 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -17,6 +17,8 @@ from infection_monkey.network.tools import check_tcp_port from infection_monkey.exploit.tools import build_monkey_commandline from infection_monkey.utils import utf_to_ascii from common.utils.exploit_enum import ExploitType +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING __author__ = 'hoffer' @@ -312,6 +314,7 @@ class RdpExploiter(HostExploiter): client_factory.done_event.wait() if client_factory.success: + VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING) exploited = True self.report_login_attempt(True, user, password) break diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index d797f3a95..912d7c108 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -7,6 +7,8 @@ from infection_monkey.exploit import HostExploiter from infection_monkey.model import * from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth, build_monkey_commandline, HTTPTools from infection_monkey.network.tools import check_tcp_port, tcp_port_to_service +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING __author__ = 'VakarisZ' @@ -307,6 +309,7 @@ class WebRCE(HostExploiter): if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp: LOG.info("Powershell not found in host. Using bitsadmin to download.") backup_command = RDP_CMDLINE_HTTP % {'monkey_path': dest_path, 'http_path': http_path} + VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING) resp = self.exploit(url, backup_command) return resp diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 841a5521d..e80e15396 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -17,8 +17,6 @@ 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 common.utils.attack_status_enum import ScanStatus -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem __author__ = 'itamar' @@ -181,11 +179,9 @@ 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/monkey_island/cc/resources/attack/__init__.py b/monkey/monkey_island/cc/resources/attack/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/resources/attack_config.py b/monkey/monkey_island/cc/resources/attack/attack_config.py similarity index 100% rename from monkey/monkey_island/cc/resources/attack_config.py rename to monkey/monkey_island/cc/resources/attack/attack_config.py diff --git a/monkey/monkey_island/cc/resources/attack_report.py b/monkey/monkey_island/cc/resources/attack/attack_report.py similarity index 69% rename from monkey/monkey_island/cc/resources/attack_report.py rename to monkey/monkey_island/cc/resources/attack/attack_report.py index 82987dde9..714836925 100644 --- a/monkey/monkey_island/cc/resources/attack_report.py +++ b/monkey/monkey_island/cc/resources/attack/attack_report.py @@ -3,11 +3,11 @@ from flask import jsonify from cc.auth import jwt_required from cc.services.attack.attack_report import AttackReportService -__author__ = "itay.mizeretz" +__author__ = "VakarisZ" class AttackReport(flask_restful.Resource): @jwt_required() def get(self): - return jsonify(AttackReportService.get_report()) + return jsonify(AttackReportService.get_latest_report()['techniques']) diff --git a/monkey/monkey_island/cc/resources/attack_telem.py b/monkey/monkey_island/cc/resources/attack/attack_telem.py similarity index 100% rename from monkey/monkey_island/cc/resources/attack_telem.py rename to monkey/monkey_island/cc/resources/attack/attack_telem.py diff --git a/monkey/monkey_island/cc/resources/root.py b/monkey/monkey_island/cc/resources/root.py index b6d2af7dc..4bc43c601 100644 --- a/monkey/monkey_island/cc/resources/root.py +++ b/monkey/monkey_island/cc/resources/root.py @@ -69,10 +69,12 @@ class Root(flask_restful.Resource): infection_done = NodeService.is_monkey_finished_running() if not infection_done: report_done = False + attack_report_done = False else: if is_any_exists: ReportService.get_report() - AttackReportService.get_report() + AttackReportService.get_latest_report() report_done = ReportService.is_report_generated() attack_report_done = AttackReportService.is_report_generated() - return dict(run_server=True, run_monkey=is_any_exists, infection_done=infection_done, report_done=report_done) + return dict(run_server=True, run_monkey=is_any_exists, infection_done=infection_done, + report_done=report_done, attack_report_done=attack_report_done) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 591255fa5..52d4d6529 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,37 +1,58 @@ import logging from cc.services.attack.technique_reports import T1210 from cc.services.attack.attack_telem import get_latest_telem +from cc.services.attack.attack_config import get_technique_values from cc.database import mongo __author__ = "VakarisZ" -logger = logging.getLogger(__name__) +LOG = logging.getLogger(__name__) TECHNIQUES = {'T1210': T1210} +REPORT_NAME = 'new_report' + + class AttackReportService: def __init__(self): pass @staticmethod def generate_new_report(): - report = {'techniques': {}, 'meta': {get_latest_telem()}} - for tech_id, value in - report.update({'T1210': T1210.get_report_data()}) - report.update({''}) + """ + Generates new report based on telemetries, replaces old report in db with new one. + :return: Report object + """ + report = {'techniques': {}, 'meta': get_latest_telem(), 'name': REPORT_NAME} + for tech_id, value in get_technique_values().items(): + if value: + try: + report['techniques'].update({tech_id: TECHNIQUES[tech_id].get_report_data()}) + except KeyError as e: + LOG.error("Attack technique does not have it's report component added " + "to attack report service. %s" % e) + mongo.db.attack_report.replace_one({'name': REPORT_NAME}, report, upsert=True) return report @staticmethod def get_latest_report(): + """ + Gets latest report (by retrieving it from db or generating a new one). + :return: report dict. + """ if AttackReportService.is_report_generated(): - telem_time = get_latest_telem_time() - lates_report = mongo.db.attack_report.find_one({'name': 'new_report'}) - if telem_time == lates_report['telem_time']: - return lates_report + telem_time = get_latest_telem() + latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) + if telem_time and telem_time['timestamp'] == latest_report['meta']['timestamp']: + return latest_report return AttackReportService.generate_new_report() @staticmethod def is_report_generated(): + """ + Checks if report is generated + :return: True if report exists, False otherwise + """ generated_report = mongo.db.attack_report.find_one({}) return generated_report is not None diff --git a/monkey/monkey_island/cc/services/attack/attack_telem.py b/monkey/monkey_island/cc/services/attack/attack_telem.py index 7521bbb6c..139837835 100644 --- a/monkey/monkey_island/cc/services/attack/attack_telem.py +++ b/monkey/monkey_island/cc/services/attack/attack_telem.py @@ -22,4 +22,4 @@ def set_results(technique, data): def get_latest_telem(): - return mongo.db.attack_results.find({'name': 'latest'}) + return mongo.db.attack_results.find_one({'name': 'latest'}) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py new file mode 100644 index 000000000..9d260bc45 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py @@ -0,0 +1,16 @@ +from monkey_island.cc.services.attack.technique_reports.technique_service import * +from cc.services.report import ReportService + +__author__ = "VakarisZ" + +TECHNIQUE = "T1197" +MESSAGES = { + 'unscanned': "Monkey didn't try to use any bits jobs.", + 'scanned': "Monkey tried to use bits jobs but failed.", + 'used': "Monkey successfully used bits jobs at least once in the network." +} + + +def get_report_data(): + data = get_tech_base_data(TECHNIQUE, MESSAGES) + return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py index 20cdcc9f3..4fb244e45 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -1,27 +1,18 @@ -from monkey_island.cc.services.attack.technique_reports.technique_service import technique_status, technique_title -from common.utils.attack_status_enum import ScanStatus +from monkey_island.cc.services.attack.technique_reports.technique_service import * from cc.services.report import ReportService __author__ = "VakarisZ" TECHNIQUE = "T1210" -UNSCANNED_MSG = "Monkey didn't scan any remote services. Maybe it didn't find any machines on the network?" -SCANNED_MSG = "Monkey scanned for remote services on the network, but couldn't exploit any of them." -USED_MSG = "Monkey scanned for remote services and exploited some on the network." +MESSAGES = { + 'unscanned': "Monkey didn't scan any remote services. Maybe it didn't find any machines on the network?", + 'scanned': "Monkey scanned for remote services on the network, but couldn't exploit any of them.", + 'used': "Monkey scanned for remote services and exploited some on the network." +} def get_report_data(): - data = {} - status = technique_status(TECHNIQUE) - title = technique_title(TECHNIQUE) - data.update({'status': status.name, 'title': title}) - if status == ScanStatus.UNSCANNED: - data.update({'message': UNSCANNED_MSG}) - return data - elif status == ScanStatus.SCANNED: - data.update({'message': SCANNED_MSG}) - else: - data.update({'message': USED_MSG}) + data = get_tech_base_data(TECHNIQUE, MESSAGES) data.update({'scanned_machines': ReportService.get_scanned()}) data.update({'exploited_machines': ReportService.get_exploited()}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py index 9533ae0b9..b59c1838d 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py @@ -1,5 +1,5 @@ from cc.database import mongo -from common.utils.attack_status_enum import ScanStatus +from common.utils.attack_utils import ScanStatus from cc.services.attack.attack_config import get_technique __author__ = "VakarisZ" @@ -16,3 +16,17 @@ def technique_status(technique): def technique_title(technique): return get_technique(technique)['title'] + + +def get_tech_base_data(technique, messages): + data = {} + status = technique_status(technique) + title = technique_title(technique) + data.update({'status': status.name, 'title': title}) + if status == ScanStatus.UNSCANNED: + data.update({'message': messages['unscanned']}) + elif status == ScanStatus.SCANNED: + data.update({'message': messages['scanned']}) + else: + data.update({'message': messages['used']}) + return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/T1197.js b/monkey/monkey_island/cc/ui/src/components/attack/T1197.js new file mode 100644 index 000000000..a5156c3f4 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/T1197.js @@ -0,0 +1,63 @@ +import React from 'react'; +import '../../styles/Collapse.scss' +import {Link} from "react-router-dom"; + +let renderArray = function(val) { + return {val.map(x => {x} )}; +}; + + +let renderMachine = function (val, index, exploited=false) { + return ( +
    + {renderArray(val.ip_addresses)} + {(val.domain_name ? " (".concat(val.domain_name, ")") : " (".concat(val.label, ")"))} : + {exploited ? renderArray(val.exploits) : renderArray(val.services)} +
    + ) +}; + +class T1210 extends React.Component { + + renderScannedMachines = (machines) => { + let content = []; + for (let i = 0; i < machines.length; i++ ){ + if (machines[i].services.length !== 0){ + content.push(renderMachine(machines[i], i)) + } + } + return
    {content}
    ; + }; + + renderExploitedMachines = (machines) => { + let content = []; + for (let i = 0; i < machines.length; i++ ){ + if (machines[i].exploits.length !== 0){ + content.push(renderMachine(machines[i], i, true)) + } + } + return
    {content}
    ; + }; + + constructor(props) { + super(props); + } + + render() { + console.log(this.props); + return ( +
    +
    {this.props.data.message}
    +
    Found services:
    + {this.renderScannedMachines(this.props.data.scanned_machines)} +
    Successful exploiters:
    + {this.renderExploitedMachines(this.props.data.exploited_machines)} +
    + To get more info about scanned and exploited machines view standard report. +
    +
    + ); + } +} + +export default T1210; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js b/monkey/monkey_island/cc/ui/src/components/attack/T1210.js index ed36aa75d..a5156c3f4 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/T1210.js @@ -1,30 +1,61 @@ import React from 'react'; -import ReactTable from 'react-table' import '../../styles/Collapse.scss' -import Collapse from '@kunukn/react-collapse'; +import {Link} from "react-router-dom"; let renderArray = function(val) { - return
    {val.map(x =>
    {x}
    )}
    ; + return {val.map(x => {x} )}; }; -let renderIpAddresses = function (val) { - return
    {renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")}
    ; + +let renderMachine = function (val, index, exploited=false) { + return ( +
    + {renderArray(val.ip_addresses)} + {(val.domain_name ? " (".concat(val.domain_name, ")") : " (".concat(val.label, ")"))} : + {exploited ? renderArray(val.exploits) : renderArray(val.services)} +
    + ) }; -const columns = [ -]; - -const pageSize = 10; - class T1210 extends React.Component { + + renderScannedMachines = (machines) => { + let content = []; + for (let i = 0; i < machines.length; i++ ){ + if (machines[i].services.length !== 0){ + content.push(renderMachine(machines[i], i)) + } + } + return
    {content}
    ; + }; + + renderExploitedMachines = (machines) => { + let content = []; + for (let i = 0; i < machines.length; i++ ){ + if (machines[i].exploits.length !== 0){ + content.push(renderMachine(machines[i], i, true)) + } + } + return
    {content}
    ; + }; + constructor(props) { super(props); } + render() { + console.log(this.props); return ( - +
    {this.props.data.message}
    - +
    Found services:
    + {this.renderScannedMachines(this.props.data.scanned_machines)} +
    Successful exploiters:
    + {this.renderExploitedMachines(this.props.data.exploited_machines)} +
    + To get more info about scanned and exploited machines view standard report. +
    +
    ); } } diff --git a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js index b34e45dde..41ff2a428 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js @@ -18,7 +18,7 @@ class AttackReportPageComponent extends AuthComponent { constructor(props) { super(props); this.state = { - report: {}, + report: false, allMonkeysAreDead: false, runStarted: true, index: 1 @@ -105,27 +105,8 @@ class AttackReportPageComponent extends AuthComponent { Object.keys(this.state.report).forEach((tech_id) => { content = this.getTechniqueCollapse(tech_id) }); - return
    {content}
    - } - - render() { - let content; - if (Object.keys(this.state.report).length === 0) { - if (this.state.runStarted) { - content = (

    Generating Report...

    ); - } else { - content = -

    - - You have to run a monkey before generating a report! -

    ; - } - } else { - content = this.generateReportContent(); - } return ( - -

    5. ATT&CK Report

    +
    +
    {content}
    +
    + ) + } + + render() { + let content; + console.log(this.state.report); + if (this.state.report === false){ + content = (

    Generating Report...

    ); + } else if (Object.keys(this.state.report).length === 0) { + if (this.state.runStarted) { + content = (

    No techniques were scanned

    ); + } else { + content = +

    + + You have to run a monkey before generating a report! +

    ; + } + } else { + content = this.generateReportContent(); + } + return ( + +

    5. ATT&CK Report

    {content}
    diff --git a/monkey/monkey_island/cc/ui/src/styles/App.css b/monkey/monkey_island/cc/ui/src/styles/App.css index 83a844670..ade81039e 100644 --- a/monkey/monkey_island/cc/ui/src/styles/App.css +++ b/monkey/monkey_island/cc/ui/src/styles/App.css @@ -541,3 +541,9 @@ body { text-align: center; margin-bottom: 20px; } + +.attack-report.footer-text{ + text-align: right; + font-size: 0.8em; + margin-top: 20px; +} From 8ee7a06769660a25b7c728fc836f841dc4e02bd4 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 9 Apr 2019 19:40:09 +0300 Subject: [PATCH 07/20] Attack report bugfixes --- monkey/common/utils/attack_utils.py | 2 +- .../infection_monkey/exploit/elasticgroovy.py | 2 +- .../transport/attack_telems/base_telem.py | 2 +- .../services/attack/technique_reports/T1197.py | 2 ++ .../services/attack/technique_reports/T1210.py | 17 ++++++++++++++--- .../cc/ui/src/components/attack/T1210.js | 6 +++--- .../ui/src/components/pages/AttackReportPage.js | 16 ++++++++-------- 7 files changed, 30 insertions(+), 17 deletions(-) diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index 50271c132..a372661ca 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -10,4 +10,4 @@ class ScanStatus(Enum): USED = 2 -BITS_UPLOAD_STRING = "Bits job was used to upload monkey to a remote system." +BITS_UPLOAD_STRING = {"usage": "Bits job was used to upload monkey to a remote system."} diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py index 1c530653e..b4d3b2be8 100644 --- a/monkey/infection_monkey/exploit/elasticgroovy.py +++ b/monkey/infection_monkey/exploit/elasticgroovy.py @@ -63,7 +63,7 @@ class ElasticGroovyExploiter(WebRCE): def upload_monkey(self, url, commands=None): result = super(ElasticGroovyExploiter, self).upload_monkey(url, commands) if 'windows' in self.host.os['type'] and result: - VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING) + VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING).send() return result def get_results(self, response): diff --git a/monkey/infection_monkey/transport/attack_telems/base_telem.py b/monkey/infection_monkey/transport/attack_telems/base_telem.py index 9d0275356..93d5bbbf7 100644 --- a/monkey/infection_monkey/transport/attack_telems/base_telem.py +++ b/monkey/infection_monkey/transport/attack_telems/base_telem.py @@ -16,7 +16,7 @@ class AttackTelem(object): 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 data: Dictionary of other relevant info. E.g. {'brute_force_blocked': True} """ self.technique = technique self.result = status diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py index 9d260bc45..6121c46e3 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py @@ -13,4 +13,6 @@ MESSAGES = { def get_report_data(): data = get_tech_base_data(TECHNIQUE, MESSAGES) + + data.update() return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py index 4fb244e45..2ec4bcc1f 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -1,5 +1,6 @@ from monkey_island.cc.services.attack.technique_reports.technique_service import * from cc.services.report import ReportService +from common.utils.attack_utils import ScanStatus __author__ = "VakarisZ" @@ -12,8 +13,18 @@ MESSAGES = { def get_report_data(): - data = get_tech_base_data(TECHNIQUE, MESSAGES) - data.update({'scanned_machines': ReportService.get_scanned()}) - data.update({'exploited_machines': ReportService.get_exploited()}) + data = {} + scanned_machines = ReportService.get_scanned() + exploited_machines = ReportService.get_exploited() + data.update({'message': MESSAGES['unscanned'], 'status': ScanStatus.UNSCANNED.name}) + for machine in scanned_machines: + if machine['services']: + data.update({'message': MESSAGES['scanned'], 'status': ScanStatus.SCANNED.name}) + for machine in exploited_machines: + if machine['exploits']: + data.update({'message': MESSAGES['used'], 'status': ScanStatus.USED.name}) + data.update({'technique': TECHNIQUE, 'title': technique_title(TECHNIQUE)}) + data.update({'scanned_machines': scanned_machines}) + data.update({'exploited_machines': exploited_machines}) return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js b/monkey/monkey_island/cc/ui/src/components/attack/T1210.js index a5156c3f4..63e0222f6 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/T1210.js @@ -3,7 +3,7 @@ import '../../styles/Collapse.scss' import {Link} from "react-router-dom"; let renderArray = function(val) { - return {val.map(x => {x} )}; + return {val.map(x => {x} )}; }; @@ -48,9 +48,9 @@ class T1210 extends React.Component { return (
    {this.props.data.message}
    -
    Found services:
    + {this.props.data.scanned_machines.length > 0 ?
    Found services:
    : ''} {this.renderScannedMachines(this.props.data.scanned_machines)} -
    Successful exploiters:
    + {this.props.data.exploited_machines.length > 0 ?
    Successful exploiters:
    : ''} {this.renderExploitedMachines(this.props.data.exploited_machines)}
    To get more info about scanned and exploited machines view standard report. diff --git a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js index 41ff2a428..b35fba619 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js @@ -128,18 +128,18 @@ class AttackReportPageComponent extends AuthComponent { render() { let content; - console.log(this.state.report); - if (this.state.report === false){ + if (! this.state.runStarted) + { + content = +

    + + You have to run a monkey before generating a report! +

    ; + } else if (this.state.report === false){ content = (

    Generating Report...

    ); } else if (Object.keys(this.state.report).length === 0) { if (this.state.runStarted) { content = (

    No techniques were scanned

    ); - } else { - content = -

    - - You have to run a monkey before generating a report! -

    ; } } else { content = this.generateReportContent(); From 1eac9856c4b2bc87b3619d69f81f493f805e40d2 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 10 Apr 2019 17:27:40 +0300 Subject: [PATCH 08/20] Refactoring T1197 to display time and use tables --- monkey/common/utils/attack_utils.py | 4 +- .../transport/attack_telems/base_telem.py | 13 +++- .../attack_telems/victim_host_telem.py | 2 +- .../cc/services/attack/attack_report.py | 7 +- .../cc/services/attack/attack_schema.py | 2 +- .../cc/services/attack/attack_telem.py | 2 +- .../attack/technique_reports/T1197.py | 13 +++- .../technique_reports/technique_service.py | 5 ++ .../cc/ui/src/components/attack/T1197.js | 65 +++++++------------ .../cc/ui/src/components/attack/T1210.js | 1 - .../src/components/pages/AttackReportPage.js | 27 ++++---- 11 files changed, 75 insertions(+), 66 deletions(-) diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index a372661ca..b7f3346b3 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -9,5 +9,5 @@ class ScanStatus(Enum): # Technique was attempted and succeeded USED = 2 - -BITS_UPLOAD_STRING = {"usage": "Bits job was used to upload monkey to a remote system."} +# Dict that describes what BITS job was used for +BITS_UPLOAD_STRING = {"usage": "BITS job was used to upload monkey to a remote system."} diff --git a/monkey/infection_monkey/transport/attack_telems/base_telem.py b/monkey/infection_monkey/transport/attack_telems/base_telem.py index 93d5bbbf7..d42bbd242 100644 --- a/monkey/infection_monkey/transport/attack_telems/base_telem.py +++ b/monkey/infection_monkey/transport/attack_telems/base_telem.py @@ -3,6 +3,7 @@ import requests import json from infection_monkey.control import ControlClient import logging +import datetime __author__ = "VakarisZ" @@ -20,7 +21,7 @@ class AttackTelem(object): """ self.technique = technique self.result = status - self.data = {'status': status, 'id': GUID} + self.data = {'status': status, 'id': GUID, 'time': AttackTelem.get_current_time_string()} if data: self.data.update(data) @@ -39,3 +40,13 @@ class AttackTelem(object): except Exception as exc: LOG.warn("Error connecting to control server %s: %s", WormConfiguration.current_server, exc) + + @staticmethod + def get_current_time_string(): + time = datetime.datetime.now() + return "%s-%s-%s %s:%s:%s" % (time.date().year, + time.date().month, + time.date().day, + time.time().hour, + time.time().minute, + time.time().second) diff --git a/monkey/infection_monkey/transport/attack_telems/victim_host_telem.py b/monkey/infection_monkey/transport/attack_telems/victim_host_telem.py index ecab5a648..fc0da7fbf 100644 --- a/monkey/infection_monkey/transport/attack_telems/victim_host_telem.py +++ b/monkey/infection_monkey/transport/attack_telems/victim_host_telem.py @@ -14,5 +14,5 @@ class VictimHostTelem(AttackTelem): :param data: Other data relevant to the attack technique """ super(VictimHostTelem, self).__init__(technique, status, data) - victim_host = {'hostname': machine.domain_name, 'ip': machine.ip_addr} + victim_host = {'domain_name': machine.domain_name, 'ip_addr': machine.ip_addr} self.data.update({'machine': victim_host}) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 52d4d6529..ebf280b00 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,5 +1,5 @@ import logging -from cc.services.attack.technique_reports import T1210 +from cc.services.attack.technique_reports import T1210, T1197 from cc.services.attack.attack_telem import get_latest_telem from cc.services.attack.attack_config import get_technique_values from cc.database import mongo @@ -9,7 +9,8 @@ __author__ = "VakarisZ" LOG = logging.getLogger(__name__) -TECHNIQUES = {'T1210': T1210} +TECHNIQUES = {'T1210': T1210, + 'T1197': T1197} REPORT_NAME = 'new_report' @@ -44,7 +45,7 @@ class AttackReportService: if AttackReportService.is_report_generated(): telem_time = get_latest_telem() latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) - if telem_time and telem_time['timestamp'] == latest_report['meta']['timestamp']: + if telem_time and latest_report['meta'] and telem_time['time'] == latest_report['meta']['time']: return latest_report return AttackReportService.generate_new_report() diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index 3cab5b620..227c03223 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -74,7 +74,7 @@ SCHEMA = { "type": "object", "properties": { "T1197": { - "title": "T1197 Bits jobs", + "title": "T1197 BITS jobs", "type": "bool", "value": True, "necessary": True, diff --git a/monkey/monkey_island/cc/services/attack/attack_telem.py b/monkey/monkey_island/cc/services/attack/attack_telem.py index 139837835..8d7e08960 100644 --- a/monkey/monkey_island/cc/services/attack/attack_telem.py +++ b/monkey/monkey_island/cc/services/attack/attack_telem.py @@ -18,7 +18,7 @@ def set_results(technique, data): """ data.update({'technique': technique}) mongo.db.attack_results.insert(data) - mongo.db.attack_results.update({'name': 'latest'}, {'name': 'latest', 'timestamp': time()}, upsert=True) + mongo.db.attack_results.update({'name': 'latest'}, {'name': 'latest', 'time': data['time']}, upsert=True) def get_latest_telem(): diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py index 6121c46e3..1b3b9e708 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py @@ -1,5 +1,5 @@ from monkey_island.cc.services.attack.technique_reports.technique_service import * -from cc.services.report import ReportService +from cc.database import mongo __author__ = "VakarisZ" @@ -13,6 +13,13 @@ MESSAGES = { def get_report_data(): data = get_tech_base_data(TECHNIQUE, MESSAGES) - - data.update() + bits_results = mongo.db.attack_results.aggregate([{'$match': {'technique': TECHNIQUE}}, + {'$group': {'_id': {'ip_addr': '$machine.ip_addr', 'usage': '$usage'}, + 'ip_addr': {'$first': '$machine.ip_addr'}, + 'domain_name': {'$first': '$machine.domain_name'}, + 'usage': {'$first': '$usage'}, + 'time': {'$first': '$time'}} + }]) + bits_results = list(bits_results) + data.update({'bits_jobs': bits_results}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py index b59c1838d..e412bea8f 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py @@ -6,6 +6,11 @@ __author__ = "VakarisZ" def technique_status(technique): + """ + Gets status of certain attack technique. If + :param technique: + :return: + """ if mongo.db.attack_results.find_one({'status': ScanStatus.USED.value, 'technique': technique}): return ScanStatus.USED elif mongo.db.attack_results.find_one({'status': ScanStatus.SCANNED.value, 'technique': technique}): diff --git a/monkey/monkey_island/cc/ui/src/components/attack/T1197.js b/monkey/monkey_island/cc/ui/src/components/attack/T1197.js index a5156c3f4..3b0e09e7c 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/T1197.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/T1197.js @@ -1,60 +1,43 @@ import React from 'react'; import '../../styles/Collapse.scss' -import {Link} from "react-router-dom"; +import ReactTable from "react-table"; -let renderArray = function(val) { - return {val.map(x => {x} )}; -}; - - -let renderMachine = function (val, index, exploited=false) { +let renderMachine = function (val) { return ( -
    - {renderArray(val.ip_addresses)} - {(val.domain_name ? " (".concat(val.domain_name, ")") : " (".concat(val.label, ")"))} : - {exploited ? renderArray(val.exploits) : renderArray(val.services)} -
    + {val.ip_addr} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} ) }; +const columns = [ + { + columns: [ + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x), style: { 'whiteSpace': 'unset' }, width: 200}, + {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, + {Header: 'Usage', id: 'usage', accessor: x => x.usage, style: { 'whiteSpace': 'unset' }} + ] + } +]; + class T1210 extends React.Component { - renderScannedMachines = (machines) => { - let content = []; - for (let i = 0; i < machines.length; i++ ){ - if (machines[i].services.length !== 0){ - content.push(renderMachine(machines[i], i)) - } - } - return
    {content}
    ; - }; - - renderExploitedMachines = (machines) => { - let content = []; - for (let i = 0; i < machines.length; i++ ){ - if (machines[i].exploits.length !== 0){ - content.push(renderMachine(machines[i], i, true)) - } - } - return
    {content}
    ; - }; - constructor(props) { super(props); } render() { - console.log(this.props); return ( -
    -
    {this.props.data.message}
    -
    Found services:
    - {this.renderScannedMachines(this.props.data.scanned_machines)} -
    Successful exploiters:
    - {this.renderExploitedMachines(this.props.data.exploited_machines)} -
    - To get more info about scanned and exploited machines view standard report. +
    +
    +
    {this.props.data.message}
    + {this.props.data.bits_jobs.length > 0 ?
    BITS jobs were used in these machines:
    : ''}
    +
    +
    ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js b/monkey/monkey_island/cc/ui/src/components/attack/T1210.js index 63e0222f6..8b688df7a 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/T1210.js @@ -44,7 +44,6 @@ class T1210 extends React.Component { } render() { - console.log(this.props); return (
    {this.props.data.message}
    diff --git a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js index b35fba619..86866e700 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js @@ -5,10 +5,12 @@ import {edgeGroupToColor, options} from 'components/map/MapOptions'; import AuthComponent from '../AuthComponent'; import Collapse from '@kunukn/react-collapse'; import T1210 from '../attack/T1210'; +import T1197 from '../attack/T1197'; import '../../styles/Collapse.scss' const tech_components = { - 'T1210': T1210 + 'T1210': T1210, + 'T1197': T1197 }; const classNames = require('classnames'); @@ -21,7 +23,7 @@ class AttackReportPageComponent extends AuthComponent { report: false, allMonkeysAreDead: false, runStarted: true, - index: 1 + collapseOpen: '' }; } @@ -54,8 +56,8 @@ class AttackReportPageComponent extends AuthComponent { } } - onToggle = index => - this.setState(state => ({ index: state.index === index ? null : index })); + onToggle = technique => + this.setState(state => ({ collapseOpen: state.collapseOpen === technique ? null : technique })); getTechniqueCollapse(tech_id){ switch (this.state.report[tech_id].status) { @@ -70,21 +72,21 @@ class AttackReportPageComponent extends AuthComponent { } return ( -
    - { - this.setState({ item1: collapseState }); + this.setState({ tech_id: collapseState }); }} onInit={({ collapseState }) => { - this.setState({ item1: collapseState }); + this.setState({ tech_id: collapseState }); }} render={collapseState => this.createTechniqueContent(collapseState, tech_id)}/>
    @@ -101,9 +103,10 @@ class AttackReportPageComponent extends AuthComponent { } generateReportContent(){ - let content = ''; + let content = []; + console.log(this.state.report); Object.keys(this.state.report).forEach((tech_id) => { - content = this.getTechniqueCollapse(tech_id) + content.push(this.getTechniqueCollapse(tech_id)) }); return (
    From fae882052871e7b91df9582ef44aae7a5166f709 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 11 Apr 2019 13:08:34 +0300 Subject: [PATCH 09/20] Cosmetic changes --- monkey/monkey_island/cc/resources/attack/__init__.py | 1 + .../cc/services/attack/technique_reports/__init__.py | 1 + .../cc/ui/src/components/pages/AttackReportPage.js | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/resources/attack/__init__.py b/monkey/monkey_island/cc/resources/attack/__init__.py index e69de29bb..98867ed4d 100644 --- a/monkey/monkey_island/cc/resources/attack/__init__.py +++ b/monkey/monkey_island/cc/resources/attack/__init__.py @@ -0,0 +1 @@ +__author__ = 'VakarisZ' 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 e69de29bb..98867ed4d 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -0,0 +1 @@ +__author__ = 'VakarisZ' diff --git a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js index 86866e700..ff3714ef9 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js @@ -1,5 +1,5 @@ import React from 'react'; -import {Button, Col, Row, TabContainer} from 'react-bootstrap'; +import {Col} from 'react-bootstrap'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {edgeGroupToColor, options} from 'components/map/MapOptions'; import AuthComponent from '../AuthComponent'; From f73fb9f3a94d4cdcee809e4e7a88e89b14f7dc86 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 12 Apr 2019 15:10:24 +0300 Subject: [PATCH 10/20] Refactored T1210 to have a dedicated telem. --- .../infection_monkey/exploit/elasticgroovy.py | 2 + monkey/infection_monkey/exploit/hadoop.py | 4 + monkey/infection_monkey/exploit/rdpgrinder.py | 5 +- monkey/infection_monkey/exploit/sambacry.py | 5 +- monkey/infection_monkey/exploit/shellshock.py | 5 +- monkey/infection_monkey/exploit/smbexec.py | 10 +++ monkey/infection_monkey/exploit/sshexec.py | 4 + monkey/infection_monkey/exploit/struts2.py | 4 + monkey/infection_monkey/exploit/weblogic.py | 5 ++ monkey/infection_monkey/exploit/wmiexec.py | 5 ++ monkey/infection_monkey/monkey.py | 2 + .../infection_monkey/network/elasticfinger.py | 4 + monkey/infection_monkey/network/httpfinger.py | 4 + .../network/mssql_fingerprint.py | 4 + .../infection_monkey/network/mysqlfinger.py | 5 +- monkey/infection_monkey/network/smbfinger.py | 5 +- monkey/infection_monkey/network/sshfinger.py | 4 + .../infection_monkey/network/tcp_scanner.py | 5 ++ .../attack/technique_reports/T1210.py | 32 ++++--- .../cc/ui/src/components/attack/T1210.js | 88 ++++++++++++------- 20 files changed, 150 insertions(+), 52 deletions(-) diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py index b4d3b2be8..eb6a3615c 100644 --- a/monkey/infection_monkey/exploit/elasticgroovy.py +++ b/monkey/infection_monkey/exploit/elasticgroovy.py @@ -58,6 +58,8 @@ class ElasticGroovyExploiter(WebRCE): result = self.get_results(response) if not result: return False + VictimHostTelem('T1210', ScanStatus.USED.value, + self.host, {'url': url, 'service': 'Elastic search'}).send() return result[0] def upload_monkey(self, url, commands=None): diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 1db521acd..059ffb9da 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -13,6 +13,8 @@ import posixpath from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.exploit.tools import HTTPTools, build_monkey_commandline, get_monkey_depth from infection_monkey.model import MONKEY_ARG, ID_STRING, HADOOP_WINDOWS_COMMAND, HADOOP_LINUX_COMMAND +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus __author__ = 'VakarisZ' @@ -48,6 +50,8 @@ class HadoopExploiter(WebRCE): return False http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() + VictimHostTelem('T1210', ScanStatus.USED.value, + self.host, {'url': self.vulnerable_urls[0], 'service': 'Hadoop'}).send() return True def exploit(self, url, command): diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index 3b81f7109..99d13aa6a 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -280,10 +280,12 @@ class RdpExploiter(HostExploiter): cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1) if self._config.rdp_use_vbs_download: + download_method = 'VBS' command = RDP_CMDLINE_HTTP_VBS % { 'monkey_path': self._config.dropper_target_path_win_32, 'http_path': http_path, 'parameters': cmdline} else: + download_method = 'BITS' command = RDP_CMDLINE_HTTP_BITS % { 'monkey_path': self._config.dropper_target_path_win_32, 'http_path': http_path, 'parameters': cmdline} @@ -314,7 +316,8 @@ class RdpExploiter(HostExploiter): client_factory.done_event.wait() if client_factory.success: - VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING) + if download_method == 'BITS': + VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING) exploited = True self.report_login_attempt(True, user, password) break diff --git a/monkey/infection_monkey/exploit/sambacry.py b/monkey/infection_monkey/exploit/sambacry.py index 2468a42bc..5bc93ff28 100644 --- a/monkey/infection_monkey/exploit/sambacry.py +++ b/monkey/infection_monkey/exploit/sambacry.py @@ -4,7 +4,6 @@ import posixpath import re import time from io import BytesIO -from os import path import impacket.smbconnection from impacket.nmb import NetBIOSError @@ -22,6 +21,8 @@ from infection_monkey.model import DROPPER_ARG from infection_monkey.network.smbfinger import SMB_SERVICE from infection_monkey.exploit.tools import build_monkey_commandline, get_target_monkey_by_os, get_monkey_depth from infection_monkey.pyinstaller_utils import get_binary_file_path +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus __author__ = 'itay.mizeretz' @@ -89,6 +90,8 @@ class SambaCryExploiter(HostExploiter): LOG.info( "Shares triggered successfully on host %s: %s" % ( self.host.ip_addr, str(successfully_triggered_shares))) + VictimHostTelem('T1210', ScanStatus.USED.value, + self.host, {'port': '139/445', 'service': 'Samba'}).send() return True else: LOG.info("No shares triggered successfully on host %s" % self.host.ip_addr) diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index a98cbda50..feeb0ccf2 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -11,6 +11,8 @@ from infection_monkey.exploit.tools import get_target_monkey, HTTPTools, get_mon from infection_monkey.model import DROPPER_ARG from infection_monkey.exploit.shellshock_resources import CGI_FILES from infection_monkey.exploit.tools import build_monkey_commandline +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus __author__ = 'danielg' @@ -143,7 +145,8 @@ class ShellShockExploiter(HostExploiter): if not (self.check_remote_file_exists(url, header, exploit, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") continue - + VictimHostTelem('T1210', ScanStatus.USED.value, + self.host, {'url': url, 'service': 'Bash'}).send() return True return False diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 579fd8f1f..8c1469831 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -10,6 +10,8 @@ from infection_monkey.network import SMBFinger from infection_monkey.network.tools import check_tcp_port from infection_monkey.exploit.tools import build_monkey_commandline from common.utils.exploit_enum import ExploitType +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus LOG = getLogger(__name__) @@ -68,6 +70,10 @@ class SmbExploiter(HostExploiter): LOG.debug("Successfully logged in %r using SMB (%s : %s : %s : %s)", self.host, user, password, lm_hash, ntlm_hash) self.report_login_attempt(True, user, password, lm_hash, ntlm_hash) + VictimHostTelem('T1210', ScanStatus.USED.value, self.host, + {'port': ("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], + SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])), + 'service': 'SMB'}).send() exploited = True break else: @@ -137,4 +143,8 @@ class SmbExploiter(HostExploiter): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", remote_full_path, self.host, cmdline) + VictimHostTelem('T1210', ScanStatus.USED.value, self.host, + {'port': ("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], + SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])), + 'service': 'Elastic'}).send() return True diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 8a58f18c6..8dcd56175 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -11,6 +11,8 @@ from infection_monkey.model import MONKEY_ARG from infection_monkey.network.tools import check_tcp_port from infection_monkey.exploit.tools import build_monkey_commandline from common.utils.exploit_enum import ExploitType +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus __author__ = 'hoffer' @@ -81,6 +83,8 @@ class SSHExploiter(HostExploiter): LOG.debug("Successfully logged in %r using SSH (%s : %s)", self.host, user, curpass) exploited = True + VictimHostTelem('T1210', ScanStatus.USED.value, + self.host, {'port': port, 'service': 'SSH'}).send() self.report_login_attempt(True, user, curpass) break diff --git a/monkey/infection_monkey/exploit/struts2.py b/monkey/infection_monkey/exploit/struts2.py index 18f3d3a7e..96e2d7da6 100644 --- a/monkey/infection_monkey/exploit/struts2.py +++ b/monkey/infection_monkey/exploit/struts2.py @@ -10,6 +10,8 @@ import re import logging from infection_monkey.exploit.web_rce import WebRCE +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus __author__ = "VakarisZ" @@ -91,4 +93,6 @@ class Struts2Exploiter(WebRCE): except httplib.IncompleteRead as e: page = e.partial + VictimHostTelem('T1210', ScanStatus.USED.value, + self.host, {'url': url, 'service': 'Struts2'}).send() return page diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index 6d0748426..4f06efec0 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -10,6 +10,8 @@ from requests import post, exceptions from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.exploit.tools import get_free_tcp_port, get_interface_to_target from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus import threading import logging @@ -67,6 +69,9 @@ class WebLogicExploiter(WebRCE): except Exception as e: print('[!] Connection Error') print(e) + + VictimHostTelem('T1210', ScanStatus.USED.value, + self.host, {'url': url, 'service': 'Weblogic'}).send() return True def add_vulnerable_urls(self, urls, stop_checking=False): diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index 66cc30fa9..f92e23639 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -10,6 +10,8 @@ from infection_monkey.exploit.tools import SmbTools, WmiTools, AccessDeniedExcep get_monkey_depth, build_monkey_commandline from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS from common.utils.exploit_enum import ExploitType +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus LOG = logging.getLogger(__name__) @@ -103,6 +105,9 @@ class WmiExploiter(HostExploiter): if (0 != result.ProcessId) and (0 == result.ReturnValue): LOG.info("Executed dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)", remote_full_path, self.host, result.ProcessId, result.ReturnValue, cmdline) + + VictimHostTelem('T1210', ScanStatus.USED.value, + self.host, {'port': 'unknown', 'service': 'WMI'}).send() success = True else: LOG.debug("Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)", diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index e80e15396..2ec3e5d1f 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 common.utils.attack_utils import ScanStatus +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem __author__ = 'itamar' diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index 3d62de687..f9f869ce9 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -8,6 +8,8 @@ from requests.exceptions import Timeout, ConnectionError import infection_monkey.config from infection_monkey.model.host import VictimHost from infection_monkey.network import HostFinger +from common.utils.attack_utils import ScanStatus +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem ES_PORT = 9200 ES_SERVICE = 'elastic-search-9200' @@ -39,6 +41,8 @@ class ElasticFinger(HostFinger): host.services[ES_SERVICE]['cluster_name'] = data['cluster_name'] host.services[ES_SERVICE]['name'] = data['name'] host.services[ES_SERVICE]['version'] = data['version']['number'] + VictimHostTelem('T1210', ScanStatus.SCANNED.value, + host, {'port': ES_PORT, 'service': 'Elastic'}).send() return True except Timeout: LOG.debug("Got timeout while trying to read header information") diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index 829c6b1b5..1b686f110 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -1,6 +1,8 @@ import infection_monkey.config from infection_monkey.network import HostFinger from infection_monkey.model.host import VictimHost +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus import logging LOG = logging.getLogger(__name__) @@ -40,6 +42,8 @@ class HTTPFinger(HostFinger): host.services['tcp-' + port[1]]['name'] = 'http' host.services['tcp-' + port[1]]['data'] = (server,ssl) LOG.info("Port %d is open on host %s " % (port[0], host)) + VictimHostTelem('T1210', ScanStatus.SCANNED.value, + host, {'port': port[0], 'service': 'HTTP/HTTPS'}).send() break # https will be the same on the same port except Timeout: pass diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index 75fde7465..08fc62e7a 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -4,6 +4,8 @@ import socket from infection_monkey.model.host import VictimHost from infection_monkey.network import HostFinger import infection_monkey.config +from common.utils.attack_utils import ScanStatus +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem __author__ = 'Maor Rayzin' @@ -68,6 +70,8 @@ class MSSQLFinger(HostFinger): # Loop through the server data instances_list = data[3:].decode().split(';;') LOG.info('{0} MSSQL instances found'.format(len(instances_list))) + VictimHostTelem('T1210', ScanStatus.SCANNED.value, + host, {'port': MSSQLFinger.SQL_BROWSER_DEFAULT_PORT, 'service': 'MsSQL'}).send() for instance in instances_list: instance_info = instance.split(';') if len(instance_info) > 1: diff --git a/monkey/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py index 70080c12b..05c5e9522 100644 --- a/monkey/infection_monkey/network/mysqlfinger.py +++ b/monkey/infection_monkey/network/mysqlfinger.py @@ -5,6 +5,8 @@ import infection_monkey.config from infection_monkey.model.host import VictimHost from infection_monkey.network import HostFinger from infection_monkey.network.tools import struct_unpack_tracker, struct_unpack_tracker_string +from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem +from common.utils.attack_utils import ScanStatus MYSQL_PORT = 3306 SQL_SERVICE = 'mysqld-3306' @@ -59,7 +61,8 @@ class MySQLFinger(HostFinger): host.services[SQL_SERVICE]['minor_version'] = version[1] host.services[SQL_SERVICE]['build_version'] = version[2] thread_id, curpos = struct_unpack_tracker(data, curpos, "{val.map(x => {x} )}; -}; +import ReactTable from "react-table"; -let renderMachine = function (val, index, exploited=false) { +let renderMachine = function (val) { return ( -
    - {renderArray(val.ip_addresses)} - {(val.domain_name ? " (".concat(val.domain_name, ")") : " (".concat(val.label, ")"))} : - {exploited ? renderArray(val.exploits) : renderArray(val.services)} -
    + {val.ip_addr} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} ) }; +let renderPort = function (service){ + if(service.url){ + return service.url + } else { + return service.port + } +}; + +const columns = [ + { + columns: [ + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x), style: { 'whiteSpace': 'unset' }, width: 200}, + {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, + {Header: 'Port/url', id: 'port', accessor: x =>renderPort(x), style: { 'whiteSpace': 'unset' }}, + {Header: 'Service', id: 'service', accessor: x => x.service, style: { 'whiteSpace': 'unset' }} + ] + } +]; + class T1210 extends React.Component { - renderScannedMachines = (machines) => { - let content = []; - for (let i = 0; i < machines.length; i++ ){ - if (machines[i].services.length !== 0){ - content.push(renderMachine(machines[i], i)) - } - } - return
    {content}
    ; - }; - - renderExploitedMachines = (machines) => { - let content = []; - for (let i = 0; i < machines.length; i++ ){ - if (machines[i].exploits.length !== 0){ - content.push(renderMachine(machines[i], i, true)) - } - } - return
    {content}
    ; - }; - constructor(props) { super(props); } + renderFoundServices(data) { + return ( +
    +
    +
    Found services:
    + +
    ) + } + + renderExploitedServices(data) { + return ( +
    +
    +
    Exploited services:
    + +
    ) + } + render() { return (
    {this.props.data.message}
    - {this.props.data.scanned_machines.length > 0 ?
    Found services:
    : ''} - {this.renderScannedMachines(this.props.data.scanned_machines)} - {this.props.data.exploited_machines.length > 0 ?
    Successful exploiters:
    : ''} - {this.renderExploitedMachines(this.props.data.exploited_machines)} + {this.props.data.found_services.length > 0 ? + this.renderFoundServices(this.props.data.found_services) : ''} + {this.props.data.exploited_services.length > 0 ? + this.renderExploitedServices(this.props.data.exploited_services) : ''}
    To get more info about scanned and exploited machines view standard report.
    From 2f66b77a3348270284207626bfdd2b5ec6a4c792 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 15 Apr 2019 09:41:03 +0300 Subject: [PATCH 11/20] small refactor in rdbgrinder --- monkey/infection_monkey/exploit/rdpgrinder.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index 99d13aa6a..28b1b7c70 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -280,12 +280,10 @@ class RdpExploiter(HostExploiter): cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1) if self._config.rdp_use_vbs_download: - download_method = 'VBS' command = RDP_CMDLINE_HTTP_VBS % { 'monkey_path': self._config.dropper_target_path_win_32, 'http_path': http_path, 'parameters': cmdline} else: - download_method = 'BITS' command = RDP_CMDLINE_HTTP_BITS % { 'monkey_path': self._config.dropper_target_path_win_32, 'http_path': http_path, 'parameters': cmdline} @@ -316,7 +314,7 @@ class RdpExploiter(HostExploiter): client_factory.done_event.wait() if client_factory.success: - if download_method == 'BITS': + if not self._config.rdp_use_vbs_download: VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING) exploited = True self.report_login_attempt(True, user, password) From 6fb06bc24d825948c92c805618c461e568249519 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 15 Apr 2019 16:01:19 +0300 Subject: [PATCH 12/20] Refactored island imports according to latest changes --- monkey/monkey_island/cc/app.py | 6 +++--- monkey/monkey_island/cc/services/attack/attack_report.py | 8 ++++---- .../cc/services/attack/technique_reports/T1197.py | 2 +- .../cc/services/attack/technique_reports/T1210.py | 1 - .../attack/technique_reports/technique_service.py | 4 ++-- .../cc/ui/src/components/pages/AttackReportPage.js | 1 - 6 files changed, 10 insertions(+), 12 deletions(-) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index a76774079..27b1fdc06 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -31,9 +31,9 @@ from monkey_island.cc.resources.pba_file_download import PBAFileDownload from monkey_island.cc.services.config import ConfigService from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.resources.pba_file_upload import FileUpload -from monkey_island.cc.resources.attack_telem import AttackTelem -from monkey_island.cc.resources.attack_config import AttackConfiguration -from monkey_island.cc.resources.attack_report import AttackReport +from monkey_island.cc.resources.attack.attack_telem import AttackTelem +from monkey_island.cc.resources.attack.attack_config import AttackConfiguration +from monkey_island.cc.resources.attack.attack_report import AttackReport __author__ = 'Barak' diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index ebf280b00..8e4b83858 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,8 +1,8 @@ import logging -from cc.services.attack.technique_reports import T1210, T1197 -from cc.services.attack.attack_telem import get_latest_telem -from cc.services.attack.attack_config import get_technique_values -from cc.database import mongo +from monkey_island.cc.services.attack.technique_reports import T1210, T1197 +from monkey_island.cc.services.attack.attack_telem import get_latest_telem +from monkey_island.cc.services.attack.attack_config import get_technique_values +from monkey_island.cc.database import mongo __author__ = "VakarisZ" diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py index 1b3b9e708..2685540b7 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py @@ -1,5 +1,5 @@ from monkey_island.cc.services.attack.technique_reports.technique_service import * -from cc.database import mongo +from monkey_island.cc.database import mongo __author__ = "VakarisZ" diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py index b796b93a5..d93652238 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -1,5 +1,4 @@ from monkey_island.cc.services.attack.technique_reports.technique_service import * -from cc.services.report import ReportService from common.utils.attack_utils import ScanStatus __author__ = "VakarisZ" diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py index e412bea8f..3538b543d 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py @@ -1,6 +1,6 @@ -from cc.database import mongo +from monkey_island.cc.database import mongo from common.utils.attack_utils import ScanStatus -from cc.services.attack.attack_config import get_technique +from monkey_island.cc.services.attack.attack_config import get_technique __author__ = "VakarisZ" diff --git a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js index ff3714ef9..6197e47ce 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/AttackReportPage.js @@ -104,7 +104,6 @@ class AttackReportPageComponent extends AuthComponent { generateReportContent(){ let content = []; - console.log(this.state.report); Object.keys(this.state.report).forEach((tech_id) => { content.push(this.getTechniqueCollapse(tech_id)) }); From c32d07ae343fac88bbf425ab28eba8e007d9b5b3 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 17 Apr 2019 11:57:33 +0300 Subject: [PATCH 13/20] T1210 refactored. Instead of dedicated telems, relevant info is appended to 'scan' and 'exploit' telemetries --- monkey/infection_monkey/exploit/__init__.py | 17 +++++++ .../infection_monkey/exploit/elasticgroovy.py | 4 +- monkey/infection_monkey/exploit/hadoop.py | 6 +-- monkey/infection_monkey/exploit/rdpgrinder.py | 2 + monkey/infection_monkey/exploit/sambacry.py | 6 +-- monkey/infection_monkey/exploit/shellshock.py | 6 +-- monkey/infection_monkey/exploit/smbexec.py | 15 +++---- monkey/infection_monkey/exploit/sshexec.py | 8 ++-- monkey/infection_monkey/exploit/struts2.py | 6 +-- monkey/infection_monkey/exploit/weblogic.py | 6 +-- monkey/infection_monkey/exploit/wmiexec.py | 6 +-- monkey/infection_monkey/network/__init__.py | 20 ++++++++- .../infection_monkey/network/elasticfinger.py | 6 +-- monkey/infection_monkey/network/httpfinger.py | 6 +-- .../network/mssql_fingerprint.py | 14 +++--- .../infection_monkey/network/mysqlfinger.py | 8 +--- monkey/infection_monkey/network/smbfinger.py | 7 ++- monkey/infection_monkey/network/sshfinger.py | 7 ++- .../infection_monkey/network/tcp_scanner.py | 7 +-- .../transport/attack_telems/base_telem.py | 14 +----- monkey/infection_monkey/utils.py | 11 ++++- .../attack/technique_reports/T1210.py | 45 ++++++++++++------- .../cc/ui/src/components/attack/T1210.js | 32 ++++++++----- 23 files changed, 141 insertions(+), 118 deletions(-) diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 0d4300b5f..41f82e50e 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -1,6 +1,7 @@ from abc import ABCMeta, abstractmethod import infection_monkey.config from common.utils.exploit_enum import ExploitType +from infection_monkey.utils import get_current_time_string __author__ = 'itamar' @@ -12,6 +13,7 @@ class HostExploiter(object): # Usual values are 'vulnerability' or 'brute_force' EXPLOIT_TYPE = ExploitType.VULNERABILITY + _EXPLOITED_SERVICE = '' def __init__(self, host): self._config = infection_monkey.config.WormConfiguration @@ -37,6 +39,21 @@ class HostExploiter(object): def exploit_host(self): raise NotImplementedError() + def add_vuln_service_info(self, port=None, url=None): + if port: + service_endpoint = port + elif url: + service_endpoint = url + else: + raise NotImplementedError("You must pass either port or url to add a vulnerable service info.") + if not self._EXPLOITED_SERVICE: + raise NotImplementedError("You must override _EXPLOITED_SERVICE to name a service this exploiter " + "is targeting") + self._exploit_info['exploited_service'] = {'name': self._EXPLOITED_SERVICE, + 'endpoint': service_endpoint, + 'time': get_current_time_string()} + return + from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter from infection_monkey.exploit.wmiexec import WmiExploiter diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py index eb6a3615c..b40d01f0a 100644 --- a/monkey/infection_monkey/exploit/elasticgroovy.py +++ b/monkey/infection_monkey/exploit/elasticgroovy.py @@ -29,6 +29,7 @@ class ElasticGroovyExploiter(WebRCE): % """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()""" _TARGET_OS_TYPE = ['linux', 'windows'] + _EXPLOITED_SERVICE = 'Elastic search' def __init__(self, host): super(ElasticGroovyExploiter, self).__init__(host) @@ -58,8 +59,7 @@ class ElasticGroovyExploiter(WebRCE): result = self.get_results(response) if not result: return False - VictimHostTelem('T1210', ScanStatus.USED.value, - self.host, {'url': url, 'service': 'Elastic search'}).send() + self.add_vuln_service_info(url=url) return result[0] def upload_monkey(self, url, commands=None): diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 059ffb9da..ef2fa506e 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -13,8 +13,6 @@ import posixpath from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.exploit.tools import HTTPTools, build_monkey_commandline, get_monkey_depth from infection_monkey.model import MONKEY_ARG, ID_STRING, HADOOP_WINDOWS_COMMAND, HADOOP_LINUX_COMMAND -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem -from common.utils.attack_utils import ScanStatus __author__ = 'VakarisZ' @@ -23,6 +21,7 @@ LOG = logging.getLogger(__name__) class HadoopExploiter(WebRCE): _TARGET_OS_TYPE = ['linux', 'windows'] + _EXPLOITED_SERVICE = 'Hadoop' HADOOP_PORTS = [["8088", False]] # How long we have our http server open for downloads in seconds DOWNLOAD_TIMEOUT = 60 @@ -50,8 +49,7 @@ class HadoopExploiter(WebRCE): return False http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() - VictimHostTelem('T1210', ScanStatus.USED.value, - self.host, {'url': self.vulnerable_urls[0], 'service': 'Hadoop'}).send() + self.add_vuln_service_info(url=self.vulnerable_urls[0]) return True def exploit(self, url, command): diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index 28b1b7c70..c7b3e1c71 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -239,6 +239,7 @@ class RdpExploiter(HostExploiter): _TARGET_OS_TYPE = ['windows'] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE + _EXPLOITED_SERVICE = 'RDP' def __init__(self, host): super(RdpExploiter, self).__init__(host) @@ -316,6 +317,7 @@ class RdpExploiter(HostExploiter): if client_factory.success: if not self._config.rdp_use_vbs_download: VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING) + self.add_vuln_service_info(port=RDP_PORT) exploited = True self.report_login_attempt(True, user, password) break diff --git a/monkey/infection_monkey/exploit/sambacry.py b/monkey/infection_monkey/exploit/sambacry.py index 5bc93ff28..143c15c83 100644 --- a/monkey/infection_monkey/exploit/sambacry.py +++ b/monkey/infection_monkey/exploit/sambacry.py @@ -21,8 +21,6 @@ from infection_monkey.model import DROPPER_ARG from infection_monkey.network.smbfinger import SMB_SERVICE from infection_monkey.exploit.tools import build_monkey_commandline, get_target_monkey_by_os, get_monkey_depth from infection_monkey.pyinstaller_utils import get_binary_file_path -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem -from common.utils.attack_utils import ScanStatus __author__ = 'itay.mizeretz' @@ -36,6 +34,7 @@ class SambaCryExploiter(HostExploiter): """ _TARGET_OS_TYPE = ['linux'] + _EXPLOITED_SERVICE = "Samba" # Name of file which contains the monkey's commandline SAMBACRY_COMMANDLINE_FILENAME = "monkey_commandline.txt" # Name of file which contains the runner's result @@ -90,8 +89,7 @@ class SambaCryExploiter(HostExploiter): LOG.info( "Shares triggered successfully on host %s: %s" % ( self.host.ip_addr, str(successfully_triggered_shares))) - VictimHostTelem('T1210', ScanStatus.USED.value, - self.host, {'port': '139/445', 'service': 'Samba'}).send() + self.add_vuln_service_info(port='139 or 445') return True else: LOG.info("No shares triggered successfully on host %s" % self.host.ip_addr) diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index feeb0ccf2..698a3fa25 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -11,8 +11,6 @@ from infection_monkey.exploit.tools import get_target_monkey, HTTPTools, get_mon from infection_monkey.model import DROPPER_ARG from infection_monkey.exploit.shellshock_resources import CGI_FILES from infection_monkey.exploit.tools import build_monkey_commandline -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem -from common.utils.attack_utils import ScanStatus __author__ = 'danielg' @@ -28,6 +26,7 @@ class ShellShockExploiter(HostExploiter): } _TARGET_OS_TYPE = ['linux'] + _EXPLOITED_SERVICE = 'Bash' def __init__(self, host): super(ShellShockExploiter, self).__init__(host) @@ -145,8 +144,7 @@ class ShellShockExploiter(HostExploiter): if not (self.check_remote_file_exists(url, header, exploit, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") continue - VictimHostTelem('T1210', ScanStatus.USED.value, - self.host, {'url': url, 'service': 'Bash'}).send() + self.add_vuln_service_info(url=url) return True return False diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 8c1469831..7edd4b528 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -10,8 +10,6 @@ from infection_monkey.network import SMBFinger from infection_monkey.network.tools import check_tcp_port from infection_monkey.exploit.tools import build_monkey_commandline from common.utils.exploit_enum import ExploitType -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem -from common.utils.attack_utils import ScanStatus LOG = getLogger(__name__) @@ -19,6 +17,7 @@ LOG = getLogger(__name__) class SmbExploiter(HostExploiter): _TARGET_OS_TYPE = ['windows'] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE + _EXPLOITED_SERVICE = 'SMB' KNOWN_PROTOCOLS = { '139/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 139), '445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445), @@ -70,10 +69,8 @@ class SmbExploiter(HostExploiter): LOG.debug("Successfully logged in %r using SMB (%s : %s : %s : %s)", self.host, user, password, lm_hash, ntlm_hash) self.report_login_attempt(True, user, password, lm_hash, ntlm_hash) - VictimHostTelem('T1210', ScanStatus.USED.value, self.host, - {'port': ("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], - SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])), - 'service': 'SMB'}).send() + self.add_vuln_service_info(port=("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], + SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1]))) exploited = True break else: @@ -143,8 +140,6 @@ class SmbExploiter(HostExploiter): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", remote_full_path, self.host, cmdline) - VictimHostTelem('T1210', ScanStatus.USED.value, self.host, - {'port': ("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], - SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])), - 'service': 'Elastic'}).send() + self.add_vuln_service_info(port=("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], + SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1]))) return True diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 8dcd56175..1fd954fec 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -11,8 +11,6 @@ from infection_monkey.model import MONKEY_ARG from infection_monkey.network.tools import check_tcp_port from infection_monkey.exploit.tools import build_monkey_commandline from common.utils.exploit_enum import ExploitType -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem -from common.utils.attack_utils import ScanStatus __author__ = 'hoffer' @@ -24,6 +22,7 @@ TRANSFER_UPDATE_RATE = 15 class SSHExploiter(HostExploiter): _TARGET_OS_TYPE = ['linux', None] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE + _EXPLOITED_SERVICE = 'SSH' def __init__(self, host): super(SSHExploiter, self).__init__(host) @@ -83,12 +82,11 @@ class SSHExploiter(HostExploiter): LOG.debug("Successfully logged in %r using SSH (%s : %s)", self.host, user, curpass) exploited = True - VictimHostTelem('T1210', ScanStatus.USED.value, - self.host, {'port': port, 'service': 'SSH'}).send() + self.add_vuln_service_info(port=port) self.report_login_attempt(True, user, curpass) break - except Exception as exc: + except paramiko.AuthenticationException as exc: LOG.debug("Error logging into victim %r with user" " %s and password '%s': (%s)", self.host, user, curpass, exc) diff --git a/monkey/infection_monkey/exploit/struts2.py b/monkey/infection_monkey/exploit/struts2.py index 96e2d7da6..f1bce7d3b 100644 --- a/monkey/infection_monkey/exploit/struts2.py +++ b/monkey/infection_monkey/exploit/struts2.py @@ -10,8 +10,6 @@ import re import logging from infection_monkey.exploit.web_rce import WebRCE -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem -from common.utils.attack_utils import ScanStatus __author__ = "VakarisZ" @@ -22,6 +20,7 @@ DOWNLOAD_TIMEOUT = 300 class Struts2Exploiter(WebRCE): _TARGET_OS_TYPE = ['linux', 'windows'] + _EXPLOITED_SERVICE = 'Struts2' def __init__(self, host): super(Struts2Exploiter, self).__init__(host, None) @@ -93,6 +92,5 @@ class Struts2Exploiter(WebRCE): except httplib.IncompleteRead as e: page = e.partial - VictimHostTelem('T1210', ScanStatus.USED.value, - self.host, {'url': url, 'service': 'Struts2'}).send() + self.add_vuln_service_info(url=url) return page diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index 4f06efec0..584883216 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -10,8 +10,6 @@ from requests import post, exceptions from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.exploit.tools import get_free_tcp_port, get_interface_to_target from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem -from common.utils.attack_utils import ScanStatus import threading import logging @@ -46,6 +44,7 @@ HEADERS = { class WebLogicExploiter(WebRCE): _TARGET_OS_TYPE = ['linux', 'windows'] + _EXPLOITED_SERVICE = 'Weblogic' def __init__(self, host): super(WebLogicExploiter, self).__init__(host, {'linux': '/tmp/monkey.sh', @@ -70,8 +69,7 @@ class WebLogicExploiter(WebRCE): print('[!] Connection Error') print(e) - VictimHostTelem('T1210', ScanStatus.USED.value, - self.host, {'url': url, 'service': 'Weblogic'}).send() + self.add_vuln_service_info(url=url) return True def add_vulnerable_urls(self, urls, stop_checking=False): diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index f92e23639..2db2321e0 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -10,8 +10,6 @@ from infection_monkey.exploit.tools import SmbTools, WmiTools, AccessDeniedExcep get_monkey_depth, build_monkey_commandline from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS from common.utils.exploit_enum import ExploitType -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem -from common.utils.attack_utils import ScanStatus LOG = logging.getLogger(__name__) @@ -19,6 +17,7 @@ LOG = logging.getLogger(__name__) class WmiExploiter(HostExploiter): _TARGET_OS_TYPE = ['windows'] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE + _EXPLOITED_SERVICE = 'WMI (Windows Management Instrumentation)' def __init__(self, host): super(WmiExploiter, self).__init__(host) @@ -106,8 +105,7 @@ class WmiExploiter(HostExploiter): LOG.info("Executed dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)", remote_full_path, self.host, result.ProcessId, result.ReturnValue, cmdline) - VictimHostTelem('T1210', ScanStatus.USED.value, - self.host, {'port': 'unknown', 'service': 'WMI'}).send() + self.add_vuln_service_info(port='unknown') success = True else: LOG.debug("Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)", diff --git a/monkey/infection_monkey/network/__init__.py b/monkey/infection_monkey/network/__init__.py index e43fa7073..b298be0c7 100644 --- a/monkey/infection_monkey/network/__init__.py +++ b/monkey/infection_monkey/network/__init__.py @@ -1,4 +1,5 @@ from abc import ABCMeta, abstractmethod +from infection_monkey.utils import get_current_time_string __author__ = 'itamar' @@ -14,10 +15,27 @@ class HostScanner(object): class HostFinger(object): __metaclass__ = ABCMeta + _SCANNED_SERVICE = '' + + def format_service_info(self, port=None, url=None): + if port: + service_endpoint = port + elif url: + service_endpoint = url + else: + raise NotImplementedError("You must pass either port or url to get formatted service info.") + if not self._SCANNED_SERVICE: + raise NotImplementedError("You must override _SCANNED_SERVICE property" + " to name what service is being scanned.") + return {'display_name': self._SCANNED_SERVICE, + 'endpoint': service_endpoint, + 'time': get_current_time_string()} + @abstractmethod def get_host_fingerprint(self, host): raise NotImplementedError() + from infection_monkey.network.ping_scanner import PingScanner from infection_monkey.network.tcp_scanner import TcpScanner from infection_monkey.network.smbfinger import SMBFinger @@ -26,4 +44,4 @@ from infection_monkey.network.httpfinger import HTTPFinger from infection_monkey.network.elasticfinger import ElasticFinger from infection_monkey.network.mysqlfinger import MySQLFinger from infection_monkey.network.info import local_ips, get_free_tcp_port -from infection_monkey.network.mssql_fingerprint import MSSQLFinger \ No newline at end of file +from infection_monkey.network.mssql_fingerprint import MSSQLFinger diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index f9f869ce9..c8b720722 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -8,8 +8,6 @@ from requests.exceptions import Timeout, ConnectionError import infection_monkey.config from infection_monkey.model.host import VictimHost from infection_monkey.network import HostFinger -from common.utils.attack_utils import ScanStatus -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem ES_PORT = 9200 ES_SERVICE = 'elastic-search-9200' @@ -22,6 +20,7 @@ class ElasticFinger(HostFinger): """ Fingerprints elastic search clusters, only on port 9200 """ + _SCANNED_SERVICE = 'Elastic search' def __init__(self): self._config = infection_monkey.config.WormConfiguration @@ -41,8 +40,7 @@ class ElasticFinger(HostFinger): host.services[ES_SERVICE]['cluster_name'] = data['cluster_name'] host.services[ES_SERVICE]['name'] = data['name'] host.services[ES_SERVICE]['version'] = data['version']['number'] - VictimHostTelem('T1210', ScanStatus.SCANNED.value, - host, {'port': ES_PORT, 'service': 'Elastic'}).send() + host.services[ES_SERVICE].update(self.format_service_info(url=url)) return True except Timeout: LOG.debug("Got timeout while trying to read header information") diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index 1b686f110..55bdcb1f1 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -1,8 +1,6 @@ import infection_monkey.config from infection_monkey.network import HostFinger from infection_monkey.model.host import VictimHost -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem -from common.utils.attack_utils import ScanStatus import logging LOG = logging.getLogger(__name__) @@ -12,6 +10,7 @@ class HTTPFinger(HostFinger): """ Goal is to recognise HTTP servers, where what we currently care about is apache. """ + _SCANNED_SERVICE = 'HTTP' def __init__(self): self._config = infection_monkey.config.WormConfiguration @@ -42,8 +41,7 @@ class HTTPFinger(HostFinger): host.services['tcp-' + port[1]]['name'] = 'http' host.services['tcp-' + port[1]]['data'] = (server,ssl) LOG.info("Port %d is open on host %s " % (port[0], host)) - VictimHostTelem('T1210', ScanStatus.SCANNED.value, - host, {'port': port[0], 'service': 'HTTP/HTTPS'}).send() + host.services['tcp-' + port[1]].update(self.format_service_info(port=port[0])) break # https will be the same on the same port except Timeout: pass diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index 08fc62e7a..dba614c85 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -4,8 +4,6 @@ import socket from infection_monkey.model.host import VictimHost from infection_monkey.network import HostFinger import infection_monkey.config -from common.utils.attack_utils import ScanStatus -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem __author__ = 'Maor Rayzin' @@ -18,7 +16,7 @@ class MSSQLFinger(HostFinger): SQL_BROWSER_DEFAULT_PORT = 1434 BUFFER_SIZE = 4096 TIMEOUT = 5 - SERVICE_NAME = 'MSSQL' + _SCANNED_SERVICE = 'MSSQL' def __init__(self): self._config = infection_monkey.config.WormConfiguration @@ -65,22 +63,20 @@ class MSSQLFinger(HostFinger): sock.close() return False - host.services[self.SERVICE_NAME] = {} + host.services[self._SCANNED_SERVICE] = {} # Loop through the server data instances_list = data[3:].decode().split(';;') LOG.info('{0} MSSQL instances found'.format(len(instances_list))) - VictimHostTelem('T1210', ScanStatus.SCANNED.value, - host, {'port': MSSQLFinger.SQL_BROWSER_DEFAULT_PORT, 'service': 'MsSQL'}).send() for instance in instances_list: instance_info = instance.split(';') if len(instance_info) > 1: - host.services[self.SERVICE_NAME][instance_info[1]] = {} + host.services[self._SCANNED_SERVICE][instance_info[1]] = {} for i in range(1, len(instance_info), 2): # Each instance's info is nested under its own name, if there are multiple instances # each will appear under its own name - host.services[self.SERVICE_NAME][instance_info[1]][instance_info[i - 1]] = instance_info[i] - + host.services[self._SCANNED_SERVICE][instance_info[1]][instance_info[i - 1]] = instance_info[i] + host.services[self._SCANNED_SERVICE].update(self.format_service_info(port=MSSQLFinger.SQL_BROWSER_DEFAULT_PORT)) # Close the socket sock.close() diff --git a/monkey/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py index 05c5e9522..b1b1a6164 100644 --- a/monkey/infection_monkey/network/mysqlfinger.py +++ b/monkey/infection_monkey/network/mysqlfinger.py @@ -5,12 +5,9 @@ import infection_monkey.config from infection_monkey.model.host import VictimHost from infection_monkey.network import HostFinger from infection_monkey.network.tools import struct_unpack_tracker, struct_unpack_tracker_string -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem -from common.utils.attack_utils import ScanStatus MYSQL_PORT = 3306 SQL_SERVICE = 'mysqld-3306' - LOG = logging.getLogger(__name__) @@ -18,7 +15,7 @@ class MySQLFinger(HostFinger): """ Fingerprints mysql databases, only on port 3306 """ - + _SCANNED_SERVICE = 'MySQL' SOCKET_TIMEOUT = 0.5 HEADER_SIZE = 4 # in bytes @@ -61,8 +58,7 @@ class MySQLFinger(HostFinger): host.services[SQL_SERVICE]['minor_version'] = version[1] host.services[SQL_SERVICE]['build_version'] = version[2] thread_id, curpos = struct_unpack_tracker(data, curpos, " renderMachine(x), style: { 'whiteSpace': 'unset' }, width: 200}, - {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, - {Header: 'Port/url', id: 'port', accessor: x =>renderPort(x), style: { 'whiteSpace': 'unset' }}, - {Header: 'Service', id: 'service', accessor: x => x.service, style: { 'whiteSpace': 'unset' }} + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), + style: { 'whiteSpace': 'unset' }, width: 200}, + {Header: 'Time', id: 'time', accessor: x => x.service.time, style: { 'whiteSpace': 'unset' }, width: 170}, + {Header: 'Port/url', id: 'port', accessor: x =>x.service.endpoint, style: { 'whiteSpace': 'unset' }}, + {Header: 'Service', id: 'service', accessor: x => x.service.name, style: { 'whiteSpace': 'unset' }} ] } ]; @@ -35,7 +40,7 @@ class T1210 extends React.Component { super(props); } - renderFoundServices(data) { + renderScannedServices(data) { return (

    @@ -64,11 +69,14 @@ class T1210 extends React.Component { } render() { + let scanned_services = this.props.data.scanned_services.map(formatScanned).flat(); + console.log(scanned_services); + console.log(this.props.data); return (
    {this.props.data.message}
    - {this.props.data.found_services.length > 0 ? - this.renderFoundServices(this.props.data.found_services) : ''} + {scanned_services.length > 0 ? + this.renderScannedServices(scanned_services) : ''} {this.props.data.exploited_services.length > 0 ? this.renderExploitedServices(this.props.data.exploited_services) : ''}
    From 5ecf626705b9bfd281136c8b6ed01ad6728537ce Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 19 Apr 2019 13:44:36 +0300 Subject: [PATCH 14/20] Another T1210 refactoring --- monkey/common/utils/attack_utils.py | 8 ++++ monkey/infection_monkey/exploit/__init__.py | 31 +++++++-------- .../infection_monkey/exploit/elasticgroovy.py | 1 - monkey/infection_monkey/exploit/hadoop.py | 1 - monkey/infection_monkey/exploit/mssqlexec.py | 1 + monkey/infection_monkey/exploit/rdpgrinder.py | 2 +- monkey/infection_monkey/exploit/sambacry.py | 2 +- monkey/infection_monkey/exploit/shellshock.py | 1 - monkey/infection_monkey/exploit/smbexec.py | 8 ++-- monkey/infection_monkey/exploit/sshexec.py | 4 +- monkey/infection_monkey/exploit/struts2.py | 1 - monkey/infection_monkey/exploit/web_rce.py | 4 +- monkey/infection_monkey/exploit/weblogic.py | 1 - monkey/infection_monkey/exploit/wmiexec.py | 2 +- monkey/infection_monkey/monkey.py | 4 +- monkey/infection_monkey/network/__init__.py | 31 +++++++-------- .../infection_monkey/network/elasticfinger.py | 4 +- monkey/infection_monkey/network/httpfinger.py | 4 +- .../network/mssql_fingerprint.py | 4 +- .../infection_monkey/network/mysqlfinger.py | 4 +- .../network/network_scanner.py | 3 -- .../infection_monkey/network/ping_scanner.py | 3 ++ monkey/infection_monkey/network/smbfinger.py | 4 +- monkey/infection_monkey/network/sshfinger.py | 5 ++- .../infection_monkey/network/tcp_scanner.py | 4 +- .../transport/attack_telems/base_telem.py | 3 +- monkey/infection_monkey/utils.py | 9 ----- .../attack/technique_reports/T1210.py | 19 +++++----- .../cc/ui/src/components/attack/T1210.js | 38 +++++++++++++------ 29 files changed, 104 insertions(+), 102 deletions(-) diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index b7f3346b3..28feaa537 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -11,3 +11,11 @@ class ScanStatus(Enum): # Dict that describes what BITS job was used for BITS_UPLOAD_STRING = {"usage": "BITS job was used to upload monkey to a remote system."} + + +def format_time(time): + return "%s-%s %s:%s:%s" % (time.date().month, + time.date().day, + time.time().hour, + time.time().minute, + time.time().second) diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 41f82e50e..d5e9fcf3a 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -1,7 +1,6 @@ -from abc import ABCMeta, abstractmethod +from abc import ABCMeta, abstractmethod, abstractproperty import infection_monkey.config from common.utils.exploit_enum import ExploitType -from infection_monkey.utils import get_current_time_string __author__ = 'itamar' @@ -13,11 +12,16 @@ class HostExploiter(object): # Usual values are 'vulnerability' or 'brute_force' EXPLOIT_TYPE = ExploitType.VULNERABILITY - _EXPLOITED_SERVICE = '' + + @abstractproperty + def _EXPLOITED_SERVICE(self): + pass def __init__(self, host): self._config = infection_monkey.config.WormConfiguration - self._exploit_info = {} + self._exploit_info = {'display_name': self._EXPLOITED_SERVICE, + 'vulnerable_urls': [], + 'vulnerable_ports': []} self._exploit_attempts = [] self.host = host @@ -39,20 +43,11 @@ class HostExploiter(object): def exploit_host(self): raise NotImplementedError() - def add_vuln_service_info(self, port=None, url=None): - if port: - service_endpoint = port - elif url: - service_endpoint = url - else: - raise NotImplementedError("You must pass either port or url to add a vulnerable service info.") - if not self._EXPLOITED_SERVICE: - raise NotImplementedError("You must override _EXPLOITED_SERVICE to name a service this exploiter " - "is targeting") - self._exploit_info['exploited_service'] = {'name': self._EXPLOITED_SERVICE, - 'endpoint': service_endpoint, - 'time': get_current_time_string()} - return + def add_vuln_url(self, url): + self._exploit_info['vulnerable_urls'].append(url) + + def add_vuln_port(self, port): + self._exploit_info['vulnerable_ports'].append(port) from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py index b40d01f0a..3a129ebc0 100644 --- a/monkey/infection_monkey/exploit/elasticgroovy.py +++ b/monkey/infection_monkey/exploit/elasticgroovy.py @@ -59,7 +59,6 @@ class ElasticGroovyExploiter(WebRCE): result = self.get_results(response) if not result: return False - self.add_vuln_service_info(url=url) return result[0] def upload_monkey(self, url, commands=None): diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index ef2fa506e..f02c4f3d3 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -49,7 +49,6 @@ class HadoopExploiter(WebRCE): return False http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() - self.add_vuln_service_info(url=self.vulnerable_urls[0]) return True def exploit(self, url, command): diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 2e8bf6c90..d2d41a336 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -13,6 +13,7 @@ LOG = logging.getLogger(__name__) class MSSQLExploiter(HostExploiter): + _EXPLOITED_SERVICE = 'MSSQL' _TARGET_OS_TYPE = ['windows'] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE LOGIN_TIMEOUT = 15 diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index c7b3e1c71..2c94dcafa 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -317,7 +317,7 @@ class RdpExploiter(HostExploiter): if client_factory.success: if not self._config.rdp_use_vbs_download: VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING) - self.add_vuln_service_info(port=RDP_PORT) + self.add_vuln_port(RDP_PORT) exploited = True self.report_login_attempt(True, user, password) break diff --git a/monkey/infection_monkey/exploit/sambacry.py b/monkey/infection_monkey/exploit/sambacry.py index 143c15c83..7c4d7790a 100644 --- a/monkey/infection_monkey/exploit/sambacry.py +++ b/monkey/infection_monkey/exploit/sambacry.py @@ -89,7 +89,7 @@ class SambaCryExploiter(HostExploiter): LOG.info( "Shares triggered successfully on host %s: %s" % ( self.host.ip_addr, str(successfully_triggered_shares))) - self.add_vuln_service_info(port='139 or 445') + self.add_vuln_port(str(writable_shares_creds_dict)) return True else: LOG.info("No shares triggered successfully on host %s" % self.host.ip_addr) diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index 698a3fa25..2f6e3516f 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -144,7 +144,6 @@ class ShellShockExploiter(HostExploiter): if not (self.check_remote_file_exists(url, header, exploit, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") continue - self.add_vuln_service_info(url=url) return True return False diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 7edd4b528..1b4071312 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -69,8 +69,8 @@ class SmbExploiter(HostExploiter): LOG.debug("Successfully logged in %r using SMB (%s : %s : %s : %s)", self.host, user, password, lm_hash, ntlm_hash) self.report_login_attempt(True, user, password, lm_hash, ntlm_hash) - self.add_vuln_service_info(port=("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], - SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1]))) + self.add_vuln_port("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], + SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])) exploited = True break else: @@ -140,6 +140,6 @@ class SmbExploiter(HostExploiter): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", remote_full_path, self.host, cmdline) - self.add_vuln_service_info(port=("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], - SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1]))) + self.add_vuln_port("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], + SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])) return True diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 1fd954fec..09982876d 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -82,11 +82,11 @@ class SSHExploiter(HostExploiter): LOG.debug("Successfully logged in %r using SSH (%s : %s)", self.host, user, curpass) exploited = True - self.add_vuln_service_info(port=port) + self.add_vuln_port(port) self.report_login_attempt(True, user, curpass) break - except paramiko.AuthenticationException as exc: + except Exception as exc: LOG.debug("Error logging into victim %r with user" " %s and password '%s': (%s)", self.host, user, curpass, exc) diff --git a/monkey/infection_monkey/exploit/struts2.py b/monkey/infection_monkey/exploit/struts2.py index f1bce7d3b..042a38580 100644 --- a/monkey/infection_monkey/exploit/struts2.py +++ b/monkey/infection_monkey/exploit/struts2.py @@ -92,5 +92,4 @@ class Struts2Exploiter(WebRCE): except httplib.IncompleteRead as e: page = e.partial - self.add_vuln_service_info(url=url) return page diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 912d7c108..f360cfd16 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -209,13 +209,11 @@ class WebRCE(HostExploiter): """ for url in urls: if self.check_if_exploitable(url): - self.vulnerable_urls.append(url) + self.add_vuln_url(url) if stop_checking: break if not self.vulnerable_urls: LOG.info("No vulnerable urls found, skipping.") - # We add urls to param used in reporting - self._exploit_info['vulnerable_urls'] = self.vulnerable_urls def get_host_arch(self, url): """ diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index 584883216..bbf0f2b60 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -69,7 +69,6 @@ class WebLogicExploiter(WebRCE): print('[!] Connection Error') print(e) - self.add_vuln_service_info(url=url) return True def add_vulnerable_urls(self, urls, stop_checking=False): diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index 2db2321e0..29bc08981 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -105,7 +105,7 @@ class WmiExploiter(HostExploiter): LOG.info("Executed dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)", remote_full_path, self.host, result.ProcessId, result.ReturnValue, cmdline) - self.add_vuln_service_info(port='unknown') + self.add_vuln_port(port='unknown') success = True else: LOG.debug("Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)", diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 2ec3e5d1f..6be73d41b 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -17,8 +17,6 @@ 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 common.utils.attack_utils import ScanStatus -from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem __author__ = 'itamar' @@ -155,7 +153,7 @@ class InfectionMonkey(object): finger.get_host_fingerprint(machine) ControlClient.send_telemetry('scan', {'machine': machine.as_dict(), - }) + 'service_count': len(machine.services)}) # skip machines that we've already exploited if machine in self._exploited_machines: diff --git a/monkey/infection_monkey/network/__init__.py b/monkey/infection_monkey/network/__init__.py index b298be0c7..b47d4ebca 100644 --- a/monkey/infection_monkey/network/__init__.py +++ b/monkey/infection_monkey/network/__init__.py @@ -1,5 +1,4 @@ -from abc import ABCMeta, abstractmethod -from infection_monkey.utils import get_current_time_string +from abc import ABCMeta, abstractmethod, abstractproperty __author__ = 'itamar' @@ -15,21 +14,23 @@ class HostScanner(object): class HostFinger(object): __metaclass__ = ABCMeta - _SCANNED_SERVICE = '' + @abstractproperty + def _SCANNED_SERVICE(self): + pass - def format_service_info(self, port=None, url=None): - if port: - service_endpoint = port - elif url: - service_endpoint = url + def init_service(self, services, service_key): + services[service_key] = {} + services[service_key]['display_name'] = self._SCANNED_SERVICE + + def add_found_port(self, services, port, key=None): + if key: + services[key]['port'] = port else: - raise NotImplementedError("You must pass either port or url to get formatted service info.") - if not self._SCANNED_SERVICE: - raise NotImplementedError("You must override _SCANNED_SERVICE property" - " to name what service is being scanned.") - return {'display_name': self._SCANNED_SERVICE, - 'endpoint': service_endpoint, - 'time': get_current_time_string()} + for service in services: + if services[service]['display_name'] == self._SCANNED_SERVICE: + service[service]['port'] = port + return + raise KeyError @abstractmethod def get_host_fingerprint(self, host): diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index c8b720722..710479cb8 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -36,11 +36,11 @@ class ElasticFinger(HostFinger): url = 'http://%s:%s/' % (host.ip_addr, ES_PORT) with closing(requests.get(url, timeout=ES_HTTP_TIMEOUT)) as req: data = json.loads(req.text) - host.services[ES_SERVICE] = {} + self.init_service(host.services, ES_SERVICE) host.services[ES_SERVICE]['cluster_name'] = data['cluster_name'] host.services[ES_SERVICE]['name'] = data['name'] host.services[ES_SERVICE]['version'] = data['version']['number'] - host.services[ES_SERVICE].update(self.format_service_info(url=url)) + self.add_found_port(host.services, ES_PORT) return True except Timeout: LOG.debug("Got timeout while trying to read header information") diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index 55bdcb1f1..1bcc70fb5 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -37,11 +37,11 @@ class HTTPFinger(HostFinger): with closing(head(url, verify=False, timeout=1)) as req: server = req.headers.get('Server') ssl = True if 'https://' in url else False - host.services['tcp-' + port[1]] = {} + self.init_service(host.services, ('tcp-' + port[1])) host.services['tcp-' + port[1]]['name'] = 'http' + self.add_found_port(host.services, port[0], ('tcp-' + port[1])) host.services['tcp-' + port[1]]['data'] = (server,ssl) LOG.info("Port %d is open on host %s " % (port[0], host)) - host.services['tcp-' + port[1]].update(self.format_service_info(port=port[0])) break # https will be the same on the same port except Timeout: pass diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index dba614c85..f99f92f5c 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -63,7 +63,7 @@ class MSSQLFinger(HostFinger): sock.close() return False - host.services[self._SCANNED_SERVICE] = {} + self.init_service(host.services, self._SCANNED_SERVICE) # Loop through the server data instances_list = data[3:].decode().split(';;') @@ -76,7 +76,7 @@ class MSSQLFinger(HostFinger): # Each instance's info is nested under its own name, if there are multiple instances # each will appear under its own name host.services[self._SCANNED_SERVICE][instance_info[1]][instance_info[i - 1]] = instance_info[i] - host.services[self._SCANNED_SERVICE].update(self.format_service_info(port=MSSQLFinger.SQL_BROWSER_DEFAULT_PORT)) + self.add_found_port(host.services, MSSQLFinger.SQL_BROWSER_DEFAULT_PORT) # Close the socket sock.close() diff --git a/monkey/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py index b1b1a6164..94077f684 100644 --- a/monkey/infection_monkey/network/mysqlfinger.py +++ b/monkey/infection_monkey/network/mysqlfinger.py @@ -51,14 +51,14 @@ class MySQLFinger(HostFinger): version, curpos = struct_unpack_tracker_string(data, curpos) # special coded to solve string parsing version = version[0] - host.services[SQL_SERVICE] = {} + self.init_service(host.services, SQL_SERVICE) host.services[SQL_SERVICE]['version'] = version version = version.split('-')[0].split('.') host.services[SQL_SERVICE]['major_version'] = version[0] host.services[SQL_SERVICE]['minor_version'] = version[1] host.services[SQL_SERVICE]['build_version'] = version[2] thread_id, curpos = struct_unpack_tracker(data, curpos, "{(val.vulnerable_urls.length !== 0 ? val.vulnerable_urls[0] : val.vulnerable_ports[0])} + ) +}; + let formatScanned = function (data){ let result = []; for(let service in data.machine.services){ let scanned_service = {'machine': data.machine, - 'service': {'endpoint': data.machine.services[service].endpoint, - 'name': data.machine.services[service].display_name, - 'time': data.machine.services[service].time}}; + 'time': data.time, + 'service': {'port': [data.machine.services[service].port], + 'display_name': data.machine.services[service].display_name}}; result.push(scanned_service) } return result }; -const columns = [ +const scanColumns = [ { columns: [ {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), style: { 'whiteSpace': 'unset' }, width: 200}, - {Header: 'Time', id: 'time', accessor: x => x.service.time, style: { 'whiteSpace': 'unset' }, width: 170}, - {Header: 'Port/url', id: 'port', accessor: x =>x.service.endpoint, style: { 'whiteSpace': 'unset' }}, - {Header: 'Service', id: 'service', accessor: x => x.service.name, style: { 'whiteSpace': 'unset' }} + {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, + {Header: 'Port', id: 'port', accessor: x =>x.service.port, style: { 'whiteSpace': 'unset' }}, + {Header: 'Service', id: 'service', accessor: x => x.service.display_name, style: { 'whiteSpace': 'unset' }} + ] + } +]; + +const exploitColumns = [ + { + columns: [ + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), + style: { 'whiteSpace': 'unset' }, width: 200}, + {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, + {Header: 'Port/url', id: 'port', accessor: x =>renderEndpoint(x.service), style: { 'whiteSpace': 'unset' }}, + {Header: 'Service', id: 'service', accessor: x => x.service.display_name, style: { 'whiteSpace': 'unset' }} ] } ]; @@ -46,7 +64,7 @@ class T1210 extends React.Component {
    Found services:
    Exploited services:
    {this.props.data.message}
    From 7f5c07c1fd4e9b5eab7cd13e828efcab9fa5b4e2 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 6 May 2019 14:57:17 +0300 Subject: [PATCH 15/20] Refactored fingerprint scanners to add port on init --- monkey/common/utils/code_utils.py | 10 +++ monkey/infection_monkey/exploit/sambacry.py | 5 +- monkey/infection_monkey/network/__init__.py | 13 +--- .../infection_monkey/network/elasticfinger.py | 3 +- monkey/infection_monkey/network/httpfinger.py | 3 +- .../network/mssql_fingerprint.py | 3 +- .../infection_monkey/network/mysqlfinger.py | 3 +- monkey/infection_monkey/network/smbfinger.py | 3 +- monkey/infection_monkey/network/sshfinger.py | 3 +- .../infection_monkey/network/tcp_scanner.py | 5 +- monkey/monkey_island/cc/resources/root.py | 4 +- .../cc/services/attack/attack_config.py | 6 +- .../cc/services/attack/attack_report.py | 14 ++-- .../attack/technique_reports/T1197.py | 38 +++++------ .../attack/technique_reports/T1210.py | 67 +++++++++++-------- .../attack/technique_reports/__init__.py | 64 +++++++++++++++++- .../technique_reports/technique_service.py | 37 ---------- .../cc/ui/src/components/Main.js | 12 ---- .../cc/ui/src/components/attack/T1197.js | 46 ------------- .../src/components/attack/techniques/T1197.js | 49 ++++++++++++++ .../attack/{ => techniques}/T1210.js | 2 +- .../cc/ui/src/components/pages/ReportPage.js | 17 +++++ .../AttackReport.js} | 13 +--- 23 files changed, 224 insertions(+), 196 deletions(-) create mode 100644 monkey/common/utils/code_utils.py delete mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/technique_service.py delete mode 100644 monkey/monkey_island/cc/ui/src/components/attack/T1197.js create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js rename monkey/monkey_island/cc/ui/src/components/attack/{ => techniques}/T1210.js (98%) rename monkey/monkey_island/cc/ui/src/components/{pages/AttackReportPage.js => report-components/AttackReport.js} (94%) diff --git a/monkey/common/utils/code_utils.py b/monkey/common/utils/code_utils.py new file mode 100644 index 000000000..d6d407706 --- /dev/null +++ b/monkey/common/utils/code_utils.py @@ -0,0 +1,10 @@ + + +# abstract, static method decorator +class abstractstatic(staticmethod): + __slots__ = () + + def __init__(self, function): + super(abstractstatic, self).__init__(function) + function.__isabstractmethod__ = True + __isabstractmethod__ = True diff --git a/monkey/infection_monkey/exploit/sambacry.py b/monkey/infection_monkey/exploit/sambacry.py index 7c4d7790a..4abdf8f33 100644 --- a/monkey/infection_monkey/exploit/sambacry.py +++ b/monkey/infection_monkey/exploit/sambacry.py @@ -89,7 +89,10 @@ class SambaCryExploiter(HostExploiter): LOG.info( "Shares triggered successfully on host %s: %s" % ( self.host.ip_addr, str(successfully_triggered_shares))) - self.add_vuln_port(str(writable_shares_creds_dict)) + # TODO: add vulnerable url + #for share, fullpath in successfully_triggered_shares: + # self.add_vuln_url("smb://@:/" % False, + # self.host.ip_addr, False, share) return True else: LOG.info("No shares triggered successfully on host %s" % self.host.ip_addr) diff --git a/monkey/infection_monkey/network/__init__.py b/monkey/infection_monkey/network/__init__.py index b47d4ebca..59a6d01d6 100644 --- a/monkey/infection_monkey/network/__init__.py +++ b/monkey/infection_monkey/network/__init__.py @@ -18,19 +18,10 @@ class HostFinger(object): def _SCANNED_SERVICE(self): pass - def init_service(self, services, service_key): + def init_service(self, services, service_key, port): services[service_key] = {} services[service_key]['display_name'] = self._SCANNED_SERVICE - - def add_found_port(self, services, port, key=None): - if key: - services[key]['port'] = port - else: - for service in services: - if services[service]['display_name'] == self._SCANNED_SERVICE: - service[service]['port'] = port - return - raise KeyError + services[service_key]['port'] = port @abstractmethod def get_host_fingerprint(self, host): diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index 710479cb8..31ce6e24a 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -36,11 +36,10 @@ class ElasticFinger(HostFinger): url = 'http://%s:%s/' % (host.ip_addr, ES_PORT) with closing(requests.get(url, timeout=ES_HTTP_TIMEOUT)) as req: data = json.loads(req.text) - self.init_service(host.services, ES_SERVICE) + self.init_service(host.services, ES_SERVICE, ES_PORT) host.services[ES_SERVICE]['cluster_name'] = data['cluster_name'] host.services[ES_SERVICE]['name'] = data['name'] host.services[ES_SERVICE]['version'] = data['version']['number'] - self.add_found_port(host.services, ES_PORT) return True except Timeout: LOG.debug("Got timeout while trying to read header information") diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index 1bcc70fb5..30292d99f 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -37,9 +37,8 @@ class HTTPFinger(HostFinger): with closing(head(url, verify=False, timeout=1)) as req: server = req.headers.get('Server') ssl = True if 'https://' in url else False - self.init_service(host.services, ('tcp-' + port[1])) + self.init_service(host.services, ('tcp-' + port[1]), port[0]) host.services['tcp-' + port[1]]['name'] = 'http' - self.add_found_port(host.services, port[0], ('tcp-' + port[1])) host.services['tcp-' + port[1]]['data'] = (server,ssl) LOG.info("Port %d is open on host %s " % (port[0], host)) break # https will be the same on the same port diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index f99f92f5c..7b666bf9f 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -63,7 +63,7 @@ class MSSQLFinger(HostFinger): sock.close() return False - self.init_service(host.services, self._SCANNED_SERVICE) + self.init_service(host.services, self._SCANNED_SERVICE, MSSQLFinger.SQL_BROWSER_DEFAULT_PORT) # Loop through the server data instances_list = data[3:].decode().split(';;') @@ -76,7 +76,6 @@ class MSSQLFinger(HostFinger): # Each instance's info is nested under its own name, if there are multiple instances # each will appear under its own name host.services[self._SCANNED_SERVICE][instance_info[1]][instance_info[i - 1]] = instance_info[i] - self.add_found_port(host.services, MSSQLFinger.SQL_BROWSER_DEFAULT_PORT) # Close the socket sock.close() diff --git a/monkey/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py index 94077f684..123f0ae47 100644 --- a/monkey/infection_monkey/network/mysqlfinger.py +++ b/monkey/infection_monkey/network/mysqlfinger.py @@ -51,14 +51,13 @@ class MySQLFinger(HostFinger): version, curpos = struct_unpack_tracker_string(data, curpos) # special coded to solve string parsing version = version[0] - self.init_service(host.services, SQL_SERVICE) + self.init_service(host.services, SQL_SERVICE, MYSQL_PORT) host.services[SQL_SERVICE]['version'] = version version = version.split('-')[0].split('.') host.services[SQL_SERVICE]['major_version'] = version[0] host.services[SQL_SERVICE]['minor_version'] = version[1] host.services[SQL_SERVICE]['build_version'] = version[2] thread_id, curpos = struct_unpack_tracker(data, curpos, "
  • -
  • - - 5. - ATT&CK report - {this.state.completedSteps.attack_report_done ? - - : ''} - -
  • @@ -196,7 +185,6 @@ class AppComponent extends AuthComponent { {this.renderRoute('/infection/telemetry', )} {this.renderRoute('/start-over', )} {this.renderRoute('/report', )} - {this.renderRoute('/attack_report', )} {this.renderRoute('/license', )} diff --git a/monkey/monkey_island/cc/ui/src/components/attack/T1197.js b/monkey/monkey_island/cc/ui/src/components/attack/T1197.js deleted file mode 100644 index 3b0e09e7c..000000000 --- a/monkey/monkey_island/cc/ui/src/components/attack/T1197.js +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import '../../styles/Collapse.scss' -import ReactTable from "react-table"; - -let renderMachine = function (val) { - return ( - {val.ip_addr} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} - ) -}; - -const columns = [ - { - columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x), style: { 'whiteSpace': 'unset' }, width: 200}, - {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, - {Header: 'Usage', id: 'usage', accessor: x => x.usage, style: { 'whiteSpace': 'unset' }} - ] - } -]; - -class T1210 extends React.Component { - - constructor(props) { - super(props); - } - - render() { - return ( -
    -
    -
    {this.props.data.message}
    - {this.props.data.bits_jobs.length > 0 ?
    BITS jobs were used in these machines:
    : ''} -
    -
    - -
    - ); - } -} - -export default T1210; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js new file mode 100644 index 000000000..b243eb644 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js @@ -0,0 +1,49 @@ +import React from 'react'; +import '../../../styles/Collapse.scss' +import ReactTable from "react-table"; + + +class T1210 extends React.Component { + + constructor(props) { + super(props); + this.columns = [ {Header: 'Machine', + id: 'machine', accessor: x => T1210.renderMachine(x), + style: { 'whiteSpace': 'unset' }, + width: 200}, + {Header: 'Time', + id: 'time', accessor: x => x.time, + style: { 'whiteSpace': 'unset' }, + width: 170}, + {Header: 'Usage', + id: 'usage', accessor: x => x.usage, + style: { 'whiteSpace': 'unset' }} + ] + } + + static renderMachine = (val) => { + return ( + {val.ip_addr} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} + ) + }; + + render() { + return ( +
    +
    +
    {this.props.data.message}
    + {this.props.data.bits_jobs.length > 0 ?
    BITS jobs were used in these machines:
    : ''} +
    +
    + +
    + ); + } +} + +export default T1210; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js similarity index 98% rename from monkey/monkey_island/cc/ui/src/components/attack/T1210.js rename to monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js index 2fd5b33e0..acc5af02b 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/T1210.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js @@ -1,5 +1,5 @@ import React from 'react'; -import '../../styles/Collapse.scss' +import '../../../styles/Collapse.scss' import {Link} from "react-router-dom"; import ReactTable from "react-table"; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 43be6367c..125efd264 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -11,6 +11,7 @@ import {Line} from 'rc-progress'; import AuthComponent from '../AuthComponent'; import PassTheHashMapPageComponent from "./PassTheHashMapPage"; import StrongUsers from "components/report-components/StrongUsers"; +import AttackReport from "components/report-components/AttackReport"; let guardicoreLogoImage = require('../../images/guardicore-logo.png'); let monkeyLogoImage = require('../../images/monkey-icon.svg'); @@ -140,6 +141,7 @@ class ReportPageComponent extends AuthComponent { {this.generateReportFindingsSection()} {this.generateReportRecommendationsSection()} {this.generateReportGlanceSection()} + {this.generateAttackSection()} {this.generateReportFooter()}
    @@ -503,6 +505,21 @@ class ReportPageComponent extends AuthComponent { ); } + generateAttackSection() { + return (
    +

    + ATT&CK report +

    +

    + This report shows information about ATT&CK techniques used by Infection Monkey. +

    +
    + +
    +
    +
    ) + } + generateReportFooter() { return (