DEV Community

Cover image for How to hide files or data in a JPEG Image
Abdulmumin yaqeen
Abdulmumin yaqeen

Posted on • Updated on • Originally published at yaqeen.me

How to hide files or data in a JPEG Image

Trojan horses have been one of the most effective ways to get payloads on a target’s device, If you don’t know what a Trojan horse is — The Idea behind It Is that you hide payload behind what the target finds legitimate.

Recently I discovered cool stuff about JPEG images and figured we could attach additional Information or store data Inside of the JPEG, without distorting the Image.

In this tutorial, we will learn using Python as our programming language, how we can hide data in JPEG images, and also, how you can read the data you stored in the image.

Disclaimer: This tutorial has nothing to do with creating Trojan horses.

What is FFD9;

FFD9 or x0FFx0D9 It is one of the many markers in JPEG images that marks the end of the image (EOI). Other markers include FFD8 the start of the image, FFFE a comment, and soo much more. you can look up all available markers on the wikipedia page.

We will be taking advantage of the end marker i.e. FFD9, and add whatever we want behind it, anything from other images, videos, audios, executables, or any bit of data you can think of.

First, let's try adding a hello world to an Image, I will be using this example image (I promise it is not bugged 😀, you can trust me 🙂) for this demo. It is safe to download.

Naruto and his teacher jiraiya share a stick sweet. Abdulmumin yaqeen

Create a new Python file.

app.py

def inject(image, data):
    image_file = open(image, 'ab')
    image_file.write(data)
    image_file.close()
Enter fullscreen mode Exit fullscreen mode

Here, we have a straightforward function that takes the image and the data we want to append to the JPEG. Very straight forward stuff; we opened the Image in ab mode, appending bytes mode.

Adding hello world;

We can call our function and add:

inject('demo.jpg', b'Hello world')
Enter fullscreen mode Exit fullscreen mode

That’s it! Hello world now in our image, and if you’re to reopen your image, no damage has been made to it. Also, we’re adding a byte sequence instead of a string.

Good to know: In Python when you see a string preceded by a b, such as b'Hello world', it signifies that the string is represented as a sequence of bytes rather than a sequence of characters.

You might be thinking, “It's that simple”! Well, Yes! Writing the data is quite a piece of cake, but, we will do a little more to get our data out of the Image.

Reading our stored data;

In other to write a function that will return our stored data, This is where FFD9 is going to come to play.

  • We will read to Image to locate the position of FFD9

  • Then, seek that index (Place our cursor in the location, so our reading of the image will start from there.)

Here is what the function will look like:

def get_end_flag(image):
  image_file = open(image, 'rb')
  content = image_file.read()
  end_flag_index = content.indexof(bytes.fromhex('FFD9'))
  end_flag_offset = end_flag_index+2
  image_file.close()
  return end_flag_offset
Enter fullscreen mode Exit fullscreen mode

The reason we’re adding 2 (two) is to skip past the marker to read the next section of the data. In this case, adding 2 to end_flag_index would position our pointer just after the FFD9 marker, ready to process the next part of the data.

Our function to read the data will look like this:

def read_injected_data(image):
  image_file = open(image, 'rb')
  image_file.seek(get_end_flag(image))
  data = image_file.read()
  image_file.close()
  return data
Enter fullscreen mode Exit fullscreen mode

Yahoo! We’re are done with the program, Let start by reading our Hello World we Initially added.

data = read_injected_data(image)
print(data)
Enter fullscreen mode Exit fullscreen mode

There you have it: our Hello World and Image, both clean and Intact.

Abdulmumin yaqeen naruto yaqeen.me blog

A Problem 🤨;

We've got a problem, you see, In order to add more data to this image, we will first need to get rid of whatever we’ve added initially.

We can write a function to do this:

def clear_injected(image):
  image_file = open(image, 'rb')
  old_content = image_file.read()
  new_content = old_content[:get_end_flag(image)]
  with open(image, 'wb') as new_image:
    new_image.write(new_content)
  image.close()
Enter fullscreen mode Exit fullscreen mode

This function basically the original content of the Image, I.e the content from the beginning to the end flag, then rewrites it to the same file. So everything after the End Of Image flag (EOI) is lost.

We can test our function again and everything we’ve appended to the Image vanishes 🪄

clear_injected('image.jpg')
Enter fullscreen mode Exit fullscreen mode

Optionally, you can call clear_injected inside of the inject function, so it clears anything added to the Image, before appending any other thing.

def inject(image, data):
  clear_injected(image)
  ## rest of function
Enter fullscreen mode Exit fullscreen mode

Injecting media files;

As said earlier, Let's see how we can Inject media files into our Image. Since everything we write is a byte sequence, we can just read any file in bytes mode and inject that into our Image.

This will look something like this:

with open('somevid.mp4', 'rb') as media:
  inject('image.jpg', media.read())
Enter fullscreen mode Exit fullscreen mode

You can see that after appending the video into the image, the size have increased:

Abdulmumin yaqeen naruto yaqeen.me blog

We can easily fetch our video using:

data = read_injected_data('image.jpg')
with open('injected-video.mp4', 'wb') as file:
    file.write(data)
Enter fullscreen mode Exit fullscreen mode

There you have, how you can hide or store data into jpeg images, without sacrificing anything in the image details, the image works everywhere without a problem. This is a god mode kind of way of hiding stuff in your computer, without ever being noticed, the only catch is that, the larger the data you’re hiding in the image, the larger it size gets.

Comeback again, Peace ✌️!

Top comments (8)

Collapse
 
phlash profile image
Phil Ashby

If you enjoyed getting that to work, you might be interested in the work of renowned polyglot(0) expert and file format blender, Ange Albertini(1), aka Corkami. You may also be amused by the publication he contributes to that is often shipped as a polyglot file (it's a PDF, it can be executed as a program if renamed, it's an image if renamed..), however it's rather rude(2)!

0/ a polyglot is a single file that can be read as multiple different formats..
1/ github.com/angea
2/ github.com/angea/pocorgtfo

Enjoy!

Collapse
 
pstorch profile image
Peter Storch

My favorite trick is to binary-append a .zip file at the end of a .jpg
Depending of the file extension the right application is used to open it. Send it as .jpg and everyone just sees the image. Rename it to .zip, you can open it with an archive manager and access the files included in it.

Collapse
 
abdulmuminyqn profile image
Abdulmumin yaqeen

That super dope man 🤭, I never thought of such, would surely try 🫶🏾.

Collapse
 
mardeg profile image
Mardeg

The closest I've seen to this is an SVG with pixelart of a book cover but the audio wasn't exactly hidden when just clicking fetches it.

Collapse
 
michaeltharrington profile image
Michael Tharrington

Good stuff Abdulmumin! This is interesting. And appreciate the disclaimer btw. 🙂

Collapse
 
abdulmuminyqn profile image
Abdulmumin yaqeen

haha 😅, Thanks!

Collapse
 
joao9aulo profile image
João Paulo Martins Silva

Very cool, looks like a simple way to do steganography.

Collapse
 
abdulmuminyqn profile image
Abdulmumin yaqeen

yeah, thats it!