Last active
November 23, 2024 18:44
-
-
Save ARISTODE/28e0f52b3bb7016c3fb937f9627b88af to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <jpeglib.h> | |
| #include <setjmp.h> | |
| // Structure to hold decoder state | |
| struct decoder_state { | |
| struct jpeg_decompress_struct* info; | |
| struct jpeg_error_mgr* err; | |
| unsigned char* image_data; | |
| size_t image_data_size; | |
| unsigned char* back_buffer; | |
| size_t back_buffer_len; | |
| jmp_buf setjmp_buffer; // For error handling | |
| }; | |
| // Forward declarations | |
| static void error_exit(j_common_ptr cinfo); | |
| static void skip_input_data(j_decompress_ptr cinfo, long num_bytes); | |
| static boolean fill_input_buffer(j_decompress_ptr cinfo); | |
| static void init_source(j_decompress_ptr cinfo); | |
| static void term_source(j_decompress_ptr cinfo); | |
| // Source manager setup function | |
| static void setup_source_manager(struct jpeg_source_mgr* src) { | |
| src->init_source = init_source; | |
| src->fill_input_buffer = fill_input_buffer; | |
| src->skip_input_data = skip_input_data; | |
| src->resync_to_restart = jpeg_resync_to_restart; | |
| src->term_source = term_source; | |
| src->bytes_in_buffer = 0; | |
| src->next_input_byte = NULL; | |
| } | |
| // Initialize source - called by jpeg_read_header() | |
| static void init_source(j_decompress_ptr cinfo) { | |
| // Vulnerability: No validation before using client_data | |
| struct decoder_state* state = (struct decoder_state*)cinfo->client_data; | |
| if (state) { | |
| state->back_buffer_len = 0; | |
| } | |
| } | |
| // Skip data - called by libjpeg when it wants to skip input data | |
| static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) { | |
| struct jpeg_source_mgr* src = cinfo->src; | |
| // Vulnerability: No validation of num_bytes from libjpeg | |
| if (num_bytes > 0) { | |
| src->next_input_byte += num_bytes; | |
| src->bytes_in_buffer -= num_bytes; | |
| } | |
| } | |
| // Fill input buffer - called by libjpeg when it needs more data | |
| static boolean fill_input_buffer(j_decompress_ptr cinfo) { | |
| // Vulnerability: No validation of cinfo pointer | |
| struct jpeg_source_mgr* src = cinfo->src; | |
| struct decoder_state* state = (struct decoder_state*)cinfo->client_data; | |
| // Vulnerability: No bounds checking on buffer access | |
| if (state && state->back_buffer_len > 0) { | |
| memcpy(state->back_buffer, | |
| src->next_input_byte, | |
| src->bytes_in_buffer); | |
| } | |
| // Simulate end of input | |
| static const JOCTET EOI_BUFFER[2] = {0xFF, JPEG_EOI}; | |
| src->next_input_byte = EOI_BUFFER; | |
| src->bytes_in_buffer = 2; | |
| return TRUE; | |
| } | |
| // Terminate source - called by libjpeg when finishing decompression | |
| static void term_source(j_decompress_ptr cinfo) { | |
| // Nothing to do | |
| } | |
| // Error handler for libjpeg | |
| static void error_exit(j_common_ptr cinfo) { | |
| // Vulnerability: Unsafe error handling using longjmp | |
| struct decoder_state* state = (struct decoder_state*)cinfo->client_data; | |
| if (state) { | |
| longjmp(state->setjmp_buffer, 1); | |
| } | |
| } | |
| // Initialize the decoder | |
| struct decoder_state* init_decoder() { | |
| // Allocate decoder state | |
| struct decoder_state* state = malloc(sizeof(struct decoder_state)); | |
| if (!state) return NULL; | |
| // Initialize to zero | |
| memset(state, 0, sizeof(struct decoder_state)); | |
| // Allocate and initialize JPEG decompression object and error manager | |
| state->info = malloc(sizeof(struct jpeg_decompress_struct)); | |
| state->err = malloc(sizeof(struct jpeg_error_mgr)); | |
| if (!state->info || !state->err) { | |
| free(state->info); | |
| free(state->err); | |
| free(state); | |
| return NULL; | |
| } | |
| // Set up error handler | |
| state->info->err = jpeg_std_error(state->err); | |
| state->err->error_exit = error_exit; | |
| // Initialize JPEG decompression object | |
| jpeg_create_decompress(state->info); | |
| // Vulnerability: Store decoder state pointer in libjpeg struct | |
| state->info->client_data = (void*)state; | |
| // Set up source manager | |
| struct jpeg_source_mgr* src = malloc(sizeof(struct jpeg_source_mgr)); | |
| if (!src) { | |
| jpeg_destroy_decompress(state->info); | |
| free(state->err); | |
| free(state->info); | |
| free(state); | |
| return NULL; | |
| } | |
| setup_source_manager(src); | |
| state->info->src = src; | |
| // Allocate back buffer for input data | |
| state->back_buffer = malloc(4096); // Fixed size buffer | |
| state->back_buffer_len = 4096; | |
| return state; | |
| } | |
| // Clean up the decoder | |
| void cleanup_decoder(struct decoder_state* state) { | |
| if (!state) return; | |
| if (state->info) { | |
| if (state->info->src) { | |
| free(state->info->src); | |
| } | |
| jpeg_destroy_decompress(state->info); | |
| free(state->info); | |
| } | |
| free(state->err); | |
| free(state->image_data); | |
| free(state->back_buffer); | |
| free(state); | |
| } | |
| // Decode JPEG data | |
| int decode_jpeg(struct decoder_state* state, | |
| const unsigned char* input_data, | |
| size_t input_length) { | |
| if (!state || !input_data) return 0; | |
| // Set up error handling | |
| if (setjmp(state->setjmp_buffer)) { | |
| // Error handling path | |
| return 0; | |
| } | |
| // Set input data | |
| state->info->src->next_input_byte = input_data; | |
| state->info->src->bytes_in_buffer = input_length; | |
| // Read JPEG header | |
| if (jpeg_read_header(state->info, TRUE) != JPEG_HEADER_OK) { | |
| return 0; | |
| } | |
| // Start decompression | |
| jpeg_start_decompress(state->info); | |
| // Allocate output buffer | |
| // Vulnerability: No integer overflow check | |
| size_t row_stride = state->info->output_width * | |
| state->info->output_components; | |
| state->image_data = malloc(row_stride * state->info->output_height); | |
| state->image_data_size = row_stride * state->info->output_height; | |
| if (!state->image_data) { | |
| return 0; | |
| } | |
| // Read scanlines | |
| unsigned char* scanline = state->image_data; | |
| while (state->info->output_scanline < state->info->output_height) { | |
| // Vulnerability: Double fetch and no bounds checking | |
| JSAMPROW row_pointer = scanline; | |
| jpeg_read_scanlines(state->info, &row_pointer, 1); | |
| scanline += row_stride; | |
| } | |
| // Finish decompression | |
| jpeg_finish_decompress(state->info); | |
| return 1; | |
| } | |
| int main() { | |
| struct decoder_state* decoder = init_decoder(); | |
| if (!decoder) { | |
| printf("Failed to initialize decoder\n"); | |
| return 1; | |
| } | |
| // Vulnerability #1: No size validation on file read | |
| unsigned char buffer[1024*1024]; | |
| FILE* f = fopen("test.jpg", "rb"); | |
| if (!f) { | |
| cleanup_decoder(decoder); | |
| return 1; | |
| } | |
| size_t bytes_read = fread(buffer, 1, sizeof(buffer), f); | |
| fclose(f); | |
| // Set up error handling | |
| if (setjmp(decoder->setjmp_buffer)) { | |
| printf("Error during decoding\n"); | |
| cleanup_decoder(decoder); | |
| return 1; | |
| } | |
| // Set input data | |
| decoder->info->src->next_input_byte = buffer; | |
| decoder->info->src->bytes_in_buffer = bytes_read; | |
| // Vulnerability #2: Using return values without validation | |
| if (jpeg_read_header(decoder->info, TRUE) != JPEG_HEADER_OK) { | |
| cleanup_decoder(decoder); | |
| return 1; | |
| } | |
| jpeg_start_decompress(decoder->info); | |
| // Vulnerability #3: Unchecked arithmetic from libjpeg values | |
| size_t row_stride = decoder->info->output_width * | |
| decoder->info->output_components; | |
| // Vulnerability #4: Using untrusted values for allocation | |
| unsigned char* output_buffer = malloc(row_stride * | |
| decoder->info->output_height); | |
| if (!output_buffer) { | |
| cleanup_decoder(decoder); | |
| return 1; | |
| } | |
| // Vulnerability #5: Double-fetch when reading scanlines | |
| unsigned char* scanline = output_buffer; | |
| while (decoder->info->output_scanline < decoder->info->output_height) { | |
| JSAMPROW row_pointer = scanline; | |
| jpeg_read_scanlines(decoder->info, &row_pointer, 1); | |
| // Vulnerable: output_width could change between iterations | |
| scanline += row_stride; | |
| } | |
| // Vulnerability #6: Direct use of function pointers | |
| if (decoder->info->err->emit_message) { | |
| decoder->info->err->emit_message( | |
| (j_common_ptr)decoder->info, | |
| JMSG_INFO); | |
| } | |
| // Vulnerability #7: Unsafe direct memory copying | |
| if (decoder->back_buffer) { | |
| memcpy(decoder->back_buffer, | |
| decoder->info->src->next_input_byte, | |
| decoder->info->src->bytes_in_buffer); | |
| } | |
| jpeg_finish_decompress(decoder->info); | |
| // Cleanup | |
| free(output_buffer); | |
| cleanup_decoder(decoder); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment