Filter out empty values for exceptions (#106685)
This commit is contained in:
parent
ced9aecab5
commit
ee04f6dc95
|
@ -823,7 +823,8 @@ describe('Exception helpers', () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
describe('ransomware protection exception items', () => {
|
||||
test('it should return pre-populated ransomware items for event code `ransomware`', () => {
|
||||
const defaultItems = defaultEndpointExceptionItems('list_id', 'my_rule', {
|
||||
_id: '123',
|
||||
|
@ -938,7 +939,9 @@ describe('Exception helpers', () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('memory protection exception items', () => {
|
||||
test('it should return pre-populated memory signature items for event code `memory_signature`', () => {
|
||||
const defaultItems = defaultEndpointExceptionItems('list_id', 'my_rule', {
|
||||
_id: '123',
|
||||
|
@ -990,6 +993,44 @@ describe('Exception helpers', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
test('it should return pre-populated memory signature items for event code `memory_signature` and skip Empty', () => {
|
||||
const defaultItems = defaultEndpointExceptionItems('list_id', 'my_rule', {
|
||||
_id: '123',
|
||||
process: {
|
||||
name: '', // name is empty
|
||||
// executable: '', left intentionally commented
|
||||
hash: {
|
||||
sha256: 'some hash',
|
||||
},
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Memory_protection: {
|
||||
feature: 'signature',
|
||||
},
|
||||
event: {
|
||||
code: 'memory_signature',
|
||||
},
|
||||
});
|
||||
|
||||
// should not contain name or executable
|
||||
expect(defaultItems[0].entries).toEqual([
|
||||
{
|
||||
field: 'Memory_protection.feature',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: 'signature',
|
||||
id: '123',
|
||||
},
|
||||
{
|
||||
field: 'process.hash.sha256',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: 'some hash',
|
||||
id: '123',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('it should return pre-populated memory shellcode items for event code `malicious_thread`', () => {
|
||||
const defaultItems = defaultEndpointExceptionItems('list_id', 'my_rule', {
|
||||
_id: '123',
|
||||
|
@ -1085,7 +1126,115 @@ describe('Exception helpers', () => {
|
|||
value: '4000',
|
||||
id: '123',
|
||||
},
|
||||
{ field: 'region_size', operator: 'included', type: 'match', value: '4000', id: '123' },
|
||||
{
|
||||
field: 'region_size',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: '4000',
|
||||
id: '123',
|
||||
},
|
||||
{
|
||||
field: 'region_protection',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: 'RWX',
|
||||
id: '123',
|
||||
},
|
||||
{
|
||||
field: 'memory_pe.imphash',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: 'a hash',
|
||||
id: '123',
|
||||
},
|
||||
],
|
||||
id: '123',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('it should return pre-populated memory shellcode items for event code `malicious_thread` and skip empty', () => {
|
||||
const defaultItems = defaultEndpointExceptionItems('list_id', 'my_rule', {
|
||||
_id: '123',
|
||||
process: {
|
||||
name: '', // name is empty
|
||||
// executable: '', left intentionally commented
|
||||
Ext: {
|
||||
token: {
|
||||
integrity_level_name: 'high',
|
||||
},
|
||||
},
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Memory_protection: {
|
||||
feature: 'shellcode_thread',
|
||||
self_injection: true,
|
||||
},
|
||||
event: {
|
||||
code: 'malicious_thread',
|
||||
},
|
||||
Target: {
|
||||
process: {
|
||||
thread: {
|
||||
Ext: {
|
||||
start_address_allocation_offset: 0,
|
||||
start_address_bytes_disasm_hash: 'a disam hash',
|
||||
start_address_details: {
|
||||
// allocation_type: '', left intentionally commented
|
||||
allocation_size: 4000,
|
||||
region_size: 4000,
|
||||
region_protection: 'RWX',
|
||||
memory_pe: {
|
||||
imphash: 'a hash',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// no name, no exceutable, no allocation_type
|
||||
expect(defaultItems[0].entries).toEqual([
|
||||
{
|
||||
field: 'Memory_protection.feature',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: 'shellcode_thread',
|
||||
id: '123',
|
||||
},
|
||||
{
|
||||
field: 'Memory_protection.self_injection',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: 'true',
|
||||
id: '123',
|
||||
},
|
||||
{
|
||||
field: 'process.Ext.token.integrity_level_name',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: 'high',
|
||||
id: '123',
|
||||
},
|
||||
{
|
||||
field: 'Target.process.thread.Ext.start_address_details',
|
||||
type: 'nested',
|
||||
entries: [
|
||||
{
|
||||
field: 'allocation_size',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: '4000',
|
||||
id: '123',
|
||||
},
|
||||
{
|
||||
field: 'region_size',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: '4000',
|
||||
id: '123',
|
||||
},
|
||||
{
|
||||
field: 'region_protection',
|
||||
operator: 'included',
|
||||
|
|
|
@ -343,6 +343,29 @@ export const getCodeSignatureValue = (
|
|||
}
|
||||
};
|
||||
|
||||
// helper type to filter empty-valued exception entries
|
||||
interface ExceptionEntry {
|
||||
value?: string;
|
||||
entries?: ExceptionEntry[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of Entries and filter out the ones with empty values.
|
||||
* It will also filter out empty values for nested entries.
|
||||
*/
|
||||
function filterEmptyExceptionEntries<T extends ExceptionEntry>(entries: T[]): T[] {
|
||||
const finalEntries: T[] = [];
|
||||
for (const entry of entries) {
|
||||
if (entry.entries !== undefined) {
|
||||
entry.entries = entry.entries.filter((el) => el.value !== undefined && el.value.length > 0);
|
||||
finalEntries.push(entry);
|
||||
} else if (entry.value !== undefined && entry.value.length > 0) {
|
||||
finalEntries.push(entry);
|
||||
}
|
||||
}
|
||||
return finalEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default values from the alert data to autofill new endpoint exceptions
|
||||
*/
|
||||
|
@ -510,34 +533,35 @@ export const getPrepopulatedMemorySignatureException = ({
|
|||
alertEcsData: Flattened<Ecs>;
|
||||
}): ExceptionsBuilderExceptionItem => {
|
||||
const { process } = alertEcsData;
|
||||
const entries = filterEmptyExceptionEntries([
|
||||
{
|
||||
field: 'Memory_protection.feature',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: alertEcsData.Memory_protection?.feature ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.executable.caseless',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: process?.executable ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.name.caseless',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: process?.name ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.hash.sha256',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: process?.hash?.sha256 ?? '',
|
||||
},
|
||||
]);
|
||||
return {
|
||||
...getNewExceptionItem({ listId, namespaceType: listNamespace, ruleName }),
|
||||
entries: addIdToEntries([
|
||||
{
|
||||
field: 'Memory_protection.feature',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: alertEcsData.Memory_protection?.feature ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.executable.caseless',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: process?.executable ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.name.caseless',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: process?.name ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.hash.sha256',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: process?.hash?.sha256 ?? '',
|
||||
},
|
||||
]),
|
||||
entries: addIdToEntries(entries),
|
||||
};
|
||||
};
|
||||
export const getPrepopulatedMemoryShellcodeException = ({
|
||||
|
@ -554,81 +578,83 @@ export const getPrepopulatedMemoryShellcodeException = ({
|
|||
alertEcsData: Flattened<Ecs>;
|
||||
}): ExceptionsBuilderExceptionItem => {
|
||||
const { process, Target } = alertEcsData;
|
||||
const entries = filterEmptyExceptionEntries([
|
||||
{
|
||||
field: 'Memory_protection.feature',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: alertEcsData.Memory_protection?.feature ?? '',
|
||||
},
|
||||
{
|
||||
field: 'Memory_protection.self_injection',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: String(alertEcsData.Memory_protection?.self_injection) ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.executable.caseless',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: process?.executable ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.name.caseless',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: process?.name ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.Ext.token.integrity_level_name',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: process?.Ext?.token?.integrity_level_name ?? '',
|
||||
},
|
||||
{
|
||||
field: 'Target.process.thread.Ext.start_address_details',
|
||||
type: 'nested' as const,
|
||||
entries: [
|
||||
{
|
||||
field: 'allocation_type',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: Target?.process?.thread?.Ext?.start_address_details?.allocation_type ?? '',
|
||||
},
|
||||
{
|
||||
field: 'allocation_size',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: String(Target?.process?.thread?.Ext?.start_address_details?.allocation_size) ?? '',
|
||||
},
|
||||
{
|
||||
field: 'region_size',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value: String(Target?.process?.thread?.Ext?.start_address_details?.region_size) ?? '',
|
||||
},
|
||||
{
|
||||
field: 'region_protection',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value:
|
||||
String(Target?.process?.thread?.Ext?.start_address_details?.region_protection) ?? '',
|
||||
},
|
||||
{
|
||||
field: 'memory_pe.imphash',
|
||||
operator: 'included' as const,
|
||||
type: 'match' as const,
|
||||
value:
|
||||
String(Target?.process?.thread?.Ext?.start_address_details?.memory_pe?.imphash) ?? '',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
return {
|
||||
...getNewExceptionItem({ listId, namespaceType: listNamespace, ruleName }),
|
||||
entries: addIdToEntries([
|
||||
{
|
||||
field: 'Memory_protection.feature',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: alertEcsData.Memory_protection?.feature ?? '',
|
||||
},
|
||||
{
|
||||
field: 'Memory_protection.self_injection',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: String(alertEcsData.Memory_protection?.self_injection) ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.executable.caseless',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: process?.executable ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.name.caseless',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: process?.name ?? '',
|
||||
},
|
||||
{
|
||||
field: 'process.Ext.token.integrity_level_name',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: process?.Ext?.token?.integrity_level_name ?? '',
|
||||
},
|
||||
{
|
||||
field: 'Target.process.thread.Ext.start_address_details',
|
||||
type: 'nested',
|
||||
entries: [
|
||||
{
|
||||
field: 'allocation_type',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: Target?.process?.thread?.Ext?.start_address_details?.allocation_type ?? '',
|
||||
},
|
||||
{
|
||||
field: 'allocation_size',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value:
|
||||
String(Target?.process?.thread?.Ext?.start_address_details?.allocation_size) ?? '',
|
||||
},
|
||||
{
|
||||
field: 'region_size',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: String(Target?.process?.thread?.Ext?.start_address_details?.region_size) ?? '',
|
||||
},
|
||||
{
|
||||
field: 'region_protection',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value:
|
||||
String(Target?.process?.thread?.Ext?.start_address_details?.region_protection) ?? '',
|
||||
},
|
||||
{
|
||||
field: 'memory_pe.imphash',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value:
|
||||
String(Target?.process?.thread?.Ext?.start_address_details?.memory_pe?.imphash) ?? '',
|
||||
},
|
||||
],
|
||||
},
|
||||
]),
|
||||
entries: addIdToEntries(entries),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether or not any entries within the given exceptionItems contain values not in the specified ECS mapping
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue