PowerShell – Exemple2 minute(s) de lecture

Dans cet article nous verrons comment programmer une fonction avancé permettant de lister les comptes locaux d’une machine appartenant au même domaine que la nôtre, dans le style “PowerSploit”, c’est à dire que les informations sont affiché uniquement si le paramètre -Verbose est spécifié et retourne un objet (au lieu de l’afficher). Je vous invite à essayer de créer la fonction en même temps sans voir la solution. La fonction sera testée sur un domaine m’appartenant, le même que celui utilisé dans l’article traitant de l’utilisation offensive de GPO en environnement active directory.

Définition

Ce sera une fonction avancée, elle doit posséder différents arguments, ils sont: Identities (permet de spécifier un/des utilisateur(s) précis à énumérer) il s’agit d’une liste de String ses alias seront Identity ainsi que User et Users. Properties (permet de spécifier une propriété, ou plusieurs, à afficher). Là aussi il s’agit d’une liste de “String” et aura comme alias Property. ComputerName (spécifie la machine cible, par défaut la nôtre) qui est une chaîne de caractère, qui n’est acceptée que si elle n’est ni vide ni nulle. Enfin Credential (qui permet d’utiliser des identifiants alternatifs pour se connecter). La fonction retourne System.Object[] et System.Management.Automation.PSCustomObject. La fonction ressemblera donc à cela:

function Get-NetLocalUsers {
    [OutputType([System.Object[]], [System.Management.Automation.PSCustomObject])]
    [CmdletBinding()]

    param (

        [Parameter(Mandatory=$false, Position=0, ValueFromPipeline=$true)]
        [Alias("Identity", "User", "Users")]
        [String[]]
        $Identities,

        [Parameter(Mandatory=$false, Position=1, ValueFromPipeline=$true)]
        [Alias("Property")]
        [String[]]
        $Properties,

        [Parameter(Mandatory=$false, Position=2, ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $ComputerName = $env:COMPUTERNAME,

        [Parameter(Mandatory=$false, Position=3, ValueFromPipeline=$false)]
        [System.Management.Automation.CredentialAttribute()]
        [System.Management.Automation.PSCredential]
        $Credential = [System.Management.Automation.PSCredential]::Empty
    )
}

Instructions

La fonction n’a pas besoin d’un BEGIN; dans le PROCESS il y aura donc toutes les instructions. Dans le END se situera tout ce qui concerne les valeurs de retours de la fonction. Pour faire les requêtes il sera utilisé System.DirectoryServices.DirectoryEntry les arguments qu’acceptent l’objet son en 1 le chemin, en 2 un nom d’utilisateur et en 3 le mot de passe (ici il faut savoir si l’argument est passé et en fonction adoptée un comportement adapté). Le chemin est constitué d’une chaîne de caractère comme celle-ci WinNT://COMPUTER. Le résultat de l’opération est sauvé dans une variable. Les objets du chemin sont accessibles avec la méthode .Children et les utilisateurs ont leur propriété SchemaClassName égal à user. Il existe nombre de propriétés, en voici quelques-unes Description, Name, objectSid, LogonScript. Le tout est stocké dans un tableau qui sera à la fin retourné (l’idée ici est d’appliquer un filtre sur le champ Name si il est égal à l’utilisateur dans le cas où il est spécifié, ajouter le PSObject à la liste). Voilà à quoi ressemble PROCESS et END:

    PROCESS {

        try {

            if ($PSBoundParameters["Credential"]) {

                Write-Verbose "[Get-NetLocalUsers] Using alternate PSCredential"
                $ADSIObject = New-Object System.DirectoryServices.DirectoryEntry -ArgumentList "WinNT://$ComputerName", $Credential.GetNetworkCredential().UserName, $Credential.GetNetworkCredential().Password
            } else {

                $ADSIObject = New-Object System.DirectoryServices.DirectoryEntry -ArgumentList "WinNT://$ComputerName"
            }
        } catch {

            Write-Verbose "[Get-NetLocalUsers] Error while requesting $_"
            return
        }

        $ADSIUsers = ($ADSIObject.Children | Where-Object {$_.SchemaClassName -eq 'user'})
        $Users = @()
        
        foreach ($user in $ADSIUsers) {

            $psObject = New-Object System.Management.Automation.PSObject -Property @{

                "UserFlags" = $user.UserFlags -as [System.String];
                "MaxStorage" = $user.MaxStorage -as [System.String];
                ...
            }

            if ($PSBoundParameters["Identities"]) {

                foreach ($Identity in $Identities) {

                    if ($psObject.Name -like $Identity) {

                        $Users += $psObject
                    }
                }
            } else {

                $Users += $psObject
            }
        }
    }

    END {

        if ($PSBoundParameters["Properties"]) {

            return ($Users | Select-Object -Property $Properties | Format-List)
        } else {

            return $Users
        }
    }
On dirait bien que notre fonction marche !

Le script entier est disponible sur github à cette adresse ! J’espère que cette conclusion vous aura plu, amusez-vous bien !

Leave a Reply