LOLDrivers: cercarli tramite PowerShell

| |

Living Off The Land Drivers is a curated list of Windows drivers used by adversaries to bypass security controls and carry out attacks. The project helps security professionals stay informed and mitigate potential threats.loldrivers.io

Sì perché mi ero ripromesso di riprendere questo discorso (del quale non avevo precedentemente scritto), cercando però di portare qualcosa in più rispetto alla notizia pura e cruda che certamente – se sei interessato all’argomento per motivi di lavoro o passione – avrai già letto qualche settimana fa (parliamo dei primi giorni di aprile all’incirca).

Living Off The Land Drivers (LOLDrivers) è un progetto guidato dalla comunità che fornisce un elenco curato di tutti i driver di Windows di cui è stato riscontrato l’abuso da parte di avversari per aggirare i controlli di sicurezza ed eseguire codice dannoso. Il progetto è stato ispirato dal lavoro di Michael Haag – costantemente alla caccia di figure da red team (e non solo) – e dalla necessità di rintracciare i driver dannosi che gli avversari utilizzano per eludere il rilevamento su sistemi altrui. Jose Enrique Hernandez è stato fondamentale per la creazione del progetto e continua a contribuire al suo sviluppo.

Il progetto è completamente open-source e accoglie i contributi della comunità attraverso GitHub. Condividendo conoscenze e competenze ci si può aiutare a vicenda a rimanere informati, dando inoltre la possibilità di difendersi meglio dalle minacce emergenti. Poco importa che siate ricercatori, soccorritori o amministratori di sistema, Living Off The Land Drivers vuole provare a essere una risorsa preziosa nella lotta contro gli attacchi informatici.

Il rilevamento

La tabella dei driver colpiti è costantemente aggiornata e disponibile sul sito web del progetto ma – dovendo lavorare i dati tramite script – la migliore cosa è muoversi tramite i file che contengono tutti quei dati in maniera organizzata. Si lavora via API e si può atterrare sul file CSV o JSON. Gli utenti Sysmon possono utilizzare la configurazione già bella e pronta. Io ho scelto di utilizzare PowerShell e il file JSON, dando un’occhiata a quanto già prodotto e disponibile in rete (si è scatenata la comunità, ed è sempre una gran bella cosa).

Puoi trovare degli esempi pratici a questi indirizzi (e più in generale nella discussione Twitter che è andata alimentandosi):

Io mi sono basato proprio sul lavoro di Conan e l’ho leggermente modificato per andare a produrre un output a video ma anche uno su GitHub (Gist), di nascosto, affinché potessi vedere solo io quei Gist caricati tramite API di GitHub, una cosa assai comoda che ho potuto associare a un processo di distribuzione dello script PowerShell tramite Quest Kace sul parco macchine:

Param(
[Parameter(Mandatory,ValueFromPipeline)][string]$GHToken
)
#Requires -Version 5.1
Set-StrictMode -Version 'latest'
$ErrorActionPreference = 'stop'
[bool]$found = $false
Start-Transcript -Path "$env:TEMP\lolDrivers_Results.txt" -IncludeInvocationHeader -Force
Function New-GitHubGist() {
[cmdletbinding(SupportsShouldProcess,DefaultParameterSetName = "Content")]
Param(
[Parameter(Position = 0, Mandatory, HelpMessage = "What is the name for your gist?",ValueFromPipelineByPropertyName)][ValidateNotNullorEmpty()][string]$Name,
[Parameter(ParameterSetName="path",Mandatory,ValueFromPipelineByPropertyName)][ValidateNotNullorEmpty()][Alias("pspath")][string]$Path,
[Parameter(ParameterSetName="Content",Mandatory)][ValidateNotNullorEmpty()][string[]]$Content,
[Alias("token")][ValidateNotNullorEmpty()][string]$UserToken = $GHToken,
[string]$Description,
[switch]$Private,
[switch]$Passthru
)
Begin {
Write-Verbose "[BEGIN ] Starting: $($MyInvocation.Mycommand)"
#create the header
$head = @{
Authorization = 'Bearer ' + $UserToken
}
#define API uri
$base = "https://api.github.com"
} #begin
Process {
#display PSBoundparameters formatted nicely for Verbose output
[string]$pb = ($PSBoundParameters | Format-Table -AutoSize | Out-String).TrimEnd()
Write-Verbose "[PROCESS] PSBoundparameters: `n$($pb.split("`n").Foreach({"$("`t"*2)$_"}) | Out-String) `n"
#json section names must be lowercase
#format content as a string
switch ($pscmdlet.ParameterSetName) {
"path" {
$gistContent = Get-Content -Path $Path | Out-String
}
"content" {
$gistContent = $Content | Out-String
}
} #close Switch
$data = @{
files = @{$Name = @{content = $gistContent}}
description = $Description
public = (-Not ($Private -as [boolean]))
} | Convertto-Json
Write-Verbose ($data| out-string)
Write-Verbose "[PROCESS] Posting to $base/gists"
If ($pscmdlet.ShouldProcess("$name [$description]")) {
#parameters to splat to Invoke-Restmethod
$invokeParams = @{
Method = 'Post'
Uri = "$base/gists"
Headers = $head
Body = $data
ContentType = 'application/json'
}
$r = Invoke-Restmethod @invokeParams
if ($Passthru) {
Write-Verbose "[PROCESS] Writing a result to the pipeline"
$r | Select @{Name="Url";Expression = {$_.html_url}},
Description,Public,
@{Name = "Created";Expression = {$_.created_at -as [datetime]}}
}
} #should process
} #process
End {
Write-Verbose "[END ] Ending: $($MyInvocation.Mycommand)"
} #end
} #end function
$DirPathDrivers = @(
'C:\WINDOWS\inf'
'C:\WINDOWS\System32\drivers'
'C:\WINDOWS\System32\DriverStore\FileRepository'
)
if ( !(Test-Path -Path 'Variable:lolDriversJson' -PathType Leaf) ) {
[datetime]::Now.ToString('o') | Write-Host -ForegroundColor Cyan
'downloading lolJdriver JSON' | Write-Host -ForegroundColor Cyan
$lolDriversJson = Invoke-RestMethod -Method Get -Uri 'https://www.loldrivers.io/api/drivers.json'
}
$execTimeStart = [datetime]::Now
[datetime]::Now.ToString('o') | Write-Host -ForegroundColor Cyan
'building hashtable of driver files and their hashes' | Write-Host -ForegroundColor Cyan
$htDriverHashPath = [hashtable]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ( $dirverDir in $DirPathDrivers ) {
foreach ( $driverFile in (Get-ChildItem -File -LiteralPath $dirverDir) ) {
foreach ( $hashType in ('SHA256', 'SHA1', 'MD5') ) {
foreach ( $driverFileHash in ($driverFile | Get-FileHash -Algorithm $hashType) ) {
if ( !$htDriverHashPath.ContainsKey($driverFileHash.Hash) ) {
$htDriverHashPath.Add(
$driverFileHash.Hash, @{
'HashType' = $hashType
'path' = $driverFileHash.Path
}
)
}
}
}
}
}
[datetime]::Now.ToString('o') | Write-Host -ForegroundColor Cyan
'looking for lolDriver hash matches' | Write-Host -ForegroundColor Cyan
$htSearchResults = [hashtable]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ( $lolDriver in $lolDriversJson ) {
foreach ( $KnownVulnerableSample in $lolDriver.KnownVulnerableSamples ) {
if ( ($KnownVulnerableSample | Get-Member).Name.Contains('SHA256') ) {
$propNameHashType = 'SHA256'
} elseif (($KnownVulnerableSample | Get-Member).Name.Contains('SHA1')) {
$propNameHashType = 'SHA1'
} elseif ( ($KnownVulnerableSample | Get-Member).Name.Contains('MD5') ) {
$propNameHashType = 'MD5'
} else {
Write-Error -Message ("fix me" + [System.Environment]::NewLine + $KnownVulnerableSample | Out-String)
}
if ( $htDriverHashPath.ContainsKey($KnownVulnerableSample.$propNameHashType) ) {
if (!$htSearchResults.ContainsKey($KnownVulnerableSample.$propNameHashType)) {
$htSearchResults.Add(
$KnownVulnerableSample.$propNameHashType, @{
'driverPath' = $htDriverHashPath.($KnownVulnerableSample.$propNameHashType)
'lolDriver' = $lolDriver
}
)
}
}
}
}
Write-Host
Write-Host
'time (seconds) to run, excluding download of lolDriver JSON' | Write-Host
(New-TimeSpan -Start $execTimeStart -End ([datetime]::Now)).TotalSeconds | Write-Host
Write-Host
Write-Host
[datetime]::Now.ToString('o') | Write-Host -ForegroundColor Cyan
if ( $htSearchResults.Count -eq 0 ) {
'no lolDrivers found' | Write-Host -ForegroundColor Green
} else {
'lolDrivers found!' | Write-Host -ForegroundColor Red
$found = $true
$htSearchResults | ConvertTo-Json | Write-Host
}
Stop-Transcript
if ($found) { New-GitHubGist -Name $env:COMPUTERNAME -Path "$env:TEMP\lolDrivers_Results.txt" -Private -UserToken $GHToken }
Remove-Item "$env:TEMP\lolDrivers_Results.txt"

La procedura di caricamento su GitHub via API si ispira (quasi completamente) a quanto scritto già da Jeffery Hicks (vedi go.gioxx.org/2za90), io ci ho solo messo lo zampino andando a correggere un errore sulla parte relativa all’autenticazione via Bearer e non più il vecchio token personale classico di GitHub (superato e che ti sconsiglio di utilizzare ancora).

Lo script è in grado di funzionare con PowerShell 7 ma anche con quella di sistema già presente su Windows 10/11 (non ho provato su Windows 7 in tutta onestà, lo usi ancora?). Dovrai semplicemente lanciarlo specificando il token (bearer) come parametro, per capirci: lolDrivers_FindMatches.ps1 -GHToken github_pat_XXX_XXX.

Ti lascio un “extra” dedicato a Gist e alla cancellazione di ciò che viene scritto in un solo colpo, sempre via PowerShell:

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Accept", "application/vnd.github+json")
$headers.Add("X-GitHub-Api-Version", "2022-11-28")
$headers.Add("Authorization", "Bearer github_pat_XXX_XXX")

$response = Invoke-RestMethod 'https://api.github.com/users/TUO-UTENTE-GITHUB/gists' -Method 'GET' -Headers $headers
$response | % {
    $gistURL = $_.url
    Write-Host "Delete $($gistURL) ..."
    $delete_gist = Invoke-RestMethod $gistURL -Method 'DELETE' -Headers $headers
    $delete_gist | ConvertTo-Json
}

Dovrai solo modificare Bearer github_pat_XXX_XXX indicando il giusto token (bearer) generato precedentemente e inserire il tuo nome utente all’interno dell’URL https://api.github.com/users/TUO-UTENTE-GITHUB/gists (ovviamente al posto di TUO-UTENTE-GITHUB). Lo script elencherà i tuoi Gist pubblicati (privati compresi) e li cancellerà uno dietro l’altro. Manco a dirlo: NON dovrai usare questo script se non intendi svuotare l’archivio dei tuoi Gist. A me è servito per fare piazza pulita dopo la fase di test e verifica dello script di scansione LOLDrivers (durante la quale mandavo in output forzato le informazioni per debug).

Puoi anche modificarlo per cancellare i Gist solo previa tua conferma a video, non è difficile (ma se hai bisogno di aiuto chiedilo nei commenti e non ti verrà negato certamente).

Ho pubblicato lo script di scansione LOLDrivers completo anche nelle risorse dedicate a Quest Kace su GitHub.

#StaySafe


Riconoscimenti:
docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#list-gists-for-a-user
docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#delete-a-gist
gist.github.com/jdhitsolutions/06cb62bf3eb4f0a1f7d82ed39b1e56ca

Correzioni, suggerimenti? Lascia un commento nell'apposita area qui di seguito o contattami privatamente.
Ti è piaciuto l'articolo? Offrimi un caffè! ☕ :-)

L'articolo potrebbe non essere aggiornato

Questo post è stato scritto più di 5 mesi fa, potrebbe non essere aggiornato. Per qualsiasi dubbio ti invito a lasciare un commento per chiedere ulteriori informazioni! :-)

Condividi l'articolo con i tuoi contatti:
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Commenti
Oldest
Newest Most Voted
Inline Feedbacks
View all comments