Package: dpapi-ng
dpapi_ng - Python DPAPI-NG De-/Encryption LibraryLibrary forDPAPI NG, also known as CNG DPAPI, de- and encryption in Python.
It is designed to replicate the behaviour ofNCryptUnprotectSecretandNCryptProtectSecret.
This can be used on non-Windows hosts to de-/encrypt DPAPI NG protected secrets, like PFX user protected password, or LAPS encrypted password.
It can either decrypt any DPAPI NG blobs using an offline copy of the domain's root key or de-/encrypt by using the credentials of the supplied user to retrieve the required information over RPC.Currently only these protection descriptors are supported:TypePurposeSIDOnly the SID user or members of the SID group can decrypt the secretThis implements theMS-GKDI Group Key Distribution Protocol.RequirementsCPython 3.7+cryptographydnspython >= 2.0.0pyspnego >= 0.9.0How to InstallTo install dpapi-ng with all the basic features, runpython-mpipinstalldpapi-ngKerberos AuthenticationKerberos authentication support won't be installed by default as it relies on system libraries and a valid compiler to be present.
The krb5 library and compiler can be installed by installing these packages:# Debian/Ubuntuapt-getinstallgccpython3-devlibkrb5-dev# Centos/RHELyuminstallgccpython-develkrb5-devel# Fedoradnfinstallgccpython-develkrb5-devel# Arch Linuxpacman-Sgcckrb5Once installed, the Kerberos Python extras can be installed withpython-mpipinstalldpapi-ng[kerberos]Kerberos also needs to be configured to talk to the domain but that is outside the scope of this page.From Sourcegitclonehttps://github.com/jborean93/dpapi-ng.gitcddpapi-ng
pipinstall-e.ExamplesThere is both a sync and asyncio API available to de-/encrypt a blob.importdpapi_ng### DECRYPTION ###dpapi_ng_blob=b"..."decrypted_blob=dpapi_ng.ncrypt_unprotect_secret(dpapi_ng_blob)# async equivalent to the abovedecrypted_blob=awaitdpapi_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 abovedpapi_ng_blob=awaitdpapi_ng.async_ncrypt_protect_secret(data,target_sid)To decrypt the blob, the key specified in the blob needs to be retrieved from the domain controller the blob was generated by.
To encrypt a blob, the group key of the target SID specified needs to be retrieved.
For decryption, the domain controller hostname is automatically retrieved through an DNSSRVlookup of_ldap._tcp.dc._msdcs.{domain_name}with the domain name found in the DPAPI-NG blob or with the value specified in theserverkwarg.
For encryption, the domain controller hostname is either determined using thedomain_namekwarg for a DNSSRVlookup, theserverkwarg directly or by using the system's search domain (if available).
It will attempt to authenticate with the current user identifier which on Linux will only exist ifkinithas already been called to retrieve a user's ticket.
Otherwise if no identity is available, theusernameandpasswordkwargs can be used to specify a custom user.The following kwargs can be used forncrypt_unprotect_secret,async_ncrypt_unprotect_secret,ncrypt_protect_secretandasync_ncrypt_protect_secret.server: Use this server as the RPC target if a key needs to be retrievedusername: The username to authenticate as for the RPC connectionpassword: The password to authenticate with for the RPC connectionauth_protocol: The authentication protocol (negotiate,kerberos,ntlm) to use for the RPC connectioncache: A cache to store keys retrieved for future operationIn addition to that,ncrypt_protect_secretandasync_ncrypt_protect_secrettake the following additional kwarg.domain_name: The name of the domain/forest to use when looking up the RPC target to use when retrieving the key inforoot_key_identifier: The root key identifier UUID, necessary in order to make the cache work for these functionsIt is also possible to encrypt and decrypt the DPAPI-NG blob by providing the root key stored in the domain.
This can either be retrieved using an offline attack or through an LDAP query if running as a Domain Admin user.
To retrieve the domain root keys using PowerShell the following can be run:$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=1RootKeyId=[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')}}The followingldapsearchcommand can be used outside of Windows:ldapsearch\-b'CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,CN=Configuration,DC=domain,DC=test'\-sone\'(objectClass=msKds-ProvRootKey)'\cn\msKds-KDFAlgorithmID\msKds-KDFParam\msKds-SecretAgreementAlgorithmID\msKds-SecretAgreementParam\msKds-PrivateKeyLength\msKds-PublicKeyLength\msKds-RootKeyDataNote: ldapsearch will most likely need the -H and user bind information to succeed.The information retrieved there can be stored in a cache and used for subsequentncrypt_protect_secretandncrypt_unprotect_secretcalls:importuuidimportdpapi_ngcache=dpapi_ng.KeyCache()root_key_id=uuid.UUID("76ec8b2d-d444-4f67-9db7-2f62b4358b35")cache.load_key(b"...",# msKds-RootKeydataroot_key_id,# cnversion=1,kdf_algorithm="SP800_108_CTR_HMAC",# msKds-KDFAlgorithmIDkdf_parameters=b"...",# msKds-KDFParamsecret_algorithm="DH",# mskds-SecretAgreementAlgorithmIDsecret_parameters=b"...",# msKds-SecretAgreementParamprivate_key_length=512,# msKds-PrivateKeyLengthpublic_key_length=2048,# msKds-PublicKeyLength)dpapi_ng.ncrypt_unprotect_secret(b"...",cache=cache)Currently theSP800_108_CTR_HMACKDF algorithm andDH,ECDH_P256, andECDH_P384secret agreement algorithms have been tested to work.
TheECDH_P521secret agreement algorithm should also work but has been untested as a test environment cannot be created with it right now.Special ThanksI would like to thank the following people (GitHub or Twitter handles in brackets) for their help on this project:Georg Sieber (@schorschii) for implementing encryption supportGrzegorz Tworek (@0gtweet) and Michał Grzegorzewski for providing more information on the internal BCrypt* API workflow used in DPAPI-NGMarc-André Moreau (@awakecoding) for their help with reverse engineering some of the Windows APIs and talking through some theoriesSkelSec (@SkelSec) for help on the RPC calls and being available as a general sounding board for my theoriesSteve Syfuhs (@SteveSyfuhs) for connecting me with some Microsoft engineers to help understand some undocumented logicWithout their patience and knowledge this probably would not have been possible.