Try and get the flag hidden in this PNG file: pngcrc.png
- type: PNG
- md5: 3e6bc1b4c62588810efd5ab7e40541d6
Opening the downloaded file, you see a fully white image.
You might want to check the MD5 hash for the file:
[email protected]~$ md5sum pngcrc.png 3e6bc1b4c62588810efd5ab7e40541d6 pngcrc.png
So far, so good.
Check File format
Now you might want to check the file format:
[email protected]~$ file pngcrc.png pngcrc.png: PNG image data, 200 x 150, 1-bit colormap, non-interlaced
This output is quite easy to break down:
- pngcrc.png: file name (…duh!),
- PNG image data: …,
- 200 x 150: this is getting boring…,
- 1-bit colormap: finally! This means we have a Palette-based PNG file and the palette is 1-bit deep (i.e. has 2 slots). This will come in handy later, so keep it in mind!
- non-interlaced: this one means the image is (guess what!) not interlaced. Interlacing isn’t really important in this specific challenge, but non-interlaced it means pixels are “in the right order”, as in they appear in the file as they appear in the image, each row from left-to-right, from top to bottom.
Interlacing is a technique used to transfer images progressively on a slow network, starting with a low resolution image and working up to a full resolution.
If you are 1337, you can check the 8-bit signature of the file that should
89 50 4E 47 0D 0A 1A 0A:
89: detects transmissions that support 7-bit data instead of 8-bit data (backward compatibility),
50 4E 47: PNG in ASCII,
0D 0A: a DOS-style line ending (CR+LF),
1A: EOF char,
0A: a Unix-style line ending (LF).
Now you know it’s a PNG file! (Or you could’ve just read the title, but you were right not to trust me! *grin*)
Please note that, according to specification, integers in PNG files must be in network byte order, i.e. big endian! Always check endianess.
PNG File format
Portable Network Graphics (PNG /ˈpɪŋ/) is a raster graphics file format that supports lossless data compression.
…cool, I guess?
But here’s the real deal: after the 8-byte header section, PNG files are split in chunks of variable size.
Chunks are divided in critical and ancillary.
Critical chunk are:
- IHDR: contains, in this order:
- PLTE: contains the palette (must appear in images with color type 3),
- IEND: where the image ends.
Ancillary chunks may also appear in the image, as the sRGB chunk to indicate the standard sRBG color space being used or tIME chunk to indicate the time of the last edit on the image.
Each chunk is divided in 4 parts:
- Length: 4 bytes, the length of Chunk data in bytes;
- Chunk type: 4 bytes, type of Chunk in ASCII (PLTE, IDAT, ecc.);
- Chunk data: “length” bytes, the real data;
- CRC: 4 bytes, the CRC32 of (Chunk type || Chunk data).
Let’s dive into the PLTE chunk!
In a standard Bitmap image, each pixel is represented by a group of 3 bytes, representing its RGB value.
If colors are repeated in the image, the RGB value must be repeated too. A simple compression method for images is storing repeated colors in a palette and have each pixel reference a position in the palette instead of an RGB value.
As I told you above, the palette in a PNG file is located a chunk with type PLTE.
In this challenge, you have a 1-bit palette, which means you can use two colors in the image.
The palette chunk is located from 0x005D to 0x006E:
00 00 00 06 50 4C 54 45 FF FF FF FF FF FF 55 7C F5 6C
This is the chunk breakdown:
||6 (i.e. 2 colors)|
||[ White, White ]|
||(the CRC32 value)|
As we can see, we have 2 colors, but both are white! WTF??
Now back to the good part!
The palette has two identical colors. This should raise suspicion, since palette are used to avoid data duplication. NOTE: suspicion should come as a variant of “what if pixels referenced position 0 and position 1 forming a pattern, but since they both render as white I can’t see the pattern?”.
If so, you can change one of the colors with another distinct color (like
00 00 00, a.k.a. black) and have the
PNG render as different colors (as originally intended).
Change one palette color
You can use any Hex editor to do this, but if you don’t know what to choose, you can try hexcurse (just apt install it). The chunk should now look like this:
00 00 00 06 50 4C 54 45 FF FF FF 00 00 00 55 7C F5 6C
Nice! Now just open the image and you should… what? An error? What does it say?
Fatal error reading PNG image file: PLTE: CRC error
Oh right! Chunks are CRCed… that’s fine, we just have to calculate the CRC32 for the block!
Fixing the CRC
Remember you have to calculate it on both the Chunk Type and Data.
You can just create a temporary file and use the
crc32 Linux utility to calculate the CRC32 (or calculate it online):
The resulting file plte_type_and_data should contain:
50 4C 54 45 FF FF FF 00 00 00 (4 bytes for “PLTE”, 3 bytes for White, and 3 bytes for Black).
50 4C 54 45 FF FF FF 00 00 00) =
55 C2 D3 7E
This will be the value we have to substitute in the CRC32 field of the chunk.
The resulting PLTE chunk should look like this:
00 00 00 06 50 4C 54 45 FF FF FF 00 00 00 55 C2 D3 7E
This is the resulting file: pngcrc_solved.png