tags: stego challenge
by AvalZ

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.

Check MD5

You might want to check the MD5 hash for the file:

avalz@zenh~$ md5sum pngcrc.png

3e6bc1b4c62588810efd5ab7e40541d6 pngcrc.png

So far, so good.

Check File format

Now you might want to check the file format:

avalz@zenh~$ 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 be 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

From Wikipedia:

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:
type HEX DEC
width 00 00 00 C8 200
height 00 00 00 96 150
bit depth 01 1
color type 03 3
compression method 00 0
filter method 00 0
interlace method 00 0
  • PLTE: contains the palette (must appear in images with color type 3),
  • IDAT:
  • 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.

Chunk structure

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:

Field HEX Value
Length 00 00 00 06 6 (i.e. 2 colors)
Chunk Type 50 4C 54 45 “PLTE”
Data FF FF FF FF FF FF [ White, White ]
CRC 55 7C F5 6C (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):

avalz@zenh~$ dd bs=1 skip=97 count=10 if=pngcrc.png of=plte_type_and_data
10+0 records in
10+0 records out
10 bytes copied, 0,000142621 s, 70,1 kB/s

avalz@zenh~$ crc32 plte_type_and_data

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).

CRC32(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

Flag: RockOn_\m/