是否可以通过分配内存来恢复秘密数据(例如用于解密的空闲内存中的RSA私钥)?

时间:2019-07-17 13:44:09

标签: c cryptography malloc

例如,让我们使用伪代码,该伪代码尝试使用以下方法将RSA私钥存储在已分配(然后为free')的内存中:

int main(){
    bigNum priKey;


    while(true) {
        void *mem = malloc(2024); //allocate a good amount of chunk

        if(rsaKeyIn(mem, &priKey))
            break;
    }

    printf("RSA PRK found: %s", priKey.getText())

    return 0;
}

问题:这可能吗?还是有可能恢复其他秘密数据?

还是为了安全起见,操作系统是否将free的内存归零?如果不是这种情况,我们应该在释放内存之前手动用零填充内存吗?

3 个答案:

答案 0 :(得分:4)

这是有可能的,因为释放内存并不一定意味着它会被清除。

例如,给出以下代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int i, len = 20;
    char *p = malloc(len);
    strcpy(p, "this is a test!!");
    printf("&p=%p, p=%s\n", &p, p);
    for (i=0; i<len; i++) {
        printf("%02x ", p[i]);
    }
    printf("\n");
    free(p);
    // undefined behavior below: dereferencing freed memory
    printf("p=%s\n",  p);
    for (i=0; i<len; i++) {
        printf("%02x ", p[i]);
    }
    printf("\n");
    return 0;
}

我的系统输出以下内容:

&p=0x7ffd24e08290, p=this is a test!!
74 68 69 73 20 69 73 20 61 20 74 65 73 74 21 21 00 00 00 00 
p=
00 00 00 00 00 00 00 00 61 20 74 65 73 74 21 21 00 00 00 00 

因此,如果您的程序存在安全漏洞,攻击者可以控制它,那么他们可以转储释放的内存中的内容并公开敏感数据。

由于这个原因,包含机密数据的内存应在不再需要时立即擦除。幼稚的方法是在内存块上调用memset,但是如果编译器发现在那一点之后不再使用内存,则会对其进行优化。

在C标准中定义了一个名为memset_s的函数,可以保证不会对其进行优化,但是并非所有实现都具有该函数。您将需要找到一些库调用,以清除不会优化的内存。像OpenSSL这样的库会在您调用其清理例程时为您执行此操作(请参见this post作为示例)。

答案 1 :(得分:3)

可能,但不能保证。

如果您使用C语言进行编码,并且目标内存先前是由程序分配的,则取决于您的实现方式,您可能会或可能不会遇到访问冲突(分段错误)。 This answer解释说,可以重新访问已由分配它的同一程序释放的数据,因为C内存函数不必在释放后将其内存返回给OS,而是可以{{3 }}(不归零),以便更快地重新分配它。但是,这是特定于实现的,并且不能保证它会起作用。 keep it around in the process's heap manager

关于另一个程序的敏感数据,If you really want to make sure sensitive data is gone, use memset() before you free it.描述了可以读取,分配或以其他方式读取另一个进程的数据的条件:以root,子/父级身份运行您的进程以这种方式分叉的程序,或使用共享内存空间的程序。在这些条件之外,这可能是不可能的。

分配内存时,内核将返回带有mmap()的匿名映射(this post on Information Security)。除非在最特殊的情况下(受约束的嵌入式系统),否则总是零填充。即使这样,也必须专门编译内核以允许这样做。

即使在最好的情况下,我也怀疑这样做是否可以做到,更不用说始终如一了。

答案 2 :(得分:1)

在单个进程中,操作系统,标准C库或其他普通库都无法通过检查释放的内存的内容来提供防止数据查找的任何保护。分配和释放内存的例程基本上 1 完全在进程内部工作,并且进程(包括进程内部的代码)对它们具有完全控制权。 (密码库可能包含特殊的规定,用于在释放内存时保护数据。)

在单个进程之外,所有通用的多用户操作系统都通过确保以前被一个进程使用的任何数据被安全数据(通常为零)覆盖后再提供给另一个进程来保护进程。

在大多数情况下,操作系统提供的保护假定内存和其他硬件的性能理想,如课堂上所教:每个内存位均包含零或一个,且别无其他。实际上,硬件是不完善的,有时可以使用缺陷来获取应受保护的数据。

脚语

1 通常,内存分配例程必须与系统进行某些交互,例如从系统请求更多内存。但是大部分内存管理是在进程内部完成的。