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.