【问题标题】:Are uninitialized values ever a security risk?未初始化的值是否会带来安全风险?
【发布时间】:2012-08-19 04:13:16
【问题描述】:

在学习 C 时,我犯了一些错误并打印了未初始化的字符数组的元素。

如果我将数组的大小扩大到相当大,比如 100 万个元素,然后打印内容,结果并不总是用户无法阅读,但似乎包含一些运行时信息。

考虑以下代码:

#include <stdio.h>
main() {

        char s[1000000];
        int c, i;

        printf("Enter input string:\n");
        for (i = 0; ( c = getchar()) != '\n'; i++) {
                s[i] = c;
        }   

        printf("Contents of input string:\n");
        for (i = 0; i < 999999; i++) {
                putchar(s[i]);
        }   
        printf("\n");

        return 0;
}

只是滚动浏览输出,我发现诸如:

... ?升?? EM> ?????? _ dyldVersionNumber_dyldVersionString_dyld_all_image_infos_dyld_fatal_error_dyld_shared_cache_ranges_error_string__mh_dylinker_header_stub_binding_helper_dyld_func_lookup_offset_to_dyld_all_image_infos__dyld_start__ZN13dyldbootstrapL30randomizeExecutableLoadAddressEPK12macho_headerPPKcPm__ZN13dyldbootstrap5startEPK12macho_headeriPPKcl__ZN4dyldL17setNewProgramVarsERK11ProgramVars__ZN4dyld17getExecutablePathEv__ZN4dyld22mainExecutablePreboundEv__ZN4dyld14mainExecutableEv__ZN4dyld21findImageByMachHeaderEPK11mach_header__ZN4dyld26findImageContainingAddressEPKv P>

还有,

Apple Inc.1&0$U ?0?*?H??ot CA0?"0ple 证书颁发机构10U ?䑩 ??GP??^y?-?6?WLU????Kl??"0?>?P?A?????f?$kУ????z ?G?[?73??M?i??r?]?_???d5#KY?????P??XPg? ?ˬ, op??0??C??=?+I(??ε??^??=?:??? ?b??q?GSU?/A????p??LE~LkP?A ??tb
?!.t??H??cd0??0+https://www.apple.com/appleca/0?+0????Reliance on 任何一方的此证书假定接受当时 适用的标准使用条款和条件,证书 poli?\6?Lx?팛??w??v?w0O????=G7?@?,Ա?ؾ?s???d?yO4آ>?x?k??}9??S? 8??O 01?H??[d?c3w?:,V??!带sO??6?U٧??2B???q?~?R??B$*??M?^c?K ?P??????????7?uu!0?0??0

我相信有一次我的$PATH 环境变量甚至被打印出来了。

未初始化变量的内容是否会带来安全风险?

更新 1

更新 2

因此,从答案中可以清楚地看出这确实存在安全风险。这让我很吃惊。

程序是否无法声明其内存内容受保护以允许操作系统限制对它的任何访问,除了初始化该内存的程序之外?

【问题讨论】:

  • 未初始化变量的内容可以做任何事情。有一天,世界将因为其中之一而终结。
  • 使用未初始化的变量会导致未定义的行为,所以是的,任何事情都可能发生,安全风险或破坏整个程序或任何可以想象的事情。但很少会发生这样的事情。
  • 是的,通常有特殊的内存处理加密版本,可确保值在使用后被清除或永远不会写入交换文件。使用 calloc() 会将内存预置零,并且在大多数 unix 平台上 malloc() 也会这样做。
  • @MartinBeckett 最? unix 平台,我经常使用的 3 不会将 malloc 上的数据归零。但是,它们通常会在将内存页面交给您之前清理它们。
  • @TomTanner,我虽然 gcc/linux 标准库做到了,但显然它比stackoverflow.com/questions/8029584/…更复杂

标签: c security


【解决方案1】:

大多数C 程序使用malloc 来分配内存。一个常见的误解是malloc 将返回的内存归零。它实际上没有。
结果,由于内存块被“回收”,因此很有可能获得具有“价值”信息的内存块。
此漏洞的一个示例是 Solaris 上的 tar 程序,它发出 /etc/passwd 的内容。根本原因是分配给tar 以从磁盘读取块的内存未初始化,在获取此内存块之前,tar 实用程序进行了操作系统系统调用以读取/etc/passwd。由于内存回收以及tar 没有初始化/etc/passwd 的块片段这一事实被打印到日志中。通过将malloc 替换为calloc 解决了这个问题。
如果您没有明确且正确地初始化内存,这是一个安全隐患的实际示例。
所以是的,请正确初始化您的内存。

更新:

程序是否无法声明其内存内容受保护 允许操作系统限制对程序以外的任何访问 初始化那个内存?

答案是肯定的(见最后)和否定的。
我认为您在这里以错误的方式看待它。更合适的问题是,例如,为什么malloc 不根据请求初始化内存或在释放时清除内存而是回收它?
答案是 API 的设计者明确决定不初始化(或清除内存),因为这样做会影响大块内存 1)会影响性能,并且 2)不是总是必要的(例如,您可能不会在您的应用程序或应用程序的几个部分中处理您真正关心的数据(如果它们被公开)。所以设计师决定不这样做,因为它会无意中影响性能,并把球交给程序员来决定。
所以也把这个带到操作系统,为什么清除页面应该是操作系统的责任呢?您希望您的操作系统及时将内存交给您,但安全性取决于程序员。

话虽如此,提供了一些机制,您可以使用它来确保敏感数据不会存储在 Linux 中使用 mlock 的交换中。

mlock() 和 mlockall() 分别锁定部分或全部调用 进程的虚拟地址空间到 RAM 中,防止该内存 从被分页到交换区域。 munlock() 和 munlockall() 执行相反的操作,分别解锁部分或全部 调用进程的虚拟地址空间,以便在 指定的虚拟地址范围可能会再次被换出,如果 内核内存管理器需要。内存锁定和解锁 以整页为单位进行。

【讨论】:

  • 安全操作系统的责任。现代操作系统在将内存页面交给应用程序之前会清除它们。但是,malloc 不一定会从操作系统请求新的内存页面,而是会频繁地重用以前由进程 free()-ed 的内存。来自未初始化内存的任何非零值都来自您自己的进程,而不是来自另一个进程(除非您的系统配置了类似 CONFIG_MMAP_ALLOW_UNINITIALIZED 的东西)。
【解决方案2】:

是的,至少在数据可能传输给外部用户的系统上是这样。

对网络服务器(甚至 iPod)进行了一系列攻击,您可以通过这些攻击从其他进程中转储内存内容,从而获取操作系统类型和版本、其他应用程序中的数据的详细信息甚至像密码表这样的东西

【讨论】:

  • 世界上到处都是蹩脚的操作系统!
  • 分配给一个正在运行的进程的内存不能被另一个进程访问,但是一旦释放,它可能被分配给另一个进程就好了。这就是为什么如果需要的话,原始进程负责在发布之前清除它。
  • @loan esp。页面/交换文件是一个臭名昭著的数据泄漏源。
  • @MartinBeckett 这意味着任何随机进程都可以在正常的操作系统操作期间访问该文件,对此我想不出合理的理由/案例,因此不应被允许。
  • @loan 如果您的进程从交换文件中获得了未清除的内存,那么它可能包含前一个应用程序留在那里的任何数据。这不应该在任何对安全性概念最不了解的操作系统中发生 - 但这是您不在 Windows 上编辑绝密文档的原因!
【解决方案3】:

很有可能在内存区域执行一些敏感工作,而不清除该缓冲区。

然后,未来的调用可以通过调用malloc() 或检查堆(通过统一的缓冲区/数组声明)来检索未清除的工作。它可以(恶意地)检查它或无意中复制它。如果您正在做任何敏感的事情,那么在分箱之前清除该内存是有意义的(memset() 或类似的),也许在使用/复制它之前。

【讨论】:

  • 这是该问题的最佳解决方案和近似值。我看到的唯一另一件事是一个异常终止并且无法清除内容的进程。在这种情况下,操作系统会按原样释放内存并可能带来风险。
【解决方案4】:

来自 C 标准:

6.7.8 初始化

"如果具有自动存储期限的对象未初始化 明确地说,它的值是不确定的。”

不确定值定义为:

 either an unspecified value or a trap representation.

陷阱表示定义为:

某些对象表示不需要表示 对象类型。如果一个对象的存储值有这样一个 表示并由不具有的左值表达式读取 字符类型,行为未定义。如果这样的表示是 由修改对象的全部或任何部分的副作用产生 通过没有字符类型的左值表达式, 行为是未定义的。41) 这种表示称为陷阱 表示。

访问此类值会导致未定义的行为,并可能构成安全威胁。

这篇论文Attacks on uninitialized variables 可以提供一些关于它们可用于利用系统的见解。

【讨论】:

    【解决方案5】:

    如果您担心安全性,最安全的方法是始终初始化您要使用的每个变量。它甚至可以帮助您找到一些错误。 不初始化内存可能有一些很好的理由,但在大多数情况下,初始化每个变量/内存将是一件好事。

    【讨论】:

      猜你喜欢
      • 2010-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-07
      • 2012-02-18
      • 1970-01-01
      • 1970-01-01
      • 2016-07-02
      相关资源
      最近更新 更多