在之前 我已经讨论过一些有关DPAPI(数据保护应用编程接口)的内容,其中包括KeePass如何在 “Windows User Account”的key参数选项中使用DPAPI 。最近我进行了一些有趣的工作,这些工作Benjamin Delpy已经进行过 ,我想在此记录一些有关结合Mimikatz滥用DPAPI的方法。
注:本文关注对用户DPAPI的滥用,但有时也会涉及对机器DPAPI key的滥用。如果在之后我把滥用机器DPAPI搞清楚了,会随后发表一篇文章。
另注:我不是最初提出这些滥用的人,也没有写一些工具来实际滥用它们。这些思想都来自Benjamin和其它本文引用的人。我只是简单地整理各种滥用样例、语法,形成一个操作指南。
我没有在文章中包含很多DPAPI背景知识,其它文章已经介绍地很好了:
我确实丢掉了一些有趣的内容,但上面的资料是我了解DPAPI如何工作以及有哪些潜在滥用情况所阅读的资料。
DPAPI提供了一个简单的API集合,使用与用户绑定或与系统绑定的隐含key来透明地加密(CryptProtectData())和解密(CryptUnprotectData())数据blob。这使得应用程序保护用户数据时不用担心key管理等工作。有许多事情要用到DPAPI,本文我只关注Chrome的Cookies和登录数据、Windows证书管理器(如保存IE或Edge登录密码和文件共享(RDP)密码)、远程桌面连接管理器的.rdg文件。
在顶层从用户角度看,用户的密码被用于产生一组用户特有的“Master Key”。这些Key的位置在C:\Users\<USER>\AppData\Roaming\Microsoft\Protect\<SID>\<GUID>,<SID>是用户的安全描述符,<GUID>是Master Key的名字。一个用户可以有多个Master Key。Master Key需要使用用户的密码或域备份key(见第四部分,Chrome)来解密,解密后的Master Key用于解密DPAPI blob。
所以,如果我们试图解密某个用户的被加密的DPAPI blob(如Chrome cookie值),我们需要关注该用户的Master Key。
Chrome使用DPAPI保存两块我们感兴趣的信息:cookie值和登录数据:
%localappdata%在大多数系统对应于“C:\Users\<USER>\AppData\Local”。同时,这一节的所有Mimikatz命令对Cookie文件和登录数据文件都有效。
Chrome在一个SQLite数据库中存储cookies,存储的cookie值被加密为DPAPI blob来进行保护。幸运的是,Benjamin在Mimikatz中实现了Chrome的SQLite数据库解析。要列出当前用户的有效cookie值,使用下面Mimikatz命令:
然而,cookie的值被DPAPI使用用户的master key加密过,master key则被用户的密码保护(或域备份key)。在某些情况下我们能够获得这些cookie值(或登录数据):
这是最简单的情况。如果你想在用户上下文执行Beacon、Mimikatz或其它可执行代码,只需在dpapi::chrome命令中增加/unprotect标志:
该命令只是通知Mimikatz使用CryptUnprotectData API来解密我们想要的cookie值。如果我们在用户上下文执行该命令,将默认使用他们的master key执行请求的解密操作。
注:如果在Chrome使用该代码,有时会遇到打开Cookies数据库失败的问题。这时,只需复制Cookie或登录数据文件到当前的操作目录并使用新路径运行dpapi::chrome命令即可。
如果你不想在另一个用户的上下文注入一个Beacon,或者你登录了一个当前已有多个用户登录的系统,你可以选择做一些事。
如果你对属于另一个用户的数据库执行/unprotect操作,调用CryptUnprotectData()进行解密将得到一个错误。较新版本的Mimikatz能识别出解密所需的master key的GUID(在Cobalt Strike更新Mimikatz,就会在输出中看到),形如:needed masker key is {b8854128-023c-433d-aac9-232b4bca414c}。
我们可以看到,这个master key是harmj0y的Chrome Cookies目录的位置。我们同样可以通过列出用户目录(C:\Users\<USER>\AppData\Roaming\Microsoft\Protect\<SID>\<GUID>)内的所有makter key的GUID来得到用户的所有master key,以此来得到所需master key的GUID。如何对每个用户进行该操作,见Seatbelt节。
我们需要想办法获取harmj0y的master key。一个选择是运行sekurlsa::dpapi从内存提取所有系统内当前登录用户的DPAPI key(有时也在sekurlsa::msv命令的输出中显示)。
注:如果你没有通过Beacon使用Mimikatz,你可以利用Mimikatz的DPAPI缓存(详见文章最后的缓存节)。因为Beacon的工作架构,每一个Mimikatz命令会在一个新进程中运行,所以各个Minikatz命令间不会保存状态。通过GUI发出多个mimikatz命令可以解决该问题,发多个命令也可以通过Aggressor脚本实现。
比较GUID {b8854128-023c-433d-aac9-232b4bca414c}与提取的DPAPI key可知,我们需要的sha1 master key是f35cfc2b44aedd7…(使用完整版本或sha1版本的master key都可以)。可以在命令中通过参数手动指定dpapi的Chrome模块的解密目标:
如果目标用户当前未登录到系统,你就需要知道他的明文密码或者密码的NTLM hash值。如果你知道明文密码,可以使用spawnas或者runas来产生一个以特定用户身份运行的新代理,然后在目标用户上下文运行:
如果你只有用户的hash,可以使用Mimikatz的sekurlsa::pth来产生新进程(或使用Beacon的path封装来获取模拟token)。然而,当Mimikatz在新创建的登录会话中以登录类型9使用证书时(如NewCredentials/netonly),这些凭证在本地主机无法使用(也就还是无法得到目标用户上下文的进程),所以使用/unprotect将以相同的错误NTE_BAD_KEY_STATE失败。
然而,如果在网络使用这些凭证,并且要获取的key是属于当前用户的,我们可以使用Mimikatz来利用MS-BKRP(BackupKey远程协议)获取需要的key。Benjamin在他的Wik 里详细描述了这个过程(在他文章“Credential Manager and Windows Vaults”节的最后也有更多细节)。实现这个RPC调用的代码是kull_m_rpc_bkrp.c 。我们需要做的是指定makter key位置并应用/rpc标志:
注:模块前面必须有@前缀,这让Beacon强制Mimikatz对新产生的Mimikatz使用模拟线程token。
到这里,我们就可以利用这个mater key解密我们指定的blob(语法见情况二)
这是最有趣的情况。
一个可选的方法是DCSync目标用户的hash并重复情况三的方法。但还有更好的方法。
域用户的master key被一个域范围的备份DPAPI key保护着。这个key被 /rpc命令用于解密每个域用户的master key,这是架构中的专门设计。那为什么不只使用这个备份key?(假设有域管理员或类似的权限):
语法是:lsadump::backupkeys /system:<DOMAIN CONTROLLER> /export。这个.pvk后缀的特权key可以解密任何一个域用户的master key,而且这个备份key不会变化。
所以,我们下载harmj0y的master key文件(b8854128-023c-433d-aac9-232b4bca414c)和Chrome的cookies数据库,还有.pvk后缀的私有key。
旁注:获取备份key
当MS-BKRP的新版本开始支持基于RPC远程获取备份key(详见3.1.4.1.3节 BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID ),Mimikatz也实现了这个RPC调用 ,lsadump::backupkeys方法使用LsaOpenPolicy/LsaRetrievePrivateData API(代替MS-BKRP)获取G$BCKUPKEY_PREFERRED LSA secret值。
我想更好地理解这个逻辑,所以我将Benjamin的远程备份key获取逻辑用C#重新实现了。项目上传到了GhostPark仓库 。 默认会从当前的域控获取DPAPI备份key并输出为base64串,但该输出行为也可以修改:
一旦你获得了一个用户的master key或域备份key,你就不用在目标主机执行解密命令。你可以下载任何用户的master key文件(详细见后面的Seatbelt节)和DPAPI容器(如Cookies)。此时你可以选择:
那么这里就用域备份key通过Mimikatz解密harmj0y的master key,然后使用master key解密Chrome cookie数据库:
如果保存一份这个.pvk key,就可以直接下载master key和需要的DPAPI blobs并离线解密。\m/
提示:下面的内容没有任何是我提出的,我只是将它们整理并以我认为最好理解的方式展示出来。所有荣誉都应当属于Benjamin在这个领域让人惊叹的工作。
从Windows 7开始,证书管理器准许用户存储网页和其它网络资源的证书。用户证书文件存储在C:\Users\<USER>\AppData\Local\Microsoft\Credentials\,系统证书文件存储在%systemroot%\System32\config\systemprofile\AppData\Local\Microsoft\Credentials\,这些证书文件被用户或系统特有的DPAPI master key保护着。
与此相关的是更难懂一点的Windows Vaults,它存储在C:\Users\<USER>\AppData\Local\Microsoft\Vault\<VAULT_GUID>\。在一个vault目录内,存在一个包含两个key(AES128和AES256)的Policy.vpol文件,该文件被一个用户特有的DPAPI master key保护着。这两个key可以用于解密相同目录里的其它*.vcrd证书。
这就是Vaults更加复杂的地方。
有一些获取这些vault证书的方法。如果证书存储的是IE或Edge登录信息,可以使用vaultcli.dll中的一系列API枚举这些证书。这可以通过Mimikatz的vault::list模块、Massimiliano Montoro的Vault转储代码 、Matt Graeber引用了相同代码的PowerShell代码 、Dwight Hohnstein引用了Graeber的C#代码 、Seatbelt 对Dwight代码的整理(seatbelt.exe DumpVault)来实现。然而,当运行这些代码时你会发现一个有趣的事情,不是所有的vault证书都返回了。为什么?
猜猜是啥?Benjamin一年多前已经在他的wiki上说明了准确的原因。下面对他的内容进行重新组织,不过还是推荐去看看他的wiki !
和我理解的一样,vault::list尝试列出和解密\AppData\Local\Microsoft\Vault\位置的证书,vault::cred尝试列出和解密\AppData\Local\Microsoft\Credentials\位置的证书。我不确定证书被分隔到两个目录的原因,看上去web证书以vaults形式存储,RDP或文件共享证书以证书文件形式存储。和Benjamin提到的一样:
那个链接已经不能访问了,我觉得那个tweet的内容应该是上面提到内容的屏幕截图。
Benjamin在wiki 中说到,微软对vault证书的描述如下:
LSASS不想让我们轻易获得这些证书。Benjamin介绍了两个获取这些证书的相关研究。较危险的一个是运行vault::cred /patch命令修改LSASS的代码逻辑跳过CRED_TYPE_DOMAIN_PASSWORD检测。这当然不被建议(不管是Benjamin还是我),修改LSASS代码逻辑是一个危险操作,很有可能出错。此外,有一个更好地办法:moar DPAPI!
Benjamin描述了我们这里会遇到的另一个问题 。根据微软介绍,“LSA线程可以通过指定CRYPTPROTECT_SYSTEM标志使用DPAPI保护那些LSA外部的无法被保护的数据(https://msdn.microsoft.com/en-us/library/ms995355.aspx )”。所以,如果你使用CryptUnprotectData(如/unprotect)解密这类型的blobs,将得到错误。但是测试发现,这些blob是被 DPAPI master key加密的。
如果我们知道用户的明文密码,你就可以使用Chrome一样的方法(情况一)解密这个master key。如果不知道,不用担心,Mimikatz依旧可以帮你。
如Benjamin的详细介绍,MS-BKRP的一个组件(微软BackupKey远程协议)是一个运行在域控上的RPC服务,专门为授权用户解密DPAPI key(基于域范围的DPAPI备份key)的服务。换句话说,如果我们当前的用户上下文有一个特定的master key,你就可以让域控为我们解密它!这不是一个脆弱性,这是专门的设计,这是为用户修改或忘记密码的情况留下的漏报,并可用于支持各种smart card功能。
所以,如果我们从拥有master key的用户上下文(与Chrome类似,情况三)运行Mimikatz
Mimikatz会请求域控(通过RPC)解密master key:
现在就可以通过dpapi::cred模块使用/masterkey:X参数解密保存的证书了!
更大的优势在于,当我们连接LSASS时,无需提权就可以对当前用户执行该方法。如果想对任何用户执行这类攻击,可以使用Chrome情况2-4的方法。
你可能会问:“任务调度证书呢?”Benjamin也包含了这块内容。你能够从内存(使用sekurlsa::dpapi)或LSA(使用lsadump::secrets)提取系统的DPAPI key,然后使用这个key解密保存在%systemroot%\System32\config\systemprofile\AppData\Local\Microsoft\Credentials的证书。
你可能还会问:“加密文件系统(EFS)呢?”。令人惊讶,Benjamin的wiki也介绍了。
里面还有解密Windows 10 SSH native SSH key的方法,Benjamin还提供了一个很好的demo视频(https://twitter.com/gentilkiwi/status/1000162256456441857 )。里面还包括了为dpapi::wifi和dpapi::wwan提供的模块(https://twitter.com/gentilkiwi/status/696021888385028096 )及其它模块。
当我起草这个文章,Benjamin分享了DPAPI的更多内容!
Windows远程桌面连接管理器 有一个选项可以保存RDP连接证书,这也是将明文密码以DPAPI blob形式存储。这些配置文件存储在.rdg文件中,并可以使用新的dpapi::rdg模块解密。这个模块还没有出现在Beacon的mimikatz模块中,但在mimikatz下一次更新中会包含进去。同样,/unprotect,plaintext/hash,sekurlsa::dpapi masterkey,域备份key(Chrome 情况1-4)在这里一样工作。
关于如何枚举这些文件,见Seatbelt节。
在文章前面提到过,基于Beacon的任务架构,每个mimikatz命令会在一个新的进程中运行,所有mimikatz命令之间无法保存状态。然而,Benjamin实现了一个很酷的DPAPI特征,我在想提一下:
如果你单独使用mimikatz.exe,Mimikatz将获得的DPAPI key存到一个缓存中以备后用。所以,如果你获得了域备份key,你可以随后解密任何master key,master key也会随后被加入缓存:
也可以保存/载入缓存来重用:
我最近整合进Seatbelt 了一些对相关DPAPI文件进程检查的功能(更多信息见http://www.harmj0y.net/blog/redteaming/ghostpack/ )。Seatbelt.exe MasterKeys会搜索用户的master keys,不论是当前用户还是任何在系统内提交了上下文的用户。这是现在SeatBelt.exe用户检查功能执行的的默认检查:
[注意]APP应用上架合规检测服务,协助应用顺利上架!