13

在学习 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;
}

只需滚动浏览输出,我就会发现以下内容:

?? ?l?? ?????? _ 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

并且,

Apple Inc.1&0$U ?0?*?H??ot CA0?"0ple Certification Authority10U ?䑩 ??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?< ?A?3???0X?Z2?h???es?g^e?I?v?3e?w??-??z0?v0U?0U ?0?0U+?iG?v ??k?.@??GM^0U#0?+?iG?v ??k?.@??GM^0?U 0?0 ??H??cd0? ?0+ https: //www.apple.com/appleca/0?+0????任何一方对本证书的依赖假定接受当时适用的标准使用条款和条件,证书 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

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

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

4

5 回答 5

11

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

更新:

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

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

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

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

于 2012-08-23T15:41:05.780 回答
8

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

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

于 2012-08-23T15:32:19.263 回答
4

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

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

于 2012-08-23T15:34:53.410 回答
1

来自 C 标准:

6.7.8 初始化

“如果具有自动存储持续时间的对象未明确初始化,则其值是不确定的。”

不确定值定义为:

 either an unspecified value or a trap representation.

陷阱表示定义为:

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

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

本文对未初始化变量的攻击可以提供一些关于它们可用于利用系统的见解。

于 2012-08-23T15:45:39.510 回答
0

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

于 2012-08-23T15:38:32.757 回答