Unknown Soldier 2
Published on Mar 22, 2024 by Kalitsune.
Original Challenge description 🇫🇷
En ouvrant la boîte du soldat inconnu, qui n'est plus si inconnu que ça, vous tombez sur une image. Selon la description, il s'agit d'une photo du soldat fraîchement enrôlé. Vous vous dites que cela serait un beau geste que de l'envoyer à sa famille en tant que dernier souvenir. Malheureusement, elle a été endommagée, essayez de la réparer avant de l'envoyer.
Challenge description 🇺🇸
When you open the box of the not-so-unknown soldier, you come across an image. According to the description, it's a photo of the freshly enlisted soldier. You think it would be a nice gesture to send it to his family as a last memento. Unfortunately, it's been damaged, so you try to repair it before sending it.
Challenge inspection
The challenge isn’t necessarily provided with any file, however, in Unknown Soldier (1) we got a file named picture_of_me.png
. This file appears to be an image, however there’s one small issue: the file is not readable.
This is strange but not unexpected, we’re in a challenge of steganography after all.
I continue my inspection by using pngcheck
to try to find the issue.
❯ pngcheck picture_of_me.png
picture_of_me.png invalid IHDR image dimensions (25231972x0)
ERROR: picture_of_me.png
It appears that the image has invalid dimensions.
Investigating the issue with the image header
To check what’s wrong with the header, you’ll need to open the file using an hex editor.
I’m personally using GHex, a GNOME hex editor.
The first line of our file looks like this:
89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 01 81 02 64 00 00 00 00 08 06 00 00 00 F7 38 9D 88 00 00 00 04 67
I took the liberty to color the bits of data that are relevant.
Okay so, what are we seeing?
According to https://en.wikipedia.org/wiki/List_of_file_signatures, the signature for the PNG file format is 89 50 4E 47 0D 0A 1A 0A. Which is great because that’s exactly what we have! This mean that we are indeed looking at a PNG file.
After googling for a bit I also found the PNG Format Specifications according to which, a PNG chunk of data is organised this way:
[Length (4 byte)] [Chunk Type (4 byte)] [Data (any)] [CRC (4 byte)]The length value is not really useful for now so let’s skip it and check what’s the chunk type. I was able to find that the signature of my chunk ( 49 48 44 52 ), matched the signature of an IHDR Image header. Which is really interesting because according to the [PNG Format Specs](http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html, the IHDR chunk is the one containing the image size information.
Let’s take a closer look to the data in this chunk then:
DATA | Width | Height | Bit depth | Color type | Compression method | Filter method | Interlace method |
---|---|---|---|---|---|---|---|
Bytes | 4 | 4 | 1 | 1 | 1 | 1 | 1 |
In this field we find that the dimensions of this file are indeed corrupted. The rest of the data looks fine.
To fix them we’ll have to use the CRC bytes which allows partial repair in case of damage.
Getting the headers dimensions back
As mentioned earlier, the CRC field may just be able to help us repair this image as it is just like a hash and we only have two values to find.
We’ll just have to brute force the Height and Width of the image until we find a CRC that match:
from binascii import crc32
# Put your CRC as bytes here:
correct_crc = int.from_bytes(b'÷8',byteorder='big')
for h in range(2000):
for w in range(2000):
# IHDR HEADER WIDTH HEIGHT REMAINING DATA (healthy)
crc=b"IHDR"+w.to_bytes(4,byteorder='big')+h.to_bytes(4,byteorder='big')+b" "
# If it match the CRC
if crc32(crc) % (1<<32) == correct_crc:
print ('FOUND!')
print ('Width: ',end="")
print (hex(w))
print ('Height :',end="")
print (hex(h))
exit()
And hooray! We found something:
FOUND!
Width: 0x181
Height :0x264
This mean that by jumping back into the hex editor and editing the Width and Height fields, we can finally fix this image! Let’s edit our header right away:
89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 01 81 00 00 02 64 08 06 00 00 00 F7 38 9D 88 00 00 00 04 67
The only thing left to do is save.
I ran a quick pngcheck just to be sure:
❯ pngcheck fixed.png
OK: fixed.png (385x612, 32-bit RGB+alpha, non-interlaced, 55.7%).
And everything seemed good!
I then opened the file and was greeted by this:
Awesome! We did it! The flag is:
NBCTF{m0n_b3ll3_un1f0rm3}
As we say in french: Champagne!