Skip to content

Commit

Permalink
Add Windows.Forensics.UEFI updates (#46)
Browse files Browse the repository at this point in the history
Add a bunch of UEFI updates including Windows.Detection.Yara.UEFI
  • Loading branch information
mgreen27 authored Nov 28, 2023
1 parent 7e92e71 commit 163b3d4
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 23 deletions.
184 changes: 184 additions & 0 deletions artifacts/definitions/Windows/Detection/Yara/Yara.UEFI.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
name: Windows.Detection.Yara.UEFI
author: Matt Green - @mgreen27
description: |
This artifact enables running yara over files in an EFI System Partition (ESP).
parameters:
- name: ImagePath
default: \\.\PhysicalDrive0
description: Raw Device for main disk containing partition table to parse.
- name: SectorSize
type: int
default: 512
- name: TargetGlob
default: "**/*.efi"
- name: SizeMax
description: maximum size of target file.
type: int64
- name: SizeMin
description: minimum size of target file.
type: int64
- name: UploadHits
type: bool
- name: DateAfter
type: timestamp
description: "search for events after this date. YYYY-MM-DDTmm:hh:ssZ"
- name: DateBefore
type: timestamp
description: "search for events before this date. YYYY-MM-DDTmm:hh:ssZ"
- name: YaraUrl
description: If configured will attempt to download Yara rules form Url
type: upload
- name: YaraRule
type: yara
description: Final Yara option and the default if no other options provided.
default: |
rule win_blacklotus_auto {
meta:
author = "Felix Bilstein - yara-signator at cocacoding dot com"
date = "2023-07-11"
description = "Detects win.blacklotus."
strings:
$sequence_0 = { 498bcf e8???????? 448bc0 498bd7 4d03c0 488bce }
$sequence_1 = { 4c897020 55 488d68c8 4881ec30010000 4c8bd1 }
$sequence_2 = { 488b0d???????? 4c8d054e140100 488bd7 488bd8 e8???????? 488b05???????? 488bcb }
$sequence_3 = { 8b0c91 498bd2 4903c9 e8???????? }
$sequence_4 = { 4585d2 743f 8b05???????? 4103c1 }
$sequence_5 = { 4883ec20 488d7910 8bea 488b1f 33f6 }
$sequence_6 = { 488bd9 b10e e8???????? 8a4b02 40b70d }
$sequence_7 = { 410f47f7 8bc6 488b742460 4883c430 415f }
$sequence_8 = { 4923d3 4803d1 440fb74a0c 440fb7520e }
$sequence_9 = { 6642837cc11010 0f859d000000 428b54c114 41bbffffff7f 4923d3 }
condition:
7 of them and filesize < 181248
}
rule MAL_Rootkit_CosmicStrand
{
meta:
author = "Natalie Zargarov @ Rapid7"
description = "CosmicStrand UEFI rootkit detection rule. Detects the compromised .efi driver "
targeting = "process,efi"
tags ="Rootkit"
strings:
$trait_0 = {89 C6 53 89 D8 BB 3F B8 11 03}
$trait_1 = {8B 3D 18 10 01 00 33 DB 8D 45 F8 50 53 53 6A 0B}
$trait_2= {83 EC 4C 53 57 68 A0 10 01 00 8D 45 F8}
$trait_3= {53 81 C7 FF 0F 00 00 68 54 44 55 00 81 E7 00 F0 FF FF 57 6A 01}
$trait_4= {50 68 08 08 08 08 68 D0 43 DE DE 68 1F 96 00 00 BF E4 10 01 00}
$string_0 = "winlogon.exe"
condition:
1 of ($trait_*) and
1 of ($string_*)
}
- name: NumberOfHits
description: This artifact will stop by default at one hit. This setting allows additional hits
default: 1
type: int
- name: ContextBytes
description: Include this amount of bytes around hit as context.
default: 0
type: int

sources:
- query: |
-- check which Yara to use
LET yara_rules <= YaraUrl || YaraRule
-- time testing
LET time_test(stamp) =
if(condition= DateBefore AND DateAfter,
then= stamp < DateBefore AND stamp > DateAfter,
else=
if(condition=DateBefore,
then= stamp < DateBefore,
else=
if(condition= DateAfter,
then= stamp > DateAfter,
else= True
)))
LET find_efi = SELECT StartOffset,EndOffset,
Size AS PartitionSize,
name AS PartitionName
FROM Artifact.Windows.Forensics.PartitionTable(
ImagePath=ImagePath, SectorSize=SectorSize)
WHERE PartitionName =~ "EFI"
LET find_files = SELECT * FROM foreach(row=find_efi,
query={
SELECT *,
StartOffset,EndOffset,
PartitionSize,
PartitionName
FROM glob(globs=TargetGlob,
accessor="fat",
root=pathspec(
DelegateAccessor="offset",
DelegatePath=pathspec(
DelegateAccessor="raw_file",
DelegatePath=ImagePath,
Path=format(format="%d", args=StartOffset))))
})
LET target_files = SELECT
StartOffset as PartitionOffset, PartitionSize,
OSPath,
Size, Mtime, Atime, Ctime, Btime,
Data.first_cluster as FirstCluster,
Data.attr AS Attr,
Data.deleted as IsDeleted,
Data.short_name AS ShortName
FROM find_files
WHERE NOT IsDir
AND if(condition=SizeMin,
then= SizeMin < Size,
else= True)
AND if(condition=SizeMax,
then= SizeMax > Size,
else= True)
AND ( time_test(stamp=Mtime)
OR time_test(stamp=Atime)
OR time_test(stamp=Ctime)
OR time_test(stamp=Btime))
-- scan files and prepare hit metadata
LET hits = SELECT * FROM foreach(row=target_files,
query={
SELECT
OSPath as _OSPath,
OSPath.Path as OSPath,
File.Size as Size,
Mtime, Atime, Ctime, Btime,
Rule, Tags, Meta,
String.Name as YaraString,
String.Offset as HitOffset,
upload( accessor='scope',
file='String.Data',
name=format(format="%v-%v-%v",
args=[
OSPath.Path,
if(condition= String.Offset - ContextBytes < 0,
then= 0,
else= String.Offset - ContextBytes),
if(condition= String.Offset + ContextBytes > Size,
then= Size,
else= String.Offset + ContextBytes) ]
)) as HitContext
FROM yara( accessor='fat', rules=yara_rules,files=OSPath,
context=ContextBytes,number=NumberOfHits )
})
-- upload files if selected
LET upload_hits = SELECT *,
upload(accessor='fat',file=_OSPath,name=_OSPath.Path) as Upload
FROM hits
-- return rows
SELECT * FROM if(condition= UploadHits,
then= upload_hits,
else= hits )
column_types:
- name: HitContext
type: preview_upload
77 changes: 58 additions & 19 deletions artifacts/definitions/Windows/Forensics/UEFI.yaml
Original file line number Diff line number Diff line change
@@ -1,33 +1,72 @@
name: Windows.Forensics.UEFI
author: Matt Green - @mgreen27
description: |
Searches for an EFI partition in the current partition table and
enumerates all files in it.
This artifact enables disk analysis over an EFI System Partition (ESP).
The artifact queries the specified pysical disk, parses the partition table
to targets the ESPs File Allocation Table (FAT).
The default artifact returns file information, and PE enrichment as typical EFI files are in the PE format.
We can looks for anomalities in EFI such as:
- unexpected time stamps outside install / OS updates
- unexpected paths (EFI/ is typically the root folder on this partition)
- unexpected metadata: signer non microsoft or known vendor (note we expect non trusted certificates here as the authenticode api does not service ESP binaries)
NOTE: default returns EFI files, rerun with ```TargetGlob=**/*``` glob and
return all files.
parameters:
- name: ImagePath
default: "\\\\?\\GLOBALROOT\\Device\\Harddisk0\\DR0"
default: \\.\PhysicalDrive0
description: Raw Device for main disk containing partition table to parse.
- name: SectorSize
type: int
default: 512
- name: TargetGlob
default: "**/*.efi"

sources:
- query: |
SELECT * FROM foreach(row={
SELECT *, Size AS PartitionSize
LET find_efi = SELECT StartOffset,EndOffset,
Size AS PartitionSize,
name AS PartitionName
FROM Artifact.Windows.Forensics.PartitionTable(
ImagePath=ImagePath, SectorSize=SectorSize)
WHERE name =~ "EFI"
}, query={
SELECT StartOffset, PartitionSize, name,
OSPath.Path AS OSPath, Size, Mtime, Atime, Ctime, Data
FROM glob(globs="/*",
accessor="fat",
root=pathspec(
DelegateAccessor="offset",
DelegatePath=pathspec(
DelegateAccessor="raw_file",
DelegatePath=ImagePath,
Path=format(format="%d", args=StartOffset))))
})
WHERE PartitionName =~ "EFI"
LET find_files = SELECT * FROM foreach(row=find_efi,
query={
SELECT *,
StartOffset as PartitionOffset,
PartitionSize,
PartitionName
FROM glob(globs=TargetGlob,
accessor="fat",
root=pathspec(
DelegateAccessor="offset",
DelegatePath=pathspec(
DelegateAccessor="raw_file",
DelegatePath=ImagePath,
Path=format(format="%d", args=StartOffset))))
})
SELECT
dict(
ImagePath=ImagePath,
PartitionOffset=PartitionOffset,
PartitionSize=PartitionSize,
PartitionName=PartitionName
) as Partition,
OSPath.Path as OSPath,
Size, Mtime, Atime, Ctime, Btime,
Data.first_cluster as FirstCluster,
Data.attr AS Attr,
Data.deleted as IsDeleted,
Data.short_name AS ShortName,
hash(accessor='fat',path=OSPath) as Hash,
magic(accessor='fat',path=OSPath) as Magic,
parse_pe(accessor='fat',file=OSPath) as PEInfo,
authenticode(accessor='fat',filename=OSPath) as Authenticode
FROM find_files
3 changes: 1 addition & 2 deletions artifacts/testdata/server/testcases/yara_detection.in.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Parameters:
uint32(uint32(0x3C)) == 0x00004550
}
Queries:
# Setup our mocks --MFT needs to be mocked as below to pass srcDir
- |
Expand Down Expand Up @@ -105,7 +104,7 @@ Queries:
Size,Rule,Mtime,Atime,Ctime,Btime
FROM Artifact.Generic.Detection.Yara.Glob(DateBefore='2021-04-01',ContextBytes=10,YaraRule=MZRule)

# test Windows.Detection.Yara.Device (shoudl also work cross platform)
# test Windows.Detection.Yara.Device (should also work cross platform)
- SELECT relpath(path=DevicePath, base=srcDir, sep="/") as TestPath,
CleanContext(HitContext=HitContext),
StartOffest,ScanLength,Rule,YaraString,HitOffset
Expand Down
29 changes: 28 additions & 1 deletion artifacts/testdata/server/testcases/yara_detection_zip.in.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
Parameters:
PartitionTable: |
[{
"StartOffset": 0,
"EndOffset": 1000,
"Size": "25MB",
"name": "EFI system partition",
"TopLevelDirectory": "a",
"Magic": "EFI test file"
}]
YaraRule: |
rule HelloWorld {
strings:
$a = "hello world" nocase wide ascii
condition:
any of them
}
Queries:
# Setup our mocks UEFI needs to be mocked as below to pass srcDir
- LET _ <= SELECT mock(artifact=Artifact.Windows.Forensics.PartitionTable, results=parse_json_array(data=PartitionTable))
FROM scope()
- SELECT relpath(path=Container, base=srcDir, sep="/") as RelativeContainer,relpath(path=ExtractedPath, base=srcDir,sep="/") as RelativeExtracted,FilePath,Rule,Size,Mtime,Atime,Ctime,Btime,Hash
FROM Artifact.Generic.Detection.Yara.Zip(TargetGlob=srcDir + '/artifacts/testdata/files/compressedpe.zip')
FROM Artifact.Generic.Detection.Yara.Zip(TargetGlob=srcDir + '/artifacts/testdata/files/compressedpe.zip')

# test Windows.Detection.Yara.UEFI
# OSPath, relpath(path=DevicePath, base=srcDir, sep="/") as TestPath, Rule, HitOffset
- SELECT OSPath, Size, Rule, HitOffset
FROM Artifact.Windows.Detection.Yara.UEFI(ImagePath=srcDir + "/artifacts/testdata/files/fat_very_small.dd",TargetGlob="**/*",YaraRule=YaraRule)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SELECT relpath(path=Container, base=srcDir, sep="/") as RelativeContainer,relpath(path=ExtractedPath, base=srcDir,sep="/") as RelativeExtracted,FilePath,Rule,Size,Mtime,Atime,Ctime,Btime,Hash FROM Artifact.Generic.Detection.Yara.Zip(TargetGlob=srcDir + '/artifacts/testdata/files/compressedpe.zip')[
LET _ <= SELECT mock(artifact=Artifact.Windows.Forensics.PartitionTable, results=parse_json_array(data=PartitionTable)) FROM scope()[]SELECT relpath(path=Container, base=srcDir, sep="/") as RelativeContainer,relpath(path=ExtractedPath, base=srcDir,sep="/") as RelativeExtracted,FilePath,Rule,Size,Mtime,Atime,Ctime,Btime,Hash FROM Artifact.Generic.Detection.Yara.Zip(TargetGlob=srcDir + '/artifacts/testdata/files/compressedpe.zip')[
{
"RelativeContainer": "artifacts/testdata/files/compressedpe.zip",
"RelativeExtracted": "artifacts/testdata/files/compressedpe.zip -\u003e /firstfolder/secondlevel.zip -\u003e /folder/smallest.zip -\u003e /smallest/smallexe64.exe",
Expand All @@ -15,4 +15,11 @@ SELECT relpath(path=Container, base=srcDir, sep="/") as RelativeContainer,relpat
"SHA256": "7895875505da2ba49261622f30ae1d8cd825a85eded354f2e676e991c52e2f23"
}
}
]SELECT OSPath, Size, Rule, HitOffset FROM Artifact.Windows.Detection.Yara.UEFI(ImagePath=srcDir + "/artifacts/testdata/files/fat_very_small.dd",TargetGlob="**/*",YaraRule=YaraRule)[
{
"OSPath": "a\\b\\hello.txt",
"Size": 12,
"Rule": "HelloWorld",
"HitOffset": 0
}
]

0 comments on commit 163b3d4

Please sign in to comment.