Давным-давно, в далёкой галактике, пред-предыдущий администратор вашего SQL Server задал в нём linked server, используя специально для этой цели созданный аккаунт со сгенерированным паролем. Теперь вам с этим линком нужно что-то сделать, например перенести его на другой SQL Server; но просто так это не сделать, потому что никто не знает пароля от того аккаунта. Знакомая ситуация?
Хотя MSSQL не хранит пароли для своих аккаунтов, а хранит только их хэши, — с linked server-ами так не получится, потому что для успешной аутентикации перед внешним сервером нужно обладать паролем в открытом виде. Пароли для linked server-ов хранятся в зашифрованном виде в таблице
![](https://habrastorage.org/r/w780q1/getpro/habr/post_images/3e6/f77/74b/3e6f7774be67d5441a1539bc42bba7c1.jpg)
Но не всё так просто. Во-первых, эта таблица недоступна из обычного SQL-соединения, а доступна только из Dedicated Administrative Connection. На DAC накладываются существенные ограничения: открыть DAC может только пользователь с привилегией sysadmin, и одновременно к одному серверу может быть открыто только одно DAC. Если у вас есть права локального администратора на сервере, но вы не можете войти в MSSQL с правами sysadmin, то есть обходной путь — заходить не из-под своего аккаунта, а из-под аккаунта сервиса MSSQL или даже из-под LocalSystem.
Во-вторых, несмотря на то, что поле с зашифрованным паролем называется
![](https://habrastorage.org/r/w780q1/getpro/habr/post_images/847/88c/afd/84788cafd7b5bc8d9f8b5904a87e8747.jpg)
Этот ключ хранится в двух экземплярах: первый (
В-третьих, ключ для расшифровки сам зашифрован, и «ключ для ключа» хранится в системном реестре в
![](https://habrastorage.org/r/w780q1/getpro/habr/post_images/890/0f6/839/8900f683975240ee4119d56d5e97162a.jpg)
Чтобы прочитать из реестра это значение, опять же требуются права локального администратора на сервере.
Для получения всех трёх составляющих и расшифровки сохранённых паролей, автор создал удобный PowerShell-скрипт.
Если запустить его из-под аккаунта локального администратора на сервере, он порадует вас примерно таким окошком:
![](https://habrastorage.org/r/w780q1/getpro/habr/post_images/fc4/ae6/fa2/fc4ae6fa26d9d78a15e62d6b2a02ccf7.jpg)
Если же вы не хотите запускать на production-сервере непонятно кем написанные скрипты, то саму расшифровку можно выполнить и без прав администратора, если сначала вытащить три составляющих при помощи SQL Studio и regedit, и вставить их в скрипт в явном виде. Первый шаг расшифровки (
Аналогичным образом расшифровываются и credentials, сохранённые в базе для выполнения команд (
С одной стороны, всё это кажется вопиющим примером security through obscurity: если расшифровка паролей для соединения с linked server-ами уже реализована в MSSQL, то почему нет возможности показать эти пароли забывчивому администратору? С другой стороны, с точки зрения безопасности всё весьма неплохо: для расшифровки паролей нужен доступ к серверу с правами локального администратора, а если злоумышленник получил такой доступ, то он уже и так может делать с сервером всё что захочет. Нежелательное повышение привилегий возможно лишь в том случае, если пароль от какого-нибудь linked server-а используется впридачу для чего-нибудь важного, например как пароль администратора того же сервера :^)
Хотя MSSQL не хранит пароли для своих аккаунтов, а хранит только их хэши, — с linked server-ами так не получится, потому что для успешной аутентикации перед внешним сервером нужно обладать паролем в открытом виде. Пароли для linked server-ов хранятся в зашифрованном виде в таблице
master.sys.syslnklgns
:![](https://habrastorage.org/getpro/habr/post_images/3e6/f77/74b/3e6f7774be67d5441a1539bc42bba7c1.jpg)
Но не всё так просто. Во-первых, эта таблица недоступна из обычного SQL-соединения, а доступна только из Dedicated Administrative Connection. На DAC накладываются существенные ограничения: открыть DAC может только пользователь с привилегией sysadmin, и одновременно к одному серверу может быть открыто только одно DAC. Если у вас есть права локального администратора на сервере, но вы не можете войти в MSSQL с правами sysadmin, то есть обходной путь — заходить не из-под своего аккаунта, а из-под аккаунта сервиса MSSQL или даже из-под LocalSystem.
Во-вторых, несмотря на то, что поле с зашифрованным паролем называется
pwdhash
— это никакой не хэш, а зашифрованные данные. Ключ для расшифровки хранится в системной таблице master.sys.key_encryptions
:![](https://habrastorage.org/getpro/habr/post_images/847/88c/afd/84788cafd7b5bc8d9f8b5904a87e8747.jpg)
Этот ключ хранится в двух экземплярах: первый (
thumbprint=0x01
) позволяет использование только из-под аккаунта сервиса MSSQL, второй (thumbprint=0x0300000001
) — из-под любого аккаунта на сервере. Обратите внимание, что ни один из хранящихся ключей не пригоден для «офлайн-расшифровки» паролей вне сервера, так что если злоумышленнику и удастся украсть данные обеих этих системных таблиц, ему это ничего не даст.В-третьих, ключ для расшифровки сам зашифрован, и «ключ для ключа» хранится в системном реестре в
HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$InstanceName\Security\Entropy
:![](https://habrastorage.org/getpro/habr/post_images/890/0f6/839/8900f683975240ee4119d56d5e97162a.jpg)
Чтобы прочитать из реестра это значение, опять же требуются права локального администратора на сервере.
Для получения всех трёх составляющих и расшифровки сохранённых паролей, автор создал удобный PowerShell-скрипт.
Если запустить его из-под аккаунта локального администратора на сервере, он порадует вас примерно таким окошком:
![](https://habrastorage.org/getpro/habr/post_images/fc4/ae6/fa2/fc4ae6fa26d9d78a15e62d6b2a02ccf7.jpg)
Если же вы не хотите запускать на production-сервере непонятно кем написанные скрипты, то саму расшифровку можно выполнить и без прав администратора, если сначала вытащить три составляющих при помощи SQL Studio и regedit, и вставить их в скрипт в явном виде. Первый шаг расшифровки (
$ServiceKey = [System.Security.Cryptography.ProtectedData]::Unprotect($SmkBytes, $Entropy, 'LocalMachine')
) обязан выполняться на сервере, но второй ($Decrypt = $Decryptor.CreateDecryptor($ServiceKey,$Logins.iv)
и последующая работа с CryptoStream) может выполняться и в офлайне.Аналогичным образом расшифровываются и credentials, сохранённые в базе для выполнения команд (
xp_cmdshell
и т.п.) от имени менее привилегированных аккаунтов, нежели сервис MSSQL.С одной стороны, всё это кажется вопиющим примером security through obscurity: если расшифровка паролей для соединения с linked server-ами уже реализована в MSSQL, то почему нет возможности показать эти пароли забывчивому администратору? С другой стороны, с точки зрения безопасности всё весьма неплохо: для расшифровки паролей нужен доступ к серверу с правами локального администратора, а если злоумышленник получил такой доступ, то он уже и так может делать с сервером всё что захочет. Нежелательное повышение привилегий возможно лишь в том случае, если пароль от какого-нибудь linked server-а используется впридачу для чего-нибудь важного, например как пароль администратора того же сервера :^)