Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle Undefined Ability in Adversary Profile #2574

Merged
merged 10 commits into from
May 27, 2022
120 changes: 85 additions & 35 deletions templates/adversaries.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,37 +114,54 @@ <h3 x-text="selectedProfileName" class="pointer tooltip has-tooltip-arrow" data-
</thead>
<tbody>
<template x-for="(ability, index) of selectedProfileAbilities">
<tr @click="selectAbility(ability.ability_id)" class="ability-row" x-bind:class="{ 'red-row': needsParser.indexOf(ability.name) > -1, 'row-hover': ability.ability_id === abilityTableDragHoverId }" x-on:mouseenter="setAbilityHover(ability.ability_id)" x-on:mouseleave="clearAbilityHover()" x-on:dragenter="abilityTableDragHoverId = ability.ability_id">
<tr @click="selectAbility(ability.ability_id)" class="ability-row"
x-bind:class="{ 'orange-row': needsParser.indexOf(ability.name) > -1 ,
'row-hover': ability.ability_id === abilityTableDragHoverId && abilityTableDragHoverId != undefined,
'red-row-unclickable': undefinedAbilities.indexOf(ability.ability_id) > -1 }"
x-on:mouseenter="setAbilityHover(ability.ability_id)" x-on:mouseleave="clearAbilityHover()" x-on:dragenter="abilityTableDragHoverId = ability.ability_id">
<td class="has-text-centered drag" @click.stop draggable="true" x-on:dragstart="startAbilitySwap" x-on:dragover.prevent="swapAbilitiesHover" x-on:dragend="swapAbilities">&#9776;</td>
<td x-text="index + 1"></td>
<td x-text="ability.name"></td>
<td>
<div class="icon-text">
<span x-text="index + 1"></span>
<span class="icon has-text-danger" x-show="!ability.ability_id">
<em class="fas fa-ban"></em>
</span>
<span class="icon has-text-warning" x-show="needsParser.indexOf(ability.name) > -1">
<em class="fas fa-exclamation-triangle"></em>
</span>
</div>
</td>
<td x-text="ability.ability_id ? ability.name : 'Undefined Ability'"></td>
<template x-if="!ability.ability_id">
<td colspan="7" x-text="'ID - ' + abilityIDs[index]"></td>
</template>
<td x-show="undefinedAbilities.indexOf(ability.ability_id)">
<span x-text="ability.tactic" x-bind:style="`border-bottom: 1px ridge ${hashStringToColor(ability.tactic)}`"></span>
</td>
<td x-text="ability.technique_name"></td>
<td>
<td x-text="ability.technique_name" x-show="undefinedAbilities.indexOf(ability.ability_id)"></td>
<td x-show="undefinedAbilities.indexOf(ability.ability_id)">
<template x-for="platform of getExecutorDetail('platforms', ability)">
<span class="has-tooltip-arrow no-underline" x-bind:data-tooltip="platform">
<span class="icon is-small"><em class="fab" x-bind:class="if (platform.includes('windows')) return 'fa-windows'; else if (platform.includes('darwin')) return 'fa-apple'; else if (platform.includes('linux')) return 'fa-linux'"></em></span>
</span>
</template>
</td>
<td class="has-text-centered" x-bind:class="{ 'unlock': onHoverUnlocks.indexOf(ability.ability_id) > -1 }">
<td class="has-text-centered" x-show="undefinedAbilities.indexOf(ability.ability_id)" x-bind:class="{ 'unlock': onHoverUnlocks.indexOf(ability.ability_id) > -1 }">
<span class=" has-tooltip-arrow no-underline" x-show="getExecutorDetail('requirements', ability)" x-bind:data-tooltip="`This ability has requirements: (${abilityDependencies[ability.ability_id].requireTypes})`">
<span class="icon is-small"><em class="fas fa-lock"></em></span>
</span>
</td>
<td class="has-text-centered" x-bind:class="{ 'lock': onHoverLocks.indexOf(ability.ability_id) > -1 }">
<td class="has-text-centered" x-show="undefinedAbilities.indexOf(ability.ability_id)" x-bind:class="{ 'lock': onHoverLocks.indexOf(ability.ability_id) > -1 }">
<span class="has-tooltip-arrow no-underline" x-show="getExecutorDetail('parser', ability)" x-bind:data-tooltip="`This ability unlocks other abilities: (${abilityDependencies[ability.ability_id].enableTypes})`">
<span class="icon is-small"><em class="fas fa-key"></em></span>
</span>
</td>
<td class="has-text-centered">
<td class="has-text-centered" x-show="undefinedAbilities.indexOf(ability.ability_id)">
<span class="has-tooltip-arrow no-underline" x-show="getExecutorDetail('payload', ability)" data-tooltip="This ability uses a payload">
<span class="icon is-small"><em class="fas fa-weight-hanging"></em></span>
</span>
</td>
<td class="has-text-centered">
<td class="has-text-centered" x-show="undefinedAbilities.indexOf(ability.ability_id)">
<span class="has-tooltip-arrow no-underline" x-show="getExecutorDetail('cleanup', ability)" data-tooltip="This ability can clean itself up">
<span class="icon is-small"><em class="fas fa-trash"></em></span>
</span>
Expand All @@ -163,6 +180,12 @@ <h3 x-text="selectedProfileName" class="pointer tooltip has-tooltip-arrow" data-
</span>
<span>One or more of the abilities have unmet requirements, which may result in a failed operation if ran sequentially.</span>
</div>
<div class="icon-text mt-2" x-show="undefinedAbilities.length">
<span class="icon has-text-danger">
<em class="fas fa-ban"></em>
</span>
<span>One or more of the referenced abilities are not defined.</span>
</div>
</div>
</template>

Expand Down Expand Up @@ -787,6 +810,7 @@ <h3>Create a profile</h3>
// Global page variables
adversaries: [],
abilities: [],
abilityIDs: [],
objectives: [],
platforms: JSON.parse('{{ platforms | tojson }}'),
payloads: JSON.parse('{{ payloads | tojson }}').sort(),
Expand All @@ -800,6 +824,7 @@ <h3>Create a profile</h3>
// Ability dependencies & parsers
abilityDependencies: {},
needsParser: [],
undefinedAbilities: [],
onHoverUnlocks: [],
onHoverLocks: [],
isTacticBreakdownActive: false,
Expand Down Expand Up @@ -881,6 +906,7 @@ <h3>Create a profile</h3>
this.selectedProfileDescription = selectedAdversary.description;
this.selectedObjectiveId = this.objectives.find((objective) => objective.id === selectedAdversary.objective).id;
this.selectedProfileAbilities = selectedAdversary.atomic_ordering.map((ability_id) => ({ ...this.abilities.find((ability) => ability.ability_id === ability_id) }));
this.abilityIDs = selectedAdversary.atomic_ordering;
this.adversarySearchQuery = selectedAdversary.name;
this.findAbilityDependencies();
},
Expand Down Expand Up @@ -1012,8 +1038,8 @@ <h3>Create a profile</h3>
yaml += `description: ${this.selectedProfileDescription}\n`;
yaml += `objective: ${this.selectedObjectiveId}\n`;
yaml += `atomic_ordering:\n`;
this.selectedProfileAbilities.forEach((ability) => yaml += `- ${ability.ability_id}\n`);

this.selectedProfileAbilities.forEach((ability, index) =>
ability.ability_id ? yaml += `- ${ability.ability_id}\n` : yaml += `- ${this.abilityIDs[index]}\n`);
const blob = new Blob([yaml], { type: 'application/x-yaml' })
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
Expand All @@ -1025,6 +1051,7 @@ <h3>Create a profile</h3>
window.URL.revokeObjectURL(url);
},


getAdversaryTactics() {
this.adversaries.forEach((adversary) => {
let tactics = adversary.atomic_ordering.map((ability_id) => {
Expand Down Expand Up @@ -1058,12 +1085,15 @@ <h3>Create a profile</h3>
let hasPayload = false;
let hasParser = false;

ability.executors.forEach((executor) => {
if (executor.cleanup.length > 0) hasCleanup = true;
if (executor.parsers.length > 0) hasParser = true;
if (executor.payloads.length > 0) hasPayload = true;
plats.push(`${executor.platform} (${executorNameMap.get(executor.name) || executor.name})`);
});
if(ability.executors)
{
ability.executors.forEach((executor) => {
if (executor.cleanup.length > 0) hasCleanup = true;
if (executor.parsers.length > 0) hasParser = true;
if (executor.payloads.length > 0) hasPayload = true;
plats.push(`${executor.platform} (${executorNameMap.get(executor.name) || executor.name})`);
});
}

switch (detail) {
case 'cleanup':
Expand All @@ -1073,7 +1103,9 @@ <h3>Create a profile</h3>
case 'payload':
return hasPayload;
case 'requirements':
return ability.requirements.length > 0;
if(ability.requirements){
return ability.requirements.length > 0;
}
case 'platforms':
return plats;
default:
Expand All @@ -1085,22 +1117,30 @@ <h3>Create a profile</h3>
const types = {};
let factsCollected = [];
let factsRequired = [];
this.undefinedAbilities = [];

this.selectedProfileAbilities.forEach((ability, index) => {
this.selectedProfileAbilities.forEach((ability, index) => {
let requireTypes = [];
let enableTypes = [];

// Get all parser types from executors
ability.executors.forEach((executor) => {
executor.parsers.forEach((parser) => {
enableTypes = enableTypes.concat(parser.parserconfigs.map((rel) => rel.source));
//skip building out enable types and require types if ability is unknown
if(ability.ability_id != undefined) {

// Get all parser types from executors
ability.executors.forEach((executor) => {
executor.parsers.forEach((parser) => {
enableTypes = enableTypes.concat(parser.parserconfigs.map((rel) => rel.source));
});
});
});

// Get all requirement types
ability.requirements.forEach((requirement) => {
requireTypes = requireTypes.concat(requirement.relationship_match.map((match) => match.source));
});
// Get all requirement types
ability.requirements.forEach((requirement) => {
requireTypes = requireTypes.concat(requirement.relationship_match.map((match) => match.source));
});

} else {
this.undefinedAbilities.push(ability.ability_id);
}

types[ability.ability_id] = {
enableTypes: [...new Set(enableTypes)],
Expand All @@ -1115,9 +1155,11 @@ <h3>Create a profile</h3>

// For each parser, look at and forward for any ability it unlocks
types[ability.ability_id].enableTypes.forEach((key) => {
for (let i = index; i < this.selectedProfileAbilities.length; i++) {
if (types[this.selectedProfileAbilities[i].ability_id].requireTypes.indexOf(key) > -1) {
enablesAbilityIds.push(this.selectedProfileAbilities[i].ability_id);
if(this.selectedProfileAbilities) {
for (let i = index; i < this.selectedProfileAbilities.length; i++) {
if (types[this.selectedProfileAbilities[i].ability_id].requireTypes.indexOf(key) > -1) {
enablesAbilityIds.push(this.selectedProfileAbilities[i].ability_id);
}
}
}
});
Expand Down Expand Up @@ -1384,8 +1426,10 @@ <h3>Create a profile</h3>

hashStringToColor(str) {
let hash = 5381;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) + hash) + str.charCodeAt(i);
if(str) {
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) + hash) + str.charCodeAt(i);
}
}

let r = (hash & 0xFF0000) >> 16;
Expand Down Expand Up @@ -1449,8 +1493,14 @@ <h3>Create a profile</h3>
cursor: pointer;
}

.red-row {
border: 2px solid #8B0000;
.red-row-unclickable {
pointer-events: none;
font-weight: bold;
border: 3px solid #8B0000;
}

.orange-row {
border: 2px solid orange;
}

.row-hover {
Expand Down