Merge pull request #731 from shreyamalviya/modifications-T1156-T1504

Telemetry modifications for "modify shell startup files" PBA
This commit is contained in:
Shreya Malviya 2020-08-02 20:59:32 +05:30 committed by GitHub
commit 7d369f7399
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 220 additions and 32 deletions

View File

@ -1,8 +1,11 @@
import subprocess
from common.data.post_breach_consts import \ from common.data.post_breach_consts import \
POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION
from infection_monkey.post_breach.pba import PBA from infection_monkey.post_breach.pba import PBA
from infection_monkey.post_breach.shell_startup_files.shell_startup_files_modification import \ from infection_monkey.post_breach.shell_startup_files.shell_startup_files_modification import \
get_commands_to_modify_shell_startup_files get_commands_to_modify_shell_startup_files
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
class ModifyShellStartupFiles(PBA): class ModifyShellStartupFiles(PBA):
@ -12,34 +15,48 @@ class ModifyShellStartupFiles(PBA):
and profile.ps1 in windows. and profile.ps1 in windows.
""" """
def __init__(self):
super().__init__(name=POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION)
def run(self): def run(self):
[pba.run() for pba in self.modify_shell_startup_PBA_list()] results = [pba.run() for pba in self.modify_shell_startup_PBA_list()]
PostBreachTelem(self, results).send()
def modify_shell_startup_PBA_list(self): def modify_shell_startup_PBA_list(self):
return ShellStartupPBAGenerator.get_modify_shell_startup_pbas() return self.ShellStartupPBAGenerator().get_modify_shell_startup_pbas()
class ShellStartupPBAGenerator(): class ShellStartupPBAGenerator():
def get_modify_shell_startup_pbas(): def get_modify_shell_startup_pbas(self):
(cmds_for_linux, shell_startup_files_for_linux, usernames_for_linux),\ (cmds_for_linux, shell_startup_files_for_linux, usernames_for_linux),\
(cmds_for_windows, shell_startup_files_per_user_for_windows) = get_commands_to_modify_shell_startup_files() (cmds_for_windows, shell_startup_files_per_user_for_windows) =\
get_commands_to_modify_shell_startup_files()
pbas = [] pbas = []
for startup_file_per_user in shell_startup_files_per_user_for_windows: for startup_file_per_user in shell_startup_files_per_user_for_windows:
windows_cmds = ' '.join(cmds_for_windows).format(startup_file_per_user) windows_cmds = ' '.join(cmds_for_windows).format(startup_file_per_user)
pbas.append(ModifyShellStartupFile(linux_cmds='', windows_cmds=['powershell.exe', windows_cmds])) pbas.append(self.ModifyShellStartupFile(linux_cmds='', windows_cmds=['powershell.exe', windows_cmds]))
for username in usernames_for_linux: for username in usernames_for_linux:
for shell_startup_file in shell_startup_files_for_linux: for shell_startup_file in shell_startup_files_for_linux:
linux_cmds = ' '.join(cmds_for_linux).format(shell_startup_file).format(username) linux_cmds = ' '.join(cmds_for_linux).format(shell_startup_file).format(username)
pbas.append(ModifyShellStartupFile(linux_cmds=linux_cmds, windows_cmds='')) pbas.append(self.ModifyShellStartupFile(linux_cmds=linux_cmds, windows_cmds=''))
return pbas return pbas
class ModifyShellStartupFile(PBA): class ModifyShellStartupFile(PBA):
def __init__(self, linux_cmds, windows_cmds): def __init__(self, linux_cmds, windows_cmds):
super(ModifyShellStartupFile, self).__init__(name=POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION, super().__init__(name=POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION,
linux_cmd=linux_cmds, linux_cmd=linux_cmds,
windows_cmd=windows_cmds) windows_cmd=windows_cmds)
def run(self):
if self.command:
try:
output = subprocess.check_output(self.command, # noqa: DUO116
stderr=subprocess.STDOUT,
shell=True).decode()
return output, True
except subprocess.CalledProcessError as e:
# Return error output of the command
return e.output.decode(), False

View File

@ -13,8 +13,6 @@ LOG = logging.getLogger(__name__)
__author__ = 'VakarisZ' __author__ = 'VakarisZ'
EXECUTION_WITHOUT_OUTPUT = "(PBA execution produced no output)"
class PBA(Plugin): class PBA(Plugin):
""" """
@ -90,8 +88,6 @@ class PBA(Plugin):
""" """
try: try:
output = subprocess.check_output(self.command, stderr=subprocess.STDOUT, shell=True).decode() output = subprocess.check_output(self.command, stderr=subprocess.STDOUT, shell=True).decode()
if not output:
output = EXECUTION_WITHOUT_OUTPUT
return output, True return output, True
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
# Return error output of the command # Return error output of the command

View File

@ -98,9 +98,9 @@ class TelemetryFeed(flask_restful.Resource):
@staticmethod @staticmethod
def get_post_breach_telem_brief(telem): def get_post_breach_telem_brief(telem):
return '%s post breach action executed on %s (%s) machine.' % (telem['data']['name'], return '%s post breach action executed on %s (%s) machine.' % (telem['data'][0]['name'],
telem['data']['hostname'], telem['data'][0]['hostname'],
telem['data']['ip']) telem['data'][0]['ip'])
@staticmethod @staticmethod
def should_show_brief(telem): def should_show_brief(telem):

View File

@ -12,3 +12,15 @@ class T1156(PostBreachTechnique):
scanned_msg = "Monkey tried modifying bash startup files but failed." scanned_msg = "Monkey tried modifying bash startup files but failed."
used_msg = "Monkey successfully modified bash startup files." used_msg = "Monkey successfully modified bash startup files."
pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION]
@staticmethod
def get_pba_query(*args):
return [{'$match': {'telem_category': 'post_breach',
'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION}},
{'$project': {'_id': 0,
'machine': {'hostname': {'$arrayElemAt': ['$data.hostname', 0]},
'ips': [{'$arrayElemAt': ['$data.ip', 0]}]},
'result': '$data.result'}},
{'$unwind': '$result'},
{'$match': {'$or': [{'result': {'$regex': r'\.bash'}},
{'result': {'$regex': r'\.profile'}}]}}]

View File

@ -12,3 +12,14 @@ class T1504(PostBreachTechnique):
scanned_msg = "Monkey tried modifying powershell startup files but failed." scanned_msg = "Monkey tried modifying powershell startup files but failed."
used_msg = "Monkey successfully modified powershell startup files." used_msg = "Monkey successfully modified powershell startup files."
pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION]
@staticmethod
def get_pba_query(*args):
return [{'$match': {'telem_category': 'post_breach',
'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION}},
{'$project': {'_id': 0,
'machine': {'hostname': {'$arrayElemAt': ['$data.hostname', 0]},
'ips': [{'$arrayElemAt': ['$data.ip', 0]}]},
'result': '$data.result'}},
{'$unwind': '$result'},
{'$match': {'result': {'$regex': r'profile\.ps1'}}}]

View File

@ -3,7 +3,6 @@ from typing import List
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.services.attack.attack_config import AttackConfig
from monkey_island.cc.services.attack.technique_reports import AttackTechnique from monkey_island.cc.services.attack.technique_reports import AttackTechnique

View File

@ -1,9 +1,13 @@
import copy
from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.models import Monkey from monkey_island.cc.models import Monkey
from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_user import \ from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_user import \
test_new_user_communication test_new_user_communication
EXECUTION_WITHOUT_OUTPUT = "(PBA execution produced no output)"
def process_communicate_as_new_user_telemetry(telemetry_json): def process_communicate_as_new_user_telemetry(telemetry_json):
current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
@ -18,10 +22,37 @@ POST_BREACH_TELEMETRY_PROCESSING_FUNCS = {
def process_post_breach_telemetry(telemetry_json): def process_post_breach_telemetry(telemetry_json):
mongo.db.monkey.update( def convert_telem_data_to_list(data):
{'guid': telemetry_json['monkey_guid']}, modified_data = [data]
{'$push': {'pba_results': telemetry_json['data']}}) if type(data['result'][0]) is list: # multiple results in one pba
modified_data = separate_results_to_single_pba_telems(data)
return modified_data
def separate_results_to_single_pba_telems(data):
modified_data = []
for result in data['result']:
temp = copy.deepcopy(data)
temp['result'] = result
modified_data.append(temp)
return modified_data
def add_message_for_blank_outputs(data):
if not data['result'][0]:
data['result'][0] = EXECUTION_WITHOUT_OUTPUT
return data
post_breach_action_name = telemetry_json["data"]["name"] post_breach_action_name = telemetry_json["data"]["name"]
if post_breach_action_name in POST_BREACH_TELEMETRY_PROCESSING_FUNCS: if post_breach_action_name in POST_BREACH_TELEMETRY_PROCESSING_FUNCS:
POST_BREACH_TELEMETRY_PROCESSING_FUNCS[post_breach_action_name](telemetry_json) POST_BREACH_TELEMETRY_PROCESSING_FUNCS[post_breach_action_name](telemetry_json)
telemetry_json['data'] = convert_telem_data_to_list(telemetry_json['data'])
for pba_data in telemetry_json['data']:
pba_data = add_message_for_blank_outputs(pba_data)
update_data(telemetry_json, pba_data)
def update_data(telemetry_json, data):
mongo.db.monkey.update(
{'guid': telemetry_json['monkey_guid']},
{'$push': {'pba_results': data}})

View File

@ -0,0 +1,85 @@
from unittest.mock import Mock
import monkey_island.cc.services.telemetry.processing.post_breach as post_breach
from .post_breach import EXECUTION_WITHOUT_OUTPUT
original_telem_multiple_results =\
{
'data': {
'command': 'COMMAND',
'hostname': 'HOST',
'ip': '127.0.1.1',
'name': 'PBA NAME',
'result': [
['SUCCESSFUL', True],
['UNSUCCESFUL', False],
['', True]
]
},
'telem_category': 'post_breach'
}
expected_telem_multiple_results =\
{
'data': [
{
'command': 'COMMAND',
'hostname': 'HOST',
'ip': '127.0.1.1',
'name': 'PBA NAME',
'result': ['SUCCESSFUL', True]
},
{
'command': 'COMMAND',
'hostname': 'HOST',
'ip': '127.0.1.1',
'name': 'PBA NAME',
'result': ['UNSUCCESFUL', False]
},
{
'command': 'COMMAND',
'hostname': 'HOST',
'ip': '127.0.1.1',
'name': 'PBA NAME',
'result': [EXECUTION_WITHOUT_OUTPUT, True]
}
],
'telem_category': 'post_breach'
}
original_telem_single_result =\
{
'data': {
'command': 'COMMAND',
'hostname': 'HOST',
'ip': '127.0.1.1',
'name': 'PBA NAME',
'result': ['', True]
},
'telem_category': 'post_breach'
}
expected_telem_single_result =\
{
'data': [
{
'command': 'COMMAND',
'hostname': 'HOST',
'ip': '127.0.1.1',
'name': 'PBA NAME',
'result': [EXECUTION_WITHOUT_OUTPUT, True]
},
],
'telem_category': 'post_breach'
}
def test_process_post_breach_telemetry():
post_breach.update_data = Mock() # actual behavior of update_data() is to access mongodb
# multiple results in PBA
post_breach.process_post_breach_telemetry(original_telem_multiple_results)
assert original_telem_multiple_results == expected_telem_multiple_results
# single result in PBA
post_breach.process_post_breach_telemetry(original_telem_single_result)
assert original_telem_single_result == expected_telem_single_result

View File

@ -2,6 +2,7 @@ import React from 'react';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import Pluralize from 'pluralize'; import Pluralize from 'pluralize';
import {renderIpAddresses} from '../common/RenderArrays'; import {renderIpAddresses} from '../common/RenderArrays';
import parsePbaResults from './PostBreachParser';
let renderMachine = function (data) { let renderMachine = function (data) {
return <div>{data.label} ( {renderIpAddresses(data)} )</div> return <div>{data.label} ( {renderIpAddresses(data)} )</div>
@ -54,6 +55,7 @@ class PostBreachComponent extends React.Component {
let pbaMachines = this.props.data.filter(function (value) { let pbaMachines = this.props.data.filter(function (value) {
return (value.pba_results !== 'None' && value.pba_results.length > 0); return (value.pba_results !== 'None' && value.pba_results.length > 0);
}); });
pbaMachines = pbaMachines.map(pbaData => parsePbaResults(pbaData));
let defaultPageSize = pbaMachines.length > pageSize ? pageSize : pbaMachines.length; let defaultPageSize = pbaMachines.length > pageSize ? pageSize : pbaMachines.length;
let showPagination = pbaMachines > pageSize; let showPagination = pbaMachines > pageSize;
const pbaCount = pbaMachines.reduce((accumulated, pbaMachine) => accumulated+pbaMachine['pba_results'].length, 0); const pbaCount = pbaMachines.reduce((accumulated, pbaMachine) => accumulated+pbaMachine['pba_results'].length, 0);

View File

@ -0,0 +1,33 @@
export default function parsePbaResults(results) {
results.pba_results = aggregateShellStartupPba(results.pba_results);
return results;
}
const SHELL_STARTUP_NAME = 'Modify shell startup file';
function aggregateShellStartupPba(results) {
let isSuccess = false;
let aggregatedPbaResult = undefined;
let successfulOutputs = '';
let failedOutputs = '';
for(let i = 0; i < results.length; i++){
if(results[i].name === SHELL_STARTUP_NAME && aggregatedPbaResult === undefined){
aggregatedPbaResult = results[i];
}
if(results[i].name === SHELL_STARTUP_NAME && results[i].result[1]){
successfulOutputs += results[i].result[0];
isSuccess = true;
}
if(results[i].name === SHELL_STARTUP_NAME && ! results[i].result[1]){
failedOutputs += results[i].result[0];
}
}
if(aggregatedPbaResult === undefined) return;
results = results.filter(result => result.name !== SHELL_STARTUP_NAME);
aggregatedPbaResult.result[0] = successfulOutputs + failedOutputs;
aggregatedPbaResult.result[1] = isSuccess;
results.push(aggregatedPbaResult);
return results;
}

View File

@ -60,10 +60,12 @@
.pba-danger { .pba-danger {
background-color: #ffc7af; background-color: #ffc7af;
white-space: pre-wrap;
} }
.pba-success { .pba-success {
background-color: #afd2a2; background-color: #afd2a2;
white-space: pre-wrap;
} }
div.report-wrapper { div.report-wrapper {