Image of the glider from the Game of Life by John Conway
Skip to content

Steganography

I have been familiar with steganography for a number of years. In fact, back when I was in middle school, I developed a fascination for encryption, and hiding messages, mostly so I could pass notes back and forth to classmates during class. It wasn't long before I found "invisible ink", which is a form of steganography. While I'm certainly no expert on the subject, I decided to have a bit of fun with my email.

I placed a hidden message in my email headers for a bit (I've since stopped, for various reasons). I considered it an "Easter Egg" of sorts, waiting for someone to notice. Here is what I placed in the headers:

Crypto-Challenge: iVBORw0KGgoAAAANSUhEUgAAADwAAAA8AQMAAAAAMksxAAAABlBMVEX
       ///8AAABVwtN+AAAAtklEQVQokXXQMQ6CMBQG4McCiykXMPEKsuEiV2nCBdoLWNgN
       Xqld7MYZeoQSFgbisyZWER//9E1//vcAYhQOvkB0X3AQotBAoZ5lV9hpA63ZxCP5Q
       SiE5N28QpjRxT0rhFzi7BWUlx3LQvMH+x1jx7IEAoer8hVqR4Crhp1fhf9QI976tF
       oAIGlYWTkCCr3I7yTCpTkaDQTqWUhjtFtCNizNgEbbZxMFDnIYrHUEwjPRnywQiHk
       CI/3gDHrryF4AAAAASUVORK5CYII=
Crypto-Hint: image/png

Quickly, you should identify the "Crypto-Challenge" header as base 64-encoded string. The hint says it's an image, of type PNG. So, the following Python code should do the trick:

1
2
3
4
# assuming the 'img_string' variable is the actual base64 string above
f = open('crypto-image.png','w')
f.write(img_string.decode('base64'))
f.close()

Running that code with the base 64-encoded string above gives the following image:

Scanning the QR code reveals the text "42", of which most geeks should recognize as "The Answer to the Ultimate Question of Life, the Universe, and Everything".

Of course, steganography isn't encryption. It's security by obscurity, which isn't security, where a message is hidden by obscuring it through some means. Wikipedia has a great article on it at https://en.wikipedia.org/wiki/Steganography.

What can you do with hidden messages in images (or vice versa, as in the case with my email "Easter Egg")? Well, for one, you can easily get around email attachment restrictions. For example, take a ZIP archive. Perhaps some organization blocks email with .zip attachments. Why not convert the archive to base 64, then convert the result to an image. You might end up with something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from PIL import Image
import base64
import math

# function to return max image size
def get_size(size):
    width = height = int(math.ceil(math.sqrt(size/3)))
    diff = int(((width * height) * 3) - size)
    return (width, height, diff)

# open our binary non-image file
f = open('archive.zip','rb')

# convert the binary to a base64-encoded string
enc_bytes = base64.b64encode(f.read())
f.close()

# get file size to hold data (square)
(w,h,d) = get_size(len(enc_bytes))

# pad with zeros, if necessary
if d > 0:
    for i in range(d):
        enc_bytes += ('\0')

# create our final image
img = Image.frombuffer('RGB',(w,h),enc_bytes,'raw','RGB',0,1)
img.save('image.png')

Your final image might end up like:

In our case, I just created a file from /dev/urandom, zipped it up, and converted to an image. Thus, the reason the data in the image appears so random. More structured files will show actual structure in the final image. Also, notice the string of black at the bottom as a result of our padded zeros to adjust for a square image, without losing data.

Of course, to get back to the archive, you just need to reverse the process of converting the image to a base64 string, then back to the original file. Now, I'm no Python expert, and I realize there is much more to add to the code, such as "try/except" blocks for testing files, writable directories, etc. The point of the code was just to demonstrate an overall algorithm.

Hopefully, this is of some interest to some of my readers. I'm open to code improvements. Thanks to https://diablohorn.wordpress.com/2010/12/04/whats-in-a-picture for use of the code above.

{ 2 } Comments

  1. Thomas using Google Chrome 15.0.874.121 on GNU/Linux 64 bits | December 16, 2011 at 1:34 am | Permalink

    « More structured files will show actual structure in the final image » : not if it's a zip file. Compressed files always appear to be "random" (better entropy).

  2. Aaron Toponce using Google Chrome 17.0.963.6 on Mac OS | December 16, 2011 at 9:01 am | Permalink

    Yes, that is true. I probably should edit to post a bit to be more clear.

Post a Comment

Your email is never published nor shared.

Switch to our mobile site