Project: imagemagick (imagemagick/imagemagick), language: c++
Vulnerability instance: imagemagick.cve-2016-7906
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/ImageMagick/ImageMagick/issues/281
## Description:
Issue: ImageMagick/ImageMagick#281
Title: imagemagick mogrify heap use after free
State: closed
Created by: marcograss
Created at: 2016-09-30 10:20:54+00:00
Labels: bug

Issue Body:
Hi, the following test case will reproduce this crash. It's attached, you need a ASAN build on master branch

> # ➜  utilities git:(master) ✗ ./magick mogrify ../../ImageMagick_bugs/mogrify_heap_uaf
> 
> ==26427==ERROR: AddressSanitizer: heap-use-after-free on address 0x60600003c868 at pc 0x0000016cfeba bp 0x7ffdebb9ff70 sp 0x7ffdebb9ff68
> READ of size 4 at 0x60600003c868 thread T0
>     #0 0x16cfeb9  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x16cfeb9)
>     #1 0x16383cf  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x16383cf)
>     #2 0x18bfcfc  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x18bfcfc)
>     #3 0x18c2594  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x18c2594)
>     #4 0x2ff1c7f  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x2ff1c7f)
>     #5 0x2f8cead  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x2f8cead)
>     #6 0x4f5da9  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x4f5da9)
>     #7 0x7f773a59e82f  (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
>     #8 0x422428  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x422428)
> 
> 0x60600003c868 is located 8 bytes inside of 56-byte region [0x60600003c860,0x60600003c898)
> freed by thread T0 here:
>     #0 0x4c23d0  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x4c23d0)
>     #1 0x5ac708  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x5ac708)
> 
> previously allocated by thread T0 here:
>     #0 0x4c2558  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x4c2558)
>     #1 0x55c149  (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x55c149)
> 
> SUMMARY: AddressSanitizer: heap-use-after-free (/home/bob/VulnResearch/misc/ImageMagick/utilities/magick+0x16cfeb9) 
> Shadow bytes around the buggy address:
>   0x0c0c7ffff8b0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fa
>   0x0c0c7ffff8c0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
>   0x0c0c7ffff8d0: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fd
>   0x0c0c7ffff8e0: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fa
>   0x0c0c7ffff8f0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
> =>0x0c0c7ffff900: 00 00 00 00 00 00 00 00 fa fa fa fa fd[fd]fd fd
>   0x0c0c7ffff910: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fa
>   0x0c0c7ffff920: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
>   0x0c0c7ffff930: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fd
>   0x0c0c7ffff940: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fa
>   0x0c0c7ffff950: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
> 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
> ==26427==ABORTING

Thanks

marco

[mogrify_heap_uaf.zip](https://github.com/ImageMagick/ImageMagick/files/502665/mogrify_heap_uaf.zip)



Comments:
Comment by mikayla-grace on 2016-09-30 19:12:49+00:00:
Unfortunately we cannot reproduce the problem you posted with ASAN under Fedora 24.  It would be helpful if your stacktrace included source line numbers.  Another option is to post the output of this command: `magick identify -list configure`.  Perhaps you have certain compiler flags we've overlooked.


---

Comment by marcograss on 2016-10-01 05:42:31+00:00:
please notice that mogrify sometimes append a ~ trailing character to the input, retry also on the file with ~ or multiple ~ appended

thank you

marco

```
Path: /home/bob/.config/ImageMagick/configure.xml

Name           Value
-------------------------------------------------------------------------------
CC             afl-clang-fast
CFLAGS         -I/usr/include/libxml2 -I/usr/include/libpng12  -I/usr/include/graphviz -I/usr/include/freetype2 -I/usr/include/freetype2    -g -O2 -Wall -mtune=haswell -fexceptions  -DMAGICKCORE_HDRI_ENABLE=1 -DMAGICKCORE_QUANTUM_DEPTH=16
CODER_PATH     /usr/local/lib/ImageMagick-7.0.3/modules-Q16HDRI/coders
CONFIGURE      ./configure  '--disable-shared' 'CC=afl-clang-fast' 'CXX=afl-clang-fast++'
CONFIGURE_PATH /usr/local/etc/ImageMagick-7/
COPYRIGHT      Copyright (C) 1999-2016 ImageMagick Studio LLC
CPPFLAGS       -I/usr/local/include/ImageMagick-7
CXX            afl-clang-fast++
CXXFLAGS       -g -O2 
DEFS           -DHAVE_CONFIG_H
DELEGATES      mpeg fontconfig freetype jbig jng jpeg lzma png ps tiff x xml zlib
DISTCHECK_CONFIG_FLAGS 'CC=afl-clang-fast' 'CXX=afl-clang-fast++'  --disable-deprecated  --with-quantum-depth=16  --with-jemalloc=no  --with-umem=no  --with-autotrace=no  --with-gslib=no  --with-fontpath=  --with-rsvg=no  --with-perl=no 
DOCUMENTATION_PATH /usr/local/share/doc/ImageMagick-7
EXEC-PREFIX    /usr/local
EXECUTABLE_PATH /usr/local/bin
FEATURES       DPC HDRI Cipher
FILTER_PATH    /usr/local/lib/ImageMagick-7.0.3/modules-Q16HDRI/filters
GIT_REVISION   
HOST           x86_64-unknown-linux-gnu
INCLUDE_PATH   /usr/local/include/ImageMagick-7
LDFLAGS        -L/usr/local/lib  
LIB_VERSION    0x703
LIB_VERSION_NUMBER 7,0,3,2
LIBRARY_PATH   /usr/local/lib/ImageMagick-7.0.3
LIBS            -ljbig  -ltiff -lfreetype  -ljpeg   -lpng12      -lfontconfig -lfreetype     -lXt   -lSM -lICE -lX11  -llzma      -lxml2 -lgvc -lcgraph -lcdt -lz  -lm -lgomp    
NAME           ImageMagick
PCFLAGS        -DMAGICKCORE_HDRI_ENABLE=1 -DMAGICKCORE_QUANTUM_DEPTH=16
PREFIX         /usr/local
QuantumDepth   16
RELEASE_DATE   2016-09-27
SHARE_PATH     /usr/local/share/ImageMagick-7
SHAREARCH_PATH /usr/local/lib/ImageMagick-7.0.3/config-Q16HDRI
TARGET_CPU     x86_64
TARGET_OS      linux-gnu
TARGET_VENDOR  unknown
VERSION        7.0.3
WEBSITE        http://www.imagemagick.org

Path: [built-in]

Name           Value
-------------------------------------------------------------------------------
FEATURES       
NAME           ImageMagick
QuantumDepth   16

```


---

Comment by mikayla-grace on 2016-10-01 13:54:39+00:00:
Thanks for the problem report.  We can reproduce it and will have a patch to fix it in GIT master branch @ https://github.com/ImageMagick/ImageMagick later today.  The patch will be available in the beta releases of ImageMagick @ http://www.imagemagick.org/download/beta/ by sometime tomorrow. 


Attachments:
https://github.com/ImageMagick/ImageMagick/files/502665/mogrify_heap_uaf.zip

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
./configure --prefix="$WORK" --disable-shared --disable-docs
make "-j$(nproc)"
make install -j$(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool
RUN git clone https://github.com/imagemagick/imagemagick imagemagick
RUN git -C imagemagick checkout f8f161d61d6e36d027d44cd98002b822e4217add
WORKDIR $SRC/imagemagick
COPY build.sh $SRC/

Additional files:
filename: crop_fuzzer.cc
content: #include <cstdint>

#include <Magick++/Blob.h>
#include <Magick++/Image.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
  uint16_t Width;
  uint16_t Height;
  if (Size < (sizeof(Width) + sizeof(Height))) {
    return 0;
  }
  Width = *reinterpret_cast<const uint16_t *>(Data);
  Height = *reinterpret_cast<const uint16_t *>(Data + sizeof(Width));
  const Magick::Blob blob(Data + sizeof(Width) + sizeof(Height),
                          Size - (sizeof(Width) + sizeof(Height)));
  Magick::Image image;
  try {
    image.read(blob);
  } catch (Magick::Exception &e) {
    return 0;
  }
  image.crop(Magick::Geometry(Width, Height));
  return 0;
}

filename: encoder_fuzzer.cc
content: #include <cstdint>

#include <Magick++/Blob.h>
#include <Magick++/Image.h>

#define FUZZ_ENCODER_STRING_LITERAL(name) #name
#define FUZZ_ENCODER FUZZ_ENCODER_STRING_LITERAL(FUZZ_IMAGEMAGICK_ENCODER)

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
  const Magick::Blob blob(Data, Size);
  Magick::Image image;
  try {
    image.read(blob);
  } catch (Magick::Exception &e) {
    return 0;
  }

  Magick::Blob outBlob;
  try {
    image.write(&outBlob, FUZZ_ENCODER);
  } catch (Magick::Exception &e) {
  }
  return 0;
}

filename: encoder_list.cc
content: #include <iostream>

#include <Magick++/Blob.h>
#include <Magick++/Image.h>

extern "C" int main() {
  size_t nFormats;
  Magick::ExceptionInfo ex;
  const Magick::MagickInfo **formats = GetMagickInfoList("*", &nFormats, &ex);

  for (size_t i = 0; i < nFormats; i++) {
    const Magick::MagickInfo *format = formats[i];
    if (format->encoder && format->name) {
      std::cout << format->name << std::endl;
    }
  }
}

filename: enhance_fuzzer.cc
content: #include <cstdint>

#include <Magick++/Blob.h>
#include <Magick++/Image.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
  const Magick::Blob blob(Data, Size);
  Magick::Image image;
  try {
    image.read(blob);
    image.enhance();
  } catch (Magick::Exception &e) {
    return 0;
  }
  return 0;
}

filename: huffman_decode_fuzzer.cc
content: #include <cstdint>

#include <Magick++/Blob.h>
#include <Magick++/Image.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
  const Magick::Blob blob(Data, Size);
  Magick::Image image;
  try {
    image.read(blob);
  } catch (Magick::Exception &e) {
    return 0;
  }
  Magick::ExceptionInfo ex;
  auto res = HuffmanDecodeImage(image.image(), &ex);
  return 0;
}

filename: rotate_fuzzer.cc
content: #include <cstdint>

#include <Magick++/Blob.h>
#include <Magick++/Image.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
  if (Size < sizeof(double)) {
    return 0;
  }
  double Degrees = *reinterpret_cast<const double *>(Data);
  if (!isfinite(Degrees)) {
    return 0;
  }
  const Magick::Blob blob(Data + sizeof(Degrees), Size - sizeof(Degrees));
  Magick::Image image;
  try {
    image.read(blob);
  } catch (Magick::Exception &e) {
    return 0;
  }
  image.rotate(Degrees);
  return 0;
}