Last active
January 31, 2018 04:12
-
-
Save Und3rf10w/1158987ca570a64b5e5643ac0aef1dd4 to your computer and use it in GitHub Desktop.
Experiment at applying LSB stego to gifs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from PIL import Image | |
from cStringIO import StringIO | |
import requests | |
import imageio | |
import base64 | |
import re | |
def GetGifPixel(gif): | |
frame = Image.open(gif) | |
nframes = 0 | |
while frame: | |
print "On frame %d" %(nframes + 1) | |
pix = frame.load() | |
print "Upper left pixel for this frame is: %s" % (str(pix[0,0])) | |
nframes += 1 | |
del pix # yay memory cleanup! | |
try: | |
frame.seek(nframes) | |
except EOFError: | |
break | |
return True | |
def AnalyseGif(gif): | |
gif = Image.open(gif) | |
print "Analyzing gif..." | |
print "Gif contains %d frames" %(gif.n_frames) | |
print "Gif dimensions: %dx%d" %(gif.size[0], gif.size[1]) | |
print "This gif can store a total of %d bytes" %(gif.n_frames * (gif.size[1]-1)) | |
return True | |
def GenerateEvilGif(data, gif): | |
data = base64.urlsafe_b64encode(data) | |
data_list = [data[i:i+(len(gif[0]) - 1)] for i in range(0, len(data), (len(gif[0])) - 1)] | |
print "Spliting given data into %d chunks" % (len(data_list)) | |
gif[0][0,0][2] = len(data_list) | |
for frame in range(1, len(data_list) + 1): | |
for chunk in data_list: | |
for index, byte in enumerate(chunk): | |
gif[frame][index,index][2] = ord(byte) | |
print "Original data: %s" % (str(data)) | |
# gif right now is a list. Need to convert back to an image | |
# This is killing the gif | |
evil_gif_io = StringIO() | |
with imageio.get_writer(evil_gif_io, mode='I', format="GIF") as writer: | |
for image in gif: | |
writer.append_data(image) | |
return evil_gif_io | |
def RebuildData(gif): | |
rebuild_size = gif[0][0,0][2] | |
rebuild_list = [] | |
for frame in range(1, rebuild_size + 1): | |
for y in range(0, len(gif[0])): | |
rebuild_list.append(chr(gif[frame][y,y][2])) | |
rebuilt_string = ''.join(rebuild_list).strip('\0') | |
new_data = re.search(r'(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?', ''.join(rebuild_list)) | |
print "Reconstructed data: %s" % (str(new_data.group())) | |
return base64.urlsafe_b64decode(str(new_data.group())) | |
# Enoding and sending logic | |
# ------------ | |
eicar = 'X5O!P%@AP[4\PZX54(P^)7CC)7}' | |
eicar += '$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' | |
nyan_cat_link = "http://media3.giphy.com/media/sIIhZliB2McAo/giphy.gif" | |
nyan_cat_io = StringIO(requests.get(nyan_cat_link).content) | |
evil_list = imageio.mimread(nyan_cat_io) | |
evil_gif_io = GenerateEvilGif(eicar, evil_list) | |
# Now the recontructed gif in 'evil_gif_io' can be uploaded | |
# Receiving and decoding logic | |
# ------------ | |
# Let's pretend we transferred that gif somewhere, downloaded it (like how we got the nyan cat gif), | |
# converted it to an IO (exactly we did above), and saved it in evil_gif_io. We can extract the | |
# bytes like so: | |
# This is what it would look like in production, as you would have dowloaded this from the internet in evil_gif_io | |
# rebuilt_gif_list = imageio.mimread(evil_gif_io) | |
# Instead, here's a workaround to demonstrate this PoC | |
rebuilt_gif_list = imageio.mimread(evil_gif_io.getvalue()) | |
RebuildData(rebuilt_gif_list) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment