Skip to content

Commit

Permalink
[Security Solution] [Attack discovery] Display additional Attack Chai…
Browse files Browse the repository at this point in the history
…n tactics (elastic#209434)

### [Security Solution] [Attack discovery] Display additional Attack Chain tactics

This PR updates the Attack discovery _Attack Chain_ allow list to visualize additional tactics (e.g. `Defense Evasion`), as illustarted by the screenshot below:

![attack_chain](https://github.com/user-attachments/assets/afc57cef-bc01-4a67-8028-8528c96e8ced)

_Above: The Attack Chain includes `Defense Evasion`, a newly allow listed tactic_

#### Details

This PR updates the Attack Chain allow list to include the following additional tactics:

- `Resource Development`
- `Defense Evasion`
- `Credential Access`
- `Collection`
- `Impact`

#### Desk testing

1. Navigate to Security > Attack discovery

2. Click `Generate` to generate Attack discoveries

**Expected result**

- The Attack Chain visualization includes the additional allow listed tactics noted in the details of this PR

3. Locate an Attack discovery where one of the newly allow listed tactics is red, (which indicates the tactic was part of the attack), and click the `View in AI Assistant` button

**Expected result**

- A `>` accordion button in the assistant appears next to the selected Attack discovery, indicating it will be included as context

4. Click the `>` button to expand the discovery in the assistant

**Expected result**

- The expanded `Attack Chain` markdown includes an entry for the newly allow listed tactic, and all other tactics that were colored red in the discovery
  • Loading branch information
andrew-goldstein authored Feb 5, 2025
1 parent 679e2ba commit 734fc17
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Your output will be parsed and type-checked according to the provided schema ins
Here is the JSON Schema instance your output must adhere to. Include the enclosing markdown codeblock:
\`\`\`json
{"type":"object","properties":{"insights":{"type":"array","items":{"type":"object","properties":{"alertIds":{"type":"array","items":{"type":"string"},"description":"The alert IDs that the insight is based on."},"detailsMarkdown":{"type":"string","description":"A detailed insight with markdown, where each markdown bullet contains a description of what happened that reads like a story of the attack as it played out and always uses special {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax for field names and values from the source data. Examples of CORRECT syntax (includes field names and values): {{ host.name hostNameValue }} {{ user.name userNameValue }} {{ source.ip sourceIpValue }} Examples of INCORRECT syntax (bad, because the field names are not included): {{ hostNameValue }} {{ userNameValue }} {{ sourceIpValue }}"},"entitySummaryMarkdown":{"type":"string","description":"A short (no more than a sentence) summary of the insight featuring only the host.name and user.name fields (when they are applicable), using the same {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax"},"mitreAttackTactics":{"type":"array","items":{"type":"string"},"description":"An array of MITRE ATT&CK tactic for the insight, using one of the following values: Reconnaissance,Initial Access,Execution,Persistence,Privilege Escalation,Discovery,Lateral Movement,Command and Control,Exfiltration"},"summaryMarkdown":{"type":"string","description":"A markdown summary of insight, using the same {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax"},"title":{"type":"string","description":"A short, no more than 7 words, title for the insight, NOT formatted with special syntax or markdown. This must be as brief as possible."}},"required":["alertIds","detailsMarkdown","summaryMarkdown","title"],"additionalProperties":false},"description":"Insights with markdown that always uses special {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax for field names and values from the source data. Examples of CORRECT syntax (includes field names and values): {{ host.name hostNameValue }} {{ user.name userNameValue }} {{ source.ip sourceIpValue }} Examples of INCORRECT syntax (bad, because the field names are not included): {{ hostNameValue }} {{ userNameValue }} {{ sourceIpValue }}"}},"required":["insights"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}
{"type":"object","properties":{"insights":{"type":"array","items":{"type":"object","properties":{"alertIds":{"type":"array","items":{"type":"string"},"description":"The alert IDs that the insight is based on."},"detailsMarkdown":{"type":"string","description":"A detailed insight with markdown, where each markdown bullet contains a description of what happened that reads like a story of the attack as it played out and always uses special {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax for field names and values from the source data. Examples of CORRECT syntax (includes field names and values): {{ host.name hostNameValue }} {{ user.name userNameValue }} {{ source.ip sourceIpValue }} Examples of INCORRECT syntax (bad, because the field names are not included): {{ hostNameValue }} {{ userNameValue }} {{ sourceIpValue }}"},"entitySummaryMarkdown":{"type":"string","description":"A short (no more than a sentence) summary of the insight featuring only the host.name and user.name fields (when they are applicable), using the same {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax"},"mitreAttackTactics":{"type":"array","items":{"type":"string"},"description":"An array of MITRE ATT&CK tactic for the insight, using one of the following values: Reconnaissance,Resource Development,Initial Access,Execution,Persistence,Privilege Escalation,Defense Evasion,Credential Access,Discovery,Lateral Movement,Collection,Command and Control,Exfiltration,Impact"},"summaryMarkdown":{"type":"string","description":"A markdown summary of insight, using the same {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax"},"title":{"type":"string","description":"A short, no more than 7 words, title for the insight, NOT formatted with special syntax or markdown. This must be as brief as possible."}},"required":["alertIds","detailsMarkdown","summaryMarkdown","title"],"additionalProperties":false},"description":"Insights with markdown that always uses special {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax for field names and values from the source data. Examples of CORRECT syntax (includes field names and values): {{ host.name hostNameValue }} {{ user.name userNameValue }} {{ source.ip sourceIpValue }} Examples of INCORRECT syntax (bad, because the field names are not included): {{ hostNameValue }} {{ userNameValue }} {{ sourceIpValue }}"}},"required":["insights"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}
\`\`\`
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Your output will be parsed and type-checked according to the provided schema ins
Here is the JSON Schema instance your output must adhere to. Include the enclosing markdown codeblock:
\`\`\`json
{"type":"object","properties":{"insights":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"alertIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"The alert IDs that the insight is based on.\"},\"detailsMarkdown\":{\"type\":\"string\",\"description\":\"A detailed insight with markdown, where each markdown bullet contains a description of what happened that reads like a story of the attack as it played out and always uses special {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax for field names and values from the source data. Examples of CORRECT syntax (includes field names and values): {{ host.name hostNameValue }} {{ user.name userNameValue }} {{ source.ip sourceIpValue }} Examples of INCORRECT syntax (bad, because the field names are not included): {{ hostNameValue }} {{ userNameValue }} {{ sourceIpValue }}\"},\"entitySummaryMarkdown\":{\"type\":\"string\",\"description\":\"A short (no more than a sentence) summary of the insight featuring only the host.name and user.name fields (when they are applicable), using the same {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax\"},\"mitreAttackTactics\":{\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"An array of MITRE ATT&CK tactic for the insight, using one of the following values: Reconnaissance,Initial Access,Execution,Persistence,Privilege Escalation,Discovery,Lateral Movement,Command and Control,Exfiltration\"},\"summaryMarkdown\":{\"type\":\"string\",\"description\":\"A markdown summary of insight, using the same {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax\"},\"title\":{\"type\":\"string\",\"description\":\"A short, no more than 7 words, title for the insight, NOT formatted with special syntax or markdown. This must be as brief as possible.\"}},\"required\":[\"alertIds\",\"detailsMarkdown\",\"summaryMarkdown\",\"title\"],\"additionalProperties\":false},\"description\":\"Insights with markdown that always uses special {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax for field names and values from the source data. Examples of CORRECT syntax (includes field names and values): {{ host.name hostNameValue }} {{ user.name userNameValue }} {{ source.ip sourceIpValue }} Examples of INCORRECT syntax (bad, because the field names are not included): {{ hostNameValue }} {{ userNameValue }} {{ sourceIpValue }}\"}},\"required\":[\"insights\"],\"additionalProperties":false,\"$schema\":\"http://json-schema.org/draft-07/schema#\"}
{"type":"object","properties":{"insights":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"alertIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"The alert IDs that the insight is based on.\"},\"detailsMarkdown\":{\"type\":\"string\",\"description\":\"A detailed insight with markdown, where each markdown bullet contains a description of what happened that reads like a story of the attack as it played out and always uses special {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax for field names and values from the source data. Examples of CORRECT syntax (includes field names and values): {{ host.name hostNameValue }} {{ user.name userNameValue }} {{ source.ip sourceIpValue }} Examples of INCORRECT syntax (bad, because the field names are not included): {{ hostNameValue }} {{ userNameValue }} {{ sourceIpValue }}\"},\"entitySummaryMarkdown\":{\"type\":\"string\",\"description\":\"A short (no more than a sentence) summary of the insight featuring only the host.name and user.name fields (when they are applicable), using the same {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax\"},\"mitreAttackTactics\":{\"type\":\"array\",\"items\":{\"type\":\"string\"},\"description\":\"An array of MITRE ATT&CK tactic for the insight, using one of the following values: Reconnaissance,Resource Development,Initial Access,Execution,Persistence,Privilege Escalation,Defense Evasion,Credential Access,Discovery,Lateral Movement,Collection,Command and Control,Exfiltration,Impact"},\"summaryMarkdown\":{\"type\":\"string\",\"description\":\"A markdown summary of insight, using the same {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax\"},\"title\":{\"type\":\"string\",\"description\":\"A short, no more than 7 words, title for the insight, NOT formatted with special syntax or markdown. This must be as brief as possible.\"}},\"required\":[\"alertIds\",\"detailsMarkdown\",\"summaryMarkdown\",\"title\"],\"additionalProperties\":false},\"description\":\"Insights with markdown that always uses special {{ field.name fieldValue1 fieldValue2 fieldValueN }} syntax for field names and values from the source data. Examples of CORRECT syntax (includes field names and values): {{ host.name hostNameValue }} {{ user.name userNameValue }} {{ source.ip sourceIpValue }} Examples of INCORRECT syntax (bad, because the field names are not included): {{ hostNameValue }} {{ userNameValue }} {{ sourceIpValue }}\"}},\"required\":[\"insights\"],\"additionalProperties":false,\"$schema\":\"http://json-schema.org/draft-07/schema#\"}
\`\`\`
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,25 +102,35 @@ const BAD_SYNTAX_EXAMPLES =
'Examples of INCORRECT syntax (bad, because the field names are not included): {{ hostNameValue }} {{ userNameValue }} {{ sourceIpValue }}';

const RECONNAISSANCE = 'Reconnaissance';
const RESOURCE_DEVELOPMENT = 'Resource Development';
const INITIAL_ACCESS = 'Initial Access';
const EXECUTION = 'Execution';
const PERSISTENCE = 'Persistence';
const PRIVILEGE_ESCALATION = 'Privilege Escalation';
const DEFENSE_EVASION = 'Defense Evasion';
const CREDENTIAL_ACCESS = 'Credential Access';
const DISCOVERY = 'Discovery';
const LATERAL_MOVEMENT = 'Lateral Movement';
const COLLECTION = 'Collection';
const COMMAND_AND_CONTROL = 'Command and Control';
const EXFILTRATION = 'Exfiltration';
const IMPACT = 'Impact';

const MITRE_ATTACK_TACTICS = [
RECONNAISSANCE,
RESOURCE_DEVELOPMENT,
INITIAL_ACCESS,
EXECUTION,
PERSISTENCE,
PRIVILEGE_ESCALATION,
DEFENSE_EVASION,
CREDENTIAL_ACCESS,
DISCOVERY,
LATERAL_MOVEMENT,
COLLECTION,
COMMAND_AND_CONTROL,
EXFILTRATION,
IMPACT,
] as const;
export const ATTACK_DISCOVERY_GENERATION_DETAILS_MARKDOWN = `A detailed insight with markdown, where each markdown bullet contains a description of what happened that reads like a story of the attack as it played out and always uses special ${SYNTAX} syntax for field names and values from the source data. ${GOOD_SYNTAX_EXAMPLES} ${BAD_SYNTAX_EXAMPLES}`;
export const ATTACK_DISCOVERY_GENERATION_ENTITY_SUMMARY_MARKDOWN = `A short (no more than a sentence) summary of the insight featuring only the host.name and user.name fields (when they are applicable), using the same ${SYNTAX} syntax`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ describe('helpers', () => {
});

describe('getTacticMetadata', () => {
const expectedDetected = ['Initial Access', 'Execution', 'Persistence', 'Privilege Escalation'];
const expectedDetected = [
'Initial Access',
'Execution',
'Persistence',
'Privilege Escalation',
'Credential Access',
];

expectedDetected.forEach((tactic) => {
it(`sets the detected property to true for the '${tactic}' tactic`, () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,44 @@ import type { AttackDiscovery } from '@kbn/elastic-assistant-common';
import * as i18n from './translations';

export const RECONNAISSANCE = 'Reconnaissance';
export const RESOURCE_DEVELOPMENT = 'Resource Development';
export const INITIAL_ACCESS = 'Initial Access';
export const EXECUTION = 'Execution';
export const PERSISTENCE = 'Persistence';
export const PRIVILEGE_ESCALATION = 'Privilege Escalation';
export const DEFENSE_EVASION = 'Defense Evasion';
export const CREDENTIAL_ACCESS = 'Credential Access';
export const DISCOVERY = 'Discovery';
export const LATERAL_MOVEMENT = 'Lateral Movement';
export const COLLECTION = 'Collection';
export const COMMAND_AND_CONTROL = 'Command and Control';
export const EXFILTRATION = 'Exfiltration';
export const IMPACT = 'Impact';

/** A subset of the Mitre Attack Tactics */
export const MITRE_ATTACK_TACTICS_SUBSET = [
RECONNAISSANCE,
RESOURCE_DEVELOPMENT,
INITIAL_ACCESS,
EXECUTION,
PERSISTENCE,
PRIVILEGE_ESCALATION,
DEFENSE_EVASION,
CREDENTIAL_ACCESS,
DISCOVERY,
LATERAL_MOVEMENT,
COLLECTION,
COMMAND_AND_CONTROL,
EXFILTRATION,
IMPACT,
] as const;

export const getTacticLabel = (tactic: string): string => {
switch (tactic) {
case RECONNAISSANCE:
return i18n.RECONNAISSANCE;
case RESOURCE_DEVELOPMENT:
return i18n.RESOURCE_DEVELOPMENT;
case INITIAL_ACCESS:
return i18n.INITIAL_ACCESS;
case EXECUTION:
Expand All @@ -43,14 +55,22 @@ export const getTacticLabel = (tactic: string): string => {
return i18n.PERSISTENCE;
case PRIVILEGE_ESCALATION:
return i18n.PRIVILEGE_ESCALATION;
case DEFENSE_EVASION:
return i18n.DEFENSE_EVASION;
case CREDENTIAL_ACCESS:
return i18n.CREDENTIAL_ACCESS;
case DISCOVERY:
return i18n.DISCOVERY;
case LATERAL_MOVEMENT:
return i18n.LATERAL_MOVEMENT;
case COLLECTION:
return i18n.COLLECTION;
case COMMAND_AND_CONTROL:
return i18n.COMMAND_AND_CONTROL;
case EXFILTRATION:
return i18n.EXFILTRATION;
case IMPACT:
return i18n.IMPACT;
default:
return tactic;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,56 @@ describe('getAttackDiscoveryMarkdown', () => {
expect(result).toBe('');
});

it('returns the expected attack chain markdown when tactics are detected', () => {
it('returns the expected attack chain markdown when a subset of tactics are detected', () => {
const result = getAttackChainMarkdown(mockAttackDiscovery);

expect(result).toBe(`### Attack Chain
- Initial Access
- Execution
- Persistence
- Privilege Escalation
- Credential Access
`);
});

it('returns the expected attack chain markdown when the full set of tactics are detected', () => {
const allTactics = {
...mockAttackDiscovery,
mitreAttackTactics: [
'Reconnaissance',
'Resource Development',
'Initial Access',
'Execution',
'Persistence',
'Privilege Escalation',
'Defense Evasion',
'Credential Access',
'Discovery',
'Lateral Movement',
'Collection',
'Command and Control',
'Exfiltration',
'Impact',
],
};

const result = getAttackChainMarkdown(allTactics);

expect(result).toBe(`### Attack Chain
- Reconnaissance
- Resource Development
- Initial Access
- Execution
- Persistence
- Privilege Escalation
- Defense Evasion
- Credential Access
- Discovery
- Lateral Movement
- Collection
- Command & Control
- Exfiltration
- Impact
`);
});
});
Expand Down Expand Up @@ -138,6 +180,7 @@ This appears to be a multi-stage attack involving malware delivery, privilege es
- Execution
- Persistence
- Privilege Escalation
- Credential Access
`;

Expand Down Expand Up @@ -174,6 +217,7 @@ This appears to be a multi-stage attack involving malware delivery, privilege es
- Execution
- Persistence
- Privilege Escalation
- Credential Access
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ const AttackChainComponent: React.FC<Props> = ({ attackDiscovery }) => {
<EuiFlexGroup gutterSize="none">
{tacticMetadata.map((tactic, i) => (
<EuiFlexItem grow={false} key={tactic.name}>
<Tactic
detected={tactic.detected}
rightJustify={i === tacticMetadata.length - 1}
tactic={tactic.name}
/>
<Tactic detected={tactic.detected} tactic={tactic.name} />
</EuiFlexItem>
))}
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,18 @@ import React, { useMemo } from 'react';
import { AxisTick } from '../axis_tick';

const INNER_CIRCLE_LEFT_JUSTIFY_X_OFFSET = 0; // px
const INNER_CIRCLE_RIGHT_JUSTIFY_X_OFFSET = 232; // px

const OUTER_CIRCLE_LEFT_JUSTIFY_X_OFFSET = -4; // px
const OUTER_CIRCLE_RIGHT_JUSTIFY_X_OFFSET = 228; // px

interface Props {
detected: boolean;
rightJustify?: boolean;
tactic: string;
}

const TacticComponent: React.FC<Props> = ({ detected, rightJustify = false, tactic }) => {
const TacticComponent: React.FC<Props> = ({ detected, tactic }) => {
const { euiTheme } = useEuiTheme();

const WIDTH = 120; // px
const TICK_COUNT = 10;
const WIDTH = 144; // px
const TICK_COUNT = 12;

const ticks = useMemo(
() => (
Expand All @@ -49,27 +45,14 @@ const TacticComponent: React.FC<Props> = ({ detected, rightJustify = false, tact
);

const color = detected ? euiTheme.colors.danger : euiTheme.colors.subduedText;
const innerCircleXOffset = rightJustify
? INNER_CIRCLE_RIGHT_JUSTIFY_X_OFFSET
: INNER_CIRCLE_LEFT_JUSTIFY_X_OFFSET;

const outerCircleXOffset = rightJustify
? OUTER_CIRCLE_RIGHT_JUSTIFY_X_OFFSET
: OUTER_CIRCLE_LEFT_JUSTIFY_X_OFFSET;

return (
<div
css={css`
width: ${WIDTH}px;
`}
>
<EuiFlexGroup
alignItems={rightJustify ? 'flexEnd' : undefined}
data-test-subj="tactic"
direction="column"
gutterSize="none"
wrap={false}
>
<EuiFlexGroup data-test-subj="tactic" direction="column" gutterSize="none" wrap={false}>
<EuiFlexItem
css={css`
position: relative;
Expand All @@ -78,26 +61,28 @@ const TacticComponent: React.FC<Props> = ({ detected, rightJustify = false, tact
grow={false}
>
<div
// eslint-disable-next-line @kbn/css/no_css_color -- euiTheme.colors.danger is a string
css={css`
background: transparent;
border: 2px solid ${color};
border-radius: 50%;
height: 8px;
position: absolute;
transform: translate(${innerCircleXOffset}px, -2px);
transform: translate(${INNER_CIRCLE_LEFT_JUSTIFY_X_OFFSET}px, -2px);
width: 8px;
`}
data-test-subj="innerCircle"
/>
<div
// eslint-disable-next-line @kbn/css/no_css_color -- euiTheme.colors.danger is a string
css={css`
background: transparent;
border: 2px solid ${color};
border-radius: 50%;
height: 16px;
opacity: ${detected ? 25 : 0}%;
position: absolute;
transform: translate(${outerCircleXOffset}px, -6px);
transform: translate(${OUTER_CIRCLE_LEFT_JUSTIFY_X_OFFSET}px, -6px);
width: 16px;
`}
data-test-subj="outerCircle"
Expand Down
Loading

0 comments on commit 734fc17

Please sign in to comment.