# 如何测试安全漏洞




这是2002年FreeBSD系统的一个安全漏洞。

当时一些开发人员意识到```getpeername```库的某个实现可能会隐藏致命的安全漏洞。

下面的代码简单的还原了漏洞的发生过程。

```
1 /*
2 * Illustration of code vulnerability similar to that found in
3 * FreeBSD’s implementation of getpeername()
4 */
5
6 /* Declaration of library function memcpy */
7 void *memcpy(void *dest, void *src, size_t n);
8
9 /* Kernel memory region holding user-accessible data */
10 #define KSIZE 1024
11 char kbuf[KSIZE];
12
13 /* Copy at most maxlen bytes from kernel region to user buffer */
14 int copy_from_kernel(void *user_dest, int maxlen) {
15    /* Byte count len is minimum of buffer size and maxlen */
16    int len = KSIZE < maxlen ? KSIZE : maxlen;
17    memcpy(user_dest, kbuf, len);
18    return len;
19 }

```

```int copy_from_kernel(void *user_dest, int maxlen)```方法的作用是将系统内核中的大小为maxlen的内存拷贝到用户内存里。在系统运行时，用户进程是没有权限访问系统内核的内存的，该方法等于是为用户进程开了个小后门，使得用户进程可以访问最大KSIZE大小的系统内核内存。由于系统内核内存中有很多运行时的敏感信息，所以该方法做了最大KSIZE大小内存的的限制，这是有道理的。但如果用户访问了任意大小的内核内存，那么系统敏感信息将会暴露，这将为不怀好意的攻击者提供足够多的入侵信息。

我们可以看到，第7行的```memcpy```方法会将n大小的内存从src拷贝到dest中，这里要注意的是n的类型是```size_t```，而```size_t```实际上就是```unsigned```，在stdio.h中有相应的typedef定义。

问题14行的maxlen参数，该参数的类型是int。在16行里，逻辑约定如果拷贝的内存大小过大，则最多拷贝KSIZE大小的内存，否则拷贝maxlen大小的内存。这个内存的最终大小是存放在```len```变量里的，len是int型，也就是说len是有符号的。

好了，下面是见证安全漏洞的时刻。

假设攻击者调用了copy_from_kernel方法，其中maxlen设为负数，那么根据第16行的逻辑，```len```变量应该也是负数，也就是有符号的负整数。

在17行的memcpy方法调用中，len是负数，然而根据函数原型，第3个参数应该是unsigned，无符号整数，根据整数的二进制表示法，负数的二进制表示正好就是unsigned的大整数表示方式，这个大家可以去看看操作系统的原理补充一下相关的上下文知识。于是结果就是，攻击者通过传入负数调用copy_from_kernel方法，可以访问巨大长度的系统内核内存，从而获取系统运行时的敏感信息，达到入侵系统的目的。

从测试的角度上总结一下，这是因为没有做等价类的测试用例导致的。这里maxlen既然是int，那么根据等价类，我们可以测试任意正整数，负整数和0，这3个等价类。

从开发的角度上看，这是因为参数类型不匹配导致的，解决方式就是将代码里所有的int类型改成unsigned就可以了，类型匹配之后，就可能消除由于类型转换而带来的各种缺陷隐患。

这就是一个深藏不露的安全性问题，不知道大家是不是可以想明白这个产生这个问题的原理呢？如果让你对这段代码进行测试，你可以发现这个安全漏洞么？




原始封面

![课程图片](https://images.unsplash.com/photo-1508144753681-9986d4df99b3?w=300)


