Skip to main content
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 Image
White 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