Windows 11 also vulnerable to “aCropalypse” image data leakage
Just yesterday, we wrote about a bug in Google Pixel phones, apparently now patched, with potentially dangerous consequences.
The bug finders, understandably excited (and concerned) by what they’d found, decided to follow the BWAIN principle for maximum, turning it into a Bug With An Impressive Name: aCropalypse.
In case you’re wondering, the word apocalypse literally means any sort of revelation, but it’s usually used to refer to the biblical text known as the Revelation of St. John, which portrays the end of the world.
Thus its metaphorical meaning, in the words of the New Oxford American Dictionary, is “an event involving destruction or damage on an awesome or catastrophic scale.”
We’re not quite convinced that this bug deserves quite such an, ahhhh, apocalyptic name, but we’re willing to concede that in a world where awesome can mean “quite good”, the name is probably acceptable, if not entirely unexceptionable.
The “Crop” in “aCropalypse”
The “crop” part of the name comes from the activity that is most likely to trigger the bug, dubbed CVE-2023-20136 in its Google incarnation: cropping photos or screenshots to remove sensitive or unwanted parts before you share them.
Loosely speaking, you can imagine that if you took, say, a 1080×1980 screenshot of your phone’s entire screen, you probably wouldn’t want to post the entire image online, or to send the whole thing to a friend.
Most people would prefer to crop off at least the top of the screenshot, thus removing details such as the name of their mobile provider, the date and the time.
And if you were snapping, say, an email or a social media posting in the middle of a list, you’d almost certainly want to obscure the emails or postings that appeared just above or just below the portion of interest.
Even after croppping the image, you might also want to redact parts of it (a jargon word meaning to obscure or censor part of a document), for example by dropping a black box over the sender’s name, email address, telephone number, or whatever.
At any rate, you might assume that if you chopped out chunks of the original, obscured some details with blocks of solid colour (which compress much more readily than regular image data), and saved the new image over the old one…
…that the new image would almost certainly be smaller, possibly much smaller, than the original.
Because of all the stuff you left out!
But that isn’t what happened on Google Pixel phones, at least until the March 2023 Android security update.
Overwritten but not truncated
The new, smaller, image file would be written over the start of the old one, but the file size would remain the same, and the now-redundant and unwanted data at the end of the original file would stay where it was.
If you sent that file to someone else and they opened it with a conventional image viewing or editing tool, their software would read the file until it reached a data chunk that said, “That’s it; you can stop now and ignore any trailing data in the file.”
In other words, the coding flaw that caused unwanted data to be left behind at the end of the file wouldn’t generally provoke any obvious errors, which presumably explains why the bug wasn’t spotted until recently.
But if the recipient opened it with a more inquisitive software tool, such as a hex editor or a cunningly modified image editor, anywhere from a few bytes to a vast amount of the original image would still be there, past the official end-of-image marker, waiting to be explored and potentially exposed.
Most screenshots are saved as PNG files, short for portable network graphics, and are internally compressed using a compression algorithm known commonly as deflate.
The left-over data therefore doesn’t look obviously like rows and columns of pixels, and it can’t be directly decompressed by conventional unpacking tools, which will consider the compressed data stream to be corrupt, which it is, and will usually refuse to try unpacking it at all.
But deflate compression typically squeezes its input data as a sequence of blocks, looking back only so far in the input for repeated text (32 Kbytes at most, for matches at most 258 bytes long) in order to reduce the amount of memory needed to run the algorithm.
Those restrictions aren’t just down to the fact that the format dates back to the 1990s, when memory space was much more precious than today.
By “resynchronising” the compressor on a regular basis, you also reduce the risk of losing absolutely everything in a compressed file if even just a few bytes at the start were to get corrupted.
Substantial reconstruction may be possible
This means that image files stored in compressed PNG format can often be substantially reconstructed, even if sizeable chunks of the original are overwritten or otherwise destroyed.
And if you’re talking about image fragments that can be reconstructed from a file that’s been cropped or redacted…
…there’s clearly a chance that the left-over data at the end, that was supposed to be chopped off, will contains recoverable image portions revealing the very parts you intended to remove permanently from the image!
You could get lucky, to be sure: if the image is stored row-by-row (so the data for top of the image is close to the start of the file, and the bottom is at the end), and you crop off the top of the image, you will probably end up with a new image consisting of the bottom half of the old image in the “official” part of the file, and the bottom half repeated in the left-over data that was supposed to be chopped off but wasn’t.
But if you crop off the bottom of the image, the new file will have the old top part “officially” re-encoded and written over the start, and the cropped-off bottom half of the image left behind exactly where it was before, in the unofficial end of the new file, waiting to be extracted by an attacker.
Windows 11 affected too
Well, the deal is that this problem of files not being truncated when they are replaced with new version also applies on Windows 11, where the Snipping Tool, like the Google Pixel Markup app, will let you crop an image without correctly cropping the file it’s saved into.
For example, here’s a PNG file we created with GIMP, and saved with a minimal set of headers and no compression:
The file is 320×200 pixels of 8-bit RGB data (three bytes per pixel), so the file is 320x200x3 bytes long (192,000), plus a few hundred bytes of header and other limited metadata, for a total size of 192,590 bytes.
In the illustrative hex dump below, you can see that the data is 0x20F04E bytes long, which is 192,590 in decimal:
We then cropped it as small as the Snipping Tool will allow (48×48 pixels seems to be the minimum) and saved it back over itself, but the “new” file ended up the same size as the uncompressed 320×200 file!
In the hex dump below, the portion highlighted in pink at the top is the entirety of what the cropped file is supposed to contain, at 0xBD bytes long, or 189 in decimal.
The new data concludes with an IEND data block, which is where the new file should end, but you can see it continues with the left-over data from before, ultimately finishing with a duplicate-but-now-redundant IEND block that has been carried over from the old file, along with almost all of its image data:
When we used the Save button to write it out under a brand new filename, the compressed 48×48 file did indeed come out at just 189 bytes long.
Note how the data in the file matches the 189 bytes highlighted in pink in the previous image:
The bug, therefore, is that saving a file back over an existing filename doesn’t truncate the old file first, and doesn’t create a new file with the expected size.
Simply put, the cropped file is partially overwritten, rather than actually replaced.
As mentioned above, we’re guessing that no one spotted this flaw until now because image viewing and editing programs read up until the first IEND tag (you can see this at the bottom right corner of the screenshot above), and silently ignore all the extra stuff at the end without reporting any anomalies or errors.
What to do?
- If you’re a Windows 11 user. Always save cropped files created with the Snipping Tool under a new filename, so there is no original content in it that can get left behind.
- If you’re a programmer. Review everywhere you create “new” files by overwriting old ones to make sure you really are truncating the original files when you open them for rewriting. Or only ever create new files by saving them to a genuinely new file first (use a securely-generated unique filename), then explicitly deleting the original file and renaming the new one.
By the way, we tested Microsoft Paint, and as far as we can see, that program will create cropped files with no left-over data from before, whether you use Save (to replace an existing file) or Save As (to produce a new one).
LEARN ABOUT FILE OPEN MODES FOR YOURSELF
Compile this code and run it.
On Windows, you can use minimalisti-C, our own curated build of the free Tiny C Compiler, if you don’t have a development system installed.
It’s under 500 KBytes in size (!), including full source code, compared to gigabytes each for Visual Studio or Clang for Windows.
Note the different between opening an existing file for writing (O_WRONLY) with and without setting the O_TRUNC flag.
Print out the contents of blah1.txt and blah2.txt after running the test program:
C:UsersduckCROP> petcc64 -stdinc -stdlib test.c Tiny C Compiler – Copyright (C) 2001-2023 Fabrice Bellard Stripped down by Paul Ducklin for use as a learning tool Version petcc64-0.9.27  – Generates 64-bit PEs only -> t1.c -> c:/users/duck/tcc/petccinc/fcntl.h . . . . -> C:/Windows/system32/msvcrt.dll -> C:/Windows/system32/kernel32.dll ——————————- virt file size section 1000 200 2a0 .text 2000 600 1cc .data 3000 800 18 .pdata ——————————- <- t1.exe (2560 bytes) C:UsersduckCROP> t1.exe C:UsersduckCROP>dir blah*.txt Volume in drive C has no label. Volume Serial Number is C001-D00D Directory of C:UsersduckCROP 22/03/2023 07:20 pm 26 blah1.txt 22/03/2023 07:20 pm 10 blah2.txt 2 File(s) 36 bytes C:UsersduckCROP> type blah1.txt ———-KLMNOPQRSTUVWXYZ C:UsersduckCROP> type blah2.txt ==========