Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Powershell - utilisation variable

14 réponses
Avatar
Bruno GUERPILLON
Bonjour,

Je voudrais utiliser des variables mais j'ai quelques misères. Le script en
lui-même :

=================================================
$COMP1 = read-host "Nom de l'ordinateur : (si local, faites <Entrée>)"
IF ($COMP1) { $opt = "-computer $COMP1" }
Get-WmiObject -class WIN32_service $opt | format-table -Property Name,
Displayname -autosize -wrap
=================================================

Mais cela ne fonctionne pas, l'erreur :

=================================================
Get-WmiObject : Demande non valide
Au niveau de F:\_Systeme_Admin\powershell\getservice.ps1 : 3 Caractère : 14
+ Get-WmiObject <<<< -class WIN32_service $opt | format-table -Property
Name, Displayname -autosize -wrap
=================================================

Le problème que j'ai c'est que la subsitution de $opt ne se fait pas. Une
idée ?

Amicalement,

Bruno

4 réponses

1 2
Avatar
Bruno GUERPILLON
Bonjour Jacques

Vos remarques (à tous) sont très interessantes mais je voudrais ajouter une
correction (si je peux me permettre).
Et je pense que cela va vous interesser aussi.
Je me suis dis, pour être sur, rien ne vaut une vérification et je me suis
empressé de la faire.
(Pour une certaine sécurité, j'ai limité l'arrêt à un service bien précis,
je ne suis pas fou :) ).

Je reposte pour le principe le script :
=================================== $COMP1 = read-host "Nom de l'ordinateur : (si local, faites <Entrée>)"
IF ($COMP1) { $opt = "-computer $COMP1" }
$command = "Get-WmiObject -class WIN32_service $opt" |
format-table -Property Name, Displayname -autosize -wrap"
write-host $command
invoke-expression $command
===================================

J'exécute donc le script et j'injecte : ". | where-object { $_.name -match
"DOAUX" } | %{ $_.stopservice()}"
Pour être encore plus sur de la commande, vous avez vu que j'ai ajouté un
write-host de $command.

=================================== PS C:scriptspowershell> gvim stopsrv.ps1
PS C:scriptspowershell> .stopsrv.ps1
Nom de l'ordinateur : (si local, faites <Entrée>): . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()}
Get-WmiObject -class WIN32_service -computer . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()} | format
-table -Property Name, Displayname -autosize -wrap

Name Displayname
---- -----------

=================================== Résultat, le service ne s'est pas arrêté. Toujours faire une vérification :
=================================== PS C:scriptspowershell> Get-WmiObject -class WIN32_Service -computer . |
where-object { $_.name -match "DOAUX"}
ExitCode : 0
Name : DOAUX
ProcessId : 2984
StartMode : Auto
State : Running
Status : OK
===================================
Donc le service fonctionne toujours. Une lueur me vient à l'ésprit : après
la commande d'arrêt du service, il y a encore des informations dans le
pipeline => | format -table -Property Name, Displayname -autosize -wrap
Je fais un test en supprimant la suite du pipeline, le script devient :

=================================== $COMP1 = read-host "Nom de l'ordinateur : (si local, faites <Entrée>)"
IF ($COMP1) { $opt = "-computer $COMP1" }
$command = "Get-WmiObject -class WIN32_service $opt"
invoke-expression $command
===================================
Et là, je relance le script dans les mêmes conditions :

=================================== PS C:scriptspowershell> .stopsrv.ps1
Nom de l'ordinateur : (si local, faites <Entrée>): . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()}
Get-WmiObject -class WIN32_service -computer . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()}


__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ReturnValue : 2
===================================
Le service est bien arrêté.
Quelle conclusion pouvont-nous tirer de ceci ?
D'après moi et de certains test, l'ordre des commandes dans le pipeline est
TRES important et conditionne le résultat final.
Dans ce cas précis, il faudrait que l'ordre d'arrêt du service soit à la fin
du pipeline pour qu'une injection de code soit dangereux.
Si vous avez des contres exemples, cela sera très passionnant.

Amicalement,

Bruno



"Jacques Barathon [MS]" a écrit dans le
message de news:
"Bruno GUERPILLON" wrote in message
news:
Bonjour Gilles

Merci pour m'avoir eclairé sur Invoke-Expression.

Au final mon script (de départ) ressemble à ceci :

$COMP1 = read-host "Nom de l'ordinateur : (si local, faites <Entrée>)"
IF ($COMP1) { $opt = "-computer $COMP1" }
$command = "Get-WmiObject -class WIN32_service $opt |
format-table -Property Name, Displayname -autosize -wrap"
invoke-expression $command


Je profite d'une brève éclaircie dans ces quelques semaines un peu
mouvementées pour apporter un bémol à cette solution à priori très
tentante, et à fortiori efficace. La commandelette invoke-expression est à
manipuler avec beaucoup de précautions dans un contexte comme celui-ci où
une partie du contenu est déterminée par la saisie de l'utilisateur. On
est alors dans un magnifique exemple d'injection de code. Imaginez que
l'utilisateur, ou très taquin ou un brin maladroit, saisisse le texte
suivant lorsque le script le lui demande le nom de l'ordinateur:

| %{$_.StopService()}

Le résultat (non testé mais à peu près garanti pur beurre) est un arrêt
immédiat de tous les services du poste. Et ceci n'est évidemment qu'un
exemple, "the sky is your limit"...

Il vaut donc mieux réserver invoke-expression à des contextes où le
contenu de la commande à exécuter peut être protégé soit par la fourniture
des éléments de texte uniquement en interne depuis le script, soit par un
contrôle très strict du texte apporté par des sources extérieures.

Dans ce cas précis, la solution de Michel (lecture de $comp1, utilisation
de $comp1 comme valeur passée au paramètre -computername) est de TRES LOIN
la plus sûre.

Jacques



Avatar
Bruno GUERPILLON
Rebonjour,

Toujours dans mes test (à cause de Jacques !!!)

Le nouveau script :
================================== PS C:scriptspowershell> type stopsrv.ps1
$COMP1 = read-host "Nom de l'ordinateur : (si local, faites <Entrée>)"
IF ($COMP1) { $opt = "-computer $COMP1" }
$command = "Get-WmiObject -class WIN32_service $opt |
format-table -Property Name, Displayname -autosize -wrap"
write-host $command
invoke-expression $command
Get-WmiObject -class WIN32_Service -computer . | where-object {
$_.name -match "DOAUX"}
==================================
Ce coup-ci je lance PowerShell en tant qu'administrateur (oui, jusqu'à
présent je ne l'étais pas).

================================== PS C:scriptspowershell> .stopsrv.ps1
Nom de l'ordinateur : (si local, faites <Entrée>): . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()}
Get-WmiObject -class WIN32_service -computer . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()} | format
-table -Property Name, Displayname -autosize -wrap

Name Displayname
---- -----------


ExitCode : 0
Name : DOAUX
ProcessId : 0
StartMode : Auto
State : Stopped
Status : OK
==================================
Je refais le test en PowerShell classique (pas en mode Administrateur).

================================== PS C:scriptspowershell> .stopsrv.ps1
Nom de l'ordinateur : (si local, faites <Entrée>): . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()}
Get-WmiObject -class WIN32_service -computer . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()} | format
-table -Property Name, Displayname -autosize -wrap

Name Displayname
---- -----------



ExitCode : 0
Name : DOAUX
ProcessId : 3060
StartMode : Auto
State : Running
Status : OK
==================================

Conclusions :
* Pour arrêter un service, il faut utiliser un PowerShell en mode
Administrateur.
* L'injection de code n'a l'air dangereux que lorsque vous exécutez une
console en tant qu'administrateur.
* J'ai découvert pleins de choses :)

Amicalement,

Bruno



"Bruno GUERPILLON" a écrit dans le message de
news:
Bonjour Jacques

Vos remarques (à tous) sont très interessantes mais je voudrais ajouter
une correction (si je peux me permettre).
Et je pense que cela va vous interesser aussi.
Je me suis dis, pour être sur, rien ne vaut une vérification et je me suis
empressé de la faire.
(Pour une certaine sécurité, j'ai limité l'arrêt à un service bien précis,
je ne suis pas fou :) ).

Je reposte pour le principe le script :
=================================== > $COMP1 = read-host "Nom de l'ordinateur : (si local, faites <Entrée>)"
IF ($COMP1) { $opt = "-computer $COMP1" }
$command = "Get-WmiObject -class WIN32_service $opt" |
format-table -Property Name, Displayname -autosize -wrap"
write-host $command
invoke-expression $command
=================================== >

J'exécute donc le script et j'injecte : ". | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()}"
Pour être encore plus sur de la commande, vous avez vu que j'ai ajouté un
write-host de $command.

=================================== > PS C:scriptspowershell> gvim stopsrv.ps1
PS C:scriptspowershell> .stopsrv.ps1
Nom de l'ordinateur : (si local, faites <Entrée>): . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()}
Get-WmiObject -class WIN32_service -computer . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()} | format
-table -Property Name, Displayname -autosize -wrap

Name Displayname
---- -----------

=================================== > Résultat, le service ne s'est pas arrêté. Toujours faire une vérification
:
=================================== > PS C:scriptspowershell> Get-WmiObject -class WIN32_Service -computer . |
where-object { $_.name -match "DOAUX"}
ExitCode : 0
Name : DOAUX
ProcessId : 2984
StartMode : Auto
State : Running
Status : OK
=================================== >
Donc le service fonctionne toujours. Une lueur me vient à l'ésprit : après
la commande d'arrêt du service, il y a encore des informations dans le
pipeline => | format -table -Property Name, Displayname -autosize -wrap
Je fais un test en supprimant la suite du pipeline, le script devient :

=================================== > $COMP1 = read-host "Nom de l'ordinateur : (si local, faites <Entrée>)"
IF ($COMP1) { $opt = "-computer $COMP1" }
$command = "Get-WmiObject -class WIN32_service $opt"
invoke-expression $command
=================================== >
Et là, je relance le script dans les mêmes conditions :

=================================== > PS C:scriptspowershell> .stopsrv.ps1
Nom de l'ordinateur : (si local, faites <Entrée>): . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()}
Get-WmiObject -class WIN32_service -computer . | where-object {
$_.name -match "DOAUX" } | %{ $_.stopservice()}


__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ReturnValue : 2
=================================== >
Le service est bien arrêté.
Quelle conclusion pouvont-nous tirer de ceci ?
D'après moi et de certains test, l'ordre des commandes dans le pipeline
est TRES important et conditionne le résultat final.
Dans ce cas précis, il faudrait que l'ordre d'arrêt du service soit à la
fin du pipeline pour qu'une injection de code soit dangereux.
Si vous avez des contres exemples, cela sera très passionnant.

Amicalement,

Bruno



"Jacques Barathon [MS]" a écrit dans le
message de news:
"Bruno GUERPILLON" wrote in message
news:
Bonjour Gilles

Merci pour m'avoir eclairé sur Invoke-Expression.

Au final mon script (de départ) ressemble à ceci :

$COMP1 = read-host "Nom de l'ordinateur : (si local, faites <Entrée>)"
IF ($COMP1) { $opt = "-computer $COMP1" }
$command = "Get-WmiObject -class WIN32_service $opt |
format-table -Property Name, Displayname -autosize -wrap"
invoke-expression $command


Je profite d'une brève éclaircie dans ces quelques semaines un peu
mouvementées pour apporter un bémol à cette solution à priori très
tentante, et à fortiori efficace. La commandelette invoke-expression est
à manipuler avec beaucoup de précautions dans un contexte comme celui-ci
où une partie du contenu est déterminée par la saisie de l'utilisateur.
On est alors dans un magnifique exemple d'injection de code. Imaginez que
l'utilisateur, ou très taquin ou un brin maladroit, saisisse le texte
suivant lorsque le script le lui demande le nom de l'ordinateur:

| %{$_.StopService()}

Le résultat (non testé mais à peu près garanti pur beurre) est un arrêt
immédiat de tous les services du poste. Et ceci n'est évidemment qu'un
exemple, "the sky is your limit"...

Il vaut donc mieux réserver invoke-expression à des contextes où le
contenu de la commande à exécuter peut être protégé soit par la
fourniture des éléments de texte uniquement en interne depuis le script,
soit par un contrôle très strict du texte apporté par des sources
extérieures.

Dans ce cas précis, la solution de Michel (lecture de $comp1, utilisation
de $comp1 comme valeur passée au paramètre -computername) est de TRES
LOIN la plus sûre.

Jacques






Avatar
Jacques Barathon [MS]
"Bruno GUERPILLON" wrote in message
news:eRoJ%
Rebonjour,

Toujours dans mes test (à cause de Jacques !!!)


Désolé :-)

<...>
Conclusions :
* Pour arrêter un service, il faut utiliser un PowerShell en mode
Administrateur.
* L'injection de code n'a l'air dangereux que lorsque vous exécutez une
console en tant qu'administrateur.


En effet, tes tests (que j'ai pu reproduire) confirment également ce que
disait Gilles: l'injection de code en tant que telle n'attribue aucun
privilège particulier. Il faut surtout se méfier du contexte dans lequel on
fait tourner un script (merci l'UAC de Vista, merci aussi la prise en charge
des zônes de confiance par PowerShell), et éventuellement de la délégation
que l'on accorde à toute personne qui devra utiliser le script. On retombe
dans les problématiques classiques de la gestion de la sécurité.

* J'ai découvert pleins de choses :)


Tant mieux. Voilà au moins un effet secondaire positif :-)

Jacques

Avatar
Bruno GUERPILLON
Rebonjour,

"Jacques Barathon [MS]" a écrit dans le
message de news:
"Bruno GUERPILLON" wrote in message
news:eRoJ%
Rebonjour,

Toujours dans mes test (à cause de Jacques !!!)


Désolé :-)

<...>
Conclusions :
* Pour arrêter un service, il faut utiliser un PowerShell en mode
Administrateur.
* L'injection de code n'a l'air dangereux que lorsque vous exécutez une
console en tant qu'administrateur.


En effet, tes tests (que j'ai pu reproduire) confirment également ce que
disait Gilles: l'injection de code en tant que telle n'attribue aucun
privilège particulier. Il faut surtout se méfier du contexte dans lequel
on fait tourner un script (merci l'UAC de Vista, merci aussi la prise en
charge des zônes de confiance par PowerShell), et éventuellement de la
délégation que l'on accorde à toute personne qui devra utiliser le script.
On retombe dans les problématiques classiques de la gestion de la
sécurité.


On est tout à fait d'accord. Ce qui est effrayant c'est de voir le nombre de
personne qui ont le premier reflexe de désactiver UAC ...
Et surtout que certaines personnes eminentes (JCB pour ne pas le citer) le
cautionne.
Les utilisateurs de Windows ont enfin (disons qu'ils l'avaient depuis
longtemps) la possibilité de travailler en simple utilisateur pour limiter
les risques liés à la sécurité.
Bref je m'égare dans ma digression.
A bientôt dans le forum.

Amicalement,
Bruno.


* J'ai découvert pleins de choses :)


Tant mieux. Voilà au moins un effet secondaire positif :-)

Jacques



1 2