破解EFS加密文件(C++)

825 阅读3分钟

什么是 EFS

加密文件系统(英语:Encrypting File System,缩写 EFS)是微软在 NTFS 3.0 中引入的一个功能,它提供文件系统级加密。此技术使文件支持透明加密以保护机密数据免受具有物理访问权限的攻击者侵害。详见维基百科:加密文件系统

EFS 加解密流程

EFSOperation.svg

1、EFS使用对称密钥加密文件,这被称为文件加密密钥(File Encryption Key),简称FEK。使用对称加密算法是因为加密和解密大量数据时这比使用非对称密钥密码本消耗更少的时间。在不同的操作系统版本和配置上将使用不同的对称加密算法。

2、然后会使用一个与加密文件的**用户相关联的公钥**加密,加密的 FEK 将被存储在加密文件的 $EFS 属性中

3、解密时,只需拿到对应用户的私钥,解密出 FEK,再使用 FEK 对文件进行解密即可。

EFS 破解

了解完 EFS 的加解密流程,下面就直奔主题:破解 EFS。流程大致如下:

EFSFlow.jpg

想要解开 EFS 加密文件,用户私钥 RSA.PrivateKey 是关键。上图是通过用户明文密码的方式去获取的用户私钥,如果你没有用户明文密码,也可以通过解析 pfx 文件等方式去获取。

MasterKey File

主密钥文件位置:C:\Users\【用户名】\AppData\Roaming\Microsoft\Protect\【用户SID】

RSA.PrivateKey File

用户私钥文件位置:C:\Users\【用户名】\AppData\Roaming\Microsoft\Crypto\RSA\【用户SID】

$EFS Attribute

常规读取文件的方式,是无法读取到 EFS 加密文件的 $EFS 属性部分的,需要基于 NTFS 系统去读取该属性的数据。

当 NTFS 加密文件时,会为文件创建一个 $EFS 属性。该属性的 ID=0x100,也叫 LOGGED_UTILITY_STREAM (100h)

其结构体如下:

 typedef struct {
     UINT32 AttributeLength; /* Length of EFS attribute in bytes. */
     UINT32 State; /* Always 0? */
     UINT32 Version; /* Efs version.  Always 2? */
     UINT32 CryptoAPIVersion; /* Always 0? */
     UINT8  Checksum[16]; /* MD5 hash of decrypted FEK?  This field is
                          created with a call to UuidCreate() so is
                          unlikely to be an MD5 hash and is more
                          likely to be GUID of this encrytped file
                          or something like that. */
     UINT8  ChecksumDDF[16]; /* MD5 hash of DDFs? */
     UINT8  ChecksumDRF[16]; /* MD5 hash of DRFs? */
     UINT32 OffsetToDDF; /* Offset in bytes to the array of data
                         decryption fields (DDF), see below.  Zero if
                         no DDFs are present. */
     UINT32 OffsetToDRF;/* Offset in bytes to the array of data
                        recovery fields (DRF), see below.  Zero if
                        no DRFs are present. */
     UINT32 Reserved;
 } EFS_HEADER;
 ​
 typedef struct {
     UINT32 Count; /* Number of data decryption/recovery fields in the array. */
 } EFS_ARRAY_HEADER;
 ​
 typedef struct {
     UINT32 DFLength; /* Length of this data decryption/recovery field in bytes. */
     UINT32 CredHeaderOffset; /* Offset in bytes to the credential header. */
     UINT32 FEKSize; /* Size in bytes of the encrypted file encryption key (FEK). */
     UINT32 FEKOffset; /* Offset in bytes to the FEK from the start of the data
                       decryption/recovery field. */
     UINT32 Reserved; /* always 0?  Might be just padding. */
 } EFS_DF_HEADER;
 ​
 typedef struct {
     UINT32 CredLength; /* Length of this credential in bytes. */
     UINT32 SIDOffset; /* Offset in bytes to the user's sid from start
                        of this structure.  Zero if no sid is present. */
     UINT32 Type; /* Type of this credential:
                  1 = CryptoAPI container.
                  2 = Unexpected type.
                  3 = Certificate thumbprint.
                  other = Unknown type. */
     union {
         /* CryptoAPI container. */
         struct {
             UINT32 ContainerNameOffset; /* Offset in bytes to
                                the name of the container from start of this
                                structure (may not be zero). */
             UINT32 ProviderNameOffset; /* Offset in bytes to the name of the provider from start of this
                                          structure (may not be zero). */
             UINT32 PublicKeyBlobOffset; /* Offset in bytes to the public key blob from start of this
                                         structure. */
             UINT32 PublicKeyBlobSize; /* Size in bytes of public key blob. */
         };
         /* Certificate thumbprint. */
         struct {
             UINT32 CertThumbprintHeaderSize; /* Size in
                                bytes of the header of the certificate
                                thumbprint. */
             UINT32 CertThumbprintHeaderOffset; /* Offset in
                                bytes to the header of the certificate
                                thumbprint from start of this structure. */
             UINT32 Unknown1; /* Always 0?  Might be padding... */
             UINT32 Unknown2; /* Always 0?  Might be padding... */
         };
     };
 } EFS_DF_CREDENTIAL_HEADER;
 ​
 typedef struct {
     UINT8 Revision;
     UINT8 SubAuthorityCount;
     UINT8 IdentifierAuthority[6];
     UINT32 SubAuthority;
     UINT32 Sid[4];
 } EFS_SID;

到这里就非常明确了,只需从 EFS 加密文件的 $EFS Attribute中读取 FEK 密文,再通过用户私钥对其进行解密,得到 FEK明文,再通过对称加密算法,将文件内容进行解密,即可。

参考资料:

[1] EFS 加密文件系统:ntfs.com/attribute-e…

[2] wikipedia:zh.wikipedia.org/wiki/%E5%8A…