Les menaces au niveau des microprogrammes (firmware) attirent de plus en plus l’attention des équipes de sécurité en raison de leur capacité à contourner les protections traditionnelles au niveau du système d’exploitation. L’une de ces vulnérabilités émergentes est PKFail, qui implique des clés de plateforme de démarrage sécurisé (PK) compromises ou mal signées. Dans ce contexte, les entreprises doivent s’assurer que leurs systèmes ne sont pas exposés à des configurations de microprogrammes non sécurisées. Pour les professionnels de l’informatique et les fournisseurs de services gérés (MSP), il est essentiel d’automatiser la détection de ces vulnérabilités. Dans cet article, vous apprendrez comment vérifier les vulnérabilités de PKFail avec PowerShell.
Contexte
La vulnérabilité PKFail est centrée sur Secure Boot, une fonction de sécurité UEFI essentielle qui garantit que seuls les micrologiciels et les chargeurs de système d’exploitation approuvés sont exécutés au cours du processus de démarrage. Les systèmes dont les clés de signature sont étiquetées avec des marqueurs tels que « DO NOT TRUST » ou « DO NOT SHIP » sont susceptibles de faire l’objet d’attaques dues à des clés de signature non autorisées ou ne faisant pas partie de la production, qui sont laissées dans des microprogrammes de production.
Des fournisseurs tels qu’Intel et Supermicro ont reconnu cette vulnérabilité, et des outils sont apparus pour faciliter la détection. Cependant, pour les équipes informatiques qui gèrent des flottes d’appareils Windows, une solution scriptable et évolutive est essentielle. Ceci est particulièrement vrai pour les fournisseurs de services gérés (MSP) qui s’appuient sur des plateformes telles que NinjaOne pour maintenir la conformité et la sécurité dans les différents environnements de leurs clients.
Le script
<#
.SYNOPSIS
Checks for the PKFail vulnerability.
.DESCRIPTION
Checks the PK(Platform Key) variable for 'DO NOT TRUST' or 'DO NOT SHIP'.
Can save a result of 'Trusted' or 'Not Trusted' to a custom field.
By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms.
Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party.
Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider.
Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations.
Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks.
Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script.
EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
.EXAMPLE
(No Parameters)
Secure Boot is Trusted
.EXAMPLE
(No Parameters)
Secure Boot is Not Trusted
PARAMETER: -CustomField "ReplaceMeWithAnyMultilineCustomField"
Name of a custom field to save the PKFail status to with either Trusted or Not Trusted.
.EXAMPLE
-CustomField "SecureBootPK"
Attempting to set Custom Field 'SecureBootPK'.
Successfully set Custom Field 'SecureBootPK'!
Secure Boot is Trusted
.NOTES
Minimum OS Supported: Windows 10, Windows Server 2016
.LINK
https://github.com/binarly-io/Vulnerability-REsearch/blob/main/PKfail/BRLY-2024-005.md
https://www.intel.com/content/www/us/en/security-center/announcement/intel-security-announcement-2024-07-25-001.html
https://www.supermicro.com/en/support/security_PKFAIL_Jul_2024
#>
[CmdletBinding()]
param (
[Parameter()]
[String]$CustomField
)
begin {
function Set-NinjaProperty {
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True)]
[String]$Name,
[Parameter()]
[String]$Type,
[Parameter(Mandatory = $True, ValueFromPipeline = $True)]
$Value,
[Parameter()]
[String]$DocumentName
)
$Characters = $Value | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters
if ($Characters -ge 200000) {
throw [System.ArgumentOutOfRangeException]::New("Character limit exceeded: the value is greater than or equal to 200,000 characters.")
}
# If requested to set the field value for a Ninja document, specify it here.
$DocumentationParams = @{}
if ($DocumentName) { $DocumentationParams["DocumentName"] = $DocumentName }
# This is a list of valid fields that can be set. If no type is specified, assume that the input does not need to be changed.
$ValidFields = "Attachment", "Checkbox", "Date", "Date or Date Time", "Decimal", "Dropdown", "Email", "Integer", "IP Address", "MultiLine", "MultiSelect", "Phone", "Secure", "Text", "Time", "URL", "WYSIWYG"
if ($Type -and $ValidFields -notcontains $Type) { Write-Warning "$Type is an invalid type. Please check here for valid types: https://ninjarmm.zendesk.com/hc/en-us/articles/16973443979789-Command-Line-Interface-CLI-Supported-Fields-and-Functionality" }
# The field below requires additional information to set.
$NeedsOptions = "Dropdown"
if ($DocumentName) {
if ($NeedsOptions -contains $Type) {
# Redirect error output to the success stream to handle errors more easily if nothing is found or something else goes wrong.
$NinjaPropertyOptions = Ninja-Property-Docs-Options -AttributeName $Name @DocumentationParams 2>&1
}
}
else {
if ($NeedsOptions -contains $Type) {
$NinjaPropertyOptions = Ninja-Property-Options -Name $Name 2>&1
}
}
# If an error is received with an exception property, exit the function with that error information.
if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions }
# The types below require values not typically given to be set. The code below will convert whatever we're given into a format ninjarmm-cli supports.
switch ($Type) {
"Checkbox" {
# Although it's highly likely we were given a value like "True" or a boolean data type, it's better to be safe than sorry.
$NinjaValue = [System.Convert]::ToBoolean($Value)
}
"Date or Date Time" {
# Ninjarmm-cli expects the GUID of the option to be selected. Therefore, match the given value with a GUID.
$Date = (Get-Date $Value).ToUniversalTime()
$TimeSpan = New-TimeSpan (Get-Date "1970-01-01 00:00:00") $Date
$NinjaValue = $TimeSpan.TotalSeconds
}
"Dropdown" {
# Ninjarmm-cli expects the GUID of the option we're trying to select, so match the value we were given with a GUID.
$Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header "GUID", "Name"
$Selection = $Options | Where-Object { $_.Name -eq $Value } | Select-Object -ExpandProperty GUID
if (-not $Selection) {
throw [System.ArgumentOutOfRangeException]::New("Value is not present in dropdown options.")
}
$NinjaValue = $Selection
}
default {
# All the other types shouldn't require additional work on the input.
$NinjaValue = $Value
}
}
# Set the field differently depending on whether it's a field in a Ninja Document or not.
if ($DocumentName) {
$CustomField = Ninja-Property-Docs-Set -AttributeName $Name -AttributeValue $NinjaValue @DocumentationParams 2>&1
}
else {
$CustomField = $NinjaValue | Ninja-Property-Set-Piped -Name $Name 2>&1
}
if ($CustomField.Exception) {
throw $CustomField
}
}
if ($env:customFieldName -and $env:customFieldName -notlike "null") {
$CustomField = $env:customFieldName
}
# Check if Secure Boot is supported on the system
try {
Get-SecureBootUEFI -Name PK -ErrorAction Stop | Out-Null
}
catch {
if ($_.Exception.Message -like "*Cmdlet not supported on this platform*") {
Write-Host "[Error] System does not support Secure Boot or is a BIOS (Non-UEFI) system."
exit 1
}
elseif ($_.Exception.Message -like "*Variable is currently undefined*") {
Write-Host "[Error] PK variable is not defined."
exit 1
}
}
function Test-IsElevated {
$id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$p = New-Object System.Security.Principal.WindowsPrincipal($id)
$p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
}
if (-not (Test-IsElevated)) {
Write-Host "[Error] Access Denied. Please run with Administrator privileges."
exit 1
}
}
process {
# Secure Boot PK cert status
try {
if ([System.Text.Encoding]::ASCII.GetString((Get-SecureBootUEFI -Name PK).bytes) -match "DO NOT TRUST|DO NOT SHIP") {
$SecureBootPK = "Not Trusted"
Write-Host "[Alert] Secure Boot is $SecureBootPK"
}
else {
$SecureBootPK = "Trusted"
Write-Host "[Info] Secure Boot is $SecureBootPK"
}
}
catch {
Write-Host "[Error] Get-SecureBootUEFI return error: $($_.Exception.Message)"
exit 1
}
if ($CustomField) {
try {
Write-Host "[Info] Attempting to set Custom Field '$CustomField'."
Set-NinjaProperty -Name $CustomField -Value $SecureBootPK
Write-Host "[Info] Successfully set Custom Field '$CustomField'!"
}
catch {
Write-Host "[Warn] Failed to set Custom Field '$CustomField'."
Write-Host "[Warn] $($_.Exception.Message)"
}
}
}
end {
}
Description détaillée
Ce script PowerShell automatise la détection de la vulnérabilité PKFail et enregistre éventuellement le résultat dans un champ personnalisé NinjaOne. Voici comment :
- Initialisation et paramètres
- Le script définit un paramètre -CustomField, qui permet aux utilisateurs de spécifier le nom d’un champ personnalisé NinjaOne dans lequel le résultat (« Trusted » ou « Not Trusted ») sera enregistré.
- Il comprend une fonction d’aide Set-NinjaProperty qui permet d’interfacer avec l’interface en ligne de commande (CLI) de NinjaOne pour définir les valeurs des champs en toute sécurité.
- Vérifications d’environnement et de compatibilité
- Le script vérifie d’abord si le système prend en charge le démarrage sécurisé en interrogeant la variable UEFI PK à l’aide de Get-SecureBootUEFI.
- Si la commande échoue en raison d’une incompatibilité de plateforme (par exemple, pour les systèmes basés sur le BIOS), le script se termine de manière élégante avec un message d’erreur détaillé.
- Validation des privilèges
- Les vérifications de Secure Boot nécessitent des privilèges élevés. Le script vérifie l’accès administratif avant de poursuivre.
- Évaluation des variables PK
- Il récupère le contenu en octets de la variable PK et le convertit en ASCII.
- Si le résultat contient des chaînes de caractères suspectes telles que « DO NOT TRUST » ou « DO NOT SHIP », le système est signalé comme Not Trusted (non fiable). Sinon, il est marqué comme Trusted (fiable).
- Intégration optionnelle de NinjaOne
- Si un champ personnalisé est spécifié, le script tente d’enregistrer l’état de confiance directement dans la plateforme NinjaOne pour une visibilité centralisée.
Suggestion d’aide visuelle:
Un diagramme montrant le flux de travail du script, de la validation des privilèges à l’évaluation PK et à l’intégration NinjaOne, peut constituer un complément précieux à la documentation.
Cas d’utilisation potentiels
Cas de figure : Une entreprise MSP de taille moyenne gérant des terminaux pour un fournisseur de soins de santé
Les politiques de sécurité exigent que tous les terminaux utilisent Secure Boot avec des microprogrammes signés par la production. L’équipe informatique intègre ce script PowerShell dans son processus d’audit de routine via NinjaOne. Le script s’exécute en tant que tâche planifiée et les résultats remplissent un champ personnalisé nommé « SecureBootPK ». Les appareils marqués comme « non fiables » déclenchent des alertes, ce qui permet aux techniciens d’intervenir avant que ne surviennent des problèmes de conformité en matière de sécurité.
Comparaisons
Contrairement aux outils spécifiques aux fournisseurs ou à l’inspection manuelle du BIOS, ce script PowerShell offre :
- Automatisation : Peut être exécuté à distance sur plusieurs appareils via des plateformes RMM.
- Évolutivité : Idéal pour les environnements comportant des centaines ou des milliers d’appareils.
- Flexibilité : S’intègre facilement dans des flux de travail plus larges de mise en conformité ou de gestion des actifs grâce à des champs personnalisés.
D’autres approches, telles que l’utilisation d’outils de fournisseurs comme les analyseurs Intel Converged Security and Management Engine (CSME), sont souvent basées sur une interface graphique et moins propices à l’écriture de scripts dans des environnements gérés.
Questions fréquentes
Q : Quels sont les systèmes qui prennent en charge la fonction Secure Boot et la variable PK ?
R : Seuls les systèmes UEFI prenant en charge Secure Boot (démarrage sécurisé) exposeront la variable PK. Les systèmes basés sur le BIOS ne sont pas compatibles.
Q : Quelles sont les autorisations requises pour exécuter ce script ?
R : Le script doit être exécuté avec des privilèges d’administrateur en raison de l’utilisation de Get-SecureBootUEFI.
Q : Le script peut-il fonctionner sans NinjaOne ?
R : Oui. Si aucun champ personnalisé n’est spécifié, le statut est simplement affiché sur la console.
Q : Que se passe-t-il si la variable PK n’est pas définie ?
R : Le script se termine et informe l’utilisateur que la variable PK n’est pas définie, ce qui peut indiquer une mauvaise configuration ou une désactivation de Secure Boot.
Implications
La détection des systèmes dotés de PK non fiables est une étape essentielle dans la sécurisation du processus de démarrage et la prévention de l’exploitation au niveau du microprogramme. Un seul appareil compromis peut devenir un point d’entrée pour des menaces persistantes avancées. Ce script permet de détecter rapidement les erreurs de configuration de l’amorçage sécurisé et d’implémenter des politiques de sécurité essentielles dans un environnement.
Pour les entreprises MSP, cela permet de fournir des services proactifs en identifiant les vulnérabilités potentielles avant qu’elles ne se transforment en incidents.
Recommandations
- Exécuter périodiquement : Inclure ce script dans les audits réguliers ou les contrôles de conformité.
- Révision et remédiation : Examinez et signez à nouveau les microprogrammes de tous les systèmes marqués comme « Not Trusted » (non fiables).
- Intégrer avec la surveillance et gestion à distance (RMM) : Utilisez NinjaOne pour stocker, surveiller et signaler l’état des PK sur tous les terminaux.
- Informer les clients: Aidez les clients finaux à comprendre la valeur des microprogrammes sécurisés et l’impact des vulnérabilités telles que PKFail sur leur posture de sécurité.
Conclusion
Les vulnérabilités des microprogrammes comme le PKFail soulignent l’importance d’étendre la sécurité au-delà du système d’exploitation. Grâce à PowerShell et à ce script, les professionnels de l’informatique disposent d’une méthode fiable pour automatiser la détection et maintenir la conformité. Utilisé conjointement avec NinjaOne, le processus devient encore plus puissant, vous permettant de suivre l’état de la confiance, de définir des alertes et de prendre des mesures pertinentes à grande échelle.