forked from p34709852/monkey
Merge pull request #731 from shreyamalviya/modifications-T1156-T1504
Telemetry modifications for "modify shell startup files" PBA
This commit is contained in:
commit
7d369f7399
|
@ -1,8 +1,11 @@
|
|||
import subprocess
|
||||
|
||||
from common.data.post_breach_consts import \
|
||||
POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION
|
||||
from infection_monkey.post_breach.pba import PBA
|
||||
from infection_monkey.post_breach.shell_startup_files.shell_startup_files_modification import \
|
||||
get_commands_to_modify_shell_startup_files
|
||||
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
|
||||
|
||||
|
||||
class ModifyShellStartupFiles(PBA):
|
||||
|
@ -12,34 +15,48 @@ class ModifyShellStartupFiles(PBA):
|
|||
and profile.ps1 in windows.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name=POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION)
|
||||
|
||||
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):
|
||||
return ShellStartupPBAGenerator.get_modify_shell_startup_pbas()
|
||||
return self.ShellStartupPBAGenerator().get_modify_shell_startup_pbas()
|
||||
|
||||
class ShellStartupPBAGenerator():
|
||||
def get_modify_shell_startup_pbas(self):
|
||||
(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()
|
||||
|
||||
class ShellStartupPBAGenerator():
|
||||
def get_modify_shell_startup_pbas():
|
||||
(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()
|
||||
pbas = []
|
||||
|
||||
pbas = []
|
||||
for startup_file_per_user in shell_startup_files_per_user_for_windows:
|
||||
windows_cmds = ' '.join(cmds_for_windows).format(startup_file_per_user)
|
||||
pbas.append(self.ModifyShellStartupFile(linux_cmds='', windows_cmds=['powershell.exe', windows_cmds]))
|
||||
|
||||
for startup_file_per_user in shell_startup_files_per_user_for_windows:
|
||||
windows_cmds = ' '.join(cmds_for_windows).format(startup_file_per_user)
|
||||
pbas.append(ModifyShellStartupFile(linux_cmds='', windows_cmds=['powershell.exe', windows_cmds]))
|
||||
for username in usernames_for_linux:
|
||||
for shell_startup_file in shell_startup_files_for_linux:
|
||||
linux_cmds = ' '.join(cmds_for_linux).format(shell_startup_file).format(username)
|
||||
pbas.append(self.ModifyShellStartupFile(linux_cmds=linux_cmds, windows_cmds=''))
|
||||
|
||||
for username in usernames_for_linux:
|
||||
for shell_startup_file in shell_startup_files_for_linux:
|
||||
linux_cmds = ' '.join(cmds_for_linux).format(shell_startup_file).format(username)
|
||||
pbas.append(ModifyShellStartupFile(linux_cmds=linux_cmds, windows_cmds=''))
|
||||
return pbas
|
||||
|
||||
return pbas
|
||||
class ModifyShellStartupFile(PBA):
|
||||
def __init__(self, linux_cmds, windows_cmds):
|
||||
super().__init__(name=POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION,
|
||||
linux_cmd=linux_cmds,
|
||||
windows_cmd=windows_cmds)
|
||||
|
||||
|
||||
class ModifyShellStartupFile(PBA):
|
||||
def __init__(self, linux_cmds, windows_cmds):
|
||||
super(ModifyShellStartupFile, self).__init__(name=POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION,
|
||||
linux_cmd=linux_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
|
||||
|
|
|
@ -13,8 +13,6 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
__author__ = 'VakarisZ'
|
||||
|
||||
EXECUTION_WITHOUT_OUTPUT = "(PBA execution produced no output)"
|
||||
|
||||
|
||||
class PBA(Plugin):
|
||||
"""
|
||||
|
@ -90,8 +88,6 @@ class PBA(Plugin):
|
|||
"""
|
||||
try:
|
||||
output = subprocess.check_output(self.command, stderr=subprocess.STDOUT, shell=True).decode()
|
||||
if not output:
|
||||
output = EXECUTION_WITHOUT_OUTPUT
|
||||
return output, True
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Return error output of the command
|
||||
|
|
|
@ -98,9 +98,9 @@ class TelemetryFeed(flask_restful.Resource):
|
|||
|
||||
@staticmethod
|
||||
def get_post_breach_telem_brief(telem):
|
||||
return '%s post breach action executed on %s (%s) machine.' % (telem['data']['name'],
|
||||
telem['data']['hostname'],
|
||||
telem['data']['ip'])
|
||||
return '%s post breach action executed on %s (%s) machine.' % (telem['data'][0]['name'],
|
||||
telem['data'][0]['hostname'],
|
||||
telem['data'][0]['ip'])
|
||||
|
||||
@staticmethod
|
||||
def should_show_brief(telem):
|
||||
|
|
|
@ -12,3 +12,15 @@ class T1156(PostBreachTechnique):
|
|||
scanned_msg = "Monkey tried modifying bash startup files but failed."
|
||||
used_msg = "Monkey successfully modified bash startup files."
|
||||
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'}}]}}]
|
||||
|
|
|
@ -12,3 +12,14 @@ class T1504(PostBreachTechnique):
|
|||
scanned_msg = "Monkey tried modifying powershell startup files but failed."
|
||||
used_msg = "Monkey successfully modified powershell startup files."
|
||||
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'}}}]
|
||||
|
|
|
@ -3,7 +3,6 @@ from typing import List
|
|||
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import copy
|
||||
|
||||
from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.models import Monkey
|
||||
from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_user import \
|
||||
test_new_user_communication
|
||||
|
||||
EXECUTION_WITHOUT_OUTPUT = "(PBA execution produced no output)"
|
||||
|
||||
|
||||
def process_communicate_as_new_user_telemetry(telemetry_json):
|
||||
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):
|
||||
mongo.db.monkey.update(
|
||||
{'guid': telemetry_json['monkey_guid']},
|
||||
{'$push': {'pba_results': telemetry_json['data']}})
|
||||
def convert_telem_data_to_list(data):
|
||||
modified_data = [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"]
|
||||
if post_breach_action_name in POST_BREACH_TELEMETRY_PROCESSING_FUNCS:
|
||||
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}})
|
||||
|
|
|
@ -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
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import ReactTable from 'react-table';
|
||||
import Pluralize from 'pluralize';
|
||||
import {renderIpAddresses} from '../common/RenderArrays';
|
||||
import parsePbaResults from './PostBreachParser';
|
||||
|
||||
let renderMachine = function (data) {
|
||||
return <div>{data.label} ( {renderIpAddresses(data)} )</div>
|
||||
|
@ -54,6 +55,7 @@ class PostBreachComponent extends React.Component {
|
|||
let pbaMachines = this.props.data.filter(function (value) {
|
||||
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 showPagination = pbaMachines > pageSize;
|
||||
const pbaCount = pbaMachines.reduce((accumulated, pbaMachine) => accumulated+pbaMachine['pba_results'].length, 0);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -60,10 +60,12 @@
|
|||
|
||||
.pba-danger {
|
||||
background-color: #ffc7af;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.pba-success {
|
||||
background-color: #afd2a2;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
div.report-wrapper {
|
||||
|
|
Loading…
Reference in New Issue