Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SECURITY: Heap-based buffer overflow (libpng 1.6.43) #612

Closed
Kerkroups opened this issue Oct 4, 2024 · 9 comments
Closed

SECURITY: Heap-based buffer overflow (libpng 1.6.43) #612

Kerkroups opened this issue Oct 4, 2024 · 9 comments

Comments

@Kerkroups
Copy link

Kerkroups commented Oct 4, 2024

During fuzzing process have found heap-based buffer overflow.

AFL output:
`==340876==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000018 at pc 0x55555558e466 bp 0x7fffffffda80 sp 0x7fffffffd240
WRITE of size 32 at 0x502000000018 thread T0
0x55555558e465 in __interceptor_memcpy (fuzzing_libpng/fuzz_libpng+0x3a465) (BuildId: 8fef77cdbc5aaa656761cf421ce6033222be80a6)
0x7ffff7f8f21c (/lib/x86_64-linux-gnu/libpng16.so.16+0x1d21c) (BuildId: b6cb6efa3c76088fb6dd86f16ee702be4d3729b2)
0x7ffff7f8223c in png_read_row (/lib/x86_64-linux-gnu/libpng16.so.16+0x1023c) (BuildId: b6cb6efa3c76088fb6dd86f16ee702be4d3729b2)
0x7ffff7f83cd8 in png_read_image (/lib/x86_64-linux-gnu/libpng16.so.16+0x11cd8) (BuildId: b6cb6efa3c76088fb6dd86f16ee702be4d3729b2)
0x5555556492d8 in LLVMFuzzerTestOneInput /fuzzing_libpng/fuzz_libpng.c:90:5
0x555555649823 in main /fuzzing_libpng/fuzz_libpng.c:140:5
0x7ffff7c8edb9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
0x7ffff7c8ee74 in __libc_start_main csu/../csu/libc-start.c:360:3
0x555555572490 in _start (/fuzzing_libpng/fuzz_libpng+0x1e490) (BuildId: 8fef77cdbc5aaa656761cf421ce6033222be80a6)

0x502000000018 is located 0 bytes after 8-byte region [0x502000000010,0x502000000018)
allocated by thread T0 here:
0x55555560d0e2 in __interceptor_malloc (fuzzing_libpng/fuzz_libpng+0xb90e2) (BuildId: 8fef77cdbc5aaa656761cf421ce6033222be80a6)
0x555555649251 in LLVMFuzzerTestOneInput /fuzzing_libpng/fuzz_libpng.c:86:38
0x555555649823 in main /fuzzing_libpng/fuzz_libpng.c:140:5

SUMMARY: AddressSanitizer: heap-buffer-overflow (/fuzzing_libpng/fuzz_libpng+0x3a465) (BuildId: 8fef77cdbc5aaa656761cf421ce6033222be80a6) in __interceptor_memcpy
Shadow bytes around the buggy address:
0x501ffffffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x501ffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x501ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x501fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x501fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x502000000000: fa fa 00[fa]fa fa 00 fa fa fa 00 fa fa fa 00 fa
0x502000000080: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
0x502000000100: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
0x502000000180: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
0x502000000200: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
0x502000000280: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
Ihis specific issue happens during a call to memcpy, triggered in the png_read_row function from libpng (https://github.com/pnggroup/libpng/blob/d3cf9b6e22fca25273e87d0b11882a7f886c97fe/pngread.c#L559)memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1);)`

The function memcpy copies row_info.rowbytes + 1 bytes from png_ptr->row_buf to png_ptr->prev_row.
If png_ptr->prev_row is not properly allocated with at least row_info.rowbytes + 1 bytes, this would lead to copying more data than the destination buffer (prev_row) can hold. This causes a heap buffer overflow.

Payloads that trigger overflow:
exploit
afl_payload

@Kerkroups Kerkroups changed the title SECURITY: Heap-based buffer overflow SECURITY: Heap-based buffer overflow (libpng 1.6.43) Oct 4, 2024
@jbowler
Copy link
Contributor

jbowler commented Oct 4, 2024

Please post the machine generated file "pnglibconf.h" from your build of libpng.

Also please state the version of zlib (or zlib-ng etc) you are using and whether you have modified it.

@ctruta: good idea to make this a requirement. Check out the rules for posting bugs on gentoo, they head off issues like "did you manage to cancel MEMALIGN" as in this case.

@Kerkroups
Copy link
Author

File pnglibconf.h:
/* pnglibconf.h - library build configuration */

/* libpng version 1.6.43 */

/* Copyright (c) 2018-2024 Cosmin Truta /
/
Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */

/* This code is released under the libpng license. /
/
For conditions of distribution and use, see the disclaimer /
/
and license in png.h */

/* pnglibconf.h /
/
Machine generated file: DO NOT EDIT /
/
Derived from: scripts/pnglibconf.dfa /
#ifndef PNGLCONF_H
#define PNGLCONF_H
/
options /
#define PNG_16BIT_SUPPORTED
#define PNG_ALIGNED_MEMORY_SUPPORTED
/
#undef PNG_ARM_NEON_API_SUPPORTED*/
/#undef PNG_ARM_NEON_CHECK_SUPPORTED/
#define PNG_BENIGN_ERRORS_SUPPORTED
#define PNG_BENIGN_READ_ERRORS_SUPPORTED
/#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED/
#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
#define PNG_COLORSPACE_SUPPORTED
#define PNG_CONSOLE_IO_SUPPORTED
#define PNG_CONVERT_tIME_SUPPORTED
/#undef PNG_DISABLE_ADLER32_CHECK_SUPPORTED/
#define PNG_EASY_ACCESS_SUPPORTED
/#undef PNG_ERROR_NUMBERS_SUPPORTED/
#define PNG_ERROR_TEXT_SUPPORTED
#define PNG_FIXED_POINT_SUPPORTED
#define PNG_FLOATING_ARITHMETIC_SUPPORTED
#define PNG_FLOATING_POINT_SUPPORTED
#define PNG_FORMAT_AFIRST_SUPPORTED
#define PNG_FORMAT_BGR_SUPPORTED
#define PNG_GAMMA_SUPPORTED
#define PNG_GET_PALETTE_MAX_SUPPORTED
#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
#define PNG_INCH_CONVERSIONS_SUPPORTED
#define PNG_INFO_IMAGE_SUPPORTED
#define PNG_IO_STATE_SUPPORTED
/#undef PNG_MIPS_MMI_API_SUPPORTED/
/#undef PNG_MIPS_MMI_CHECK_SUPPORTED/
/#undef PNG_MIPS_MSA_API_SUPPORTED/
/#undef PNG_MIPS_MSA_CHECK_SUPPORTED/
#define PNG_MNG_FEATURES_SUPPORTED
#define PNG_POINTER_INDEXING_SUPPORTED
/#undef PNG_POWERPC_VSX_API_SUPPORTED/
/#undef PNG_POWERPC_VSX_CHECK_SUPPORTED/
#define PNG_PROGRESSIVE_READ_SUPPORTED
#define PNG_READ_16BIT_SUPPORTED
#define PNG_READ_ALPHA_MODE_SUPPORTED
#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
#define PNG_READ_BACKGROUND_SUPPORTED
#define PNG_READ_BGR_SUPPORTED
#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
#define PNG_READ_COMPOSITE_NODIV_SUPPORTED
#define PNG_READ_COMPRESSED_TEXT_SUPPORTED
#define PNG_READ_EXPAND_16_SUPPORTED
#define PNG_READ_EXPAND_SUPPORTED
#define PNG_READ_FILLER_SUPPORTED
#define PNG_READ_GAMMA_SUPPORTED
#define PNG_READ_GET_PALETTE_MAX_SUPPORTED
#define PNG_READ_GRAY_TO_RGB_SUPPORTED
#define PNG_READ_INTERLACING_SUPPORTED
#define PNG_READ_INT_FUNCTIONS_SUPPORTED
#define PNG_READ_INVERT_ALPHA_SUPPORTED
#define PNG_READ_INVERT_SUPPORTED
#define PNG_READ_OPT_PLTE_SUPPORTED
#define PNG_READ_PACKSWAP_SUPPORTED
#define PNG_READ_PACK_SUPPORTED
#define PNG_READ_QUANTIZE_SUPPORTED
#define PNG_READ_RGB_TO_GRAY_SUPPORTED
#define PNG_READ_SCALE_16_TO_8_SUPPORTED
#define PNG_READ_SHIFT_SUPPORTED
#define PNG_READ_STRIP_16_TO_8_SUPPORTED
#define PNG_READ_STRIP_ALPHA_SUPPORTED
#define PNG_READ_SUPPORTED
#define PNG_READ_SWAP_ALPHA_SUPPORTED
#define PNG_READ_SWAP_SUPPORTED
#define PNG_READ_TEXT_SUPPORTED
#define PNG_READ_TRANSFORMS_SUPPORTED
#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_READ_USER_CHUNKS_SUPPORTED
#define PNG_READ_USER_TRANSFORM_SUPPORTED
#define PNG_READ_bKGD_SUPPORTED
#define PNG_READ_cHRM_SUPPORTED
#define PNG_READ_eXIf_SUPPORTED
#define PNG_READ_gAMA_SUPPORTED
#define PNG_READ_hIST_SUPPORTED
#define PNG_READ_iCCP_SUPPORTED
#define PNG_READ_iTXt_SUPPORTED
#define PNG_READ_oFFs_SUPPORTED
#define PNG_READ_pCAL_SUPPORTED
#define PNG_READ_pHYs_SUPPORTED
#define PNG_READ_sBIT_SUPPORTED
#define PNG_READ_sCAL_SUPPORTED
#define PNG_READ_sPLT_SUPPORTED
#define PNG_READ_sRGB_SUPPORTED
#define PNG_READ_tEXt_SUPPORTED
#define PNG_READ_tIME_SUPPORTED
#define PNG_READ_tRNS_SUPPORTED
#define PNG_READ_zTXt_SUPPORTED
#define PNG_SAVE_INT_32_SUPPORTED
#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_SEQUENTIAL_READ_SUPPORTED
#define PNG_SETJMP_SUPPORTED
#define PNG_SET_OPTION_SUPPORTED
#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_SET_USER_LIMITS_SUPPORTED
#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED
#define PNG_SIMPLIFIED_READ_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_SUPPORTED
#define PNG_STDIO_SUPPORTED
#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_TEXT_SUPPORTED
#define PNG_TIME_RFC1123_SUPPORTED
#define PNG_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_USER_CHUNKS_SUPPORTED
#define PNG_USER_LIMITS_SUPPORTED
#define PNG_USER_MEM_SUPPORTED
#define PNG_USER_TRANSFORM_INFO_SUPPORTED
#define PNG_USER_TRANSFORM_PTR_SUPPORTED
#define PNG_WARNINGS_SUPPORTED
#define PNG_WRITE_16BIT_SUPPORTED
#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
#define PNG_WRITE_BGR_SUPPORTED
#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
#define PNG_WRITE_FILLER_SUPPORTED
#define PNG_WRITE_FILTER_SUPPORTED
#define PNG_WRITE_FLUSH_SUPPORTED
#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED
#define PNG_WRITE_INTERLACING_SUPPORTED
#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
#define PNG_WRITE_INVERT_SUPPORTED
#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
#define PNG_WRITE_PACKSWAP_SUPPORTED
#define PNG_WRITE_PACK_SUPPORTED
#define PNG_WRITE_SHIFT_SUPPORTED
#define PNG_WRITE_SUPPORTED
#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
#define PNG_WRITE_SWAP_SUPPORTED
#define PNG_WRITE_TEXT_SUPPORTED
#define PNG_WRITE_TRANSFORMS_SUPPORTED
#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
#define PNG_WRITE_bKGD_SUPPORTED
#define PNG_WRITE_cHRM_SUPPORTED
#define PNG_WRITE_eXIf_SUPPORTED
#define PNG_WRITE_gAMA_SUPPORTED
#define PNG_WRITE_hIST_SUPPORTED
#define PNG_WRITE_iCCP_SUPPORTED
#define PNG_WRITE_iTXt_SUPPORTED
#define PNG_WRITE_oFFs_SUPPORTED
#define PNG_WRITE_pCAL_SUPPORTED
#define PNG_WRITE_pHYs_SUPPORTED
#define PNG_WRITE_sBIT_SUPPORTED
#define PNG_WRITE_sCAL_SUPPORTED
#define PNG_WRITE_sPLT_SUPPORTED
#define PNG_WRITE_sRGB_SUPPORTED
#define PNG_WRITE_tEXt_SUPPORTED
#define PNG_WRITE_tIME_SUPPORTED
#define PNG_WRITE_tRNS_SUPPORTED
#define PNG_WRITE_zTXt_SUPPORTED
#define PNG_bKGD_SUPPORTED
#define PNG_cHRM_SUPPORTED
#define PNG_eXIf_SUPPORTED
#define PNG_gAMA_SUPPORTED
#define PNG_hIST_SUPPORTED
#define PNG_iCCP_SUPPORTED
#define PNG_iTXt_SUPPORTED
#define PNG_oFFs_SUPPORTED
#define PNG_pCAL_SUPPORTED
#define PNG_pHYs_SUPPORTED
#define PNG_sBIT_SUPPORTED
#define PNG_sCAL_SUPPORTED
#define PNG_sPLT_SUPPORTED
#define PNG_sRGB_SUPPORTED
#define PNG_tEXt_SUPPORTED
#define PNG_tIME_SUPPORTED
#define PNG_tRNS_SUPPORTED
#define PNG_zTXt_SUPPORTED
/* end of options /
/
settings /
#define PNG_API_RULE 0
#define PNG_DEFAULT_READ_MACROS 1
#define PNG_GAMMA_THRESHOLD_FIXED 5000
#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
#define PNG_INFLATE_BUF_SIZE 1024
#define PNG_LINKAGE_API extern
#define PNG_LINKAGE_CALLBACK extern
#define PNG_LINKAGE_DATA extern
#define PNG_LINKAGE_FUNCTION extern
#define PNG_MAX_GAMMA_8 11
#define PNG_QUANTIZE_BLUE_BITS 5
#define PNG_QUANTIZE_GREEN_BITS 5
#define PNG_QUANTIZE_RED_BITS 5
#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
#define PNG_TEXT_Z_DEFAULT_STRATEGY 0
#define PNG_USER_CHUNK_CACHE_MAX 1000
#define PNG_USER_CHUNK_MALLOC_MAX 8000000
#define PNG_USER_HEIGHT_MAX 1000000
#define PNG_USER_WIDTH_MAX 1000000
#define PNG_ZBUF_SIZE 8192
#define PNG_ZLIB_VERNUM 0x1300
#define PNG_Z_DEFAULT_COMPRESSION (-1)
#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0
#define PNG_Z_DEFAULT_STRATEGY 1
#define PNG_sCAL_PRECISION 5
#define PNG_sRGB_PROFILE_CHECKS 2
/
end of settings /
#endif /
PNGLCONF_H */

zlib version: 1.3.1

@jbowler
Copy link
Contributor

jbowler commented Oct 5, 2024

ALIGNED_MEMORY_SUPPORTED
IHDR is well formed, bad CRC (0)
IDAT length 16 zlib returns a compression error and inflate error "-3" (data error) so hopelessly corrupt

libpng-1.6.43 does a png_error 'incorrect header check' in pngrutil.c when starting to read the IDAT chunk. It's a 'png_chunk_error' so it can't be avoided. The message is coming from png_ptr->zstream.msg (i.e. it is a zlib errror). It's a 1x1 image, 'rowbytes' is 4 (RGBA). png_ptr->height is 1 and the image is not interlaced.

The test case does a png_error in the very first call to png_read_IDAT_data (as would be expected). png_read_row never gets to the memcpy; it's immediately after the png_read_IDAT_data call.

@Kerkroups there is something wrong with your png_error callback implementation. PLease supply a test program if you think this analysis is wrong.

@ctruta: provisional label: "invalid"

@Kerkroups
Copy link
Author

Test program code:

#include <png.h>
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

// Error handler for libpng
void handle_error(png_structp png_ptr, png_const_charp error_msg) {
fprintf(stderr, "libpng error: %s\n", error_msg);
longjmp(png_jmpbuf(png_ptr), 1); // Jump to error recovery
}

int process_png_file(const char *filename) {
FILE *fp = fopen(filename, "rb");
if (!fp) {
perror("File opening failed");
return 1;
}

// Read the PNG signature (first 8 bytes)
unsigned char header[8];
fread(header, 1, 8, fp);
if (png_sig_cmp(header, 0, 8)) {
    fprintf(stderr, "Not a valid PNG file\n");
    fclose(fp);
    return 1;
}

// Initialize libpng structures
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, handle_error, NULL);
if (!png_ptr) {
    fclose(fp);
    return 1;
}

png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
    png_destroy_read_struct(&png_ptr, NULL, NULL);
    fclose(fp);
    return 1;
}

if (setjmp(png_jmpbuf(png_ptr))) {
    // Error during reading
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    fclose(fp);
    return 1;
}

png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 8);  // Tell libpng we've read the signature
png_read_info(png_ptr, info_ptr);

// Get image information
int width = png_get_image_width(png_ptr, info_ptr);
int height = png_get_image_height(png_ptr, info_ptr);
png_byte color_type = png_get_color_type(png_ptr, info_ptr);
png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);

printf("PNG Image Info:\n");
printf("  Width: %d\n", width);
printf("  Height: %d\n", height);
printf("  Color Type: %d\n", color_type);
printf("  Bit Depth: %d\n", bit_depth);

// Optional: Set transformations for different color types, bit depths, etc.
if (bit_depth == 16) png_set_strip_16(png_ptr);
if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr);

// Update info structure after transformations
png_read_update_info(png_ptr, info_ptr);

// Allocate memory for rows
png_bytep *row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * height);
for (int y = 0; y < height; y++) {
    row_pointers[y] = (png_byte *)malloc(png_get_rowbytes(png_ptr, info_ptr));
}

// Read the image data
png_read_image(png_ptr, row_pointers);

// Clean up and free memory
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);

// Free allocated row data
for (int y = 0; y < height; y++) {
    free(row_pointers[y]);
}
free(row_pointers);

return 0;

}

int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s \n", argv[0]);
return 1;
}

return process_png_file(argv[1]);

}

@jbowler
Copy link
Contributor

jbowler commented Oct 5, 2024

@Kerkroups That program does not even get to the printf; it errors out inside png_read_info so if it continues passed that when you run it there is something seriously broken about the error handling (handle_error) in your build. Apart from not checking the result of fread the program looks correct.

If I modify the program to ignore the CRC error then it errors out with the libpng error: IDAT: incorrect header chunk error.

You should be seeing the CRC error and the program should exit then. Since it doesn't whatever happens afterwards is down to invalid error handling.

Also please put code in a code block. I've attached the re-indented fixed (for fread) and no-CRC test file here:
issue612.c.txt

@Kerkroups
Copy link
Author

@jbowler Thank you for help, it seems that is invalid error handling.

@jbowler
Copy link
Contributor

jbowler commented Oct 5, 2024

@ctruta: confirmed invalid

@ctruta
Copy link
Member

ctruta commented Oct 6, 2024

Please post the machine generated file "pnglibconf.h" from your build of libpng.

Also please state the version of zlib (or zlib-ng etc) you are using and whether you have modified it.

@ctruta: good idea to make this a requirement. Check out the rules for posting bugs on gentoo, they head off issues like "did you manage to cancel MEMALIGN" as in this case.

I can do it, after I look into how to do it. But @jbowler, I think you should be able to do it, too, with your membership rights. (If you can't, please let me know.)

@ctruta
Copy link
Member

ctruta commented Oct 6, 2024

@ctruta: confirmed invalid

Roger that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants