burp-ui/burpui/routes.py
2023-03-19 15:13:01 +01:00

602 lines
17 KiB
Python

# -*- coding: utf8 -*-
"""
.. module:: burpui.routes
:platform: Unix
:synopsis: Burp-UI routes module.
.. moduleauthor:: Ziirish <hi+burpui@ziirish.me>
"""
import math
import re
import uuid
from flask import (
Blueprint,
abort,
current_app,
flash,
g,
redirect,
render_template,
request,
session,
url_for,
)
from flask_babel import gettext as _
from flask_babel import refresh as refresh_babel
from flask_login import current_user, login_required, login_user, logout_user
from ._compat import quote
from .desc import __doc__, __url__
from .engines.server import BUIServer # noqa
from .exceptions import BUIserverException
from .forms import LoginForm
from .security import is_safe_url, sanitize_string
from .sessions import session_manager
from .utils import human_readable as _hr
bui = current_app # type: BUIServer
view = Blueprint("view", "burpui", template_folder="templates")
"""
Here are some custom filters
"""
@view.app_template_filter()
def mypad(value):
"""
Filter: used to pad 0's to backup numbers as in the burp's status monitor
"""
if not value:
return "0000000"
return "{0:07d}".format(int(value))
@view.app_template_filter()
def time_human(value):
"""Convert the input value to human readable time"""
string = ""
seconds = (((value % 31536000) % 86400) % 3600) % 60
minutes = math.floor((((value % 31536000) % 86400) % 3600) / 60)
hours = math.floor(((value % 31536000) % 86400) / 3600)
if hours > 0:
string = "%02dH" % hours
return "%s %02dm %02ds" % (string, minutes, seconds)
@view.app_template_filter()
def bytes_human(value):
"""Convert the input value to human readable bytes"""
return "{0:.1eM}".format(_hr(value))
@view.app_template_filter()
def regex_replace(value, regex, replace):
"""Replace every string matching the given regex with the replacement"""
return re.sub(regex, replace, value, flags=re.IGNORECASE)
"""
And here is the main site
"""
@view.route("/calendar")
@view.route("/calendar/<client>")
@view.route("/<server>/calendar")
@view.route("/<server>/calendar/<client>")
@login_required
def calendar(server=None, client=None):
server = server or request.args.get("serverName")
client = client or request.args.get("clientName")
if not bui.config["STANDALONE"]:
servers = not server and not client
clients = bool(server)
cli = bool(client)
else:
servers = False
cli = bool(client)
clients = not cli
return render_template(
"calendar.html",
calendar=True,
server=server,
cname=client,
client=cli,
servers=servers,
clients=clients,
)
@view.route("/settings")
@view.route("/settings/<path:conf>")
@view.route("/<server>/settings")
@view.route("/<server>/settings/<path:conf>")
@login_required
def settings(server=None, conf=None):
# Only the admin can edit the configuration
if (
not current_user.is_anonymous
and not current_user.acl.is_admin()
and not current_user.acl.is_moderator()
):
abort(403)
if not conf:
try:
conf = quote(request.args.get("conf"), safe="")
if conf:
return redirect(url_for(".settings", server=server, conf=conf))
except:
pass
server = server or request.args.get("serverName")
return render_template(
"settings.html",
settings=True,
is_admin=current_user.acl.is_admin(),
is_moderator=current_user.acl.is_moderator(),
server=server,
conf=conf,
ng_controller="ConfigCtrl",
)
@view.route("/admin/sessions/<user>")
@login_required
def admin_sessions(user):
# Only the admin can access this page
if (
not current_user.is_anonymous
and not current_user.acl.is_admin()
and not current_user.acl.is_moderator()
):
abort(403)
return render_template(
"admin/sessions.html",
admin=True,
sessions=True,
user=user,
ng_controller="AdminCtrl",
)
@view.route("/admin/authentication/<user>")
@login_required
def admin_authentication(user):
# Only the admin can access this page
if (
not current_user.is_anonymous
and not current_user.acl.is_admin()
and not current_user.acl.is_moderator()
):
abort(403)
backend = request.args.get("backend")
if not backend:
abort(400)
return render_template(
"admin/authentication.html",
admin=True,
authentication=True,
user=user,
backend=backend,
ng_controller="AdminCtrl",
)
@view.route("/admin/authorization/grant/<grant>")
@login_required
def admin_grant_authorization(grant):
# Only the admin can access this page
if (
not current_user.is_anonymous
and not current_user.acl.is_admin()
and not current_user.acl.is_moderator()
):
abort(403)
backend = request.args.get("backend")
if not backend:
abort(400)
return render_template(
"admin/grant-authorization.html",
admin=True,
authorization=True,
grant=grant,
backend=backend,
ng_controller="AdminCtrl",
)
@view.route("/admin/authorization/group/<group>")
@login_required
def admin_group_authorization(group):
# Only the admin can access this page
if (
not current_user.is_anonymous
and not current_user.acl.is_admin()
and not current_user.acl.is_moderator()
):
abort(403)
backend = request.args.get("backend")
if not backend:
abort(400)
return render_template(
"admin/group-authorization.html",
admin=True,
authorization=True,
group=group,
backend=backend,
ng_controller="AdminCtrl",
)
@view.route("/admin/backend/<backend>")
@login_required
def admin_backend(backend):
# Only the admin can access this page
if (
not current_user.is_anonymous
and not current_user.acl.is_admin()
and not current_user.acl.is_moderator()
):
abort(403)
return render_template(
"admin/backend.html",
admin=True,
onebackend=True,
backend=backend,
ng_controller="AdminCtrl",
)
@view.route("/admin/backends")
@login_required
def admin_backends():
# Only the admin can access this page
if (
not current_user.is_anonymous
and not current_user.acl.is_admin()
and not current_user.acl.is_moderator()
):
abort(403)
return render_template(
"admin-backends.html", admin=True, backends=True, ng_controller="AdminCtrl"
)
@view.route("/admin/authorizations")
@login_required
def admin_authorizations():
# Only the admin can access this page
if (
not current_user.is_anonymous
and not current_user.acl.is_admin()
and not current_user.acl.is_moderator()
):
abort(403)
return render_template(
"admin-authorizations.html",
admin=True,
authorizations=True,
ng_controller="AdminCtrl",
)
@view.route("/admin/authentications")
@login_required
def admin_authentications():
# Only the admin can access this page
if (
not current_user.is_anonymous
and not current_user.acl.is_admin()
and not current_user.acl.is_moderator()
):
abort(403)
return render_template(
"admin-authentications.html",
admin=True,
authentications=True,
ng_controller="AdminCtrl",
)
@view.route("/me")
@login_required
def me():
return render_template("user.html", me=True, ng_controller="UserCtrl")
@view.route("/client/client-settings")
@view.route("/<client>/client-settings")
@view.route("/<client>/client-settings/<path:conf>")
@view.route("/<server>/client/client-settings")
@view.route("/<server>/<client>/client-settings")
@view.route("/<server>/<client>/client-settings/<path:conf>")
@login_required
def cli_settings(server=None, client=None, conf=None):
# Only the admin can edit the configuration
if (
not current_user.is_anonymous
and not current_user.acl.is_admin()
and not current_user.acl.is_moderator()
):
abort(403)
if not conf:
try:
conf = quote(request.args.get("conf"), safe="")
if conf:
return redirect(
url_for(".cli_settings", server=server, client=client, conf=conf)
)
except:
pass
client = client or request.args.get("client")
server = server or request.args.get("serverName")
template = request.args.get("template") or False
statictemplate = request.args.get("statictemplate") or False
return render_template(
"settings.html",
settings=True,
client_mode=True,
template=template,
statictemplate=statictemplate,
client=client,
server=server,
conf=conf,
ng_controller="ConfigCtrl",
)
@view.route("/live-monitor")
@view.route("/<server>/live-monitor")
@view.route("/live-monitor/<name>")
@view.route("/<server>/live-monitor/<name>")
@login_required
def live_monitor(server=None, name=None):
"""Live status monitor view"""
server = server or request.args.get("serverName")
return render_template(
"live-monitor.html",
live=True,
cname=name,
server=server,
ng_controller="LiveCtrl",
)
@view.route("/edit-server-initiated-restore/<name>", methods=["GET"])
@view.route("/<server>/edit-server-initiated-restore/<name>", methods=["GET"])
@login_required
def edit_server_initiated_restore(server=None, name=None):
data = bui.client.is_server_restore(name, server)
to = None
if not data or not data["found"]:
flash(_("Sorry, there are no restore file found for this client"), "warning")
return redirect(url_for(".home"))
if data.get("orig_client"):
to = name
name = data["orig_client"]
return redirect(
url_for(
".client_browse",
server=server,
name=name,
backup=data["backup"],
edit=1,
to=to,
)
)
@view.route("/client-browse/<name>", methods=["GET"])
@view.route("/<server>/client-browse/<name>", methods=["GET"])
@view.route("/client-browse/<name>/<int:backup>")
@view.route("/<server>/client-browse/<name>/<int:backup>")
@view.route("/client-browse/<name>/<int:backup>/<int:encrypted>")
@view.route("/<server>/client-browse/<name>/<int:backup>/<int:encrypted>")
@login_required
def client_browse(server=None, name=None, backup=None, encrypted=None, edit=None):
"""Browse a specific backup of a specific client"""
if request.args.get("encrypted") == "1":
encrypted = 1
if request.args.get("edit") == "1":
to = request.args.get("to") or name
edit = bui.client.is_server_restore(to, server)
if not edit or not edit["found"]:
flash(
_("Sorry, there are no restore file found for this client"), "warning"
)
edit = None
else:
edit["roots"] = [x["key"] for x in edit["list"]]
server = server or request.args.get("serverName")
bkp = request.args.get("backup")
if bkp and not backup:
return redirect(
url_for(
".client_browse",
name=name,
backup=bkp,
encrypted=encrypted,
server=server,
)
)
return render_template(
"client-browse.html",
tree=True,
backup=True,
overview=True,
cname=name,
nbackup=backup,
encrypted=encrypted,
server=server,
edit=edit,
ng_controller="BrowseCtrl",
)
@view.route("/client-report/<name>")
@view.route("/<server>/client-report/<name>")
@login_required
def client_report(server=None, name=None):
"""Specific client report"""
server = server or request.args.get("serverName")
try:
res = bui.client.get_client(name, agent=server)
except BUIserverException:
res = []
if len(res) == 1:
return redirect(
url_for(".backup_report", name=name, backup=res[0]["number"], server=server)
)
return render_template(
"client-report.html", client=True, report=True, cname=name, server=server
)
@view.route("/clients-report")
@view.route("/<server>/clients-report")
@login_required
def clients_report(server=None):
"""Global report"""
server = server or request.args.get("serverName")
return render_template(
"clients-report.html", clients=True, report=True, server=server
)
@view.route("/backup-report/<name>", methods=["GET"])
@view.route("/<server>/backup-report/<name>", methods=["GET"])
@view.route("/backup-report/<name>/<int:backup>", methods=["GET"])
@view.route("/<server>/backup-report/<name>/<int:backup>", methods=["GET"])
@login_required
def backup_report(server=None, name=None, backup=None):
"""Backup specific report"""
backup = backup or request.args.get("backup")
server = server or request.args.get("serverName")
return render_template(
"backup-report.html",
client=True,
backup=True,
report=True,
cname=name,
nbackup=backup,
server=server,
)
@view.route("/client", methods=["GET"])
@view.route("/<server>/client", methods=["GET"])
@view.route("/client/<name>")
@view.route("/<server>/client/<name>")
@login_required
def client(server=None, name=None):
"""Specific client overview"""
c = name or request.args.get("name")
server = server or request.args.get("serverName")
# burp 2 allows to browse backups will backing up whereas burp 1 does not
if bui.client.version(server) == 1 and bui.client.is_backup_running(
c, agent=server
):
return redirect(url_for(".live_monitor", name=c, server=server))
return render_template(
"client.html", client=True, overview=True, cname=c, server=server
)
@view.route("/clients", methods=["GET"])
@view.route("/<server>/clients", methods=["GET"])
@login_required
def clients(server=None):
server = server or request.args.get("serverName")
return render_template("clients.html", clients=True, overview=True, server=server)
@view.route("/servers", methods=["GET"])
@login_required
def servers():
return render_template("servers.html", servers=True, overview=True)
@view.route("/servers-report", methods=["GET"])
@login_required
def servers_report():
return render_template("servers-report.html", servers=True, report=True)
@view.route("/login", methods=["POST", "GET"])
def login():
form = LoginForm(request.form)
if form.validate_on_submit():
# allow to switch to another backend
refresh = True
session["tag_id"] = uuid.uuid4()
session["language"] = form.language.data
session["_extra"] = g.now
user = bui.uhandler.user(form.username.data, refresh)
# at the time the context is loaded, the locale is not set
refresh_babel()
if user and user.is_active and user.login(form.password.data):
login_user(user, remember=form.remember.data)
flash(_("Logged in successfully"), "success")
session_manager.store_session(
form.username.data,
request.remote_addr,
request.headers.get("User-Agent"),
form.remember.data,
)
next_hop = request.args.get("next") or url_for(".home")
if is_safe_url(next_hop):
return redirect(next_hop)
return abort(400)
else:
flash(_("Wrong username or password"), "danger")
sanitized_user = repr(sanitize_string(form.username.data, paranoid=True))
msg = f"Wrong username or password attempted by {sanitized_user} at {request.remote_addr}"
if user:
audit = f"Wrong password for {sanitized_user} at {request.remote_addr}"
else:
audit = f"Unknown username {sanitized_user} at {request.remote_addr}"
bui.logger.critical(msg)
bui.audit.logger.critical(audit)
elif form.is_submitted():
flash(_("Wrong CSRF token, please try again"), "warning")
return render_template("login.html", form=form, login=True)
@view.route("/logout")
@login_required
def logout():
session_manager.delete_session()
# cleanup the session at logout to avoid further reuse by another user
session.clear()
logout_user()
return redirect(url_for(".home"))
@view.route("/about")
def about():
"""about view"""
return render_template(
"about.html",
about=True,
login=(not current_user.is_authenticated),
doc=__doc__,
url=__url__,
)
@view.route("/")
@login_required
def home():
"""Home page"""
if bui.config["STANDALONE"]:
return redirect(url_for(".clients"))
else:
server = request.args.get("serverName")
if server:
return redirect(url_for(".clients", server=server))
return redirect(url_for(".servers"))