T1210 implemented, T1197 started

This commit is contained in:
VakarisZ 2019-04-09 10:59:53 +03:00
parent c3d717a6bf
commit 993736a973
19 changed files with 232 additions and 68 deletions

View File

@ -8,3 +8,6 @@ class ScanStatus(Enum):
SCANNED = 1 SCANNED = 1
# Technique was attempted and succeeded # Technique was attempted and succeeded
USED = 2 USED = 2
BITS_UPLOAD_STRING = "Bits job was used to upload monkey to a remote system."

View File

@ -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,\ from infection_monkey.model import WGET_HTTP_UPLOAD, RDP_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX,\
DOWNLOAD_TIMEOUT DOWNLOAD_TIMEOUT
from infection_monkey.network.elasticfinger import ES_PORT, ES_SERVICE 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 import re
@ -58,6 +60,12 @@ class ElasticGroovyExploiter(WebRCE):
return False return False
return result[0] 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): def get_results(self, response):
""" """
Extracts the result data from our attack Extracts the result data from our attack

View File

@ -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.exploit.tools import build_monkey_commandline
from infection_monkey.utils import utf_to_ascii from infection_monkey.utils import utf_to_ascii
from common.utils.exploit_enum import ExploitType 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' __author__ = 'hoffer'
@ -312,6 +314,7 @@ class RdpExploiter(HostExploiter):
client_factory.done_event.wait() client_factory.done_event.wait()
if client_factory.success: if client_factory.success:
VictimHostTelem("T1197", ScanStatus.USED.value, self.host, BITS_UPLOAD_STRING)
exploited = True exploited = True
self.report_login_attempt(True, user, password) self.report_login_attempt(True, user, password)
break break

View File

@ -7,6 +7,8 @@ from infection_monkey.exploit import HostExploiter
from infection_monkey.model import * from infection_monkey.model import *
from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth, build_monkey_commandline, HTTPTools 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.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' __author__ = 'VakarisZ'
@ -307,6 +309,7 @@ class WebRCE(HostExploiter):
if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp: if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp:
LOG.info("Powershell not found in host. Using bitsadmin to download.") LOG.info("Powershell not found in host. Using bitsadmin to download.")
backup_command = RDP_CMDLINE_HTTP % {'monkey_path': dest_path, 'http_path': http_path} 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) resp = self.exploit(url, backup_command)
return resp return resp

View File

@ -17,8 +17,6 @@ from infection_monkey.system_info import SystemInfoCollector
from infection_monkey.system_singleton import SystemSingleton from infection_monkey.system_singleton import SystemSingleton
from infection_monkey.windows_upgrader import WindowsUpgrader from infection_monkey.windows_upgrader import WindowsUpgrader
from infection_monkey.post_breach.post_breach_handler import PostBreach 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' __author__ = 'itamar'
@ -181,11 +179,9 @@ class InfectionMonkey(object):
for exploiter in [exploiter(machine) for exploiter in self._exploiters]: for exploiter in [exploiter(machine) for exploiter in self._exploiters]:
if self.try_exploiting(machine, exploiter): if self.try_exploiting(machine, exploiter):
host_exploited = True host_exploited = True
VictimHostTelem('T1210', ScanStatus.USED.value, machine=machine).send()
break break
if not host_exploited: if not host_exploited:
self._fail_exploitation_machines.add(machine) self._fail_exploitation_machines.add(machine)
VictimHostTelem('T1210', ScanStatus.SCANNED.value, machine=machine).send()
if not self._keep_running: if not self._keep_running:
break break

View File

@ -3,11 +3,11 @@ from flask import jsonify
from cc.auth import jwt_required from cc.auth import jwt_required
from cc.services.attack.attack_report import AttackReportService from cc.services.attack.attack_report import AttackReportService
__author__ = "itay.mizeretz" __author__ = "VakarisZ"
class AttackReport(flask_restful.Resource): class AttackReport(flask_restful.Resource):
@jwt_required() @jwt_required()
def get(self): def get(self):
return jsonify(AttackReportService.get_report()) return jsonify(AttackReportService.get_latest_report()['techniques'])

View File

@ -69,10 +69,12 @@ class Root(flask_restful.Resource):
infection_done = NodeService.is_monkey_finished_running() infection_done = NodeService.is_monkey_finished_running()
if not infection_done: if not infection_done:
report_done = False report_done = False
attack_report_done = False
else: else:
if is_any_exists: if is_any_exists:
ReportService.get_report() ReportService.get_report()
AttackReportService.get_report() AttackReportService.get_latest_report()
report_done = ReportService.is_report_generated() report_done = ReportService.is_report_generated()
attack_report_done = AttackReportService.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)

View File

@ -1,37 +1,58 @@
import logging import logging
from cc.services.attack.technique_reports import T1210 from cc.services.attack.technique_reports import T1210
from cc.services.attack.attack_telem import get_latest_telem 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 cc.database import mongo
__author__ = "VakarisZ" __author__ = "VakarisZ"
logger = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
TECHNIQUES = {'T1210': T1210} TECHNIQUES = {'T1210': T1210}
REPORT_NAME = 'new_report'
class AttackReportService: class AttackReportService:
def __init__(self): def __init__(self):
pass pass
@staticmethod @staticmethod
def generate_new_report(): def generate_new_report():
report = {'techniques': {}, 'meta': {get_latest_telem()}} """
for tech_id, value in Generates new report based on telemetries, replaces old report in db with new one.
report.update({'T1210': T1210.get_report_data()}) :return: Report object
report.update({''}) """
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 return report
@staticmethod @staticmethod
def get_latest_report(): 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(): if AttackReportService.is_report_generated():
telem_time = get_latest_telem_time() telem_time = get_latest_telem()
lates_report = mongo.db.attack_report.find_one({'name': 'new_report'}) latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME})
if telem_time == lates_report['telem_time']: if telem_time and telem_time['timestamp'] == latest_report['meta']['timestamp']:
return lates_report return latest_report
return AttackReportService.generate_new_report() return AttackReportService.generate_new_report()
@staticmethod @staticmethod
def is_report_generated(): def is_report_generated():
"""
Checks if report is generated
:return: True if report exists, False otherwise
"""
generated_report = mongo.db.attack_report.find_one({}) generated_report = mongo.db.attack_report.find_one({})
return generated_report is not None return generated_report is not None

View File

@ -22,4 +22,4 @@ def set_results(technique, data):
def get_latest_telem(): def get_latest_telem():
return mongo.db.attack_results.find({'name': 'latest'}) return mongo.db.attack_results.find_one({'name': 'latest'})

View File

@ -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

View File

@ -1,27 +1,18 @@
from monkey_island.cc.services.attack.technique_reports.technique_service import technique_status, technique_title from monkey_island.cc.services.attack.technique_reports.technique_service import *
from common.utils.attack_status_enum import ScanStatus
from cc.services.report import ReportService from cc.services.report import ReportService
__author__ = "VakarisZ" __author__ = "VakarisZ"
TECHNIQUE = "T1210" TECHNIQUE = "T1210"
UNSCANNED_MSG = "Monkey didn't scan any remote services. Maybe it didn't find any machines on the network?" MESSAGES = {
SCANNED_MSG = "Monkey scanned for remote services on the network, but couldn't exploit any of them." 'unscanned': "Monkey didn't scan any remote services. Maybe it didn't find any machines on the network?",
USED_MSG = "Monkey scanned for remote services and exploited some 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(): def get_report_data():
data = {} data = get_tech_base_data(TECHNIQUE, MESSAGES)
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({'scanned_machines': ReportService.get_scanned()})
data.update({'exploited_machines': ReportService.get_exploited()}) data.update({'exploited_machines': ReportService.get_exploited()})
return data return data

View File

@ -1,5 +1,5 @@
from cc.database import mongo 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 from cc.services.attack.attack_config import get_technique
__author__ = "VakarisZ" __author__ = "VakarisZ"
@ -16,3 +16,17 @@ def technique_status(technique):
def technique_title(technique): def technique_title(technique):
return get_technique(technique)['title'] 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

View File

@ -0,0 +1,63 @@
import React from 'react';
import '../../styles/Collapse.scss'
import {Link} from "react-router-dom";
let renderArray = function(val) {
return <span>{val.map(x => <span key={x.toString()}>{x} </span>)}</span>;
};
let renderMachine = function (val, index, exploited=false) {
return (
<div key={index}>
{renderArray(val.ip_addresses)}
{(val.domain_name ? " (".concat(val.domain_name, ")") : " (".concat(val.label, ")"))} :
{exploited ? renderArray(val.exploits) : renderArray(val.services)}
</div>
)
};
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 <div>{content}</div>;
};
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 <div>{content}</div>;
};
constructor(props) {
super(props);
}
render() {
console.log(this.props);
return (
<div>
<div>{this.props.data.message}</div>
<div>Found services: </div>
{this.renderScannedMachines(this.props.data.scanned_machines)}
<div>Successful exploiters:</div>
{this.renderExploitedMachines(this.props.data.exploited_machines)}
<div className="attack-report footer-text">
To get more info about scanned and exploited machines view <Link to="/report">standard report.</Link>
</div>
</div>
);
}
}
export default T1210;

View File

@ -1,30 +1,61 @@
import React from 'react'; import React from 'react';
import ReactTable from 'react-table'
import '../../styles/Collapse.scss' import '../../styles/Collapse.scss'
import Collapse from '@kunukn/react-collapse'; import {Link} from "react-router-dom";
let renderArray = function(val) { let renderArray = function(val) {
return <div>{val.map(x => <div>{x}</div>)}</div>; return <span>{val.map(x => <span key={x.toString()}>{x} </span>)}</span>;
}; };
let renderIpAddresses = function (val) {
return <div>{renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} </div>; let renderMachine = function (val, index, exploited=false) {
return (
<div key={index}>
{renderArray(val.ip_addresses)}
{(val.domain_name ? " (".concat(val.domain_name, ")") : " (".concat(val.label, ")"))} :
{exploited ? renderArray(val.exploits) : renderArray(val.services)}
</div>
)
}; };
const columns = [
];
const pageSize = 10;
class T1210 extends React.Component { 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 <div>{content}</div>;
};
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 <div>{content}</div>;
};
constructor(props) { constructor(props) {
super(props); super(props);
} }
render() { render() {
console.log(this.props);
return ( return (
<Collapse isOpen={true || false}> <div>
<div>{this.props.data.message}</div> <div>{this.props.data.message}</div>
</Collapse> <div>Found services: </div>
{this.renderScannedMachines(this.props.data.scanned_machines)}
<div>Successful exploiters:</div>
{this.renderExploitedMachines(this.props.data.exploited_machines)}
<div className="attack-report footer-text">
To get more info about scanned and exploited machines view <Link to="/report">standard report.</Link>
</div>
</div>
); );
} }
} }

View File

@ -18,7 +18,7 @@ class AttackReportPageComponent extends AuthComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
report: {}, report: false,
allMonkeysAreDead: false, allMonkeysAreDead: false,
runStarted: true, runStarted: true,
index: 1 index: 1
@ -105,27 +105,8 @@ class AttackReportPageComponent extends AuthComponent {
Object.keys(this.state.report).forEach((tech_id) => { Object.keys(this.state.report).forEach((tech_id) => {
content = this.getTechniqueCollapse(tech_id) content = this.getTechniqueCollapse(tech_id)
}); });
return <section className="app">{content}</section>
}
render() {
let content;
if (Object.keys(this.state.report).length === 0) {
if (this.state.runStarted) {
content = (<h1>Generating Report...</h1>);
} else {
content =
<p className="alert alert-warning">
<i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
You have to run a monkey before generating a report!
</p>;
}
} else {
content = this.generateReportContent();
}
return ( return (
<Col xs={12} lg={8}> <div>
<h1 className="page-title no-print">5. ATT&CK Report</h1>
<div id="header" className="row justify-content-between attack-legend"> <div id="header" className="row justify-content-between attack-legend">
<Col xs={4}> <Col xs={4}>
<i className="fa fa-circle icon-default"></i> <i className="fa fa-circle icon-default"></i>
@ -140,6 +121,32 @@ class AttackReportPageComponent extends AuthComponent {
<span> - Used</span> <span> - Used</span>
</Col> </Col>
</div> </div>
<section className="app">{content}</section>
</div>
)
}
render() {
let content;
console.log(this.state.report);
if (this.state.report === false){
content = (<h1>Generating Report...</h1>);
} else if (Object.keys(this.state.report).length === 0) {
if (this.state.runStarted) {
content = (<h1>No techniques were scanned</h1>);
} else {
content =
<p className="alert alert-warning">
<i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
You have to run a monkey before generating a report!
</p>;
}
} else {
content = this.generateReportContent();
}
return (
<Col xs={12} lg={8}>
<h1 className="page-title no-print">5. ATT&CK Report</h1>
<div style={{'fontSize': '1.2em'}}> <div style={{'fontSize': '1.2em'}}>
{content} {content}
</div> </div>

View File

@ -541,3 +541,9 @@ body {
text-align: center; text-align: center;
margin-bottom: 20px; margin-bottom: 20px;
} }
.attack-report.footer-text{
text-align: right;
font-size: 0.8em;
margin-top: 20px;
}