In the weekend I gave a quick fuzzing try to the new lepton project by Dropbox which seems to offer amazing (20%) space saving on the already compressed jpeg format!

I found several memory corruptions issues which I reported on their bugtracker.

Initially I tried HackerOne, but Dropbox said those bugs are not eligible because the lepton project runs under a tight seccomp sandbox (I haven’t checked how strict this sandbox is yet).

I don’t fully agree with this decision because a sandboxing mechanism should in general be a complimentary security measure, not a pillar of your security model. Especially since the seccomp sandbox is not some ultra advanced sandbox mechanism with hypervisor and stuff, you still have part of the kernel attack surface (in general) plus whatever is left open by your seccomp profile.

Anyway, those issues got CVEs from CVE-2016-6234 to CVE-2016-6238.

You can download the reproducers here and you can reproduce on a vulnerable version of lepton with : ./lepton/lepton -singlethread -unjailed -preload testcase.jpeg /tmp/out.lep with a ASAN build of the project.

unknown.jpeg

lepton v1.0-91619e2
START ACHIEVED 1468716163 822728
decode error in scan0 / mcu2TS_MAIN (0) 0.000000
TS_MODEL_INIT_BEGIN (0) 0.004525
TS_MODEL_INIT (0) 0.004543
TS_READ_STARTED (0) 0.004637
TS_READ_FINISHED (0) 0.004768
TS_JPEG_DECODE_STARTED (0) 0.004768
TS_JPEG_DECODE_FINISHED (0) 0.004975
TS_DONE (0) 0.004976
6573388 bytes needed to decompress this file

::::BILL::::

==76344==ERROR: AddressSanitizer: unknown-crash on address 0x0000008cb038 at pc 0x00000052eb79 bp 0x7ffd0cfd5720 sp 0x7ffd0cfd5710
READ of size 208 at 0x0000008cb038 thread T0
#0 0x52eb78 in std::__atomic_base::load(std::memory_order) const /usr/include/c++/6/bits/atomic_base.h:396
#1 0x52eb78 in std::__atomic_base::operator unsigned int() const /usr/include/c++/6/bits/atomic_base.h:259
#2 0x52eb78 in print_bill(int) src/vp8/util/billing.cc:145
#3 0x46b7f3 in process_file(IOUtil::FileReader, IOUtil::FileWriter, int, bool) src/lepton/jpgcoder.cc:1616
#4 0x406c73 in main src/lepton/jpgcoder.cc:773
#5 0x7effabc9182f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#6 0x40afc8 in _start (/home/marco/VulnResearch/misc/lepton/lepton+0x40afc8)

0x0000008cb090 is located 0 bytes to the right of global variable 'billing_map' defined in 'src/vp8/util/billing.cc:10:23' (0x8cafc0) of size 208
SUMMARY: AddressSanitizer: unknown-crash /usr/include/c++/6/bits/atomic_base.h:396 in std::__atomic_base::load(std::memory_order) const
Shadow bytes around the buggy address:
0x0000801115b0: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
0x0000801115c0: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
0x0000801115d0: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
0x0000801115e0: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
0x0000801115f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x000080111600: 00 00 00 00 00 00 00[00]00 00 00 00 00 00 00 00
0x000080111610: 00 00 f9 f9 f9 f9 f9 f9 00 00 00 00 04 f9 f9 f9
0x000080111620: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
0x000080111630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080111640: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
0x000080111650: 00 00 00 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07 
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==76344==ABORTING
SHORT_READ

invalid_access.jpeg

lepton v1.0-91619e2
START ACHIEVED 1468716389 229829

ASAN:DEADLYSIGNAL

==20540==ERROR: AddressSanitizer: SEGV on unknown address 0x0000008cec00 (pc 0x000000455164 bp 0x000000119d80 sp 0x7ffc54cfc1c0 T0)
#0 0x455163 in setup_imginfo_jpg(bool) src/lepton/jpgcoder.cc:4023
#1 0x49649f in bool read_jpeg(std::vector, std::allocator > >, ibytestream) src/lepton/jpgcoder.cc:2096
#2 0x446089 in std::function::operator()() const /usr/include/c++/6/functional:2136
#3 0x446089 in execute(std::function const&) src/lepton/jpgcoder.cc:1728
#4 0x46d8af in process_file(IOUtil::FileReader, IOUtil::FileWriter, int, bool) src/lepton/jpgcoder.cc:1510
#5 0x406c73 in main src/lepton/jpgcoder.cc:773
#6 0x7f816da5d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#7 0x40afc8 in _start (/home/marco/VulnResearch/misc/lepton/lepton+0x40afc8)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV src/lepton/jpgcoder.cc:4023 in setup_imginfo_jpg(bool)
==20540==ABORTING
SHORT_READ

global_bof.jpeg

lepton v1.0-91619e2

START ACHIEVED 1468716464 964521

==45164==ERROR: AddressSanitizer: global-buffer-overflow on address 0x0000008c9f00 at pc 0x0000004571f1 bp 0x7ffeecdd5e30 sp 0x7ffeecdd5e20
READ of size 2 at 0x0000008c9f00 thread T0
#0 0x4571f0 in setup_imginfo_jpg(bool) src/lepton/jpgcoder.cc:4023
#1 0x49649f in bool read_jpeg(std::vector, std::allocator > >, ibytestream) src/lepton/jpgcoder.cc:2096
#2 0x446089 in std::function::operator()() const /usr/include/c++/6/functional:2136
#3 0x446089 in execute(std::function const&) src/lepton/jpgcoder.cc:1728
#4 0x46d8af in process_file(IOUtil::FileReader, IOUtil::FileWriter, int, bool) src/lepton/jpgcoder.cc:1510
#5 0x406c73 in main src/lepton/jpgcoder.cc:773
#6 0x7f16d425282f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#7 0x40afc8 in _start (/home/marco/VulnResearch/misc/lepton/lepton+0x40afc8)

0x0000008c9f00 is located 32 bytes to the left of global variable 'read_done' defined in 'src/lepton/jpgcoder.cc:302:9' (0x8c9f20) of size 8
0x0000008c9f00 is located 24 bytes to the right of global variable 'overall_start' defined in 'src/lepton/jpgcoder.cc:303:9' (0x8c9ee0) of size 8
SUMMARY: AddressSanitizer: global-buffer-overflow src/lepton/jpgcoder.cc:4023 in setup_imginfo_jpg(bool)
Shadow bytes around the buggy address:
0x000080111390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0000801113a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0000801113b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0000801113c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0000801113d0: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9
=>0x0000801113e0:[f9]f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9
0x0000801113f0: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
0x000080111400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080111410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080111420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080111430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07 
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==45164==ABORTING
SHORT_READ

global_bof2.jpeg

lepton v1.0-91619e2

START ACHIEVED 1468716550 602819

==73428==ERROR: AddressSanitizer: global-buffer-overflow on address 0x0000008c7c28 at pc 0x00000045392d bp 0x7fff93915920 sp 0x7fff93915910
WRITE of size 2 at 0x0000008c7c28 thread T0
#0 0x45392c in build_huffcodes(unsigned char, unsigned char, huffCodes, huffTree) src/lepton/jpgcoder.cc:5099
#1 0x45392c in parse_jfif_jpg(unsigned char, unsigned int, unsigned char) src/lepton/jpgcoder.cc:4109
#2 0x46ffd0 in decode_jpeg(std::vector, std::allocator > > const&, std::vector >) src/lepton/jpgcoder.cc:2465
#3 0x446089 in std::function::operator()() const /usr/include/c++/6/functional:2136
#4 0x446089 in execute(std::function const&) src/lepton/jpgcoder.cc:1728
#5 0x46ccc8 in process_file(IOUtil::FileReader, IOUtil::FileWriter, int, bool) src/lepton/jpgcoder.cc:1525
#6 0x406c73 in main src/lepton/jpgcoder.cc:773
#7 0x7f8525d6782f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#8 0x40afc8 in _start (/home/marco/VulnResearch/misc/lepton/lepton+0x40afc8)

0x0000008c7c28 is located 24 bytes to the left of global variable 'hcodes' defined in 'src/lepton/jpgcoder.cc:311:16' (0x8c7c40) of size 8208
0x0000008c7c28 is located 8 bytes to the right of global variable 'htrees' defined in 'src/lepton/jpgcoder.cc:312:16' (0x8c5c20) of size 8192
SUMMARY: AddressSanitizer: global-buffer-overflow src/lepton/jpgcoder.cc:5099 in build_huffcodes(unsigned char, unsigned char, huffCodes, huffTree)
Shadow bytes around the buggy address:
0x000080110f30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080110f40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080110f50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080110f60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080110f70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x000080110f80: 00 00 00 00 f9[f9]f9 f9 00 00 00 00 00 00 00 00
0x000080110f90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080110fa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080110fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080110fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000080110fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07 
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==73428==ABORTING
SHORT_READ

global_bof3.jpeg

lepton v1.0-91619e2

START ACHIEVED 1468716625 47000

==97386==ERROR: AddressSanitizer: global-buffer-overflow on address 0x0000008cad0a at pc 0x0000004fe249 bp 0x7fff934d51b0 sp 0x7fff934d51a0
READ of size 2 at 0x0000008cad0a thread T0
#0 0x4fe248 in ProbabilityTablesBase::set_quantization_table(BlockType, unsigned short const) src/vp8/model/model.hh:233
#1 0x4fe248 in VP8ComponentEncoder::vp8_full_encoder(UncompressedComponents const, IOUtil::FileWriter, ThreadHandoff const, unsigned int) src/lepton/vp8_encoder.cc:465
#2 0x47b3a8 in write_ujpg(std::vector >, std::vector >) src/lepton/jpgcoder.cc:3660
#3 0x48b4ee in bool std::_Bind<bool ((std::vector >, std::vector >))(std::vector >, std::vector >)>::__call(std::tuple<>&&, std::_Index_tuple<0ul, 1ul>) /usr/include/c++/6/functional:943
#4 0x48b4ee in bool std::_Bind >, std::vector >))(std::vector >, std::vector >)>::operator()<, bool>() /usr/include/c++/6/functional:1002
#5 0x48b4ee in std::_Function_handler >, std::vector >))(std::vector >, std::vector >)> >::_M_invoke(std::_Any_data const&) /usr/include/c++/6/functional:1726
#6 0x446089 in std::function::operator()() const /usr/include/c++/6/functional:2136
#7 0x446089 in execute(std::function const&) src/lepton/jpgcoder.cc:1728
#8 0x46ceb5 in process_file(IOUtil::FileReader, IOUtil::FileWriter, int, bool) src/lepton/jpgcoder.cc:1531
#9 0x406c73 in main src/lepton/jpgcoder.cc:773
#10 0x7fe3fb5cd82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#11 0x40afc8 in _start (/home/marco/VulnResearch/misc/lepton/lepton+0x40afc8)

0x0000008cad0a is located 54 bytes to the left of global variable 'chroma_debug_height' defined in 'src/vp8/util/debug.cc:23:5' (0x8cad40) of size 4
0x0000008cad0a is located 6 bytes to the right of global variable 'raw_decoded_fp_Y' defined in 'src/vp8/util/debug.cc:109:5' (0x8cad00) of size 4
SUMMARY: AddressSanitizer: global-buffer-overflow src/vp8/model/model.hh:233 in ProbabilityTablesBase::set_quantization_table(BlockType, unsigned short const*)
Shadow bytes around the buggy address:
0x000080111550: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9
0x000080111560: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 f9 f9 f9
0x000080111570: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
0x000080111580: 00 00 00 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9
0x000080111590: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
=>0x0000801115a0: 04[f9]f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
0x0000801115b0: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
0x0000801115c0: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
0x0000801115d0: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
0x0000801115e0: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
0x0000801115f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07 
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==97386==ABORTING
SHORT_READ