理论基础
漏洞可以定义为“在软件和硬件组件中发现的计算逻辑(例如代码)中的弱点,当被利用时,会对机密性,完整性或可用性产生负面影响”。
软件漏洞是信息安全系统漏洞的重要组成部分,它通常被认为是软件生命周期中出现的设计错误、编码缺陷和运行故障造成的。
软件漏洞从产生、发现、解决这些维度它可以分为:
1、0 day漏洞:表示已经被发现,但未被公开还未发布补丁的漏洞;
2、1 day漏洞:表示厂商已经发现并公开了相关补丁,但由于部分用户还未及时打补丁,这个漏洞还是具有可利用性;
3、历史漏洞:这个漏洞的补丁发布时间很久,不可利用的漏洞。
软件漏洞的等级划分:
1、低级漏洞:这里漏洞利用非常困难或影响很小;
2、中等漏洞:这类漏洞由于默认配置、审核或利用难度等因素大大减轻了其影响;
3、重要漏洞:利用此类漏洞可能会危及用户数据的机密性、完整性或可用性,或者危及处理资源的完整性或可用性;
4、严重漏洞:利用此类漏洞,Internet病毒不需要用户操作就可以扩散。
软件漏洞基于成因可分类:内存破坏类漏洞、逻辑错误类漏洞、输入验证类漏洞、设计错误类漏洞、配置错误类漏洞。
缓冲区漏洞
当程序尝试读取或写入超出范围的缓冲区时,会发生缓冲区溢出。它可能导致覆盖或追加现有代码中的数据。
缓冲区溢出使攻击者能够执行代码、更改程序流程、读取敏感数据或使系统崩溃。包含缓冲区溢出漏洞通常发生在体系结构和设计、实现或操作阶段。
这个缓冲区漏洞最常发生在 C、C++的开发的程序中,但也可能发生在缺少内存管理支持的任何语言中。
缓冲区根据溢出的内存类型可以分为:栈内的数据溢出和堆内的数据溢出。
缓冲区漏洞被攻击者利用通常可以进行远程代码执行的功能。
导致出现缓冲区溢出漏洞问题点:
1、接受不受限制长度的输入
2、允许对来自无效索引的数组执行读取操作。
下面是出现缓冲区漏洞的例子:
上面代码从str向buffer复制数据,当str长度超过16时,就会出现缓冲区溢出。
问题根源在于strcpy没有限制复制数据长度,存在类似的问题还有strcat(),sprintf(),vsprintf(),gets(),scanf()等。建议可以采用安全的系统函数(对操作数据进行长度判断的函数)例如strcpy_s
下面也是同样会出现溢出的风险,
降低缓冲区漏洞方案
若要防止利用缓冲区溢出漏洞,可以使用包含功能或扩展的编译器来限制输入。
还有确保正确分配缓冲区空间,并且能够对输入的数据进行做限制和校验输入大小的方法和函数。
“防止利用缓冲区溢出漏洞的最佳方法之一是在软件投入使用之前从源代码中检测并消除它们”。
编码过程中,安全函数的使用可以降低缓冲区溢出的漏洞。
可以学习下华为开源的安全函数库,以此提高编码过程中的安全性,从而降低开发出一些漏洞的代码。
https://gitee.com/openarkcompiler/OpenArkCompiler/tree/master/src/mapleall/huawei_secure_c/src
字符串漏洞
在C、C++编程语言中正确和准确地操作Strings 时,必须考虑到 String 是以 null 结尾的字符序列。
对该 String 表示的不精确理解通常会导致一些最常见的错误:无界字符串副本、off-by-one 错误、空终止错误和字符串截断。
下面代码段展示了未绑定字符串副本的案例。该程序准备从标准输入(stdin)接收最多八个字符。然而,使用函数 gets() 并不能限制用户输入的字符数。攻击者可以通过输入8个以上的字符并将其传递给程序来探索此问题。
由于在这种情况下空终止符是重叠的,因此程序未分配的其他内存位置用于存储可能导致程序出现意外行为的额外字符。
在这类型的编码中,建议更改fgets函数的使用来直接缓解。该替代函数“最多将少于指定数量的字符从流读取到数组中”。
下面也是一个漏洞例子,其中发生了一个偏差错误。与未绑定的字符串副本一样,逐个错误与写入字符串边界外的字符有关。
在此类问题中,长度为 10 的字符串正确存储在准确定义了存储容量的变量源中。该漏洞始于字符串dest的内存分配。此操作使用函数strlen来计算字符串的字符数,直到找到空终止符。因此,并没有为dest字符串保留负责指示字符串终止的字符的位置。
由于将字符从字符串源复制到字符串dest的循环从位置 1 开始,因此最后一个命令将零字符写入字符串dest 的边界之外。
上面的问题点,可以通过在字符串分配期间考虑空终止符的位置并调整为负责字符复制的循环定义的索引来修复。
下图是上面的修复后的安全编码风格。
整数漏洞
当计算尝试递增一个大于用于在相关表示形式中存储该整数的整数值时,存在整数溢出漏洞。发生此错误时,整数值可能会转换为负数或非常小的数字。
当计算结果用于处理控制循环,确定行为(如复制,内存分配,串联等)的大小或偏移量并做出决定时,此弱点变得安全至关重要。
整数操作中的大多数错误和漏洞都涉及对存储此类型数据的变量进行限制检查不足。
下面的代码显示了由不精确的类型转换生成的常见错误。
尽管该函数检查通知的 size参数值是否符合最大数组大小的限制,但没有与参数值的信号相关的检查。因此,传递给函数的负大小将被视为它允许的大小,并且malloc 函数将使用负值调用。由于malloc期望size_t类型的参数,它将大小值转换为较大的无符号数字,这可能导致值大于MAX_ARRAY_SIZE中定义的值。
不正确的权限或身份验证漏洞
当未正确分配、跟踪、修改或验证用户特权和凭据时,会发生不正确的特权或身份验证。这些漏洞可能使攻击者能够滥用权限、执行受限任务或访问受限数据。
权限或身份验证不当的示例包括:
1、未撤消的临时权限提升。
2、通过列入黑名单而不是白名单来限制权限。
3、允许较低权限级别影响较高权限的帐户,例如重置管理员密码。
4、不受限制的登录尝试或会话限制。
特权或身份验证漏洞通常在开发的体系结构和设计、实现或操作阶段引入。任何语言都可能出现这些漏洞。
应该将最小特权原则应用于与你的软件和系统交互的所有用户和服务。通过在整个程序和环境中应用访问控制来限制用户和实体功能。应将权限限制为用户或服务所需的那些资源。
如果可能,请将高级权限分为多个角色。分离有助于限制“高级用户”,并降低攻击者滥用访问权限的能力。这样还可以应用多重身份验证方法,以防止攻击者绕过系统或轻松访问。
小结
软件漏洞似乎不可避免,但大多数漏洞可以被消除或至少减少,只有通过细致的软件设计,良好的编码,发现漏洞后快速的响应来尽量减少软件漏洞的产生,以及降低漏洞被利用后所带来的危害。
同时为了提高软件的安全性减少漏洞的出现,做好代码审计,并且对软件进行做静态和动态运行时测试代码,以确保软件在发布前能够发现漏洞,解决漏洞。