Avant d’être un outil puissant et très pratique pour la phase de latéralisation durant un test d’intrusion en environnement Active Directory, PowerShell Remoting est surtout une solution efficace pour administrer une machine en ligne de commande sous Windows. Abrégé en PowerShell Remoting, il désigne l’utilisation conjointe du langage PowerShell et du protocole de gestion à distance WinRM pour l’administration de machines Windows. Il est alors possible d’exécuter sur la machine distante des commandes ou des scripts, parmi les cas d’usage les plus courants. Cet article a été écrit par Remi Madec, Consultant Sécurité de NBS System. ll se présente davantage comme une cheatsheet des commandes PowerShell qu’un guide complet. Cependant, il n’aborde pas l’étape de l’activation et le paramétrage du PowerShell est bien décrite, notamment dans la Documentation Microsoft.
Exemple de lancement des commandes via PowerShell :
Invoke-Command
L’utilisation de cmdlet Invoke-Command permet fin de lancer une unique commande. La syntaxe est la suivante :
Invoke-Command -ScriptBlock{COMMAND} -ComputerName SERVERNAME -Credential DOMAINE\USERNAME
Par exemple, pour lancer la commande hostname sur le serveur Server avec le compte local remi, il suffit d’exécuter la ligne suivante :
Invoke-Command -ScriptBlock{hostname} -ComputerName Server -Credential remi
PS C:\Users\Public> hostname Client PS C:\Users\Public> Invoke-Command -ScriptBlock{hostname} -ComputerName Server -Credential remi Server
Authentification
Le paramètre Credential permet de soumettre au serveur des informations d’authentification. Plusieurs méthodes existent, la plus répandue en environnement Active Directory étant Kerberos, qui nécessite toutefois que le client et le serveur soient membres de la même forêt, ou qu’une relation de confiance soit établie en forêt.
1. Saisie d’informations d’authentification
- Avec l’utilisateur courant
Dans le cas où l’utilisateur courant est membre du domaine et est autorisé sur le serveur, il n’est pas nécessaire de préciser dans la commande les informations d’authentification : celles de l’utilisateur courant seront relayées au serveur. La commande précédente devient donc :
Invoke-Command -ScriptBlock{COMMAND} -ComputerName SERVERNAME
Si l’on souhaite utiliser les informations d’authentification d’un autre utilisateur, il est nécessaire d’utiliser le paramètre Credential. Plusieurs solutions existent dans ce cas.
- Avec le nom d’utilisateur et une popup
Si ces informations n’ont pas besoin d’être réutilisées pour des commandes ultérieures, il suffit de passer seulement le nom d’utilisateur (domaine et nom d’utilisateur dans le cas d’un compte du domaine). C’est ce qui est fait avec la commande suivante :
Invoke-Command -ScriptBlock{hostname} -ComputerName Server -Credential remi
Pour saisir le mot de passe, un popup s’affiche :
- Stockées dans une variable (popup)
Il est également possible de stocker ces informations d’authentification dans une variable locale, évitant ainsi de devoir les saisir à nouveau lors de chaque commande. Pour les stocker dans la variable cred, la syntaxe suivante est utilisée :
$cred = Get-Credential
Comme dans le cas précédent, un popup apparaît pour saisir ses informations :
Cette variable doit ensuite être passée en argument du paramètre Credential :
Invoke-Command -ScriptBlock{COMMAND} -ComputerName SERVERNAME -Credential $cred
- Stockées dans une variable (ligne de commande)
La méthode précédente est pratique, mais la saisie d’informations d’authentification dans une popup n’est pas toujours une solution envisageable pour diverses raisons. Par exemple, si le prompt PowerShell est obtenu depuis un reverse shell, la popup s’affichera sur l’ordinateur distant ! C’est d’autant plus regrettable qu’en plus de ne pas pouvoir s’authentifier, l’attaquant perd la main sur le shell.
Il est alors possible d’utiliser l’objet PSCredential avec la saisie du mot de passe en ligne de commande. La syntaxe à utiliser est alors la suivante :
$Username = « USERNAME »
$SecurePassword = « PLAINPASSWORD » | ConvertTo-SecureString -AsPlainText -Force
$PScred = New-Object System.Management.Automation.PSCredential -ArgumentList $UserName,$SecurePassword
L’utilisation de cette variable s’effectue de la même manière que précédemment :
-
- Exit
La commande Exit permet de revenir dans le contexte du client.
2. Session persistante
Il est également possible de créer des sessions persistantes.
- New-PSSession
Le cmdlet New-PSSession permet ceci :
New-PSSession -ComputerName SERVERNAME -Credential $cred
Pour faciliter l’organisation et la gestion des sessions, il est possible de leur donner un nom et de les stocker dans une variable locale :
$session1 = New-PSSession -Name Session1 -ComputerName Server -Credential $cred
- Get-PSSession
La commande suivante effectue la visualisation des sessions existantes :
-
Les paramètres Name et Id (et bien d’autres, par exemple ComputerName) peuvent être utilisés pour filtrer les résultats. Ainsi, les deux commandes suivantes retournent le même résultat :
Get-PSSession -Name Session1
Get-PSSession -Id 19
De nombreuses possibilités d’interaction sont disponibles. Par exemple, il est possible d’assigner à une variable une session déjà existante, par exemple avec la commande suivante :
$session0 = Get-PSSession -Id 18
- Enter-PSSession
Pour interagir avec une session existante, il suffit d’utiliser le cmdlet Enter-PSSession accompagné d’un moyen d’identification de la session (son nom, son Id ou la variable dans laquelle elle est stockée). Les commandes suivantes aboutissent au même résultat :
Enter-PSSession -Id 19
Enter-PSSession -Name Session1
Enter-PSSession -Session $session1
- Disconnect-PSSession & Connect-PSSession
La commande Exit est utilisée pour revenir dans le contexte du Client. Toutefois, la session est toujours active. Il est possible de la déconnecter à l’aide de la commande suivante :
Disconnect-PSSession -Id 18
-
Celle-ci n’est cependant pas supprimée. Il est par exemple possible de lancer un script, de déconnecter la session, puis de reconnecter la session plus tard pour consulter les résultats. C’est d’autant plus pratique que cette session est conservée côté serveur, et il est donc possible de s’y reconnecter depuis une machine cliente différente de la première (ce qui nécessite tout de même d’utiliser le même compte utilisateur).
Pour se reconnecter à une session existante, il convient d’utiliser le cmdlet Connect-PSSession :
Connect-PSSession -Id 18
La commande suivante est utilisée pour supprimer définitivement une session :
Remove-PSSession -Id 18
-
3. Sessions multiples
Pour lancer des commandes ou des scripts sur de multiples machines, par exemple lors de la phase de post-exploitation, plusieurs sessions (sur le même serveur ou sur plusieurs serveurs compromis) peuvent être stockées dans une unique variable :
$sessions = New-PSSession -ComputerName Server,Server -Credential $cred
Il suffit alors de l’utiliser de la même manière que précédemment pour obtenir le résultat de toutes les sessions :
Invoke-Command -ScriptBlock{hostname} -Session $sessions
Dans certains cas, il peut également être intéressant de créer de multiples sessions, chacune stockée dans une variable. C’est possible en une unique ligne avec la syntaxe suivante :
$sess1, $sess2 = New-PSSession -ComputerName Server,Server -Credential $cred
Chaque variable correspond alors à une session :
Lancement des commandes à distance via le PowerShell Remoting
1. Le PowerShell Remoting explicite
Les commandes via PowerShell Remoting offrent de nombreuses possibilités pour lancer une ou plusieurs commandes, des scripts, importer des modules …
- Commandes et scripts distants
Pour lancer plusieurs commandes, Il suffit de les séparer par un point-virgule dans le paramètre ScriptBlock :
Invoke-Command -ScriptBlock{hostname;whoami} -Session $session1
Il est possible de lancer un script déjà présent sur le serveur en précisant son chemin relatif dans le paramètre ScriptBlock :
Invoke-Command -ScriptBlock{C:\Users\Public\hostname.ps1} -Session $session1
Ces fonctionnalités permettent par exemple de charger un module pour en exécuter des fonctions en une seule ligne :
Invoke-Command -ScriptBlock{Import-Module C:\PATHTOMODULE; MODULEFUNCTION} -Session $session1
- Scripts stockés localement
Déposer des scripts sur une machines compromise n’est pas forcément la manière la plus furtive de procéder. Il est cependant possible d’exécuter sur la machine distante des scripts stockés sur le client. Pour ce faire, le paramètre FilePath est utilisé :
Invoke-Command -FilePath C:\Users\Public\local_hostname_script.ps1 -Session $session1
2. Le PowerShell Remoting implicite
L’ensemble des commandes utilisées précédemment est exécuté explicitement sur le serveur : il est clair que c’est à travers la session qu’elles sont envoyées.
Par exemple, pour importer le module Powerview.ps1 stocké sur la machine distante et en lancer une commande (ici Get-NetLoggedon), la syntaxe explicite est la suivante :
Invoke-Command -ScriptBlock{Import-Module C:\Users\Public\Powerview.ps1;Get-NetLoggedon}-Session $session1
Sur le serveur et la commande exécuté, il y a bien ce module qui est chargé :
- Export-PSSession
Cette syntaxe est plutôt lourde, et c’est ici qu’intervient le Remoting implicite : il est possible d’exporter le contexte de la session pour lancer des commandes de ce module sur le serveur comme si le module était chargé localement. Pour ce faire, le cmdlet Export-PSSession est utilisé :
Export-PSSession -Session $Session1 -OutputModule Powerview
Cette commande stocke localement ce contexte dans le module passé en argument du paramètre OutputModule. Il suffit de l’importer et d’en utiliser les fonctions. De manière transparente, celles-ci sont toujours exécutées sur le serveur, au travers de la session dont provient l’export :
Interaction avec une session persistante
Enfin, autre fonctionnalité pratique : il est possible de se servir d’une session PowerShell pour transférer des fichiers.
- Depuis le client vers le serveur
La commande à utiliser est :
Copy-Item -Path C:\LOCALPATHTOFILE -ToSession $session1 -Destination C:\REMOTEDESTINATIONFILEPATH
- Depuis le serveur vers le client
Le paramètre FromSession remplace ToSession :
Copy-Item -Path C:\REMOTEPATHTOFILE -FromSession $session1 -Destination C:\LOCALDESTINATIONFILEPATH
Cheatsheet de commandes PowerShell Remoting
1. Syntaxe générique
Exécution de commande :
Invoke-Command -ScriptBlock{COMMAND} -ComputerName SERVERNAME
2. Authentification
Popup :
Invoke-Command -ScriptBlock{COMMAND} -ComputerName SERVERNAME -Credential DOMAINE\USERNAME
Stockage dans une variable (popup) :
$cred = Get-Credential
Invoke-Command -ScriptBlock{COMMAND} -ComputerName SERVERNAME -Credential $cred
Stockage dans une variable (CLI) :
$Username = « USERNAME »
$SecurePassword = « PLAINPASSWORD » | ConvertTo-SecureString -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $UserName,$SecurePassword
Invoke-Command -ScriptBlock{COMMAND} -ComputerName SERVERNAME -Credential $cred
3. Double saut Kerberos (CredSSP)
Activation du la délégation (client) :
Enable-WSManCredSSP -Role Client –
DelegateComputer CLIENTNAME :
Activation du la délégation (serveur) Enable-WSManCredSSP -Role Server -Force
Exécution de commande :
Invoke-Command -ScriptBlock{COMMAND} -ComputerName SERVERNAME -Credential $cred -Authentication Credssp
4. Gestion des sessions
Session interactive :
Enter-PSSession -ComputerName SERVERNAME -Credential $cred
Session persistante :
New-PSSession -ComputerName SERVERNAME -Credential $cred
Session persistante nommée et stockée :
$SESSIONVAR = New-PSSession -Name SESSIONNAME -ComputerName SERVERNAME -Credential $cred
Visualisation des sessions :
Get-PSSession
Filtre par nom/Id :
Get-PSSession -Name SESSIONNAME
Get-PSSession -Id SESSIONID
Interaction par Id/nom/variable :
Enter-PSSession -Id SESSIONID
Enter-PSSession -Name SESSIONNAME
Enter-PSSession -Session $SESSIONVAR
Déconnexion :
Disconnect-PSSession -Id SESSIONID
Reconnexion :
Connect-PSSession -Id SESSIONID
Suppression définitive :
Remove-PSSession -Id SESSIONID
Stockage X sessions dans 1 variable :
$SESSIONVAR = New-PSSession -ComputerName SERVERNAME1,SERVERNAME2 -Credential $cred
Stockage X sessions dans X variable :
$SESSIONVAR1, $SESSIONVAR2 = New-PSSession -ComputerName SERVERNAME1,SERVERNAME2 -Credential $cred
5. Commandes et scripts
Multiples commandes :
Invoke-Command -ScriptBlock{COMMAND1;COMMAND2} -Session $SESSIONVAR
Script distant :
Invoke-Command -ScriptBlock{REMOTEPATHTOSCRIPT\SCRIPT.ps1} -Session $SESSIONVAR
Module distant :
Invoke-Command -ScriptBlock{Import-Module C:\REMOTEPATHTOMODULE; MODULEFUNCTION} -Session $SESSIONVAR
Script local :
Invoke-Command -FilePath REMOTEPATHTOSCRIPT\SCRIPT.ps1 -Session $SESSIONVAR
Remoting implicite :
Export-PSSession -Session $SESSIONVAR -OutputModule OUTPUTMODULENAME
6. Transfert de fichiers
Client -> Serveur :
Copy-Item -Path C:\LOCALPATHTOFILE\FILE -ToSession $SESSIONVAR -Destination C:\REMOTEDESTINATIONFILEPATH
Serveur -> Client :
Copy-Item -Path C:\REMOTEPATHTOFILE\FILE -FromSession $SESSIONVAR -Destination C:\LOCALDESTINATIONFILEPATH
Article de Remi Madec, consultant sécurité de NBS System
Vous souhaitez en savoir plus ?
Nos équipes sont là pour vous accompagner concernant les commandes via le PowerShell