Forensics
Medium
250 points

Cropped

Recuite 2025 - HCMUS
6 tháng 10, 2025
Acropalypse
CVE-2023-21036
PNG
Screenshot
Recuite 2025 - HCMUS
Forensics

Cropped - Writeup

Challenge Information

  • Category: Forensics
  • File: flag.png (1409×1004, size 1.7 MB)
  • Vulnerability: Acropalypse (CVE-2023-21036)

What is Acropalypse?

The vulnerability appears in some cropping/screenshot tools (e.g. Google Pixel, Windows Snipping Tool):

  • When an image is cropped and overwritten on the original file
  • The tool does not properly truncate the end of the file
  • Original data still exists in the "trailing bytes" after the PNG IEND chunk
  • From this redundant data, the original uncropped image can be reconstructed!

Verification

with open('flag.png', 'rb') as f:
    data = f.read()
    iend_pos = data.find(b'IEND')
    iend_chunk_end = iend_pos + 8
    trailing_size = len(data) - iend_chunk_end
    print(f"Trailing data: {trailing_size} bytes")

Result: 1,379,551 bytes trailing data!

Recovery Algorithm

  1. Extract trailing data located after IEND chunk.
  2. Find IDAT chunks (zlib compressed data) in the trailing part.
  3. Decompress zlib data while trying bit-shifts (trying bit alignments to find correct prefix).
  4. Use decompressed data to reconstruct PNG/bitmap with original dimensions (e.g. 2560×1600).

Implementation (Conceptual)

def reconstruct_image(cropped, output, orig_width, orig_height):
    # Open cropped PNG
    f = open(cropped, 'rb')
    magic = f.read(8)  # PNG signature
    
    # Iterate chunks until IEND
    while True:
        ctype, body = parse_png_chunk(f)
        if ctype == b"IEND":
            break
    # Read trailing part (data after IEND)
    trailer = f.read()
    
    # Find IDAT chunks in trailer
    idat = extract_idat_from_trailer(trailer)
    
    # Build bitstream from IDAT to try different alignments
    bitstream = build_bitstream(idat)
    
    # Try different bit offsets / byte shifts to find valid prefix
    for i in range(len(idat)):
        truncated = byte_offsets[i%8][i//8:]
        try:
            decompressed = decompress_with_prefix(truncated)
            if valid_parse(decompressed):
                break
        except:
            pass
    
    # Use decompressed data to create output image with original dimensions
    create_bmp(decompressed, orig_width, orig_height, output)

Note: In practice, recovery requires trying multiple alignments (both bit and byte level), validating PNG header after assembly, and verifying output image.

Result

Running the script recovered the original image with dimensions 2560×1600, and the flag is clearly visible on the image!

White Background ImageWhite Background Image

Flag

BPCTF{3v3ryth1ng_c0uld_be_4_CVE_i_gU355}

Message: "Everything could be a CVE" — even small bugs in screenshot tools!

Key Takeaways

  1. File Size Anomalies: Always check file size — PNG files too large/not matching dimensions might contain trailing data.
  2. Real-world CVEs: CVE-2023-21036 (Google Pixel), CVE-2023-28303 (Windows Snipping Tool).
  3. Understand PNG Structure: Knowing chunks (IHDR/IDAT/IEND) is crucial for recovery.
  4. Bit-shifting: Recovery sometimes requires testing multiple bit/byte alignments to decompress successfully.
  5. Security Impact: Small bugs in desktop tools can leak sensitive data.

References

250
Points
Medium
Difficulty
Forensics
Category