【问题标题】:Why is strncpy insecure?为什么 strncpy 不安全?
【发布时间】:2010-10-26 13:46:51
【问题描述】:

我正在寻找为什么 strncpy 被认为是不安全的。有没有人有关于这方面的任何文档或使用它的漏洞利用示例?

【问题讨论】:

  • 我的理解是 strcpy 是不安全的,而 strncpy 是它的安全版本。
  • 虽然名字上没有_s。没有 _s 就无法保证安全...
  • 如果你用的是c++,干脆不要用它,用std::string。
  • strncpy 并非设计为 strcpy 的安全版本,但它被滥用于此目的。请参阅此处了解说明:lysator.liu.se/c/rat/d11.html。现在可以争论 strncpy 是否设计错误。历史是 C 委员会的借口 :) 如果您需要安全版本的 strcpy,我建议您使用其他功能。
  • @stimms:当你说你“对 strncpy_s 更感兴趣”时,你的意思是 strncpy_s() 如何不安全,或者为什么 strncpy() 与 strncpy_s() 相比不安全? strncpy_s() 是 strncpy() 的“安全”版本,它所做的一切(与 strncpy() 不同)是要求您指定目标缓冲区的长度。

标签: c++ c string strncpy


【解决方案1】:

看看this site;这是一个相当详细的解释。基本上,strncpy() 不需要 NUL 终止,因此容易受到各种攻击。

【讨论】:

  • 好点。因此,即使使用 strncpy,也要确保缓冲区以 '\0' 结尾。
  • +1,但需要注意的是 strncpy 仅在您忘记手动添加空终止符时才不安全。如果你记不住,考虑一个包装函数,它总是在调用 strncpy 后将最后一个字符设置为 '\0'。
  • @RBerteig,请注意 MS strncpy_s 和 company 不是 1:1 strncpy 替代品,因为它们不会零填充缓冲区。尽管恕我直言 strl* 函数更通用且更快,但它们是更好的 strcpy。 @ojrac,它仍然很容易被滥用,一方面你必须记住 来复制 (bufsize-1) 而不是 (bufsize) 或者让你的 "rm -Rf /a"变成了“rm -Rf /”。
  • “NULL 终止”不正确。 ASCII 字符 \0 通常写成“NUL”以避免与 NULL 指针混淆。
  • @Billy ONeal、opengroup.org/onlinepubs/007908775/xsh/strncpy.htmlopengroup.org/onlinepubs/009695399/functions/strncpy.html 和 C1x 草案不同意你的观点。除非 ANSI C 另有说明,否则零填充不是非标准行为。再说一次,微软根本不支持 C。他们只有一个 C++ 编译器。
【解决方案2】:

原来的问题显然是strcpy(3)不是memory-safe operation,所以攻击者可以提供一个比缓冲区更长的字符串,这会覆盖堆栈上的代码,如果精心安排,可以执行攻击者的任意代码.

但是strncpy(3) 有另一个问题,它不会在目的地的所有情况下都提供空终止。 (想象一个源字符串比目标缓冲区长。)未来的操作可能期望在相同大小的缓冲区之间符合 C 以 nul 结尾的字符串,并且当结果被复制到第三个缓冲区时下游会发生故障。

使用 strncpy(3) 比 strcpy(3) 好,但 strlcpy(3) 之类的东西更好。

【讨论】:

  • strncpy优于strcpystrlcpy 在可用的地方很有用,在不可用的地方很容易重写。
【解决方案3】:

为了安全地使用 strncpy,必须 (1) 手动将空字符粘贴到结果缓冲区,(2) 事先知道缓冲区以空结尾,并将 (length-1) 传递给 strncpy,或者 (3 ) 知道永远不会使用任何不会将其长度绑定到缓冲区长度的方法来复制缓冲区。

请务必注意,strncpy 会将缓冲区中复制字符串之后的所有内容填零,而其他长度受限的 strcpy 变体则不会。这在某些情况下可能会消耗性能,但在其他情况下会带来安全优势。例如,如果使用 strlcpy 将“supercalifragilisticexpalidocious”复制到缓冲区,然后复制“it”,则缓冲区将保存“it^ercalifragilisticexpalidocious^”(使用“^”表示零字节)。如果缓冲区被复制为固定大小的格式,额外的数据可能会随之标记。

【讨论】:

    【解决方案4】:

    问题是基于“加载”的前提,这使得问题本身无效。

    这里的底线是strncpy 不被认为是不安全的,并且从未被认为是不安全的。可以附加到该函数的唯一“不安全”声明是 C 内存模型和 C 语言本身普遍不安全的广泛声明。 (但这显然是一个完全不同的话题)。

    在 C 语言领域中,strncpy 中固有的某种“不安全”的错误信念源于使用 strncpy 进行“安全字符串复制”的普遍可疑模式,即此函数不做的事情并且从未打算用于。这种用法确实很容易出错。但即使你在“极易出错”和“不安全”之间加上等号,它仍然是使用问题(即缺乏教育问题)而不是strncpy 问题。

    基本上,可以说strncpy 的唯一问题是一个不幸的命名,这使得新手程序员假设他们理解这个函数的作用,而不是实际阅读规范。看函数名,不称职的程序员会认为strncpystrcpy 的“安全版本”,而实际上这两个函数完全不相关。

    例如,可以对除法运算符提出完全相同的主张。众所周知,关于 C 语言的最常见问题之一是“我认为 1/2 将评估为 0.5,但我得到了 0。为什么?”然而,我们不会仅仅因为语言初学者倾向于误解它的行为就声称除法运算符是不安全的。

    再举一个例子,我们之所以称伪随机数生成器函数为“不安全”,只是因为不称职的程序员常常对他们的输出并非真正随机这一事实感到不快。

    strncpy 函数就是这样。就像初学者程序员需要时间来了解伪随机数生成器的实际作用一样,他们也需要时间来了解strncpy 的实际作用。需要时间了解strncpy 是一个conversion 函数,用于将零终止 字符串转换为固定宽度 字符串。需要时间来了解strncpy 与“安全字符串复制”完全无关,并且不能有意义地用于此目的。

    当然,语言学生学习strncpy 的目的通常比使用除法运算符解决问题要花更长的时间。但是,这是针对 strncpy 的任何“不安全”声明的基础。

    附:已接受答案中链接的 CERT 文档专门用于:证明典型的无能滥用 strncpy 函数作为 strcpy 的“安全”版本的不安全性。它绝不是要声称strncpy 本身在某种程度上是不安全的。

    【讨论】:

    • 当人们说“strncpy 不安全”时,他们的意思是“它没有pit of success”。这是真的。
    【解决方案5】:

    Git 2.19 (Q3 2018) 的一个pathc发现太容易误用strcat()等系统API函数; strncpy(); ...并禁止此代码库中的这些功能。

    参见Jeff King (peff)@commit e488b7acommit cc8fdaecommit 1b11b64(2018 年 7 月 24 日)和commit c8af66a(2018 年 7 月 26 日)。
    (由@987654327 中的Junio C Hamano -- gitster -- 合并@,2018 年 8 月 15 日)

    banned.h:将strcat()标记为禁止

    strcat() 函数与strcpy() 存在所有相同的溢出问题。
    而且作为奖励,很容易意外地以二次方告终,因为每个后续调用都必须遍历现有字符串。

    最后一个strcat() 调用在f063d38 中消失(守护进程:使用 重新生成时的 cld->env_array,2015-09-24,Git 2.7.0)。
    通常,strcat() 可以替换为动态字符串 (strbufxstrfmt),或者如果您知道长度是有界的,则使用 xsnprintf

    【讨论】:

    • 这个答案对前面的答案有什么补充?
    • @chqrlie 只是 OP 9 年后的一个例证,这些功能仍然如此危险(并且补丁说明了原因),以至于它们被 C 代码库禁止(比如,这里是来自 Git 的那个)
    猜你喜欢
    • 1970-01-01
    • 2010-11-30
    • 2019-10-27
    • 2014-12-04
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 2010-11-18
    • 2017-04-22
    相关资源
    最近更新 更多