任何编程语言和软件项目,都会存在安全漏洞,只有漏洞利用成本的高低!
本文章仅提供学习,切勿将其用于不法手段!
一、缓冲区溢出(最经典!)
原理:
C语言的数组没有“自动刹车”,如果往固定大小的缓冲区塞入过多数据,就会像往杯子里倒满水后继续倒,水会流到桌子上(覆盖相邻内存)。攻击者利用这点覆盖关键数据(如返回地址),让程序跳转到恶意代码执行。
渗透测试利用方法:
- 构造超长输入:比如登录时输入200个字符的用户名,覆盖函数返回地址。
# 假设漏洞代码: char buffer[10]; gets(buffer); // 危险!不检查输入长度 - 覆盖返回地址:用调试工具(如GDB)找到缓冲区地址,构造输入覆盖返回地址为
system("/bin/sh")的地址。 - 获取Shell:成功后程序会执行恶意命令,比如弹出计算器或打开终端。
防御:
- 用
fgets()代替gets(),限制输入长度。 - 编译时开启栈保护(
-fstack-protector)。
二、格式化字符串漏洞
原理:
printf等函数如果直接使用用户输入作为格式字符串(如printf(user_input)),攻击者可输入%x泄露内存数据,或用%n修改内存值。
渗透测试利用方法:
- 泄露内存:输入
%x.%x.%x,程序会输出栈上的敏感数据(如密码、地址)。 - 修改内存:输入
\xef\xbe\xad\xde%6$n,将0xdeadbeef写入指定地址,可能劫持程序流程。
防御:
- 明确指定格式字符串,如
printf("%s", user_input)。 - 禁用危险格式符(如
%n)。
三、整数溢出
原理:
计算时整数超出类型范围(如int最大是2147483647),结果会“回绕”成负数。攻击者利用这点让内存分配失败或覆盖关键数据。
渗透测试利用方法:
// 漏洞代码:分配内存时计算大小
int size = user_input + 100;
char *buf = malloc(size); // 如果user_input是负数,size可能极大!
构造输入user_input = -2000000000,导致malloc分配超大内存或返回NULL,引发崩溃或后续溢出。
防御:
- 检查输入范围,使用
size_t等无符号类型。 - 开启编译器溢出检测(
-ftrapv)。
四、空指针解引用
原理:
指针未初始化或指向已释放的内存,直接解引用会导致程序崩溃或数据篡改。
渗透测试利用方法:
- 触发崩溃:让程序访问
NULL指针,导致段错误。 - 劫持控制流:若指针指向恶意地址,可能执行任意代码。
防御:
- 指针初始化为
NULL,使用前检查是否为NULL。 - 释放内存后立即置空(
free(ptr); ptr=NULL;)。
五、悬垂指针(Use-After-Free)
原理:
内存释放后仍继续使用指针,攻击者可重新分配该内存并注入恶意数据。
渗透测试利用方法:
- 释放内存:调用
free(ptr)。 - 重新分配:通过其他操作让系统将原内存分配给新对象(如字符串)。
- 篡改数据:修改新对象内容,覆盖函数指针或返回地址。
防御:
- 使用智能指针(C++)或手动置空指针。
- 启用内存检测工具(如Valgrind)。
六、内存泄漏
原理:
动态分配的内存未释放,长期运行导致内存耗尽,程序崩溃或系统变慢。
渗透测试利用方法:
- 持续触发内存泄漏,观察程序内存增长直至崩溃。
防御:
- 每次
malloc后必须free。 - 使用工具检测(如
valgrind --leak-check=full)。
总结:C语言漏洞的“防身三招”
- 严格输入检查:所有用户输入必须限制长度、过滤特殊字符。
- 安全替代危险函数:
strcpy→strncpygets→fgetsprintf→printf("%s", input)
- 启用防护工具:
- 编译时加
-Wall -Wextra -fsanitize=address(地址消毒剂)。 - 用GDB调试内存问题,Valgrind查泄漏。
- 编译时加
一句话口诀:
指针要检查,输入别乱接,溢出要防御,工具不能歇!
注:所有技术研究需遵循《网络安全法》及《数据安全法》相关规定,践行合法合规的网络安全技术探索。
提示:最有效的防御办法,是让攻击者由于攻击成本过高,而主动放弃针对目标进行攻击!
没有攻不破的城墙,只有 由于 付出成本 远超于 收获价值 而 选择 主动放弃 攻击行为 的 敌人 !
C语言安全漏洞及渗透测试利用方法


被折叠的 条评论
为什么被折叠?



