在学习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;
}
只需滚动输出,我就会找到如下内容:
... ?升?? ?????? _ 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&amp; 0 $ U?0?*?H ?? CA0?“0ple Certification Authority10U ?GP ?? GP ?? ^ y? - ?6?WLU ???? Kl ??“0?&gt;?P?A ????? f?$kУ???? z ·G·[?73 -4 M?我?R'] _ ??? D5#KY ????? P 25 XPG? ?ˬ, op ?? 0 ?? C ?? =?+ I(??ε?? ^ ?? =?:?b ?? q?GSU?/ A ???? p ?? LE~LkP?A ?? TB
?!T&LT?; ?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????Reliance on 任何一方的此证书都假定接受当时的 适用的标准使用条款和条件,证书 poli?\ 6?Lx?팛?? w ?? v?w0O ???? = G7?@?,Ա?s?s?d?yO4&gt;?x?k ??} 9 ?? S? 8I 30 O 01 H 19 [d c3w:???!?????,V 10ںSO - 6 U7 ?? 2B ??? Q〜R 10 B $ * -4 M ^ Cķ 2 P ???????? 7?UU!0?0&epsiv; 0
我相信有一次我的$PATH
环境变量被打印出来了。
未初始化变量的内容是否会带来安全风险?
更新1
更新2
因此从答案中可以清楚地看出这确实存在安全风险。这让我感到惊讶。
除了初始化该内存的程序之外,程序是否无法声明其内存内容受到保护以允许操作系统限制对它的任何访问?
答案 0 :(得分:10)
大多数C
程序使用malloc
来分配内存。一个常见的误解是malloc
将返回的内存归零。它实际上没有。
结果,由于内存块被“回收”的事实,很可能得到一个具有“价值”信息的内容。
此漏洞的一个示例是Solaris上的tar
程序,它发出了/etc/passwd
的内容。根本原因是分配给tar
从磁盘读取块的内存未初始化,在获取此内存块之前,tar
实用程序调用了OS系统调用/etc/passwd
。由于内存回收以及tar
未初始化/etc/passwd
的块片段的事实被打印到日志中。通过将malloc
替换为calloc
来解决此问题
如果您没有明确且正确地初始化内存,这是安全隐含的实际示例
所以是的,请正确初始化你的记忆。
更新:
程序是否无法声明其内存内容受到保护 允许操作系统限制除程序之外的任何访问权限 初始化那个记忆?
答案是肯定的(见最后)并且没有。
我认为你在这里看错了。更合适的问题是,例如,为什么malloc
不会根据请求初始化内存或在释放时清除内存,而是回收它?
答案是API的设计者明确决定不初始化(或清除内存),因为对大型内存块执行此操作1)会影响性能,2)不总是总是必要(例如,在您的应用程序或您的应用程序中的几个部分中,可能不会处理您实际关注的数据(如果它们被暴露)。因此,设计师决定不这样做,因为它会无意中影响性能,并将球传给程序员来决定这一点。
因此,对于操作系统而言,为什么操作系统有责任清除页面?您期望从您的操作系统及时交出内存,但安全性取决于程序员。
已经说过,你可以使用一些机制来确保敏感数据不会在Linux中使用mlock存储在交换中。
mlock()和mlockall()分别锁定部分或全部调用 进程的虚拟地址空间进入RAM,防止内存 从被分页到交换区域。 munlock()和munlockall() 执行逆向操作,分别解锁部分或全部 调用进程的虚拟地址空间,以便在页面中 如果指定的虚拟地址范围可能再次被换出 内核内存管理器需要的。内存锁定和解锁 以整页为单位进行。
答案 1 :(得分:8)
是的,至少在可以将数据传输给外部用户的系统上。
对网络服务器(甚至是iPod)进行了一系列的攻击,你可以从其他进程转储内存的内容 - 因此获取操作系统类型和版本的详细信息,其他应用程序中的数据甚至像密码表这样的东西
答案 2 :(得分:4)
很有可能在内存区域执行一些敏感工作,而不清楚缓冲区。
然后,未来的调用可以通过调用malloc()
或通过检查堆(通过单元化的缓冲区/数组声明)来检索未清除的工作。它可以(恶意地)检查它或无意中复制它。如果您正在做任何敏感的事情,那么在对它进行分箱(memset()
或类似)之前清除该内存是有意义的,并且可能在使用/复制它之前。
答案 3 :(得分:1)
来自C标准:
6.7.8初始化
“如果没有初始化具有自动存储持续时间的对象 显然,它的价值是不确定的。“
不确定值定义为:
either an unspecified value or a trap representation.
陷阱表示定义为:
某些对象表示不需要表示的值 对象类型。如果对象的存储值具有这样的值 表示,并由没有的左值表达式读取 字符类型,行为未定义。如果这样的表示是 通过修改对象的全部或任何部分的副作用产生的 通过一个没有字符类型的左值表达式, 行为是未定义的.41)这种表示称为陷阱 表示。
访问此类值会导致未定义的行为,并可能造成安全威胁。
本文Attacks on uninitialized variables可以提供一些有关它们可用于利用系统的见解。
答案 4 :(得分:0)
如果您担心安全性,最安全的方法是始终初始化您将要使用的每个变量。它甚至可以帮助你找到一些错误。 没有初始化内存可能有一些很好的理由,但在大多数情况下初始化每个变量/内存将是一件好事。