本文将针对编码过程中的一些漏洞进行总结,并给出相关示例
内存操作漏洞
字符串与数组操作
缓冲区溢出
1 |
|
这是一个典型的缓冲区溢出漏洞,id的类型是unsigned char,但是在scanf
中格式却是%d
,这将导致当输入较大的数字时,会对id
后面的空间进行覆盖,导致输出错误。
差一错误
1 | constexpr char buf[] = "hello world"; |
上面是典型的差一错误,HELLO_WORLD后面是有\0
的,所以长度实际为12,比11大。
外部可控的format
1 | void OutputLog(char* loginfo){ |
上面直接用printf()
未经格式化输出loginfo,那么如果外部精心构造了一个format
,那么可能导致password泄漏。
函数漏洞
使用安全的函数
一些老旧的函数不甚安全
先看一个例子:
1 | void GetScore(int* scoreList){ |
上面scoreList并没有指定拷贝长度,如果scoreList长度小于score,那么会造成缓冲区溢出,所以在传入指针指向的空间时,要同时传入空间大小。在这里,memcpy
就是一个典型的不安全函数。
C11标准对于不安全的函数指定了安全版本,请使用安全版本替代不安全函数。安全函数的安全特性如下:
- 边界检查及入参检查
- 字符串强制0结尾
- 增加错误码
- 内存重叠检查
- 限定操作内存的最大长度
对于memcpy
,其安全函数版本如下:
1 | errno_t memcpy_s(void *dest, size_t dsetMax, const void* src, size_t count); |
使用安全函数要谨慎
即使有了安全函数,也可能出现不正确使用的情况,例如将源长度直接作为目的长度,属于典型的掩耳盗铃的做法:
1 | memcpy_s(dest, sizeof(src), src, sizeof(src)); // 禁止掩耳盗铃 |
同时,确保目的长度确实地等于传入的目的地址,有时候定义了一个宏作为目的长度,结果输入的目的内存空间长度和宏不相等,也是要禁止的。
必须检查安全函数返回值
安全函数为我们设置了返回值,我们不能忽略,使用时一定要检查安全函数的返回值。
整数
谨慎地进行整数间比较操作
char
类型最大值比int
类型最大值小,如果使用char
的类型和int
类型比较,很有可能出现无效比较的情况,需要特别注意。
注意整数越界问题
1 | char g_studentScore[100] = {0}; |
上面的代码看似会对length
进行检查,保证length
不超过100,但是问题就出现在相加的语句上:
1 | length = g_curOffset + num; |
这一句并没有进行整数越界检查,如果num + g_curOffset
超过了65535,那么length
会溢出,导致实际相加的结果很小,但是num
又很大,后面的循环会导致缓冲区溢出。可以在循环的时候添加检查,如果越界,就退出。
内存
参考文献
- 1.[输入验证](Input Validation - OWASP Cheat Sheet Series) ↩