Bootloaders.io and LOLdrivers Scanner Modules
In the ever-evolving world of cybersecurity, the need to stay ahead of potential threats and vulnerabilities is paramount. With ATTACKIFY, our Breach & Attack Simulation Platform, we always strive to be at the forefront of this battle. While we’ve been providing numerous modules to automate various cyber threats and suspicious behaviour for some time now, ATTACKIFY also provides a number of security audit modules for various security audit scans and compliance checks of endpoints.
Today, we’re excited to introduce two new security control modules to our platform: the LOLdrivers Scanner and the Bootloaders.io Scanner
A Brief Insight into LOLdrivers.io & Bootloaders.io
Before we touch on the two new modules, let’s take a moment to understand the importance of these two projects by a great team of security researchers.
Bootloaders.io: This community-driven project offers a curated list of all malicious bootloaders known to bypass security controls and execute malicious code. Inspired by Michael Haag’s dedication to hunting evil, this project initially started to track malicious drivers is now extended to bootloaders. We owe a significant amount of gratitude to Jose Enrique Hernandez for his relentless contributions and development efforts towards this project.
Living Off The Land Drivers (LOLdrivers): Like its Bootloaders counterpart, LOLdrivers is a community-driven initiative that provides a curated list of all Windows drivers abused by adversaries. Once again, Michael Haag’s work has been a significant inspiration, and the commitment of Jose Enrique Hernandez to the project is commendable.
Bringing Bootloaders.io & LOLdrivers to ATTACKIFY
As part of our ongoing commitment to enhancing ATTACKIFY's capabilities, integrating these two projects was a no-brainer. Users can now perform scans and analyses of their Windows endpoints, leveraging the data provided by the JSON APIs of both these projects.
The integration works in a straightforward manner:
- 1. Each module takes an inventory of the endpoints (installed drivers or installed bootloaders)
- 2. The latest JSON data is retrieved from either of the projects API endpoint
- 3. The inventory data is analysed, hashed & matched against the JSON data
- 4. Any positive results are sent to ATTACKIFY
ATTACKIFY Bootloader Module Results
To give you a glimpse into our process, we'd like to showcase how the
Bootloaders.io module works, as this will serve as the main talking point of our blog and to provide you with some tooling/scripts.
Bootloaders.io Scanner
The Bootloaders.io scanner module implemented uses the bcdedit.exe
utility on Windows to enumerate installed bootloaders on the endpoint. The module will then parse the output, analyze each file path, and hash it. This hashed values are then compared to the latest Bootloaders.io JSON data to determine if there’s a suspicious or malicious bootloader matched.
To illustrate, here’s a simplified Python script that demonstrates a basic approach:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
| # Bootloaders.io Python scanner
# Author: Gareth Phillips
# Website: www.attackify.com
#
# Usage:
# python bootscan.py
# python bootscan.py -d c:\specific\directory\to_scan
#
import json
import subprocess
import os
import hashlib
import requests
import argparse
def fetch_json(url):
"""Fetch JSON data from a URL."""
print("Fetching JSON data from:", url)
try:
response = requests.get(url)
response.raise_for_status()
print("Successfully fetched JSON data.")
return response.json()
except requests.RequestException as e:
print(f"Error fetching data from {url}: {e}")
return []
def compute_sha256(file_path):
"""Compute the SHA256 hash of a file."""
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
def enumerate_bootloader_paths():
"""Enumerate bootloaders using bcdedit and return the paths."""
print("Enumerating bootloader paths...")
try:
output = subprocess.check_output("bcdedit /enum", shell=True, text=True)
paths = []
partition = None # Store the partition value when we find it
for line in output.split('\n'):
line = line.strip()
# Extract the partition value
if line.startswith('device'):
partition = line.split('partition=')[-1].rstrip(':')
# Extract the path and append it to the partition value
if line.startswith('path') and partition:
path_value = line.split('path')[-1].strip()
full_path = f"{partition}:{path_value}"
paths.append(full_path)
print(f"Found {len(paths)} path(s).")
return paths
except Exception as e:
print(f"Error enumerating bootloaders: {e}")
return []
def scan_directory(directory):
"""Scan a directory for files and return their paths."""
print(f"Scanning directory: {directory}")
file_paths = []
for root, _, files in os.walk(directory):
for file in files:
file_paths.append(os.path.join(root, file))
return file_paths
def is_admin():
"""Check if the script has administrative privileges."""
try:
# Running the `net session` command requires admin rights.
# If this command runs without throwing an exception, it indicates the script has admin rights.
subprocess.check_output("net session", shell=True, stderr=subprocess.STDOUT, text=True)
return True
except subprocess.CalledProcessError:
return False
def main():
# Check for administrative privileges at the beginning
if not is_admin():
print("This script requires administrative privileges. Please run as administrator.")
return
parser = argparse.ArgumentParser(description="Scan for matching bootloaders using provided directory or bcdedit.")
parser.add_argument("-d", "--directory", help="Directory to scan for files", default=None)
args = parser.parse_args()
# Fetch JSON data from URL
url = "https://www.bootloaders.io/api/bootloaders.json"
data = fetch_json(url)
# Enumerate files (either from bootloaders or directory)
if args.directory:
files_to_check = scan_directory(args.directory)
else:
files_to_check = [f for f in enumerate_bootloader_paths()]
matches_found = 0
# Check each file against JSON data
for file_path in files_to_check:
if os.path.exists(file_path):
file_hash = compute_sha256(file_path)
for item in data:
for sample in item.get("KnownVulnerableSamples", []):
if file_hash == sample.get("SHA256"):
print(f"Match found for file at path {file_path} with hash {file_hash}:")
print(f" - Filename: {sample.get('Filename', 'N/A')}")
print(f" - OperatingSystem: {item['Commands'].get('OperatingSystem', 'N/A')}")
print(f" - Privileges: {item['Commands'].get('Privileges', 'N/A')}")
print(f" - Usecase: {item['Commands'].get('Usecase', 'N/A')}")
print(f" - MitreID: {item.get('MitreID', 'N/A')}")
print(f" - CVE: {', '.join(item.get('CVE', ['N/A']))}")
print(f" - SHA256: {sample.get('SHA256', 'N/A')}")
print("---------------------------------------------------------")
matches_found += 1
print(f"Total matches found: {matches_found}")
if __name__ == "__main__":
main()
|
The script will fetch the latest JSON data from the
Bootloaders.io API each time it is run, then enumerate currently installed bootloaders on the endpoint using
bcdedit.exe
, compute the
SHA256 hash of each file found and then compare it to the values in the JSON data to find any possible matches.
You will need to run the script with Administrative privileges
Output from the python scriptYou can also scan a specific directory for matching malicious bootloaders from Bootloaders.io, simply add the -d switch and specify the directory to scan (This will ignore the installed bootloaders lookup on the endpoint).
Below we have included a PowerShell script equivelant of the Python script, it was a little bit tricky parsing the JSON with PowerShells built-in ConvertFrom-Json
and had to resort to using .NETs JavaScriptSerializer
to correctly parse the JSON.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
| # Bootloaders.io PowerShell scanner
# Author: Gareth Phillips
# Website: www.attackify.com
#
# Usage:
# powershell bootscan.ps1
# powershell bootscan.ps1 C:\Windows
#
function Fetch-Json([string]$url) {
Write-Host "Fetching data from: $url"
try {
$response = Invoke-WebRequest -Uri $url -Method Get -UseBasicParsing
# Use .NET's JavaScriptSerializer for JSON parsing
Add-Type -AssemblyName System.Web.Extensions
$serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer
return $serializer.DeserializeObject($response.Content)
} catch {
Write-Host "Error fetching or parsing data: $_"
Write-Host "Error in: $($_.InvocationInfo.Line)"
return $null
}
}
function Compute-SHA256([string]$filePath) {
$hash = Get-FileHash -Path $filePath -Algorithm SHA256
return $hash.Hash.ToLower()
}
function Enumerate-BootloaderPaths {
Write-Host "Enumerating bootloader paths..."
$output = bcdedit /enum
$partition = $null
$paths = @()
$output -split "`n" | ForEach-Object {
$_ = $_.Trim()
if ($_ -match "^device.*partition=(.*):") {
$partition = $matches[1] + ":"
} elseif ($_ -match "^path\s+(.*)") {
if ($partition) {
$paths += "$partition$($matches[1])"
}
}
}
return $paths
}
function Scan-Directory([string]$directory) {
Write-Host "Scanning directory: $directory"
return Get-ChildItem -Path $directory -Recurse -File | ForEach-Object { $_.FullName }
}
function Is-Admin {
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
return $isAdmin
}
# Main logic
if (-not (Is-Admin)) {
Write-Host "This script requires administrative privileges. Please run as administrator."
exit
}
$url = "https://www.bootloaders.io/api/bootloaders.json"
$data = Fetch-Json -url $url
if ($data) {
Write-Host "Data seems ok..."
} else {
Write-Host "Data is null. Skipping further processing."
exit
}
$directory = $args[0] # Assuming the first argument is the directory
if ($directory) {
$filesToCheck = Scan-Directory -directory $directory
} else {
$filesToCheck = Enumerate-BootloaderPaths
}
$matchesFound = 0
foreach ($filePath in $filesToCheck) {
Write-Host " $filePath"
if (Test-Path $filePath) {
$fileHash = Compute-SHA256 -filePath $filePath
foreach ($item in $data) {
if ($item.KnownVulnerableSamples) {
foreach ($sample in $item.KnownVulnerableSamples) {
if ($fileHash -eq $sample.SHA256) {
Write-Host "Match found for file at path ${filePath} with hash ${fileHash}:"
Write-Host " - Filename: $($sample.Filename)"
Write-Host " - OperatingSystem: $($item.Commands.OperatingSystem)"
Write-Host " - Privileges: $($item.Commands.Privileges)"
Write-Host " - Usecase: $($item.Commands.Usecase)"
Write-Host " - MitreID: $($item.MitreID)"
Write-Host " - CVE: $($item.CVE -join ', ')"
Write-Host " - SHA256: $($sample.SHA256)"
Write-Host "---------------------------------------------------------"
$matchesFound++
}
}
}
}
}
}
Write-Host "Total matches found: $matchesFound"
|
Output from the powershell script scanning a specific directoryAdditional Methods
There are several additional methods to enumerate boot configurations and installed bootloaders on Windows endpoints, here are two of them:
1. Windows Management Instrumentation (WMI)
WMI provides a wealth of information about Windows systems, including boot configurations.
You can query the Win32_BootConfiguration
class to get boot configuration data.For instance, using PowerShell: Get-WmiObject -Class Win32_BootConfiguration
, below we have provided a PowerShell script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| # Run script with Administrative permissions
# Fetch boot configuration via WMI
$bootConfig = Get-WmiObject -Class Win32_BootConfiguration
Write-Output "Boot Configuration via WMI:"
$bootConfig | Format-List *
# Fetch BCD store details using bcdedit
Write-Output "`nBCD Store Details:"
bcdedit /enum all
# Fetch details from the EFI partition (if exists)
$efiPartition = Get-Partition | Where-Object { $_.Type -eq 'System' }
if ($efiPartition) {
Write-Output "`nEFI Partition Details:"
Get-ChildItem -Path $efiPartition.AccessPaths[0] -Recurse
}
# Fetch details about bootup hardware configuration
$bootupHardwareConfig = Get-WmiObject -Class Win32_SystemBootConfiguration
Write-Output "`nBootup Hardware Configuration:"
$bootupHardwareConfig | Format-List *
# End of script
Write-Output "`nEnd of boot configuration details."
|
2. Registry
Boot configurations can also be stored in the Windows Registry, particularly under the
HKLM\BCD00000000
key.
Reading the hive might be less intuitive than using bcdedit.exe
however we have provided a PowerShell script to query the data for you directly from the Registry:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| # Run script with Administrative permissions
# Function to recursively fetch registry keys and values
function Get-RegistryKeyRecursive {
param(
[Alias("PSPath")]
[Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[String]$Path
)
$Path = $Path.TrimEnd("\")
$Key = Get-Item -LiteralPath $Path
# Return the key itself
$Key
# Recursively fetch child keys
$Key.GetSubKeyNames() | ForEach-Object {
Get-RegistryKeyRecursive -Path "$Path\$_"
}
}
# Fetch registry details from the BCD00000000 key
$registryPath = "HKLM:\BCD00000000"
Write-Output "`nBoot Configuration Details from Registry:"
Get-RegistryKeyRecursive -Path $registryPath | ForEach-Object {
$currentKey = $_
Write-Output "`nInspecting Key: $currentKey.Name"
$currentKey.GetValueNames() | ForEach-Object {
$valueName = $_
try {
$valueData = $currentKey.GetValue($valueName, $null)
if ($null -eq $valueData) {
Write-Warning "No data found for value $valueName under $($currentKey.Name)"
} else {
Write-Output " $valueName : $valueData"
}
} catch {
Write-Warning "Failed to retrieve value for $valueName under $($currentKey.Name): $($_.Exception.Message)"
}
}
}
|
Verbose output from the Registry queryConclusion
The addition of the LOLdrivers and Bootloaders.io scanner modules to ATTACKIFY is a testament to our commitment to offering users the best tools and resources to help secure their environments, understand what cyber attacks look like and improve their overall security controls and time to detection. Armed with these modules, users can fortify their Windows endpoints against malicious bootloaders and insecure drivers, laying a foundation for exploration into malicious bootloader and Windows driver research.
Stay tuned for more updates and enhancements from ATTACKIFY. Together, let’s make the cyber world a safer place!