7.1.4 CVE-2017-13089 wget skip_short_body 栈溢出漏洞

下载文件

漏洞描述

wget 是一个从网络上自动下载文件的工具,支持通过 HTTP、HTTPS、FTP 三种最常见的 TCP/IP 协议。

在处理例如重定向的情况时,wget 会调用到 skip_short_body() 函数,函数中会对分块编码的数据调用 strtol() 函数读取每个块的长度,但在版本 1.19.2 之前,没有对这个长度进行必要的检查,例如其是否为负数。然后 wget 通过使用 MIN() 宏跳过块的 512 个字节,将负数传递给了函数 fd_read()。由于 fd_read() 接收的参数类型为 int,所以块长度的高 32 位会被丢弃,使得攻击者可以控制传递给 fd_read() 的参数。

漏洞复现

推荐使用的环境
备注

操作系统

Ubuntu 16.04

体系结构:64 位

调试器

gdb-peda

版本号:7.11.1

漏洞软件

wget

版本号:1.19.1

首先编译安装 wget-1.19.1:

$ sudo apt-get install libneon27-gnutls-dev
$ wget https://ftp.gnu.org/gnu/wget/wget-1.19.1.tar.gz
$ tar zxvf wget-1.19.1.tar.gz
$ cd wget-1.19.1
$ ./configure
$ make && sudo make install
$ wget -V | head -n1
GNU Wget 1.19.1 built on linux-gnu.

引发崩溃的 payload 如下:

stack smashing 现场:

漏洞分析

关键函数 skip_short_body()

一般是这样调用的:

所以要想进入到漏洞代码,只需要 contlen 的长度不大于 4096 且使用了分块编码 chunked_transfer_encoding。对于参数 chunked_transfer_encoding 的设置在函数 gethttp() 中:

contlen 的赋值为 contlen = MIN (remaining_chunk_size, SKIP_SIZE);MIN() 宏函数定义如下,用于获得两个值中小的那一个:

remaining_chunk_size 为负值时,同样满足小于 SKIP_SIZE,所以 contlen 实际上是可控的。

随后进入 fd_read() 函数,从 fd 读取 bufsize 个字节到 buf 中,于是引起缓冲区溢出:

补丁

补丁也很简单,就是对 remaining_chunk_size 是否为负值进行了判断。

Exploit

在这里我们做一点有趣的事情。先修改一下配置文件 configure.ac,把堆栈保护技术都关掉,也就是加上下面所示的这几行:

然后编译安装,结果如下:

好了,接下来可以搞事情了。为了方便确认栈溢出的地址,把前面 payload 的 body 部分用 pattern 替代掉:

在另一个 shell 里启动 gdb 调试 wget:

所以 rsp 的地址位于栈偏移 568 的地方。而栈地址位于 0x7fffffffcf40

构造 exp 来生成 paylaod:

继续使用 gdb 来跟踪。经过 strtol() 函数返回的 remaining_chunk_size0xffffffff00000300

继续调试,到达函数 fd_read(),可以看到由于强制类型转换的原因其参数只取出了 0xffffffff00000300 的低 4 个字节 0x300,所以该函数将读入 0x300 个字节的数据到栈地址 0x7fffffffcf40 中:

成功跳转到 shellcode,获得 shell:

Bingo!!!

参考资料

Last updated

Was this helpful?