PowerShell – Exemple

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 !

Laisser un commentaire