使内存安全的棘手代码

信息安全 应用安全 职业教育 缓冲区溢出 C
2021-08-23 01:28:05

我正在为正在学习内存安全和编写安全 C 代码的学生设计一个家庭作业挑战。作为其中的一部分,我正在寻找一个小型编程任务,其中编写没有缓冲区溢出、数组越界错误和/或其他内存安全错误的 C 代码并非易事。这种任务的一个很好的例子是什么?

换句话说:我指定了所需的功能;他们用C实现它;如果他们在实施时不小心,他们的代码很有可能存在内存安全漏洞。理想情况下,我更喜欢可以简洁地实现的东西(最多几百行代码)以保持任务的可管理大小,如果任务在工业上或实际相关或现实中会特别酷或代表现实世界的编程。

举一个不同领域的例子,在排序列表上实现二进制搜索是一个容易指定的编程任务的经典例子,如果你在实现它时不小心,你很可能会遇到某种逻辑错误(例如,一个错误,一些输入的无限循环,诸如此类)。对于安全性,特别是内存安全漏洞,是否有任何好的相应任务?

4个回答

非平凡缓冲区处理的一个典型示例是解析可以包含任意长度字符串的二进制文件(或网络数据包)。(是否有任何 ASN.1 解析器有时没有缓冲区溢出错误?)

例如,考虑PNG 文件中文本数据块的格式

关键字和文本字符串由零字节(空字符)分隔。关键字和文本字符串都不能包含空字符。文本字符串不是以空值结尾的(块的长度定义了结尾)。

因此,作业可以是一个将所有文本数据输出为 PNG 文件的工具。

如果您需要处理任意长的行,那么即使是纯文本也不是很简单,因此需要动态调整缓冲区的大小。例如:

实现不带参数的尾部。(行可以比任何静态缓冲区都长。整个文件可以大于可用内存。)

当人们需要从结构中解析字符数据时,我看到了很多问题,特别是当它是一个通常以 null 终止的缓冲区时,但可以是没有终止符的缓冲区的大小。

short canary = 0x5678;
struct customer {
    char name[4];
    char suffix[3];
};
short fencepost = 0x1234;

让他们用“Joe”和“Jr”之类的数据填充客户。要求他们以这种确切的格式输出所有数据:

canary: 0x5678
name: Joe
suffix: Jr
fencepost: 0x1234

然后让他们用“John”和“Esq”填写客户。他们的输出应该是这样的:

canary: 0x5678
name: John
suffix: Esq
fencepost: 0x1234

这是一项常见而简单的任务。这应该告诉他们他们不能只是 strcpy("Esq") 进入缓冲区,因为它会杀死金丝雀。它应该要求他们从缓冲区复制数据,以便空终止它以打印它。

我建议您先对此进行测试,然后检查编译器设置。在 Microsoft 的旧版 Visual C++ 中,调试版本将链接到内存分配器的调试版本中,该内存分配器在分配的内存周围添加了栅栏。它将使调试器能够报告覆盖的内存错误。我不知道较新的版本是否仍然这样做,但我知道发布版本不会。

如果你想给他们一个“为什么”,说明输入像 Fred@@@@@@@@ 这样的名字会导致崩溃,并使黑客能够接管程序。

这是一个艰难的过程,因为 afaik 现代 linux 内核没有可执行堆栈,因此他们无法在普通的 linux 发行版上测试他们的程序。(我记得在我的 debian 机器上测试 Alef One 论文中列出的代码时遇到了问题)

你可以做的是为他们提供一个自定义的虚拟机(比如该死的易受攻击的 linux),它禁用了安全设置,并告诉他们在那里测试他们的代码。

更重要的是,解决方案是让他们编写一个程序,该程序将文件名作为命令行参数或用户输入

  • 打开文件
  • 从中读取一些配置
  • 解析它/寻找特定的东西
  • 将相关部分推入网络套接字
  • 另一个进程读取数据并且必须打印一些相关部分。

您的情况可能是:

您必须在 c 中编写一个程序,该程序从用户提供的文件中读取航空公司的航班时刻表,并通过 tcp 套接字将其推送到另一个进程,然后显示它。

为了使事情变得有趣,您可以提供一个必须处理所有内容的结构。

这很简单,因为大部分代码都可以在网上找到(从文件读取,写入套接字),学生只需要做内存处理

根据您的平台限制,我建议让他们实现一些DCOM

实现 DCOM 调用涉及许多不同的问题领域——网络调用、接口处理、编组、分布式引用计数和对象生命周期管理、共享内存安全等等——如果做得好,它还包括一些低级 ACL 检查等。许多小块和复杂的结构飞来飞去。

从未见过任何 DCOM 代码——无论是调用客户端还是服务器——都没有重大错误(除非以某种“安全”语言实现,如 VB,即使这样也很常见)。