PI Services

Le blog des collaborateurs de PI Services

Module Powershell SecretStore - Exemple avec API Crowdstrike

SecretStore est un module Powershell permettant de facilement stocker et gérér des Credentials.

Dans l’exemple ci-dessous nous le mettons en place et l’utilisons pour demander, dans cet exemple, un token d’acces au cloud de l’EDR Crowdstrike.

NB : Le module SecretStore requiert l’installation du module SecretManagement

Comme tout module Powershell, si ils ne peut pas être directement téléchargé en ligne sur un repository avec la commande Install-Module, il est possible de le(s) récupérer sur Github ou encore sur Powershell Galery

(https://www.powershellgallery.com/packages/Microsoft.PowerShell.SecretStore)

(https://www.powershellgallery.com/packages/Microsoft.PowerShell.SecretManagement)

Une fois les module décompressé et stocké dans un des dossier contenant en standard des modules (ex : «C:\Program Files\WindowsPowerShell\Modules»)

NB : Si le module n’est pas stocké dans un chemin déclaré dans la variable $env:PSModulePath, son chemin complet devra être renseigné lors de l’import. Ceci peut être problématique aussi après l’import, dans le cadre de certaines commandes du module. Pour cela il est préférable qu’il soit stocké dans un chemin reconnu par la variable $env:PSModulePath

NB : Pour qu’un module soit correctement reconnu par Powershell, le nom du dossier le contenant doit être identique au nom du fichier psd1 ou psm1 :

 C:\Program Files\WindowsPowerShell\Modules \microsoft.powershell.secretstore

Dans une fenêtre powershell, exécuter Import-module en spécifiant le chemin d’accès aux fichiers indiqué ci-dessous

*****

Import-module microsoft.powershell.secretmanagement

Import-module microsoft.powershell.secretstore

*****

Par défaut le module SecretStore requiert un password pour accéder a chaque au coffre-fort de mot passe. La commande suivante permet de spécifier que l’authentification ne sera pas demandée , uniquement pour l’utilisation actuel (CurrentUser)

 

Set-SecretStoreConfiguration -Scope CurrentUser -Authentication None -Interaction None

La commande demande un password qui ne sera pas redemandé

La commande suivante permet de créer le coffre-fort (vault). Indiquer un nom explicite pour l’usage de ce coffre. (La commande requiert d’indiquer le module SecretStore)

Register-SecretVault -ModuleName microsoft.powershell.secretstore -Name MyVault

A présent que le coffre est créé, nous allons y stocker les informations requise, dans cet exemple, pour récupérer un token Crowdstrike.

Dans une variable de type hashtable ($ApiClient) On renseigne le Client Id, Le Client Secret, et le Hostname (url de l’api). On ‘range’  ($ApiClient) dans le coffre MYVault. (Set-secret)

$ApiClient = @{
    ClientId     = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    ClientSecret = ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    Hostname     = 'https://api.eu-1.crowdstrike.com'
}

Set-Secret -Name MyApiClient -Secret $ApiClient -Vault MyVault

On peut tester a présent que le credential peut être récupéré :

Get-Secret -Name MyApiClient -Vault MyVault -AsPlainText

Et dans notre exemple, on peut maintenant récupérer notre token Crowdstrike

Get-Secret -Name MyApiClient -Vault MyVault -AsPlainText | ForEach-Object { Request-FalconToken @_ }

Notre token doit etre valable

$(Test-FalconToken).token

Les cmdlets du module PSFalcon peuvent maintenant être exécutées, par exemple :

Get-FalconHost -All -Detailed

NB : Les modules powershell SecretStore et SecretManagement peuvent même etre supprimé (Remove-Module). Le coffre crée sera toujours accessible par le compte l’ayant crée.

Crowdstrike EDR - Scan de machines cibles

L’EDR Crowdstrike integre bien sur une fonction de scan de machines cibles, a la demande ou planifiés.

Aller sur le lien Endpoint security/On-demand scans

Cliquer Create a scan

Sélectionner Now ou In the future pour planifier un scan

Laisser coché l’option «Limit how long this scan runs» pour limiter la durée du scan. NB : La durée de 2 heures par défaut pourra être adapté au vu de la durée observée en moyenne dans le résultat final du/des scans.

Sélectionner une ou plusieurs et/ou des groupes de machines (la selection dans le champs affiche automatiquement les éléments)

Inscrire un ou plusieurs chemin a scanner (1 par ligne)

Inscrire éventuellement un ou plusieurs chemin a exclure

Le champ «Exclusions to test against above pattern» peut être utilisé pour tester les exclusions.

Dans l’exemple ci-dessus, on veut scanner le lecteur D:\, en excluant tout le contenu (*) de D:\App1

(la coche et la couleur du test effectué  valide le fait que la syntaxe de l’exclusion est correcte. Au contraire, par exemple  ne sera pas exclu)

Le bouton  peut être utilisé pour charger une liste d’exclusion en respectant une ligne par exclusion.

Renseigner une description explicite

Laisser par défaut la niveau de détection et de prévention a Moderate

Pour information la description des niveau de détection est la suivante :

Level

Description

Disabled

Disable all detections or preventions.

Cautious

Detect or prevent only when our machine learning system has high confidence that something is malicious.

Moderate

Detect or prevent when our machine learning system has moderate confidence that something is malicious. We recommend this setting for most use cases. This setting also detects and prevents activity that would be detected or prevented by Cautious.

Aggressive

Detect or prevent when our machine learning system has low confidence that something is malicious. This setting also detects and prevents activity that would be detected or prevented by Moderate and Cautious.

Extra Aggressive

Detect or prevent when our machine learning system has the lowest confidence that something is malicious. This setting also detects and prevents activity that would be detected or prevented by Aggressive, Moderate, and Cautious. 

 

 

Selectionner un niveau maximum de consommation CPU a utiliser (par defaut Low up to 25%)

Décocher Show notifications to end user si vous ne voulez pas que l’utilisateur connecté sur la machine reçoive une notification de Scan (le paramètre Pause duration permet d’autoriser l’utilisateur connecté à reporter le scan pour une durée donnée)

Cliquer Create Scan

 

Le scan apparait dans la liste et passez en état Completed lorsque le scan est fini.

Un clic sur le scan permet d’avoir le détail

Cliquer sur See full details pour afficher les détails complets

Dans notre exemple, la machine cible est bien dans les «Hosts without detection»

Le lien  permet d’accéder si besoin a des éléments de diagnostique/remédiation, dans le cas d’une machine ou des détections ont été remontées :

 

 

 

 

Script - Crowdstrike - Exemple d'utilisation du module PSFalcon

Falcon Crowdsrike est l'un des antivirus next generation (EDR) les plus en vue et les plus performants du marché.  Le script ci-dessous propose un exemple de requetage de l'API de Crowdstrike via l'utilisation du module Powershell PSFalcon (https://www.powershellgallery.com/packages/PSFalcon)

L'acces et l'utilisation de l'Api Crowdstrike requiert bien sur un abonnement Crowdstrike (https://www.crowdstrike.com/)

Le script utilisant pour l'acces a l'API, une clé d'encryptage (AES.key), un fichier contenant le SecID (SecID.txt), et un fichier contenant la passphrase (pass.txt), il est necessaire de recreer ces éléments avec le contexte de l'abonnement auquel on se connecte (SecID).

Le script interroge l'API pour recuperer des éléments tel que les hotes, les comportements (Behaviors), les detections, les incidents.

 

QueryCrowdstrike.ps1 (11,62 kb)

 

##############################################
### QUERY CROWDSTRIKE WITH PSFALCON MODULE ###
##############################################


<# 

    .SYNOPSIS 
        INTERROGATION D'UN TENANT FALCON CROWDSTRIKE VIA L'UTILISATION DU MODULE POWERSHELL PSFALCON
        NB: LES DONNEES SONT EXTRAITES SOUS FORME D'UN FICHIERS CSV 

    .PARAMETER  
        ClientID : Client ID crowdstrike
        Pass : Password du compte d'acces a l'API
        
        ExportFolder : Dossier d'export du fichier CSV
        LogFolder : Chemin du dossier où creer le log du script

 
    .EXAMPLE 
     .\QueryCrowdstrike.ps1 -CloudUrl "https://api.eu-1.crowdstrike.com"  -ClientID <clientid> -Pass <pass> -ExportFolder ./ -LogFolder ./
#>


[CmdletBinding()]
param(
[Parameter(Mandatory,HelpMessage="Cloud URL")]
$CloudUrl,

[Parameter(HelpMessage="Client ID crowdstrike")]
$ClientID,

[Parameter(HelpMessage="Password du compte d'acces a l'API")]
$Pass,

[Parameter(Mandatory,HelpMessage="Dossier d'export du fichier CSV")]
[string]$ExportFolder="./CSV",

[Parameter(Mandatory,HelpMessage="Chemin du dossier où creer le log du script")] 
[string]$LogFolder="./",

[Parameter(Mandatory,HelpMessage="Chemin de la clé utilisée pour l'encryption du CID et de la passphrase")] 
$KeyFile = "D:\MyFolder\CROWDSTRIKE\AES.key",

[Parameter(Mandatory,HelpMessage="Chemin du fichier contenant le SecID du client")] 
$SecIDFile = "D:\MyFolder\CROWDSTRIKE\SecID.txt",

[Parameter(Mandatory,HelpMessage="Chemin du fichier contenant la passphrase")] 
$PassFile = "D:\MyFolder\CROWDSTRIKE\Pass.txt"


)


# TO RENEW KEY FILE USED FOR CLIENT ID AND PASSPHRASE ENCRYPTION
<#
    # Generate Key File
    $Key = New-Object Byte[] 16   # You can use 16, 24, or 32 for AES
    [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key)
    $Key | out-file $KeyFile

#>


# TO RENEW SECID ENCRYPTION, EXECUTE THE CODE BETWEEN <# #>

<#
    # Create secure string object
    $SecIDFile = "D:\MyFolder\CROWDSTRIKE\SecID.txt"
    $Key = Get-Content $KeyFile
    $SecID = read-host  -Prompt "Enter client SecID" -AsSecureString
    $SecID | ConvertFrom-SecureString -key $Key | Out-File $SecIDFile

#>


# TO RENEW PASSPHRASE ENCRYPTION, EXECUTE THE CODE BETWEEN <# #>

<#
    # Create secure string object
    $PassFile = "D:\MyFolder\CROWDSTRIKE\Pass.txt"
    $Key = Get-Content $KeyFile
    $Pass = read-host  -Prompt "Enter client PassPhrase" -AsSecureString
    $Pass | ConvertFrom-SecureString -key $Key | Out-File $PassFile

#>






# OTHER VARIABLES
# Script Name
$ScriptName = "QueryCrowdstrike.ps1"
# LogName = ScriptName without extension
$Log = $ScriptName.Split('.')[0]


# FUNCTIONS


function Write-Log 
{ 
    <# 
    .SYNOPSIS 
        This function creates or appends a line to a log file. 
 
    .PARAMETER  Message 
        The message parameter is the log message you'd like to record to the log file. 
 
    .EXAMPLE 
        PS C:\> Write-Log -Message 'Value1' 
        This example shows how to call the Write-Log function with named parameters. 
    #> 
    [CmdletBinding()] 
    param ( 
        [Parameter(Mandatory)] 
        [string]$Message,
        [Parameter(Mandatory)] 
        [string]$LogPath, 
        [Parameter(Mandatory)] 
        [string]$LogName
        
    ) 
     
    try 
    { 
        $DateTime = Get-Date -Format ‘MM-dd-yy HH:mm:ss’ 
        Add-Content -Value "$DateTime - $Message" -Path "$LogPath\$LogName.log" 
    } 
    catch 
    { 
        Write-Error $_.Exception.Message 
    } 
} 



            




# Requis si l'import du module echoue en en renvoyant plusieurs messages 'Unable to find [System.Net.Http.(...)*]' 
Add-Type -AssemblyName System.Net.Http 

# Import Module
Import-Module -Name D:\MyFolder\CROWDSTRIKE\Powershell_Module\psfalcon-master\PSFalcon.psm1



$Key = Get-Content $KeyFile

# Get file content and Decrypt ClientID
$ClientID = Get-Content $SecIDFile | ConvertTo-SecureString -Key $key
$ClientID = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ClientID))

# Get file content and Decrypt Pass
$Pass = Get-Content $PassFile | ConvertTo-SecureString -Key $key
$Pass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Pass))







# Query for Falcon Token
try
    {
    Request-FalconToken -ClientId $ClientID -ClientSecret $Pass -Hostname $CloudUrl
    }
catch
    {
    $Message = "Error during Request-FalconToken - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


# Test if Test-FalconToken return True
if ($(Test-FalconToken).token -eq $true)  
      {
      $Message = "Token is valid - OK"
      write-host -F Green $Message
      Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
      }
Else
    {
    $Message = "Problem with Token - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }




# GET DATA

#region Falcon Hosts 

# Get Falcon Hosts (exemple option -filter:  –Filter "hostname:'SRV'")
try
    {
    $FalconHosts = Get-FalconHost -All -Detailed
    #$FalconHosts | select hostname,os_version,agent_version,status | ft -AutoSize
    }
catch
    {
    $Message = "Problem during Get-FalconHost - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


# Convert To CSV
 try
    {
    $FalconHostsCSV = $($FalconHosts | select `
        hostname,
        os_version,
        platform_name,
        status,
        agent_version,
        config_id_base,
        config_id_build,
        @{Name='First_Seen';Expression={[datetime]::ParseExact($_.first_seen.Replace('T','').Replace('Z',''),'yyyy-MM-ddHH:mm:ss',$null)}},
        @{Name='Last_Seen';Expression={[datetime]::ParseExact($_.last_seen.Replace('T','').Replace('Z',''),'yyyy-MM-ddHH:mm:ss',$null)}},
        reduced_functionality_mode | ConvertTo-Csv -Delimiter ',' -NoTypeInformation).Replace('"','')
    
    # Display CSV File
    $FalconHostsCSV
    }
catch
    {
    $Message = "Error during convert to CSV file for FalconHosts - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }

# Export CSV
 try
    {   
    $FalconHostsCSV | Out-File -FilePath "$ExportFolder`/FalconHosts.csv" -Force
    }
catch
    {
    $Message = "Error during export of CSV file for FalconHosts - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


#endregion Falcon Hosts 







#region Falcon Detection




#Get-FalconDetection
write-host "FALCON DETECTION EVENTS" -B White -f Blue
try
    {
    $FalconDetection = Get-FalconDetection -Detailed -All  
    $FalconDetection | sort created_timestamp -Descending
    }
catch
    {
    $Message = "Error during Get-FalconDetection - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


# Convert To CSV
 try
    {
    $FalconDetectionCSV = $($FalconDetection | select `
    detection_id,
    @{Name='Detection_Creation_Date';Expression={[datetime]::ParseExact($_[0].created_timestamp.split('.')[0].Replace('T',''),'yyyy-MM-ddHH:mm:ss',$null)}},
    @{Name='HostName';Expression={$_.device.hostname}},
    @{Name='HostIP';Expression={$_.device.local_ip}},
    @{Name='DeviceID';Expression={$_.device.device_id}},
    @{Name='BehaviorID';Expression={$_.Behaviors.Behavior_id}},
    @{Name='Behavior_FileName';Expression={$_.Behaviors.filename}},
    @{Name='Behavior_FilePath';Expression={$_.Behaviors.filepath}},
    @{Name='Behavior_cmdline';Expression={$_.Behaviors.cmdline}},
    status,
    max_severity_displayname,
    email_sent | ConvertTo-Csv -Delimiter ',' -NoTypeInformation).Replace('"','')
    }
catch
    {
    $Message = "Error during convert to CSV file for FalconDetection - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }

# Export CSV
 try
    {   
    $FalconDetectionCSV | Out-File -FilePath "$ExportFolder`/FalconDetection.csv"
    }
catch
    {
    $Message = "Error during export of CSV file for FalconDetection - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }




#endregion Falcon Detection




#region Falcon Behavior

# Get-FalconBehavior (NB: Transform TimeStamp to more readable format with [datetime]::ParseExact)
write-host "FALCON BEHAVIOR EVENTS" -B White -f Blue

try
    {
    $FalconBehavior = Get-FalconBehavior | foreach {Get-FalconBehavior -Ids $_} | Select -property  behavior_id,display_name,objective,@{Name='BehaviorDate';Expression={[datetime]::ParseExact($_.timestamp.Replace('T','').replace('Z',''),'yyyy-MM-ddHH:mm:ss',$null)}},cmdline,filepath,user_name,incident_id,tactic_id,tactic,technique_id,technique | sort BehaviorDate -Descending
    $FalconBehavior
    }
catch
    {
    $Message = "Error during Get-FalconBehavior - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


# Convert To CSV
 try
    {
    $FalconBehaviorCSV = $($FalconBehavior | select BehaviorDate,cmdline,filepath,user_name | ConvertTo-Csv -Delimiter ';' -NoTypeInformation).Replace('"','')
    }
catch
    {
    $Message = "Error during convert to CSV file for FalconBehavior - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }

# Export CSV
 try
    {   
    $FalconBehaviorCSV | Out-File -FilePath "$ExportFolder`/FalconBehavior.csv"
    }
catch
    {
    $Message = "Error during export of CSV file for FalconBehavior - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }



#endregion Falcon Behavior





#region Falcon Incident

write-host "FALCON INCIDENTS EVENTS" -B White -f Blue
try
    {
    $FalconIncidents= Get-FalconIncident -Detailed -All  
    $FalconIncidents | sort created -Descending
    }
catch
    {
    $Message = "Error during Get-FalconIncident - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


# Convert To CSV
 try
    {
    $FalconIncidentsCSV = $($FalconIncidents | select `
    incident_id,
    @{Name='Creation_Date';Expression={[datetime]::ParseExact($_.created.Replace('T','').Replace('Z',''),'yyyy-MM-ddHH:mm:ss',$null)}},
    state,
    email_state  | ConvertTo-Csv -Delimiter ',' -NoTypeInformation).Replace('"','')



        

    }
catch
    {
    $Message = "Error during convert to CSV file for FalconIncident - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }

# Export CSV
 try
    {   
    $FalconIncidentsCSV | Out-File -FilePath "$ExportFolder`/FalconIncidents.csv"
    }
catch
    {
    $Message = "Error during export of CSV file for FalconIncidents - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }




#endregion Falcon Incident