
Python DPAPI NG Decryptor for non-Windows Platforms
一个用于 DPAPI NG(也称为 CNG DPAPI)在 Python 中进行解密和加密的库。 它旨在模拟 NCryptUnprotectSecret 和 NCryptProtectSecret 的行为。 它可以在非 Windows 主机上用于解密/加密受 DPAPI NG 保护的机密,例如 PFX 用户保护的密码,或 LAPS 加密的密码。 它既可以使用域根密钥的离线副本来解密任何 DPAPI NG blob,也可以使用所提供用户的凭据通过 RPC 检索所需信息,从而进行解密/加密。
目前仅支持以下保护描述符:
| 类型 | 用途 |
|---|---|
| SID | 只有 SID 用户或 SID 组成员才能解密该机密 |
本库实现了 MS-GKDI 组密钥分发协议。
要安装包含所有基本功能的 dpapi-ng,请运行
python -m pip install dpapi-ng
Kerberos 身份验证支持默认不会安装,因为它依赖于系统库和有效的编译器。 可以通过安装以下软件包来安装 krb5 库和编译器:
# Debian/Ubuntu
apt-get install gcc python3-dev libkrb5-dev
# Centos/RHEL
yum install gcc python-devel krb5-devel
# Fedora
dnf install gcc python-devel krb5-devel
# Arch Linux
pacman -S gcc krb5
安装完成后,可以使用以下命令安装 Kerberos Python 附加组件:
python -m pip install dpapi-ng[kerberos]
Kerberos 还需要配置为与域通信,但这不在本页面的讨论范围内。
git clone https://github.com/jborean93/dpapi-ng.git
cd dpapi-ng
pip install -e .
提供了同步和 asyncio API 用于解密/加密 blob。
import dpapi_ng
### DECRYPTION ###
dpapi_ng_blob = b"..."
decrypted_blob = dpapi_ng.ncrypt_unprotect_secret(dpapi_ng_blob)
# async equivalent to the above
decrypted_blob = await dpapi_ng.async_ncrypt_unprotect_secret(dpapi_ng_blob)
### ENCRYPTION ###
data = b"..."
target_sid = "S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX"
dpapi_ng_blob = dpapi_ng.ncrypt_protect_secret(data, target_sid)
# async equivalent to the above
dpapi_ng_blob = await dpapi_ng.async_ncrypt_protect_secret(data, target_sid)
要解密 blob,需要从生成该 blob 的域控制器检索 blob 中指定的密钥。
要加密 blob,需要检索所指定目标 SID 的组密钥。
对于解密,域控制器主机名通过 DNS SRV 查询 _ldap._tcp.dc._msdcs.{domain_name} 自动获取,域名取自 DPAPI-NG blob 中发现的域名,或使用 server 关键字参数中指定的值。
对于加密,域控制器主机名可以通过 domain_name 关键字参数进行 DNS SRV 查询来确定,直接使用 server 关键字参数,或使用系统的搜索域(如果可用)。
它将尝试使用当前用户标识进行身份验证,在 Linux 上,仅当已经调用 kinit 获取用户票据时该标识才会存在。
否则,如果没有可用的身份,可以使用 username 和 password 关键字参数来指定自定义用户。
以下关键字参数可用于 ncrypt_unprotect_secret、async_ncrypt_unprotect_secret、ncrypt_protect_secret 和 async_ncrypt_protect_secret。
server:如果需要检索密钥,则使用此服务器作为 RPC 目标username:用于 RPC 连接身份验证的用户名password:用于 RPC 连接身份验证的密码auth_protocol:用于 RPC 连接的身份验证协议(negotiate、kerberos、ntlm)cache:用于存储检索到的密钥以供将来操作使用的缓存此外,ncrypt_protect_secret 和 async_ncrypt_protect_secret 还接受以下额外的关键字参数。
domain_name:在检索密钥信息时,用于查找要使用的 RPC 目标的域/林名称root_key_identifier:根密钥标识符 UUID,为使缓存对这些函数生效所必需还可以通过提供存储在域中的根密钥来加密和解密 DPAPI-NG blob。 该根密钥可以通过离线攻击获取,或者在以域管理员用户身份运行时通过 LDAP 查询获取。 要使用 PowerShell 检索域根密钥,可以运行以下命令:
$configurationContext = (Get-ADRootDSE).configurationNamingContext
$getParams = @{
LDAPFilter = '(objectClass=msKds-ProvRootKey)'
SearchBase = "CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,$configurationContext"
SearchScope = 'OneLevel'
Properties = @(
'cn'
'msKds-KDFAlgorithmID'
'msKds-KDFParam'
'msKds-SecretAgreementAlgorithmID'
'msKds-SecretAgreementParam'
'msKds-PrivateKeyLength'
'msKds-PublicKeyLength'
'msKds-RootKeyData'
)
}
Get-ADObject @getParams | ForEach-Object {
[PSCustomObject]@{
Version = 1
RootKeyId = [Guid]::new($_.cn)
KdfAlgorithm = $_.'msKds-KDFAlgorithmID'
KdfParameters = [System.Convert]::ToBase64String($_.'msKds-KDFParam')
SecretAgreementAlgorithm = $_.'msKds-SecretAgreementAlgorithmID'
SecretAgreementParameters = [System.Convert]::ToBase64String($_.'msKds-SecretAgreementParam')
PrivateKeyLength = $_.'msKds-PrivateKeyLength'
PublicKeyLength = $_.'msKds-PublicKeyLength'
RootKeyData = [System.Convert]::ToBase64String($_.'msKds-RootKeyData')
}
}
在 Windows 之外可以使用以下 ldapsearch 命令:
ldapsearch \
-b 'CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,CN=Configuration,DC=domain,DC=test' \
-s one \
'(objectClass=msKds-ProvRootKey)' \
cn \
msKds-KDFAlgorithmID \
msKds-KDFParam \
msKds-SecretAgreementAlgorithmID \
msKds-SecretAgreementParam \
msKds-PrivateKeyLength \
msKds-PublicKeyLength \
msKds-RootKeyData
注意:ldapsearch 很可能需要 -H 和用户绑定信息才能成功。
在那里检索到的信息可以存储在缓存中,并用于后续的 ncrypt_protect_secret 和 ncrypt_unprotect_secret 调用:
import uuid
import dpapi_ng
cache = dpapi_ng.KeyCache()
root_key_id = uuid.UUID("76ec8b2d-d444-4f67-9db7-2f62b4358b35")
cache.load_key(
b"...", # msKds-RootKeydata
root_key_id, # cn
version=1,
kdf_algorithm="SP800_108_CTR_HMAC", # msKds-KDFAlgorithmID
kdf_parameters=b"...", # msKds-KDFParam
secret_algorithm="DH", # mskds-SecretAgreementAlgorithmID
secret_parameters=b"...", # msKds-SecretAgreementParam
private_key_length=512, # msKds-PrivateKeyLength
public_key_length=2048, # msKds-PublicKeyLength
)
dpapi_ng.ncrypt_unprotect_secret(b"...", cache=cache)
目前,SP800_108_CTR_HMAC KDF 算法以及 DH、ECDH_P256 和 ECDH_P384 密钥协商算法已经过测试并确认可以正常工作。
ECDH_P521 密钥协商算法应该也能工作,但由于目前无法用它创建测试环境,尚未经过测试。
我想感谢以下人员(括号内为 GitHub 或 Twitter 用户名)对本项目的帮助:
如果没有他们的耐心和知识,这很可能无法实现。