Add T1146 (clear command history)

This commit is contained in:
Shreya 2020-08-23 22:42:37 +05:30
parent 8677c219ac
commit 2431d45b74
11 changed files with 241 additions and 29 deletions

View File

@ -6,3 +6,4 @@ POST_BREACH_HIDDEN_FILES = "Hide files and directories"
POST_BREACH_TRAP_COMMAND = "Execute command when a particular signal is received"
POST_BREACH_SETUID_SETGID = "Setuid and Setgid"
POST_BREACH_JOB_SCHEDULING = "Schedule jobs"
POST_BREACH_CLEAR_CMD_HISTORY = "Clear command history"

View File

@ -0,0 +1,49 @@
import subprocess
from common.data.post_breach_consts import POST_BREACH_CLEAR_CMD_HISTORY
from infection_monkey.post_breach.pba import PBA
from infection_monkey.post_breach.clear_command_history.clear_command_history import \
get_commands_to_clear_command_history
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
class ClearCommandHistory(PBA):
def __init__(self):
super().__init__(name=POST_BREACH_CLEAR_CMD_HISTORY)
def run(self):
results = [pba.run() for pba in self.clear_command_history_PBA_list()]
PostBreachTelem(self, results).send()
def clear_command_history_PBA_list(self):
return self.CommandHistoryPBAGenerator().get_clear_command_history_pbas()
class CommandHistoryPBAGenerator():
def get_clear_command_history_pbas(self):
(cmds_for_linux, command_history_files_for_linux, usernames_for_linux) =\
get_commands_to_clear_command_history()
pbas = []
for username in usernames_for_linux:
for command_history_file in command_history_files_for_linux:
linux_cmds = ' '.join(cmds_for_linux).format(command_history_file).format(username)
pbas.append(self.ClearCommandHistoryFile(linux_cmds=linux_cmds))
return pbas
class ClearCommandHistoryFile(PBA):
def __init__(self, linux_cmds):
super().__init__(name=POST_BREACH_CLEAR_CMD_HISTORY,
linux_cmd=linux_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

@ -0,0 +1,7 @@
from infection_monkey.post_breach.clear_command_history.linux_clear_command_history import \
get_linux_commands_to_clear_command_history
def get_commands_to_clear_command_history():
linux_cmds = get_linux_commands_to_clear_command_history()
return linux_cmds

View File

@ -0,0 +1,41 @@
import subprocess
from infection_monkey.utils.environment import is_windows_os
TEMP_HIST_FILE = '$HOME/monkey-temp-hist-file'
def get_linux_commands_to_clear_command_history():
if is_windows_os():
return '', [], []
HOME_DIR = "/home/"
# get list of usernames
USERS = subprocess.check_output( # noqa: DUO116
"cut -d: -f1,3 /etc/passwd | egrep ':[0-9]{4}$' | cut -d: -f1",
shell=True
).decode().split('\n')[:-1]
# get list of paths of different shell history files (default values) with place for username
STARTUP_FILES = [
file_path.format(HOME_DIR) for file_path in
[
"{0}{{0}}/.bash_history", # bash
"{0}{{0}}/.local/share/fish/fish_history", # fish
"{0}{{0}}/.zsh_history", # zsh
"{0}{{0}}/.sh_history", # ksh
"{0}{{0}}/.history" # csh, tcsh
]
]
return [
'3<{0} 3<&- && ', # check for existence of file
'cat {0} ' # copy contents of history file to...
f'> {TEMP_HIST_FILE} && ', # ...temporary file
'echo > {0} && ', # clear contents of file
'echo \"Successfully cleared {0}\" && ', # if successfully cleared
f'cat {TEMP_HIST_FILE} ', # restore history file back with...
'> {0} ;' # ...original contents
f'rm {TEMP_HIST_FILE} -f' # remove temp history file
], STARTUP_FILES, USERS

View File

@ -14,11 +14,12 @@ from monkey_island.cc.services.attack.technique_reports import (T1003, T1005,
T1106, T1107,
T1110, T1129,
T1136, T1145,
T1154, T1156,
T1158, T1166,
T1168, T1188,
T1197, T1210,
T1222, T1504)
T1146, T1154,
T1156, T1158,
T1166, T1168,
T1188, T1197,
T1210, T1222,
T1504)
from monkey_island.cc.services.reporting.report_generation_synchronisation import \
safe_generate_attack_report
@ -57,7 +58,8 @@ TECHNIQUES = {'T1210': T1210.T1210,
'T1154': T1154.T1154,
'T1166': T1166.T1166,
'T1168': T1168.T1168,
'T1053': T1053.T1053
'T1053': T1053.T1053,
'T1146': T1146.T1146
}
REPORT_NAME = 'new_report'

View File

@ -168,6 +168,15 @@ SCHEMA = {
"description": "Adversaries may abuse BITS to download, execute, "
"and even clean up after running malicious code."
},
"T1146": {
"title": "Clear command history",
"type": "bool",
"value": True,
"necessary": False,
"link": "https://attack.mitre.org/techniques/T1146",
"description": "Adversaries may clear/disable command history of a compromised "
"account to conceal the actions undertaken during an intrusion."
},
"T1107": {
"title": "File Deletion",
"type": "bool",

View File

@ -0,0 +1,22 @@
from common.data.post_breach_consts import POST_BREACH_CLEAR_CMD_HISTORY
from monkey_island.cc.services.attack.technique_reports.pba_technique import \
PostBreachTechnique
__author__ = "shreyamalviya"
class T1146(PostBreachTechnique):
tech_id = "T1146"
unscanned_msg = "Monkey didn't try clearing/disabling the command history since it didn't run on any Linux machines."
scanned_msg = "Monkey tried clearing/disabling the command history but failed."
used_msg = "Monkey successfully cleared/disabled the command history."
pba_names = [POST_BREACH_CLEAR_CMD_HISTORY]
@staticmethod
def get_pba_query(*args):
return [{'$match': {'telem_category': 'post_breach',
'data.name': POST_BREACH_CLEAR_CMD_HISTORY}},
{'$project': {'_id': 0,
'machine': {'hostname': {'$arrayElemAt': ['$data.hostname', 0]},
'ips': [{'$arrayElemAt': ['$data.ip', 0]}]},
'result': '$data.result'}}]

View File

@ -70,6 +70,15 @@ POST_BREACH_ACTIONS = {
"title": "Job scheduling",
"info": "Attempts to create a scheduled job on the system and remove it.",
"attack_techniques": ["T1168", "T1053"]
},
{
"type": "string",
"enum": [
"ClearCommandHistory"
],
"title": "Clear command history",
"info": "Attempts to clear the command history.",
"attack_techniques": ["T1146"]
}
]
}

View File

@ -67,7 +67,8 @@ MONKEY = {
"HiddenFiles",
"TrapCommand",
"ChangeSetuidSetgid",
"ScheduleJobs"
"ScheduleJobs",
"ClearCommandHistory"
]
},
}

View File

@ -0,0 +1,45 @@
import React from 'react';
import ReactTable from 'react-table';
import {renderMachineFromSystemData, ScanStatus} from './Helpers';
import MitigationsComponent from './MitigationsComponent';
class T1146 extends React.Component {
constructor(props) {
super(props);
}
static getColumns() {
return ([{
columns: [
{ Header: 'Machine',
id: 'machine',
accessor: x => renderMachineFromSystemData(x.machine),
style: {'whiteSpace': 'unset'}},
{ Header: 'Result',
id: 'result',
accessor: x => x.result,
style: {'whiteSpace': 'unset'}}
]
}])
}
render() {
return (
<div>
<div>{this.props.data.message}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable
columns={T1146.getColumns()}
data={this.props.data.info}
showPagination={false}
defaultPageSize={this.props.data.info.length}
/> : ''}
<MitigationsComponent mitigations={this.props.data.mitigations}/>
</div>
);
}
}
export default T1146;

View File

@ -1,33 +1,59 @@
export default function parsePbaResults(results) {
results.pba_results = aggregateShellStartupPba(results.pba_results);
results.pba_results = aggregateMultipleResultsPba(results.pba_results);
return results;
}
const SHELL_STARTUP_NAME = 'Modify shell startup file';
const CMD_HISTORY_NAME = 'Clear command history';
function aggregateShellStartupPba(results) {
let isSuccess = false;
let aggregatedPbaResult = undefined;
let successfulOutputs = '';
let failedOutputs = '';
function aggregateMultipleResultsPba(results) {
let aggregatedPbaResults = {
'Modify shell startup file': {
aggregatedResult: undefined,
successfulOutputs: '',
failedOutputs: '',
isSuccess: false
},
'Clear command history': {
aggregatedResult: undefined,
successfulOutputs: '',
failedOutputs: '',
isSuccess: false
}
}
for(let i = 0; i < results.length; i++){
if(results[i].name === SHELL_STARTUP_NAME && aggregatedPbaResult === undefined){
aggregatedPbaResult = results[i];
function aggregateResults(result) {
if (aggregatedPbaResults[result.name].aggregatedResult === undefined) {
aggregatedPbaResults[result.name].aggregatedResult = result;
}
if(results[i].name === SHELL_STARTUP_NAME && results[i].result[1]){
successfulOutputs += results[i].result[0];
isSuccess = true;
if (result.result[1]) {
aggregatedPbaResults[result.name].successfulOutputs += result.result[0];
aggregatedPbaResults[result.name].isSuccess = true;
}
if(results[i].name === SHELL_STARTUP_NAME && ! results[i].result[1]){
failedOutputs += results[i].result[0];
if (!result.result[1]) {
aggregatedPbaResults[result.name].failedOutputs += result.result[0];
}
}
if(aggregatedPbaResult === undefined) return results;
results = results.filter(result => result.name !== SHELL_STARTUP_NAME);
aggregatedPbaResult.result[0] = successfulOutputs + failedOutputs;
aggregatedPbaResult.result[1] = isSuccess;
results.push(aggregatedPbaResult);
function checkAggregatedResults(pbaName) {
if (aggregatedPbaResults[pbaName].aggregatedResult !== undefined) {
aggregatedPbaResults[pbaName].aggregatedResult.result[0] = aggregatedPbaResults[pbaName].successfulOutputs + aggregatedPbaResults[pbaName].failedOutputs;
aggregatedPbaResults[pbaName].aggregatedResult.result[1] = aggregatedPbaResults[pbaName].isSuccess;
results.push(aggregatedPbaResults[pbaName].aggregatedResult);
}
}
for (let i = 0; i < results.length; i++)
if (results[i].name === SHELL_STARTUP_NAME || results[i].name === CMD_HISTORY_NAME)
aggregateResults(results[i]);
if (aggregatedPbaResults[SHELL_STARTUP_NAME].aggregatedResult === undefined &&
aggregatedPbaResults[CMD_HISTORY_NAME].aggregatedResult === undefined)
return results;
results = results.filter(result => result.name !== SHELL_STARTUP_NAME && result.name !== CMD_HISTORY_NAME);
checkAggregatedResults(SHELL_STARTUP_NAME);
checkAggregatedResults(CMD_HISTORY_NAME);
return results;
}