7.1.1 CVE-2017-11543 tcpdump sliplink_print 栈溢出漏洞
漏洞描述
tcpdump 是 Linux 上一个强大的网络数据采集分析工具,其 4.9.0 版本的 sliplink_print 函数(位于 print-sl.c)中存在一个栈溢出漏洞,原因是程序在进行内存存取的操作前未对一些值做判断,导致操作了非法的内存地址。攻击者可以利用这个漏洞触发拒绝服务,甚至任意代码执行。
这个漏洞是发现者用 AFL 做 fuzz 时发现的。
漏洞复现
操作系统
Ubuntu 16.04
体系结构:32 位
调试器
gdb-peda
版本号:7.11.1
漏洞软件
tcpdump
版本号:4.9.0
为了编译 tcpdump,我们需要安装 dev 版本的 libpcap:
$ sudo apt-get install libpcap-dev
$ dpkg -l libpcap-dev
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-===================-==============-==============-============================================
ii libpcap-dev 1.7.4-2 all development library for libpcap (transitiona下载安装有漏洞的 tcpdump 4.9.0:
执行 configure 会生成相应的 Makefile,然后 make install 就可以了,但是这里我们修改下 Makefile,给 gcc 加上参数 -fsanitize=address,以开启内存检测功能:
最后:
使用下面的 poc 即可成功地触发漏洞产生 Segment Fault:
漏洞分析
首先介绍一下 pcap 包的文件格式,文件头是这样一个结构体,总共 24 个字节:
magic:标识位:4 字节,这个标识位的值是 16 进制的 0xa1b2c3d4major:主版本号:2 字节,默认值为 0x2minor:副版本号:2 字节,默认值为 0x04thiszone:区域时间:4 字节,实际上并未使用,因此被设置为 0sigfigs:精确时间戳:4 字节,实际上并未使用,因此被设置为 0snaplen:数据包最大长度:4 字节,该值设置所抓获的数据包的最大长度linktype:链路层类型:4 字节,数据包的链路层包头决定了链路层的类型
接下来是数据包头,总共 16 个字节:
ts:时间戳:8 字节,4字节表示秒数,4字节表示微秒数caplen:当前数据区长度:4 字节,表示所抓获的数据包保存在 pcap 文件中的实际长度len:离线数据长度:4 字节,如果文件中保存的不是完整数据包,可能比 caplen 大
我们从 tcpdump 的测试集中找到这样一个测试用例,整个包是这样的:
所以其链路层类型为 08,即 SLIP(Serial Line Internet Protocol)。通常一个 SLIP 的包结构如下:
direction 字段指示发送或接收
0:表示本机接收的包1:表示本机发送的包
在这里 direction 是 0xe7,并且由于 packet type 被设置了,所以 payload 是一个压缩的 TCP/IP 包,它的 packet type 和 compression information 共同构成了压缩的 TCP/IP 数据报,其结构如下:
在 sliplink_print 函数处下断点:
参数 p=0xb65ba800 位置处存放着从 pcap 中解析出来的 data,总共 39 个字节。
然后语句 dir = p[SLX_DIR] 从 data 中取出第一个字节作为 dir,即 0xe7:
然后程序将 dir==0xe7 与 SLIPDIR_IN==0 作比较,肯定不相等,于是错误地把 dir 当成 SLIPDIR_OUT==1 处理了:
继续往下执行,终于在执行到语句 lastlen[dir][lastconn] = length - (hlen << 2); 的时候挂掉了,它访问了一个不合法的地址:
说一下 compressed_sl_print 的参数:
dir=0xe7是 directionlength=0xe7e7e726是长度,由包头的len计算得到ip=0xb65ba810指向 datachdr=0xb65ba801指向压缩的 TCP/IP 头ndo=0xbfffdb90是其他一些选项
在 lastlen[dir][lastconn] = length - (hlen << 2); 语句中:
lastlen:被定义为static u_int lastlen[2][256];hlen是未压缩的 TCP/IP 头的长度length - hlen是 data 的总数
于是这里传入的 dir==0xe7,超出了 lastlen 定义的范围,发生错误。
回溯一下栈调用情况:
问题发生的原因是 sliplink_print 函数的 ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O ")); 没有考虑到 dir 既不是 0 也不是 1 的情况,错误地把它当做一个发送的数据包处理,然后调用了 compressed_sl_print 函数,导致非法内存地址访问。
漏洞程序代码如下:
漏洞修复
在最新的 tcpdump 中已经修复了该漏洞,当发现 direction 是错误的值时,直接返回:
具体代码的修改如下所示,文件 print-sl.c 用于打印 CSLIP(Compressed Serial Line Internet Protocol),即压缩的 SLIP:
commit:CVE-2017-11543/Make sure the SLIP direction octet is valid.
参考资料
Last updated
Was this helpful?