Headline
CVE-2023-39125: [2] SEGV at loadBMP (WRITE memory access) · Issue #32 · LMP88959/NTSC-CRT
NTSC-CRT 2.2.1 has an integer overflow and out-of-bounds write in loadBMP in bmp_rw.c because a file’s width, height, and BPP are not validated. NOTE: the vendor’s perspective is “this main application was not intended to be a well tested program, it’s just something to demonstrate it works and for the user to see how to integrate it into their own programs.”
Root Cause
f = fopen(file, “rb”);
if (f == NULL) {
return NULL;
}
fread(header, sizeof(unsigned char), 54, f);
width = *(int*) &header[18];
height = *(int*) &header[22];
BPP = *(int*) &header[28];
size = (width * height * (BPP / 8));
padding = ((4 - (width * (BPP / 8)) % 4) % 4);
data = calloc_func(size, (BPP / 8));
if (data == NULL) {
return NULL;
}
fseek(f, 54, SEEK_SET);
for (Y = height - 1; Y >= 0; Y–) {
for (X = 0; X < width; X++) {
fread(&data[(Y * width + X) * (BPP / 8)], (BPP / 8), 1, f);
}
fread(pad, padding, 1, f);
}
You can check that the file given as an input in the loadBMP function is opened and read into memory. Since the value is not verified by reading width, height, and BPP, there is a possibility that the value will be a small value beyond the range of unsigned int. In other words, you can allocate a buffer smaller than the size that will be used.
Alternatively, if the result of the size operation on line 43 exceeds the range of unsigned int, and integer overflow is likely. Later, on line 45, a heap address of size *(BPP/8) is returned via the calloc function. The integer overflow can cause problems if the size is unintentionally small enough.
This causes a problem on line 52. When a file is read into the heap through the fread function, the buffer allocated for data overflows.
ASAN output
AddressSanitizer:DEADLYSIGNAL
=================================================================
==958252==ERROR: AddressSanitizer: SEGV on unknown address 0x7ff98f791000 (pc 0x7ff91336d9ac bp 0x000000000023 sp 0x7fffe29d67c8 T0)
==958252==The signal is caused by a WRITE memory access.
#0 0x7ff91336d9ac (/lib/x86_64-linux-gnu/libc.so.6+0x1a09ac)
#1 0x7ff9132584a2 in __GI__IO_file_xsgetn libio/fileops.c:1295
#2 0x7ff91324cc28 in __GI__IO_fread libio/iofread.c:38
#3 0x7ff913434753 in __interceptor_fread ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1045
#4 0x5604cb259cdb in fread /usr/include/x86_64-linux-gnu/bits/stdio2.h:293
#5 0x5604cb259cdb in loadBMP /home/user/Analysis/NTSC-CRT/bmp_rw.c:52
#6 0x5604cb259cdb in loadBMPconverter /home/user/Analysis/NTSC-CRT/bmp_rw.c:73
#7 0x5604cb259cdb in bmp_read24 /home/user/Analysis/NTSC-CRT/bmp_rw.c:152
#8 0x5604cb254207 in main /home/user/Analysis/NTSC-CRT/crt_main.c:210
#9 0x7ff9131f6d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#10 0x7ff9131f6e3f in __libc_start_main_impl ../csu/libc-start.c:392
#11 0x5604cb254614 in _start (/home/user/Analysis/build/ntsc+0x4614)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib/x86_64-linux-gnu/libc.so.6+0x1a09ac)
==958252==ABORTING
POC
poc.zip
How to reproduce
ntsc -op 640 480 24 0 crash out.ppm