From a84a045e353c8bb2b3780065adbccc2b66b15d46 Mon Sep 17 00:00:00 2001 From: Andrew Macri Date: Thu, 19 Sep 2024 18:37:50 -0400 Subject: [PATCH] [Security Solution] [Attack discovery] Fixes a display issue when an entity is repeated (#193428) ## [Security Solution] [Attack discovery] Fixes a display issue when an entity is repeated ### Summary This PR fixes a display issue in Attack discovery where entities repeated in a description were displayed with a UUID instead of the value, as illustrated by the _Before_ and _After_ screenshots below: **Before** ![before](https://github.com/user-attachments/assets/9197c9b0-27d2-4f46-a967-35bb8ca254d0) **After** ![after](https://github.com/user-attachments/assets/2d7c16bd-972c-45cc-a22f-abe7c8c6c019) --- .../tabs/attack_discovery_tab/index.test.tsx | 37 +++++++++++++++++++ .../tabs/attack_discovery_tab/index.tsx | 18 +++++---- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.test.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.test.tsx index 3c05a10a6eb06..74751d4efaf30 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.test.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.test.tsx @@ -18,6 +18,8 @@ describe('AttackDiscoveryTab', () => { const mockReplacements: Replacements = { '5e454c38-439c-4096-8478-0a55511c76e3': 'foo.hostname', '3bdc7952-a334-4d95-8092-cd176546e18a': 'bar.username', + 'c5ba13c4-2391-4045-962e-ec965fc1eb06': 'SRVWIN07', + '2da30969-4127-4ddb-ba0c-2d8ac44d15d7': 'Administrator', }; describe('when showAnonymized is false', () => { @@ -136,4 +138,39 @@ describe('AttackDiscoveryTab', () => { expect(investigateInTimelineButton).toBeInTheDocument(); }); }); + + describe('when multiple substitutions for the same replacement are required', () => { + it('replaces all occurrences', () => { + const detailsMarkdownRequiresMultipleSubstitutions = + '## Microsoft Office spawned PowerShell obfuscation on host {{ host.name c5ba13c4-2391-4045-962e-ec965fc1eb06 }} by user {{ user.name 2da30969-4127-4ddb-ba0c-2d8ac44d15d7 }}\n* **Tactic:** Initial Access, Execution\n* **Technique:** Phishing, Command and Scripting Interpreter\n* **Subtechnique:** Spearphishing Attachment, PowerShell\n\nThe user {{ user.name 2da30969-4127-4ddb-ba0c-2d8ac44d15d7 }} opened a malicious Microsoft Word document ({{ process.parent.executable C:\\Program Files\\Microsoft Office\\root\\Office16\\WINWORD.EXE }}) that dropped and executed a VBScript file ({{ process.parent.args wscript C:\\ProgramData\\WindowsAppPool\\AppPool.vbs }}). This VBScript file then created a scheduled task ({{ process.command_line \\"C:\\Windows\\System32\\cmd.exe\\" /C schtasks /create /F /sc minute /mo 1 /tn \\"\\WindowsAppPool\\AppPool\\" /tr \\"wscript /b \\"C:\\ProgramData\\WindowsAppPool\\AppPool.vbs\\"\\" }}) to execute the VBScript every minute. The VBScript then spawned an obfuscated PowerShell process ({{ process.command_line \\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\\" -exec bypass -file C:\\ProgramData\\WindowsAppPool\\AppPool.ps1 }}). This sequence of events suggests an attempt to gain initial access to the host and establish persistence through scheduled tasks and obfuscated PowerShell scripts.'; + + const expected = `Microsoft Office spawned PowerShell obfuscation on host SRVWIN07 by user Administrator + +Tactic: Initial Access, Execution +Technique: Phishing, Command and Scripting Interpreter +Subtechnique: Spearphishing Attachment, PowerShell + +The user Administrator opened a malicious Microsoft Word document (C:\\Program Files\\Microsoft Office\\root\\Office16\\WINWORD.EXE) that dropped and executed a VBScript file (wscript C:\\ProgramData\\WindowsAppPool\\AppPool.vbs). This VBScript file then created a scheduled task (\\"C:\\Windows\\System32\\cmd.exe\\" /C schtasks /create /F /sc minute /mo 1 /tn \\"\\WindowsAppPool\\AppPool\\" /tr \\"wscript /b \\"C:\\ProgramData\\WindowsAppPool\\AppPool.vbs\\"\\") to execute the VBScript every minute. The VBScript then spawned an obfuscated PowerShell process (\\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\\" -exec bypass -file C:\\ProgramData\\WindowsAppPool\\AppPool.ps1 ). This sequence of events suggests an attempt to gain initial access to the host and establish persistence through scheduled tasks and obfuscated PowerShell scripts.`; + + const mockAttackDiscoveryWithMultipleSubstitutions = { + ...mockAttackDiscovery, + detailsMarkdown: detailsMarkdownRequiresMultipleSubstitutions, + }; + + render( + + + + ); + + const markdownFormatters = screen.getAllByTestId('attackDiscoveryMarkdownFormatter'); + const detailsMarkdown = markdownFormatters[1]; + + expect(detailsMarkdown.textContent).toEqual(expected); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.tsx index a850ed1a5d41b..5958e4a3c4446 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.tsx @@ -34,19 +34,21 @@ const AttackDiscoveryTabComponent: React.FC = ({ const summaryMarkdownWithReplacements = useMemo( () => - Object.entries(replacements ?? {}).reduce( - (acc, [key, value]) => acc.replace(key, value), - summaryMarkdown - ), + Object.entries(replacements ?? {}).reduce((acc, [key, value]) => { + const regex = new RegExp(key, 'g'); + + return acc.replace(regex, value); + }, summaryMarkdown), [replacements, summaryMarkdown] ); const detailsMarkdownWithReplacements = useMemo( () => - Object.entries(replacements ?? {}).reduce( - (acc, [key, value]) => acc.replace(key, value), - detailsMarkdown - ), + Object.entries(replacements ?? {}).reduce((acc, [key, value]) => { + const regex = new RegExp(key, 'g'); + + return acc.replace(regex, value); + }, detailsMarkdown), [detailsMarkdown, replacements] );