# -*- coding: utf8 -*- """ .. module:: burpui.routes :platform: Unix :synopsis: Burp-UI routes module. .. moduleauthor:: Ziirish """ import re import math import uuid from flask import request, render_template, redirect, url_for, abort, \ flash, Blueprint, session, current_app, g from flask_login import login_user, login_required, logout_user, current_user from flask_babel import gettext as _, refresh as refresh_babel from .desc import __url__, __doc__ from .engines.server import BUIServer # noqa from .sessions import session_manager from ._compat import quote from .forms import LoginForm from .exceptions import BUIserverException from .utils import human_readable as _hr from .security import sanitize_string, is_safe_url 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/') @view.route('//calendar') @view.route('//calendar/') @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/') @view.route('//settings') @view.route('//settings/') @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/') @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/') @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/') @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/') @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/') @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-settings') @view.route('//client-settings/') @view.route('//client/client-settings') @view.route('///client-settings') @view.route('///client-settings/') @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('//live-monitor') @view.route('/live-monitor/') @view.route('//live-monitor/') @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/', methods=['GET']) @view.route('//edit-server-initiated-restore/', 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/', methods=['GET']) @view.route('//client-browse/', methods=['GET']) @view.route('/client-browse//') @view.route('//client-browse//') @view.route('/client-browse///') @view.route('//client-browse///') @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/') @view.route('//client-report/') @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('//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/', methods=['GET']) @view.route('//backup-report/', methods=['GET']) @view.route('/backup-report//', methods=['GET']) @view.route('//backup-report//', 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('//client', methods=['GET']) @view.route('/client/') @view.route('//client/') @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('//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'))