Skip to content

Instantly share code, notes, and snippets.

@trackd
Forked from IISResetMe/Scan-LOLDrivers.ps1
Last active November 1, 2023 11:24
Show Gist options
  • Save trackd/1600e22b5ba8e2453ea165c3eb63eeb1 to your computer and use it in GitHub Desktop.
Save trackd/1600e22b5ba8e2453ea165c3eb63eeb1 to your computer and use it in GitHub Desktop.
minor refactor, outputs objects etc.
function Scan-LOLDrivers {
<#
.EXAMPLE
Scan-LOLDrivers -Path C:\Windows\System32\drivers
$Results = Scan-LOLDrivers -Path C:\Windows\inf
$Results | Select-Object *
$Results[0].all
$Results[0].all.KnownVulnerableSamples
.EXAMPLE
$iwantitall = 'C:\WINDOWS\inf',
'C:\WINDOWS\System32\drivers',
'C:\WINDOWS\System32\DriverStore\FileRepository' |
Scan-LOLDrivers -Verbose
.NOTES
if you are unsure if something is working or not, use -Verbose to see what's happening.
.PARAMETER Path
The path to scan.
.PARAMETER FloodMyScreen
Floods your screen with the results. Useful for debug?
usually enough to just use -Verbose.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs','')]
param(
[Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('FullName')]
[string]
$Path,
[Switch]
$FloodMyScreen
)
begin {
$stopwatch = [Diagnostics.Stopwatch]::StartNew()
Update-TypeData -TypeName 'LOL.please.no.cve.hell' -DefaultDisplayPropertySet 'Created','MitreID','Vendor','FilePath' -Force
if (-Not ('LOLScanner' -as [type])) {
Write-Verbose "Compiling LOLScanner class..."
Add-Type -TypeDefinition @'
using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
public class LOLScanner {
public static string ComputeSha256(string path) {
try {
using (FileStream stream = File.OpenRead(path))
using (SHA256 sha = SHA256.Create()) {
byte[] checksum = sha.ComputeHash(stream);
return BitConverter.ToString(checksum).Replace("-", String.Empty);
}
}
catch (Exception) {
return null;
}
}
public static string GetAuthenticodeHash(string path) {
try {
using (X509Certificate2 cert = new X509Certificate2(path)) {
return BitConverter.ToString(cert.GetCertHash()).Replace("-", String.Empty);
}
}
catch (Exception) {
return null;
}
}
public static string GetAuthenticodeCertSubject(string path) {
try {
using (X509Certificate2 cert = new X509Certificate2(path)) {
return cert.Subject;
}
}
catch (Exception) {
return null;
}
}
}
'@
}
Write-Verbose "Downloading drivers.json..."
$driversJsonUrl = "https://www.loldrivers.io/api/drivers.json"
$ProgressPreference = 'silentlyContinue'
# weird json formatting issue with the init property.
$driverData = (Invoke-WebRequest -Uri $driversJsonUrl).Content.Replace('"init"', '"INIT"') | ConvertFrom-Json
Write-Verbose "Download complete. $($driverdata.count) drivers found."
Write-Verbose "Building correlation tables"
$fileHashes = @{}
$authenticodeHashes = @{}
foreach ($driverInfo in $driverData) {
foreach ($sample in $driverInfo.KnownVulnerableSamples) {
'MD5 SHA1 SHA256'.Split() | ForEach-Object {
$fileHashValue = $sample.$_
if ($fileHashValue) {
$fileHashes[$fileHashValue] = $driverInfo
}
$authCodeHashValue = $sample.Authentihash.$_
if ($authCodeHashValue) {
$authenticodeHashes[$authCodeHashValue] = $driverInfo
}
}
}
}
Write-Verbose "Done building correlation tables for $($fileHashes.count) file hashes and $($authenticodeHashes.count) authenticode hashes."
function Scan-Directory {
[CmdletBinding()]
param(
[string]
$Directory,
[Switch]
$FloodMyScreen
)
Write-Verbose "Starting scan of $Directory"
$sep = '-' * $host.ui.RawUI.WindowSize.Width
$i = 0
Get-ChildItem -Path $Directory -Recurse -File -ErrorVariable aclproblem -ErrorAction SilentlyContinue | ForEach-Object {
$i++
$filePath = $_.FullName
$filehash = [LOLScanner]::ComputeSha256($filePath)
$fileAuthenticodeHash = [LOLScanner]::GetAuthenticodeHash($filePath)
if ($Floodmyscreen) {
Write-Verbose "Hash computed for -> $filePath`nFilehash: $fileHash`nAuthenticode: $fileAuthenticodeHash`n$sep"
}
if ($fileHashes.ContainsKey($fileHash)) {
$driverInfo = $fileHashes[$fileHash]
$Certinfo = [LOLScanner]::GetAuthenticodeCertSubject($filePath)
$vendor = if ($Certinfo -match 'O=(.*?)(,|$)') { $Matches[1] } else { $Certinfo }
Out-LOLDrivers $filePath $fileHash $driverInfo $vendor
}
if ($fileAuthenticodeHash -and $authenticodeHashes.ContainsKey($fileAuthenticodeHash)) {
$driverInfo = $authenticodeHashes[$fileAuthenticodeHash]
$Certinfo = [LOLScanner]::GetAuthenticodeCertSubject($filePath)
$vendor = if ($Certinfo -match 'O=(.*?)(,|$)') { $Matches[1] } else { $Certinfo }
Out-LOLDrivers $filePath $fileAuthenticodeHash $driverInfo $vendor
}
}
$aclproblem | ForEach-Object {
# maybe we should just use error instead, i prefer this.
Write-Warning "$($_.exception.message)"
}
Write-Verbose "Scan complete. $i files scanned in $Directory"
}
function Out-LOLDrivers {
param($filePath,$hash,$driverInfo,$vendor)
return [PSCustomObject]@{
PSTypeName = 'LOL.please.no.cve.hell'
FilePath = $filePath
Hash = $hash
Vendor = $vendor
Category = $driverInfo.Category
Created = $driverInfo.Created
MitreID = $driverinfo.MitreID
Resources = $driverInfo.Resources -join ', '
KnownVulnerableSamples = ($driverInfo.KnownVulnerableSamples.filename | Sort-Object -Unique) -join ', '
Commands = $driverInfo.Commands.Command -join ', '
All = $driverInfo
}
}
}
process {
$splat = @{
Directory = $Path
}
if ($FloodMyScreen) {
$VerbosePreference = 'Continue'
$splat.FloodMyScreen = $true
}
Scan-Directory @splat
}
end {
Write-Verbose "Scan complete. $($stopwatch.Elapsed.TotalSeconds) seconds elapsed."
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment