Hide Data Inside an Image using LSB Steganography With Python

Least Significant Bit steganography with randomly chosen pixels

Images can be a great way to store hidden messages. We deal with hundreds or even thousands of images every day and, potentially, each one of them can contain some kind of hidden message. The interesting point is that we cannot detect visually if an image contains a message or not, because they are altered in such a way that our eyes won’t be able to notice.

So, without further ado, let’s get started.

Note: This post assumes a basic knowledge of Python, steganography and the Least Significant Bit technique. Before starting, if you don’t know anything about steganography go check it out on Wikipedia.

What is LSB technique?

When trying to hide information inside images, the simplest method is by using LSB steganography. LSB stands for Least Significant Bit, which basically means that we are going to use the last bit of each pixel to carry our hidden message.

LSB methods are extremely easy to implement and capable of hiding large quantities of information, but they also have a few drawbacks. Apart from being vulnerable to changes resulting from lossy compression or image processing, LSB methods are also vulnerable to steganalysis.

When trying this tool, we will use PNG images as carriers. JPEG uses a lossy compression algorithm, meaning that it brings to the loss of information and, as we are hiding messages, we don’t want to lose anything, not even one bit.

To level up confidentiality, there are two main methods:

  1. Encryption of the message: you need a key to understand the message
  2. Bits placement randomization: you need to know the seed used by a pseudo-random number generator algorithm to rebuild the message

In the following steps, I will use the second approach.

1. Demonstration

If you want to try the script I have built beforehand, I’ll show you two examples that you can run. First, be sure to have Python 3.6 installed. Then, install the following libraries using Pip:

$ pip install pillow numpy

Hiding some data:

$ python script.py --input_data "Hello World!" --output secret.png --carrier carrier.png enc 123345

Retrieving the message:

$ python script.py --input_image secret.png dec 123345

And you get the output:

All done!
The hidden message is:
Hello World!

2. Hiding data

Photo by Author

To hide data inside our image, we need 4 arguments:

  1. A seed, used for the pseudo-random number generator (PRNG). This is extremely important and must be known both from the sender and the receiver.
  2. input_data, contains the text that we want to hide
  3. output_file, the name of the image that we are going to create
  4. A carrier, which is basically the container used to hide our text

From line 3 to 5, we open the image using PIL.

Then, at line 6 we initialize the PRNG.

At line 7, we create a set to keep track of the pixels used to hide data. We need it to avoid choosing the same pixel multiple times.

The code below, is responsible for the hiding process.

Hiding data

At line 1, we randomly choose a pixel that will contain the next 3 bits of data. Then, we get the Least Significant Bit of every color integer (lines 5–12) and we use bit manipulation (lines 14–18) in order to modify it accordingly to the bit we want to hide. In particular, if the LSB is equal to the bit we want to hide, we leave it as it is. Otherwise, we flip it. Finally, we save the modified pixel (line 19). We repeat the process until every byte of our message will be written.

3. Retrieve hidden data

To retrieve hidden text inside an image, we essentially need 2 things:

  1. The seed used to initialize the PRNG while hiding
  2. input_file, the image file containing hidden data

This is the main part of the retrieving process:

Snippet taken from main.py with a focus on LSB retrieving function

Until the retrieving process is not done, we get the LSB from every pixel (line 8) and we convert every byte found to ASCII (remember: the right seed is needed to rebuild the hidden message).

If the last characters decoded are the ones used as End of File markers, it means that the hidden message is completely recovered (lines 18–19).

Final notes

This code is written for fun and learning purposes, so don’t use it for sensitive activities because it is not safe! Use it responsibly.

This post is also on my personal blog, go check it out if you want.

Thank you for your time 😉!

I’m a Computer Science student, with a passion for questioning things. Check my website to know more about me🌎: https://lorenzobn.github.io/