创建密码重置盘的时候,系统都做了些什么
注:本文作为一篇研究报告,最早发布于我的GitHub repo。进行格式整理与更新后在这里重新发布。
“密码重置盘”是从Windows XP开始即加入Windows系统,并且一直保留至今的一项功能。其目的是在用户忘记登录密码时,可以使用预先创建好的物理凭据重置密码。
几年前,我在网上找到了一位前辈对密码重置盘功能的基于行为的分析,由此对这个功能产生了研究兴趣。本研究报告编写于去年暑假,花了三天时间,基于逆向工程对密码重置盘功能进行分析。
基本原理
密码重置盘的本质是一个RSA私钥,用于解密在系统中(为密码重置而专门)存储的用户密码。
创建密码重置盘时,系统会生成一个RSA公私钥对和一张自签名的证书。用户密码被公钥加密并签名,并和证书一起存储在计算机中(具体细节见下);私钥则被保存在密码重置盘根目录下的userkey.psw
文件中。
由这个过程,我们可以得到密码重置盘的几个性质:
- 密码重置盘与用户一一对应;更换用户,密码重置盘会失效。
- 系统识别密码重置盘只会看根目录下的
userkey.psw
文件,与其他文件一律无关。由此,使用一个可移动介质做多张密码重置盘是可行的,不过需要每次使用之前手动修改文件名。
- 密码重置盘只能重置密码是系统有意进行的限制。由密码重置盘恢复密码是可能的。
下文中,我的研究方向也就在“如何从密码重置盘恢复密码”上。对于其它方向涉及的可能不算深入。
技术细节
值得注意的是,微软在密码重置盘这个功能上基本没有做出过任何改动或改进。下文中,若非特殊说明,分析结论适用于自Windows XP开始所有版本的Windows。
0. 向导程序的手动调用方式
密码重置盘的创建向导和使用向导都在系统的凭据管理器keymgr.dll
中,可以使用命令手动调用:
创建向导(当前用户):rundll32 keymgr.dll,PRShowSaveWizardEx
创建向导(指定用户):rundll32 keymgr.dll,PRShowSaveFromMsginaW <用户名>
重置向导(指定用户):rundll32 keymgr.dll,PRShowRestoreWizardEx <用户名>
在启动向导之前要确保电脑中已有可移动介质插入,否则向导会报错,拒绝启动。
1. 自签名证书
证书因为只做签名目的与恢复密码无关,不做深入研究。
生成的证书被放在本地计算机的一个名为Recovery
的存储区中。这些证书具有如下性质:
- 颁发给/颁发者:空
- 有效期从:创建密码重置盘的时间
- 有效期至:创建密码重置盘的时间 + 5年
- 签名算法:SHA1
- 公钥:RSA 2048位
注意在没创建过密码重置盘的系统中,Recovery
存储区是不存在的。
2. 密码重置盘/RSA私钥
上文所述的userkey.psw
文件具有只读、归档属性。
在Windows XP上,.psw
还有类型标注,名为Password Backup
。同时在注册表中(HKEY_CLASSES_ROOT\PSWFile
)还标注了NoOpen
属性,即在试图打开psw类型文件时,会弹出一个“系统文件警告”提示。
文件的内容本质上是系统API CryptExportKey
的导出结果,外加了一个8字节的文件头。私钥明文存储,文件结构见userkey.h
。
由这个文件的内容,可以使用这里提供的方法还原出一个标准格式的RSA私钥文件。具体操作不再赘述。
3. 加密的用户密码
这些数据(不出所料地)被放进了注册表。
在Windows XP上,具体的路径为HKEY_LOCAL_MACHINE\SECURITY\Recovery\<SID>
的默认键值,类型为REG_BINARY,其中<SID>
为用户的SID。
在Windows 7及以上版本,注册表路径变为了HKEY_LOCAL_MACHINE\C80ED86A-0D28-40dc-B379-BB594E14EA1B\<SID>
(原文如此),而且这个项变成了按需加载;密码重置盘创建完毕时这个项就会被卸载。卸载后的注册表文件位于%SystemRoot%\System32\Microsoft\Protect\Recovery\Recovery.dat
。
注意:无论是注册表还是卸载后的文件,都需要至少SYSTEM权限才能够读取。
这些REG_BINARY数据的结构如regdata.h
所示。数据的最后256字节便是加密过的用户密码。解密步骤如下:
- 取这256字节的数据,进行一次字节顺序翻转。
- 设翻转后的数据保存为文件
enc.bin
,使用上面的步骤生成的DER格式私钥为key.der
。使用OpenSSL解密数据:openssl rsautl -decrypt -in enc.bin -out dec.bin -inkey key.der -keyform DER
。
- 文件
dec.bin
中便是以Unicode格式(Windows口中的Unicode,即UTF-16 LE)存储的密码明文。
X. 逆向工程的更多细节
向导的GUI界面,和密钥/证书生成部分的逻辑,都是在keymgr.dll
中实现的。
由界面到生成逻辑的引用链如下所示(符号名称来自PDB):
1 2 3 4 5 6 7 8 9 10 11 12 13 | PRShowSaveWizardExW
SPageProc1
SaveThread
SaveInfo(进入逻辑部分,返回时保存私钥)
PRGenerateRecoveryKey(生成私钥)
GenerateRecoveryCert(生成证书)
PRImportRecoveryKey(RPC,通知系统保存密码)
PRShowRestoreWizardExW
PRShowRestoreWizardW
RPageProc1
SetAccountPassword(进入逻辑部分)
PRRecoverPassword(RPC,通知系统验证与修改密码)
|
两条调用链都终止于RPC调用,确切的说是LPC端口protected_storage
。有趣的是提供这个端口的服务Protected Storage
服务早在Windows 8时就因为不安全而被移除了,但Windows 10中这个功能仍然可用。
查阅资料得到关键信息:这个端口下有PasswordRecovery
接口,GUID为5cbe92cb-f4be-45c9-9fc9-33e73e557b20
。使用RpcView工具轻易查到,提供这个接口的模块是由lsass.exe
加载的dpapisrv.dll
。
P.S. RpcView的上次Release是2017年,又因为有对系统文件版本的检查,导致其拒绝启动。最终对程序进行了patch才能用。
接口中的三个方法调用链如下:
1 2 3 4 5 6 7 8 9 10 11 12 | keymgr!PRImportRecoveryKey
s_SSRecoverImportRecoveryKey
EncryptRecoveryPassword(构造注册表数据并保存)
(各类细化的操作)
keymgr!PRRecoverPassword
s_SSRecoverPassword
???(未进行分析)
???
s_SSRecoverQueryStatus
???(这个方法好像没用到)
|
注册表数据中的SHA1和签名分别由API CertGetCertificateContextProperty
和内部函数LogonCredGenerateSignature
生成,过程复杂,未进行分析。
负责加密的是API BCryptEncrypt
函数,之后由FMyReverseBytes
进行字节顺序翻转。
最后保存数据的是RecoverySetSupplementalCredential
函数。由此可见,微软内部称密码重置盘为“追加凭据”。那个让前辈不明所以的GUID字符串C80ED86A-0D28-40dc-B379-BB594E14EA1B
也得到了解释:这个字符串是硬编码进去的,不具备什么实际意义。
更多逆向工程细节(以及伪造密码重置盘的尝试)参见本文后续
参考
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2020-10-10 21:50
被张三千编辑
,原因: