Захист і шифрування паролів в скриптах PowerShell

Адміністратори часто при написанні сценаріїв автоматизації на PowerShell зберігають паролі безпосередньо в тілі PoSh скрипта. Як ви розумієте, це вкрай небезпечно при використанні в продуктивної середовищі, тому що пароль у відкритому вигляді можуть побачити інші користувачі сервера або адміністратори. Тому бажано використовувати більш безпечний метод використання паролів в скриптах PowerShell, або шифрувати паролі, якщо не можна користуватися інтерактивним введенням.

Безпечно можна запросити від користувача ввести пароль в скрипті інтерактивно за допомогою командлета Get-Credential. Наприклад, запитаємо ім'я і пароль користувача і збережемо його в об'єкті типу PSCredential:

$ Cred = Get-Credential

При зверненні до властивостей змінної можна дізнатися ім'я ползователей, яка була вказана.

$ Cred.Username

Але при спробі вивести пароль, повернеться текст System.Security.SecureString, тому що пароль тепер зберігається у вигляді SecureString.

$ Cred.Password

Об'єкт PSCredential, який ми зберегли в змінній $ Cred тепер можна використовуватися в Командлети, які підтримують даний вид об'єктів.

Параметри $ Cred.Username і $ Cred.Password можна використовувати в Командлети, які не підтримують об'єкти PSCredential, але вимагають окремого введення імені і пароля користувача.

Також для запиту пароля користувача можна використовувати команлет Read-Host з атрибутом AsSecureString:
$ Pass = Read-Host "Введіть пароль" -AsSecureString

В даному випадку, ви також не зможете побачити вміст змінної $ pass, в якій зберігається пароль.

У розглянутих вище способах використання пароля в скриптах PowerShell передбачався інтерактивний введення пароля при виконанні скрипта. Але цей спосіб не підійде для різних сценаріїв, що запускаються автоматично або через планувальник.

В цьому випадку зручніше зашифрувати дані облікового запису (ім'я та пароль) і зберегти їх в зашифрованому вигляді в текстовий файл на диску або використовувати безпосередньо в скрипті.

Отже, за допомогою комадлета ConvertFrom-SecureString можна перетворити пароль з формату SecureString в шифровану рядок (шифрування виконується за допомогою Windows Data Protection API - DPAPI). Ви можете вивести шифрований пароль на екран або зберегти в файл:

$ Cred.Password | ConvertFrom-SecureString | Set-Content c: \ ps \ passfile.txt

Щоб використовувати зашифрований пароль з файлу потрібно виконати зворотне перетворення в формат Securestring за допомогою командлета ConvertTo-SecureString:

$ Username = "corp \ administrator"
$ Pass = Get-Content c: \ ps \ passfile.txt | ConvertTo-SecureString
$ Creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ username, $ pass

Таким чином в змінній $ creds ми отримали об'єкт PSCredential з обліковими даними користувача.

Однак, якщо спробувати скопіювати файл passfile.txt на інший комп'ютер або використовувати його під іншим користувачем (не тим, під яким створювався пароль), ви побачите, що змінна $ creds.password порожня і не містить пароля. Справа в тому, що шифруванням за допомогою DPAPI виконується за допомогою ключів, що зберігаються в профілі користувача. Без цих ключів на іншому комп'ютері ви не зможете розшифрувати файл з паролем.
ConvertTo-SecureString: Ключ не може бути використаний в зазначеному стані.
"Неможливо обробити аргумент, так як значенням аргументу" password "є NULL.
Вкажіть для аргументу "password" значення, відмінне від NULL. "

Таким чином, якщо скрипт буде запускатися під іншим (сервісним) аккаунтом або на іншому комп'ютері, необхідно використовувати інший механізм шифрування, отдічний від DPAPI. Зовнішній ключ шифрування можна вказати за допомогою параметрів -Key або -SecureKey.

Наприклад, ви можете за допомогою PowerShell згенерувати 256 бітний AES ключ, який можна використовувати для розшифровки файлу. Збережемо ключ в текстовий файл password_aes.key.

$ AESKey = New-Object Byte [] 32
[Security.Cryptography.RNGCryptoServiceProvider] :: Create (). GetBytes ($ AESKey)
$ AESKey | out-file C: \ ps \ password_aes.key

Тепер можна зберегти пароль в файл за допомогою даного ключа:

$ Cred.Password | ConvertFrom-SecureString -Key (get-content C: \ ps \ password_aes.key) | Set-Content c: \ ps \ passfile.txt

Не забувайте, що якщо в Powershell скрипте у вас вказується доменний обліковий запис, і на неї діє політика регулярної зміни пароля, вам доведеться оновлювати даний файл при кожній зміні пароля (ви можете створити для певних учёток окрему політику паролів за допомогою множинних політик паролів FGPP.

Таким чином у нас вийшло два файли: файл із зашифрованим паролем (passfile.txt) і файл з ключем шифрування (password_aes.key).

Їх можна перенести на інший комп'ютер і спробувати з PowerShell отримати пароль з файлу (можна розмістити файл ключа в мережевому каталозі)

$ Pass = Get-Content c: \ ps \ passfile.txt | ConvertTo-SecureString -Key (get-content \\ Server1 \ Share \ password_aes.key)
$ pass

Якщо не хочеться морочитися з окремим файлом з AES ключем, можна зашити ключ шифрування прямо в скрипт. У цьому випадку замість ключа в обох випадках потрібно використовувати

[Byte []] $ key = (1 ... 16)
$ Cred.Password | ConvertFrom-SecureString -Key $ key | Set-Content c: \ ps \ passfile.txt

А для розшифровки:

[Byte []] $ key = (1 ... 16)
$ Pass = Get-Content c: \ ps \ passfile.txt | ConvertTo-SecureString -Key $ key

Як ви бачите пароль не порожній, значить він був успішно розшифрований і може бути використаний на інших комп'ютерах.

Порада. Необхідно обмежити доступ до файлу з AES ключем, щоб тільки користувач або аккаунт, під яким запускається скрипт мав до нього доступ. Уважно перевірте NTFS дозволу на файл password_aes.key при розміщенні його в мережевому каталозі.

І наостанок, найсумніший момент. Пароль з об'єкта PSCredential у відкритому вигляді витягується дуже просто:

$ Cred.GetNetworkCredential (). Password

Можна витягти пароль в текстовому вигляді і з SecureString:

$ BSTR = [System.Runtime.InteropServices.Marshal] :: SecureStringToBSTR ($ pass)
[System.Runtime.InteropServices.Marshal] :: PtrToStringAuto ($ BSTR)

Як ви розумієте, саме тому небажано зберігати паролі привілейованих облікових записів, таких як Domain Admins де б то не було крім DC.

Порада. Для захисту адміністративних врахованих записів від добування паролів з пам'яті за допомогою утиліт подібних Mimikatz потрібно використовувати комплексні заходи, в тому числі організаційного плану.