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 } }
Le script entier est disponible sur github à cette adresse ! J’espère que cette conclusion vous aura plu, amusez-vous bien !