Comment exécuter une commande en mode Administrateur sans être Administrateur ?

A.    Présentation

Au gré de mes formations sur Powershell (ou plus généralement Windows) et lors de certaines interventions en clientèle, cette question revient régulièrement.

Comment exécuter une commande en mode Administrateur sans être Administrateur, sans divulguer le mot de passe associé  ?

La réponse est toujours la même, ce n’est possible qu’à condition d’accepter les failles de sécurité que cela implique.

Pour exécuter une commande avec des références utilisateur (credentials) d’administrateur, sans les divulguer, il est nécessaire de stocker ces informations. Ce stockage doit, par essence, être sur un emplacement sécurisé à accès contraint.

Typiquement, une infrastructure telle qu’Active Directory (et Kerberos) permet de traiter facilement ces contraintes avec un degré de sécurité “acceptable”, via des comptes de service, des GPO ou des taches planifiées mais pour des machines isolées ou en Workgroup, il faut trouver des alternatives.

Toutefois, gardez à l’esprit que le stockage d’un mot de passe dans un fichier, bien que chiffré ou compilé dans un programme, sera toujours exposé potentiellement aux malveillances.

Cela étant dit, je vous propose une approche de ce sujet via quelques exemples sous Powershell …

B.     Première approche – 2 passes – via l’admin local intégré

Vous trouverez un exemple sur https://gallery.technet.microsoft.com/scriptcenter/Execute-PowerShell-Script-38881dce . Cette technique consiste à stocker le mot de passe de l’administrateur local dans un fichier encodé “securestring.txt” puis d’utiliser cette clé pour passer le mot de passe au second script.

makekey.ps1

$BuiltinAdmin = Get-WMIObject  -query { Select name, SID From Win32_UserAccount where SID like '%500'}
Read-Host "Entrez le mot de passe de $($BuiltinAdmin.Name)" -AsSecureString | ConvertFrom-SecureString | Out-File $PWD\SecureString.txt

Exécutez ce script, entrez le mot de passe de l’administrateur intégré

Vous devriez trouver un fichier “SecureString.txt” de cette forme

01000000d08c9ddf0115d1118c7a00c04fc297eb0100000086ce0605f4452046b6d6dfa15eb0843700000000020000000000106600000001000020000000fe96ee9dfe1406e7b08849e76523e59606c391a7f713937fdf89c0139464c8bc000000000e800000000200002000000032dbfeac33e28e00860cdb1914045c7cc123e37627b3a7caad8676d56ac60ef8200000000bcaef960cb2f773f84272fdb54f029dd3ca01d4110616e93d56cf0c041cf5d2400000007b0b9e2b7884e21b24f258fb2aa542f74875489c2ec6dda42256d45072739d19c4c3f3a328e09650f3d5d7280c890072fbd9de9ea483feb78a09766b1076decd

Ce qui correspond au mot de passe chiffré avec la clé de l’ordinateur. De fait, le script suivant ne sera fonctionnel que sur ce même ordinateur.

runasadmin.ps1

$BuiltinAdmin = Get-WMIObject  -query { Select name, SID From Win32_UserAccount where SID like '%500'}
$SPAdmin = "$env:COMPUTERNAME\$($BuiltinAdmin.Name)"
$Password = Get-Content $PWD\securestring.txt -Encoding UTF8 | convertto-securestring
$Cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $SPAdmin, $Password
$Command = 'regedt32.exe'
Start-Process -FilePath $Command -Credential $Cred -WorkingDirectory 'c:\windows\system32'

Exécutez ce script avec un compte d’utilisateur standard. Cet exemple devrait lancer l’éditeur du registre sans aucune confirmation. (avec les privilèges de l’administrateur intégré) Vous pouvez le vérifier avec le gestionnaire des taches, ou des outils plus avancés tels que “Process Explorer” de Microsoft/Sysinternals.

Attention : La principale faille de cette technique est que l’exécutable en question, ici l’éditeur graphique regedit, peut ouvrir un explorateur, et de fait, ouvrir n’importe quel autre programme ou fichier avec ce même niveau de privilèges.

C.    Deuxième approche – 1 passe – via l’admin local intégré

La technique précédente nécessite de générer un fichier de mot de passe chiffré sur chaque ordinateur. Bien que cette opération puisse être réalisée à distance (Via WinRM si vous l’avez configuré au préalable), on peut imaginer que le mot de passe est identique sur tous les ordinateurs concernés (pas bien 🙁 ) et réaliser un script unique.

L’idée est donc de stocker le mot de passe en clair dans le script (vraiment pas bien 🙁 🙁  ) et de compiler le script en exécutable via un outil tel que ps2exe 🙂  (disponible ici)

$username = ".\Administrateur"
$password = "Pa$$w0rd/*"
$credentials = New-Object System.Management.Automation.PSCredential -ArgumentList @($username,(ConvertTo-SecureString -String $password -AsPlainText -Force))
$Command = "regedt32.exe"
Start-Process -FilePath $Command -Credential $Credentials -WorkingDirectory 'c:\windows\system32'

Cette technique présente la même faille que l’exemple précédent. De plus, on peut considérer que l’on ne maitrise plus les ordinateurs sur lesquels ce programme est exécuté. Je vous conseille vivement d’ajouter un test préalable d’exécution consistant à tester l’habilitation, comme par exemple, solliciter un web-service ou un fichier sur une ressource partagée afin de vérifier/valider l’exécution.

D.    Troisième approche – Générer un script protégé

Cette technique est inspirée d’un exemple fournit sur http://powershell.com/cs/blogs/tips/archive/2013/10/08/password-obfuscator-script.aspx et consiste à utiliser un script Powershell pour générer le code d’un autre script en y incluant les identifiants protégés de votre choix.

Write "Ce script génère un squelette de script protégé avec les identifiants de votre choix"
$user = Read-Host 'Nom du compte'
$pass = Read-Host 'Mot de passe (en clair)'
$key = 1..32 | ForEach-Object { Get-Random -Maximum 256 }
$passencrypted = $pass |
  ConvertTo-SecureString -AsPlainText -Force |
  ConvertFrom-SecureString -Key $key

$text = @()
$text += '#-------- Code généré -------------'
$text += '$password = "{0}"' -f ($passencrypted -join ' ')
$text += '$key = "{0}"' -f ($key -join ' ')
$text += '$passwordSecure = ConvertTo-SecureString -String $password -Key ([Byte[]]$key.Split(" "))'
$text += '$cred = New-Object system.Management.Automation.PSCredential("{0}", $passwordSecure)' -f $user
$text += '$cred'
$text += '#-----------------------------------'
$text += '# Vous pouvez utiliser la variable $cred comme par exemple'
$text += '# Get-WmiObject -Class Win32_Bios -Credential $cred '
$text += '#-----------------------------------'

$Pscript = Read-Host 'Nom du script protégé'
Clear-Host
if (-not($Pscript.ToLower().EndsWith('.ps1'))) {
  $Pscript = $Pscript+'.ps1'
}

Set-Content -Path $Pscript -Value $text
Write "Le script sécurisé [$PWD\$Pscript] a été généré."
Get-Content -Path "$PWD\$Pscript"

E.     Quatrième approche – 1 passe – via un autre compte local d’administration

Cette technique présente la difficulté supplémentaire de devoir élever les privilèges afin d’appeler le processus en tant qu’administrateur. En effet, la présence du contrôle de compte d’utilisateur (Alias UAC), filtre le jeton des administrateurs et il est donc nécessaire d’invoquer l’élévation. Sous Powershell, et en l’occurrence via l’applet de commande “Start-Process”, il est possible d’utiliser l’argument “-verb RunAs”. Toutefois, cette directive ne permet pas d’effectuer l’action au sein d’un script (probablement en raison d’une contrainte de portée ou de contexte).

Note : Cette procédure s’applique également dans le cas où le compte d’Administrateur intégré serait soumis à l’UAC – cf GPO : “Contrôle de compte d’utilisateur : utiliser le mode Approbation administrateur pour le compte Administrateur intégré” = Désactivé par défaut

Pour la démonstration, (et les valeurs UAC par défaut), je vous propose de créer un équivalent Admin local (à partir d’une invite de commande exécutée via en tant qu’administrateur) et d’autoriser l’exécution des scripts .ps1, via les commandes suivantes :

net user AdminLocal "Zero+0=Toto" /add

net localgroup Administrateurs AdminLocal /add

powershell -Command Set-ExecutionPolicy RemoteSigned

Quittez l’invite de commande.

L’astuce que je vous propose, consiste donc à invoquer l’élévation en sollicitant une nouvelle instance de l’interpréteur Powershell au sein du script lui-même. Autrement dit, via le script suivant :

Notepad C:\Scripts\RegeditAsAdmin2exe.ps1

$username = ".\AdminLocal"
# pour les paranos, découpez le mot de passe en plusieurs parties
$pass = @("Zero","+0","=","Toto")
$password = $pass[0]+ $pass[1] + $pass[2] + $pass[3]
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList @($username,(ConvertTo-SecureString -String $password -AsPlainText -Force))
$Command = "regedt32.exe"
Start-Process powershell.exe -Credential $Cred -NoNewWindow -ArgumentList "Start-Process $Command -WorkingDirectory 'c:\windows\system32' -Verb runAs"

Enregistrez puis exécutez le script avec un compte lambda.

Si vous n’avez pas autorisé la stratégie d’exécution Powershell comme mentionné précédemment, vous pouvez saisir la commande suivante dans une invite CMD ou le menu “Exécuter” : “powershell.exe -Noninteractive -ExecutionPolicy bypass -file C:\Scripts\RegeditAsAdmin2exe.ps1”

Il y a une fenêtre PowerShell qui s’ouvre, puis une confirmation d’élévation, on clique sur “OK” et on arrive dans un regedit en mode admin. Ce n’est pas super propre mais ça marche. Ne pas oublier de compiler le script en .exe

Pour PS2EXE, c’est ici : https://gallery.technet.microsoft.com/PS2EXE-Convert-PowerShell-9e4e07f1

Je ne prétends aucunement être un champion du hacking, mais je ne retrouve aucune trace, même partielle du compte et mot de passe dans l’édition du code .exe compilé. Vous pouvez également recourir à d’autres outils tels que PowerGUI, ou Powershell Studio pour effectuer cette compilation.

Vous pouvez aussi envisager de passer la commande à exécuter en tant que paramètre du script compilé, mais dans ce cas, vous risquez d’augmenter encore les failles de sécurité. Sachez cependant que l’interpréteur Powershell supporte le passage d’une commande encodée en paramètre :

$command = 'dir "c:\\program files" '
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)
powershell.exe -encodedCommand $encodedCommand

En conclusion, je vous conseille d’éviter le lancement d’outils graphiques trop dangereux tels que regedit, notepad, etc et d’ajouter un petit contrôle local (clé de registre, fichier ou spécificité de réseau local) ou un contrôle distant (fichier central), avant d’autoriser l’exécution de ce script. Cela permettra d’en limiter l’usage sur votre parc.

Bien à vous

Christophe

1 Commentaire

  1. kadargo

    Merci pour cet article clair et précis

    Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. Apprenez comment les données de vos commentaires sont utilisées.