Under the hood: Hiding data in JPEG images

Ever wondered how tools like ExifTool or stegano programs work under the hood? Ever wanted to create your own program to embed secret data into images?
In this is a short blog post on how to embed secret data in image files. This is something you can do as a party trick, some sort of CTF challenge or, more interestingly hide payloads or decryption keys.

The basics

In order to understand how to embed secret data in image files, you first need to understand how the JPEG file structure is build up.
Let’s take an example of an Image, thrown into a hex editor (such as HxD):

This image has an empty alt attribute; its file name is image-3.png

This hexdump goes on for a while and might look impressive if you are not used to this, but don’t worry. I’m going to explain the relevant parts and stick to that only. There are only a few important bytes to achieve what we are trying to do in this blog post.

FF xx bytes signify markers in the JPEG structure, markers are used for various things such as metadata, thumbnail generation, the beginning of the JPEG file, the ending of the JPEG file, and more.

For our objective, the following markers are of importance:

FF D8 => This is the marker that signifies the beginning of the JPEG data stream

FF D9 => This signifies the ending of the JPEG data stream

FF DA =>This one is a tad complex, but on a very high level this marker signifies the actual start of the “picture” in the JPEG.

As a result each of these 4 bytes will be present in literally any existing JPEG file, and this is useful information if you want to parse JPEG images and you need to figure out where they start and end.

Markers always follow the same convention (with the exception of the FF D8 marker):

FF => Beginning of marker
xx => litteraly any hex value (1 byte) to “identify” the marker.
xx xx => 2 bytes of data space signifying the size of the marker.
IMPORTANT: this total size MUST include these 2 bytes in itself as well! (example: if you need a size of 32 bytes, you’d actually need to allocate 34 bytes!)
DATA => all your data bytes

Note: There are some values already reserved. For a comprehensive list check https://www.disktuna.com/list-of-jpeg-markers/

Picking the right marker and a note on data overrides

The best bet we have to achieve our use case is to use one of the following marker values:

FF E2 – FF EF => application markers that are not being used to decode the JPEG image, these are usually used for metadata.

FF FE => this is a “comment” marker and is ignored by JPEG decoders as well.
These markers are exactly how we are going to insert our data and still have a valid image 🙂

Before you begin, you’ll have to know that if you start overriding data in another marker, you will break the image.
I would generally advise against overriding anything, except of you know what you are doing, the easiest thing to do is to just insert your payload instead.
The downside of this is that your image size will become larger as a direct result of the insertion.

This Is Where The Fun Begins. GIF by gorlah | Gfycat

Example: Hiding an AES-256 decryption key

An AES-256 decryption key would be a pretty nice use case for this scenario, AES-256 is a strong encryption algorithm, and hiding a key will not bloat an image as much as hiding an entire payload.

An AES-256 key requires a 32 letter sentence/word (1 character is 1 byte => 32 bytes = 256 bits, in case you are unfamiliar with bytes and bits) in this case I chose for the following key: ThisIsAsuperSecretDecryptionKey!
Which is exactly 32 bit, don’t believe me? maybe you’ll believe powershell:

This image has an empty alt attribute; its file name is image-1.png

Now to figure out the hex values of this key:

This image has an empty alt attribute; its file name is image-2.png

Now that we have a key of the correct length and the hex value of the key, we can construct our marker:

FF => Beginning of marker
FE => signifying we are making a “comment”.
00 22 => 2 bytes of data space signifying the size of the marker (34 is 00 22 in hex)
54686973497341537570657253656372657444656372797074696f6e4b657921 => (hex value of ThisIsAsuperSecretDecryptionKey!)

We can INSERT (again don’t override) this marker right before any other marker starts (marked with FF) before the FF DA marker (actual start of the image).

Can you spot the difference?

They look pretty identical to me… but what about now?

start hexdump unicorn 1

start of hexdump unicorn 2

Alternative approach + Detection

Another safe approach would be to append your payload after the end of image marker (FF D9) This is how most “steganography” programs work. You can even masquerade your payload by adding junk data afterwards so your payload isn’t just at the end of the hexdump. All that is left to do now is write a program that hunts for your decryption key in the image hexdump 🙂

Detecting this is pretty hard, as you would need to inspect all images that get downloaded in your organization, my advice would be if you start seeing indicators of compromise and you see a download to an image, hexdump the image and start looking at the markers (the FF bytes) especially right before the FF DA and right after the FF D9 markers.

About the author

Jean-François Maes is a red teaming and social engineering expert working in the NVISO Cyber Resilience team. When he is not working, you can probably find Jean-François in the Gym or conducting research. Apart from his work with NVISO, he is also the creator of redteamer.tips, a website dedicated to help red teamers.
He was also ranked #1 on the Belgian leaderboard of Hack The Box (a popular penetration testing platform).
You can find Jean-François on LinkedIn and on Hack The Box.

Leave a comment

Leave a Reply

%d bloggers like this: