18 enero, 2019

Cifrar passwords con PowerShell

Con PowerShell podemos generar o almacenar contraseñas simétricas, secretos (tipo API Key o Access Token) o simplemente cadenas de texto y guardarlas cifradas de forma segura en ficheros de scripts Powershell.

En un caso práctico este método lo llevo utilizando ya un tiempo para almacenar las contraseñas necesarias que se usan en scripts automatizados para realizar en este caso copias de seguridad.

Crear passwords cifradas con PowerShell para usar desde el mismo usuario

Cuando establecemos una password en Powershell y la volcamos en un fichero, el fichero que contiene la password cifrada solo sería se podría usar desde el mismo usuario que la estableció.

Para establecer una password con Powershell y volcarla a un fichero de forma cifrada convirtiendo la cadena de texto claro establecida a una cadena cifrada segura (ConvertFrom-SecureString).
Read-Host -AsSecureString "Password fichero" | ConvertFrom-SecureString | Set-Content "C:\Passwords\fichero-passwd-cifrada"
Llamar al fichero con la password cifrada almacenada en un script Powershell, convertir la cadena cifrada en texto claro (ConvertTo-SecureString).
Get-Content "C:\Passwords\fichero-passwd-cifrada" | ConvertTo-SecureString
Repositorio donde se muestra este método y un video demo PoC.

Crear passwords cifradas con PowerShell generando un keyfile portable para usar desde cualquier usuario o máquina

Otra forma de cifrar la passwords en Powershell sería generar un keyfile (fichero llave único para descifrarlas). De ese modo teniendo el keyfile y referenciándolo en el script de Powershell se pueden cifrar con el keyfile y posteriormente descifrar las password establecida volcada en el fichero.

La ventaja respecto al método anterior, es que se pueden descifrar los ficheros que almacenan las passwords cifradas desde cualquier usuario de la misma máquina o cualquier usuario de otra máquina distinta. Pudiendo así transportar los ficheros de passwords y keyfile para ser utilizados de una forma portable por otros usuarios o máquinas.

Generar el keyfile y establecer la password para volcarla a un fichero asociándola al keyfile (Read-Host -AsSecureString , ConvertFrom-SecureString -Key).
$CifradoKeyFile = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($CifradoKeyFile)
$CifradoKeyFile | Out-File "C:\Passwords\CifradoKeyFile.key"

Read-Host -AsSecureString "Establece una Password" | ConvertFrom-SecureString -Key (Get-Content "C:\Passwords\CifradoKeyFile.key") | Set-Content "C:\Passwords\fichero-passwd-cifrada"
Para descifrar y leer el contenido cifrado a través de un script Powershell se concatena el fichero de password cifrado y su keyfile (ConvertTo-SecureString -Key).
Get-Content "C:\Passwords\fichero-passwd-cifrada" | ConvertTo-SecureString -Key (Get-Content "C:\Passwords\CifradoKeyFile.key")

¿Cómo parametrizar usuario y password en un script Powershell sin establecer la password en texto claro?

Haciendo uso de lo anterior, usando un fichero key para que la password pueda ser leída desde cualquier usuario o máquina, un ejemplo podría ser el siguiente.
$password = Get-Content C:\Passwords\password.txt | ConvertTo-SecureString -Key (Get-Content C:\Passwords\aes.key)
$credential = New-Object System.Management.Automation.PsCredential("Username",$password)
Repositorio donde se muestra este método con generación de keyfile y un video demo PoC.

ConverTo-SecureString hace uso de DPAPI (Data Protection API) usando un algoritmo de cifrado AES256 en versiones modernas o 3DES en antiguas, crea un hash en base a la clave del usuario. Más info de como funciona el proceso de cifrado.

Liberar o eliminar las passwords almacenadas en un puntero de memoria

Cuando se crea una cadena tipo SecureString esta es almacenada en un espacio de memoria a parte, donde el recolector de basura de .NET de Powershell (CG - Garbage Collector) no puede acceder por lo que es recomendable vaciar este espacio de memoria que se asigna a un puntero.

ZeroFreeCoTaskMemUnicode: Libera un puntero a una cadena no administrada que se ha asignado con el método.

En el siguiente ejemplo se muestra como se crea una cadena segura y se almacenada en un variable. Se convierte a texto claro gracias a que conocemos el indicador del puntero en memoria y finalmente liberamos ese puntero. Con esto evitará el almacenamiento en memoria de la credencial almacenada. 
PS C:\> $sec = Read-Host -AsSecureString
*******
PS C:\> $ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec)
PS C:\> $ptr
114228428
PS C:\> $plaintext = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($ptr)
PS C:\> $plaintext
Abc123.
PS C:\> [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($ptr)
Saludos!

Autor: Adrián Lois

No hay comentarios

Publicar un comentario