Récupérer les local security policies via un script Powershell

Written by Sebastien Lambot on . Posted in Articles techniques, Windows

Les « local security policies » d’une machine distante ne peuvent pas être récupérées directement en utilisant WMI ou un cmdlet Powershell. Il faut donc utiliser un moyen détourné pour les récupérer.

La méthode que nous utiliserons ici est la plus simple à mettre en place. Elle consiste à exécuter les étapes suivantes dans l’ordre:

  1. Création d’un processus sur la machine distante afin de lui faire exécuter secdump.exe et d’envoyer le résultat dans un fichier texte.
  2. Récupération du fichier texte sur la machine locale
  3. Traitement du fichier texte local pour le convertir en un objet Powershell
  4. Conversion de l’objet Powershell en fichier XML

Commençons par la création du processus sur la machine distante. Nous avons pour celà créé la fonction « Run-WmiRemote Process », qui prend comme arguments des strings contenant le nom de la machine distante, la commande à exécuter et un timeout (en secondes).


# Run-WmiRemoteProcess
Function Run-WmiRemoteProcess
{
    Param(
        [string]$computername=$env:COMPUTERNAME,
        [string]$cmd=$(Throw "You must enter the full path to the command which will create the process."),
        [int]$timeout = 0
    )

    Write-Host "Process to create on $computername is $cmd"
    [wmiclass]$wmi="\$computernamerootcimv2:win32_process"
    # Exit if the object didn't get created
    if (!$wmi) {return}

    $remote=$wmi.Create($cmd)

    if ($remote.returnvalue -eq 0) {
        Write-Host ("Successfully launched $cmd on $computername with a process id of " + $remote.processid)
    } else {
        Write-Host ("Failed to launch $cmd on $computername. ReturnValue is " + $remote.ReturnValue)
    }

    # Wait for the process to complete or to reach timeout
    $processId = $remote.processid
    $processActive = 1
    while ( $processActive -ge 1) {
        $process = Get-Wmiobject -class win32_process `
        -namespace "rootcimv2" -computerName $computername -Filter "ProcessId = $processId"
        if ($process -ne $null) {
            if ($processActive -ge $timeout -and $timeout -ne 0){
                Write-Host "Remote process execution is taking too long and timed out"
                return
            }
            $processActive++
            Start-Sleep -Seconds 1
        } else {
            Write-Host "Remote process finished"
            $processActive = 0
        }
    }
}

La fonction Run-WmiRemoteProcess instancie une classe Win32_process et lui fait exécuter la ligne de commande qui lui est passée en argument.

Il suffit d’exécuter la commande suivante pour créer le fichier contenant les policies sur la machine distante (qui pour notre test sera localhost):

Run-WmiRemoteProcess 'localhost' 'secedit.exe /export /cfg c:secdump.txt' | Wait-Process
Start-Sleep -Seconds 3  # wait for file to be created

Nous allons ensuite copier ce fichier à l’endroit où se trouve notre script:

[string]$strScriptPath = Split-Path $MyInvocation.MyCommand.Path
$file = ($strScriptPath + "secdump.txt")
Copy-Item \127.0.0.1c`$secdump.txt $file

Pour ensuite le parcourir et récupérer les informations qui nous intéressent afin de construire notre objet grâce à plusieurs fonctions que nous allons créer:

function Get-NameFromSid
{
    Param (
        [String]$currentSid
    )

    $objSID = $null
    $objUser = $null

    try {
        $sid = $currentSid.Replace("`*","")
        $objSID = New-Object System.Security.Principal.SecurityIdentifier ($sid)
        $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
        Write-Host "SID $sid translated to $objUser.Value" return $objUser.Value
    } catch {
        Write-Host "SID $sid could not be translated" return $currentSid
    }
}

# to find the index of an element in an array
function Get-IndexOf {
    Param (
        [object[]]$array, $element
    )

    $line = 0..($array.length - 1) | where {$array[$_] -eq $element}
    return $line
}

# Parse the text file from the secdump and outputs an array of policies
function Parse-SecdumpFileToObject {
    Param (
        [String]$file
    )

    # The array that will be returned
    $policies = @()

    # put the text file to an array
    $fileContent = Get-Content $file

    # Find the delimitations of the security policies
    $start = IndexOf $fileContent "[Privilege Rights]"
    $end = IndexOf $fileContent "[Version]"

    # Extract the security policies between those delimitations
    For ($i = $start+1; $i -lt $end; $i++) {
        $policy = New-Object Object
        $line = $fileContent[$i].split(" =")

        # Add policy name to the policy
        Add-Member -memberType NoteProperty -name name -value $line[0] -inputObject $policy
        # Extract array of members, translate the SIDs, and add the members array to the policy
        $members = $line[3].split(",")
        For ($j = 0; $j -lt $members.Count; $j++) {
            if ($members[$j] -like "``**") {
                $members[$j] = Get-NameFromSid $members[$j]
            }
        }
        Add-Member -memberType NoteProperty -name members -value $members -inputObject $policy

        # Add the policy to the "policies" array
        $policies += $policy
    }
    return $policies
}

Nous récupérons le résultat grâce à la ligne suivante:

$dumpResult = Parse-SecdumpFileToObject $file
Start-Sleep -Seconds 1

Nous supprimons les fichiers temporaires:

Remove-Item \127.0.0.1c`$secdump.txt
Remove-Item $file

Et nous pouvons convertir notre objet en fichier XML et le sauver:

# convert the dump to XML to a test file
$XMLDump = $dumpResult | ConvertTo-XML -NoTypeInformation
# Save Dump Data in the Output File
$XMLDump.Save("secdump.xml")

Si tout s’est bien déroulé, vous devriez voir le fichier XML apparaître dans le même répertoire que votre script et celui-ci devrait contenir toutes les local security policies.

Tags: ,

Trackback from your site.

Leave a comment

You must be logged in to post a comment.