Heap Buffer Overflow - AddressSanitizer output - what is needed to exploit this condition?

I understand where you're coming from, I fuzz various products (chrome, flash, among some other things). The last thing you want to do is give away a possible bounty or advisory (I support the former more than the latter, especially considering what happened to Aaron Schwartz).

Think about it: in this case apngdis can yield a possible parser or file format vulnerability in libpng, which is used by Chrome, Firefox, Webkit (iOS, OSX), etc. The aforementioned all have bug bounties. At the very least, Google offers bounties for securing open source projects. I don't know if apngdis is used in any products directly.

Considering the aforementioned, if you need help, you can do what I've done in the past regarding several vulnerabilities I've found:

  1. Ask help from someone who has too much reputation to steal your bounty/advisory (#corelan/Peter Van Eekhoutte comes to mind), but offer a shoutout/thanks in the advisory...curiousity/a challenge/fame/altruism is a great motivator
  2. Ask someone to sign a NDA to ensure no theft occurs. In return, however, you'll have to give a portion of the bounty/glory (a shoutout in the advisory, or even co-authorship)
  3. Review previous tutorials and exploit writeups, looking for similarities
  4. Ask for help offering nothing in return on stackexchange, which is fairly disrespectful. At least if its a public disclosure, give a thanks to whomever helps...there's a reason why you don't see more people asking for exploitation help on stackexchange.

If you choose #4, we'll need the following from you:

We need the disassembly of the read at [apngdis+0x4022bc], the previous instruction, and the call stack. Same goes for the write (disassembly of RIP, RIP-1 instruction, call stack)

Consider: interpreting the ASan output won't necessarily help you with exploitability without the aforementioned. For instance:

==24222== ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000010000 at pc 0x4022bd bp 0x7fff9b142e90 sp 0x7fff9b142e88
READ of size 1 at 0x602000010000 thread T0
#0 0x4022bc (/home/user/final/apngdis-2.5.orig/apngdis+0x4022bc)
...

CVE-2017-6193 has been reserved for this specific vulnerability (present in version 2.8). Your name has been included as the discoverer and as a co-contributor.

Tomorrow this post will be online for a year, and at time of writing has been viewed almost 2000 times. So to prevent anyone from misusing this, and seeing as the details are still public, I hope you don't mind I took the liberty of running apngdis in an afl-fuzz cluster to reconstruct the issue for reporting.

Among rediscovering these memory corruptions, there was also one in the chunk size descriptor, not to mention a textbook overflow in the handling of the filename parameter.

I debugged the issues and notified the developer. After explaining the seriousness of the situation that went on behind his back, he was very responsive and is now kind enough to be working on version 2.9.

The memory corruption in the compose_frame function is still present in the latest version 2.8, and is easy to explain. memcpy() gets invoked over a bunch of zeroes with an attacker supplied length, within some constraints.

First, after making sure it's actually a PNG, and an IHDR size field, the value gets loaded from the file:

    w0 = w = png_get_uint_32(chunkIHDR.p + 8);
    h0 = h = png_get_uint_32(chunkIHDR.p + 12);

Then after checking the values to be less than const unsigned long cMaxPNGSize = 1000000UL;, the following gets called:

compose_frame(frameCur.rows, frameRaw.rows, bop, x0, y0, w0, h0);

Which consist of:

void compose_frame(unsigned char ** rows_dst, unsigned char ** rows_src, unsigned char bop, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
{
  unsigned int  i, j;
  int u, v, al;

  for (j=0; j<h; j++)
  {
    unsigned char * sp = rows_src[j];
    unsigned char * dp = rows_dst[j+y] + x*4;

    if (bop == 0)
      memcpy(dp, sp, w*4);

Memcpy gets invoked and happily starts copying a few million zeroes before running into unmapped memory.:

Breakpoint 1, compose_frame (rows_dst=0x7fffef54f010, rows_src=0x7ffff3150010, bop=0 '\000', x=0, y=0, w=983040, h=983040) at apngdis.cpp:77
77          if (bop == 0)
(gdb) n
78            memcpy(dp, sp, w*4);
(gdb) print dp
$1 = (unsigned char *) 0x55555576f910 ""
(gdb) print sp
$2 = (unsigned char *) 0x55555576f2a0 ""
(gdb) print w

On Windows x86, this compiles to the following assembly and we can see the copying happens from ESI to EDI:

.text:011FFA90 loc_11FFA90: ; CODE XREF: .text:011FFAEBj .text:011FFA90 movdqa xmm0, oword ptr [esi] .text:011FFA94 movdqa xmm1, oword ptr [esi+10h] .text:011FFA99 movdqa xmm2, oword ptr [esi+20h] .text:011FFA9E movdqa xmm3, oword ptr [esi+30h] .text:011FFAA3 movdqa oword ptr [edi], xmm0 .text:011FFAA7 movdqa oword ptr [edi+10h], xmm1 .text:011FFAAC movdqa oword ptr [edi+20h], xmm2 .text:011FFAB1 movdqa oword ptr [edi+30h], xmm3 .text:011FFAB6 movdqa xmm4, oword ptr [esi+40h] .text:011FFABB movdqa xmm5, oword ptr [esi+50h] .text:011FFAC0 movdqa xmm6, oword ptr [esi+60h] .text:011FFAC5 movdqa xmm7, oword ptr [esi+70h] .text:011FFACA movdqa oword ptr [edi+40h], xmm4 .text:011FFACF movdqa oword ptr [edi+50h], xmm5 .text:011FFAD4 movdqa oword ptr [edi+60h], xmm6 .text:011FFAD9 movdqa oword ptr [edi+70h], xmm7 .text:011FFADE lea esi, [esi+80h] .text:011FFAE4 lea edi, [edi+80h] .text:011FFAEA dec edx .text:011FFAEB jnz short loc_11FFA90