burp-ui/burpui/cli.py
2018-06-06 18:29:08 +02:00

1190 lines
39 KiB
Python

# -*- coding: utf8 -*-
"""
Burp-UI is a web-ui for burp backup written in python with Flask and
jQuery/Bootstrap
.. module:: burpui.cli
:platform: Unix
:synopsis: Burp-UI CLI module.
.. moduleauthor:: Ziirish <hi+burpui@ziirish.me>
"""
import os
import sys
import click
if os.getenv('BUI_MODE') in ['server', 'ws'] or 'websocket' in sys.argv:
try:
from gevent import monkey
monkey.patch_socket()
except ImportError:
pass
from .app import create_app # noqa
from .exceptions import BUIserverException # noqa
try:
from flask_socketio import SocketIO # noqa
WS_AVAILABLE = True
except ImportError:
WS_AVAILABLE = False
ROOT = os.path.dirname(os.path.realpath(__file__))
DEBUG = os.getenv('BUI_DEBUG') or os.getenv('FLASK_DEBUG') or False
DEBUG = DEBUG and DEBUG.lower() not in ['false', 'no', '0']
VERBOSE = os.getenv('BUI_VERBOSE') or 0
if VERBOSE:
try:
VERBOSE = int(VERBOSE)
except ValueError:
VERBOSE = 0
# UNITTEST is used to skip the burp-2 requirements for modes != server
UNITTEST = os.getenv('BUI_MODE') not in ['server', 'manage', 'celery', 'legacy', 'ws']
CLI = os.getenv('BUI_MODE') not in ['server', 'legacy']
app = create_app(
conf=os.environ.get('BUI_CONFIG'),
verbose=VERBOSE,
logfile=os.environ.get('BUI_LOGFILE'),
debug=DEBUG,
gunicorn=False,
unittest=UNITTEST,
cli=CLI,
websocket_server=(os.getenv('BUI_MODE') == 'ws' or 'websocket' in sys.argv)
)
try:
from .app import create_db
from .ext.sql import db
from flask_migrate import Migrate
# This may have been reseted by create_app
if isinstance(app.database, bool):
app.config['WITH_SQL'] = app.database
else:
app.config['WITH_SQL'] = app.database and \
app.database.lower() != 'none'
if app.config['WITH_SQL']:
create_db(app, True)
mig_dir = os.getenv('BUI_MIGRATIONS')
if mig_dir:
migrate = Migrate(app, db, mig_dir)
else:
migrate = Migrate(app, db)
except ImportError:
pass
def _die(error, appli=None):
appli = " '{}'".format(appli) if appli else ''
click.echo(
click.style(
'Unable to initialize the application{}: {}'.format(appli, error),
fg='red'
),
err=True
)
sys.exit(2)
@app.cli.command()
def legacy():
"""Legacy server for backward compatibility."""
click.echo(
click.style(
'If you want to pass options, you should run \'python -m burpui '
'-m legacy [...]\' instead',
fg='yellow'
)
)
app.manual_run()
@app.cli.command()
@click.option('-b', '--bind', default='127.0.0.1',
help='Which address to bind to for the websocket server')
@click.option('-p', '--port', default=5001,
help='Which port to listen on for the websocket server')
@click.option('-d', '--debug', default=False, is_flag=True,
help='Whether to start the websocket server in debug mode')
def websocket(bind, port, debug):
"""Start a new websocket server."""
try:
from .ext.ws import socketio
except ImportError:
_die('Missing requirement, did you ran \'pip install'
' "burp-ui[websocket]"\'?', 'websocket')
socketio.run(app, host=bind, port=port, debug=debug)
@app.cli.command()
@click.option('-b', '--backend', default='BASIC',
help='User Backend (default is BASIC).')
@click.option('-p', '--password', help='Password to assign to user.',
default=None)
@click.option('-a', '--ask', default=False, is_flag=True,
help='If no password is provided and this flag is enabled, '
'you\'ll be prompted for one, else a random one will be '
'generated.')
@click.option('-v', '--verbose', default=False, is_flag=True,
help='Add extra debug messages.')
@click.argument('name')
def create_user(backend, password, ask, verbose, name):
"""Create a new user."""
try:
msg = app.load_modules(True)
except Exception as e:
msg = str(e)
if msg:
_die(msg, 'create_user')
click.echo(click.style('[*] Adding \'{}\' user...'.format(name), fg='blue'))
try:
handler = getattr(app, 'uhandler')
except AttributeError:
handler = None
if not handler or len(handler.backends) == 0 or \
backend not in handler.backends:
click.echo(click.style('[!] No authentication backend found', fg='red'))
sys.exit(1)
back = handler.backends[backend]
if back.add_user is False:
click.echo(click.style("[!] The '{}' backend does not support user "
"creation".format(backend), fg='red'))
sys.exit(2)
if not password:
if ask:
import getpass
password = getpass.getpass()
confirm = getpass.getpass('Confirm: ')
if password != confirm:
click.echo(click.style("[!] Passwords mismatch", fg='red'))
sys.exit(3)
else:
import random
alphabet = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLM" \
"NOPQRSTUVWXYZ"
pw_length = 8
mypw = ""
for i in range(pw_length):
next_index = random.randrange(len(alphabet))
mypw += alphabet[next_index]
password = mypw
click.echo(
click.style(
'[+] Generated password: {}'.format(password),
fg='blue'
)
)
success, message, _ = back.add_user(name, password)
click.echo(click.style(
'[+] Success: {}{}'.format(
success, ' -> {}'.format(message) if verbose and message else ''
),
fg='green' if success else 'red')
)
@app.cli.command()
@click.option('-p', '--password', help='Password to assign to user.',
default=None)
@click.option('-u', '--username', help='Provide the username to get the full '
'configuration line.',
default=None)
@click.option('-b', '--batch', default=False, is_flag=True,
help='Don\'t be extra verbose so that you can use the output '
'directly in your scripts. Requires both -u and -p.')
def hash_password(password, username, batch):
"""Hash a given password to fill the configuration file."""
from werkzeug.security import generate_password_hash
if batch and (not username or not password):
click.echo(click.style(
'You need to provide both a username and a password using the '
'-u and -p flags!',
fg='red')
)
sys.exit(1)
askpass = False
if not password:
askpass = True
import getpass
password = getpass.getpass()
hashed = generate_password_hash(password)
if not batch:
click.echo("'{}' hashed into: {}".format(
password if not askpass else '*' * 8,
hashed)
)
if username:
if not batch:
click.echo(click.style(
'#8<{}'.format('-' * 77),
fg='blue')
)
click.echo('{} = {}'.format(username, hashed))
if not batch:
click.echo(click.style(
'#8<{}'.format('-' * 77),
fg='blue')
)
@app.cli.command()
@click.argument('language')
def init_translation(language):
"""Initialize a new translation for the given language."""
try:
import babel # noqa
except ImportError:
click.echo(
click.style('Missing i18n requirements, giving up', fg='yellow')
)
return
os.chdir(os.path.join(ROOT, '..'))
os.system('pybabel extract -F babel.cfg -k __ -k lazy_gettext -o messages.pot burpui')
os.system('pybabel init -i messages.pot -d burpui/translations -l {}'.format(language))
os.unlink('messages.pot')
@app.cli.command()
def update_translation():
"""Update translation files."""
try:
import babel # noqa
except ImportError:
click.echo(
click.style('Missing i18n requirements, giving up', fg='yellow')
)
return
os.chdir(os.path.join(ROOT, '..'))
os.system('pybabel extract -F babel.cfg -k __ -k lazy_gettext -o messages.pot burpui')
os.system('pybabel update -i messages.pot -d burpui/translations')
os.unlink('messages.pot')
@app.cli.command()
def compile_translation():
"""Compile translations."""
try:
import babel # noqa
except ImportError:
click.echo(
click.style('Missing i18n requirements, giving up', fg='yellow')
)
return
os.chdir(os.path.join(ROOT, '..'))
os.system('pybabel compile -f -d burpui/translations')
@app.cli.command()
@click.option('-b', '--burp-conf-cli', 'bconfcli', default=None,
help='Burp client configuration file')
@click.option('-s', '--burp-conf-serv', 'bconfsrv', default=None,
help='Burp server configuration file')
@click.option('-c', '--client', default='bui',
help='Name of the burp client that will be used by Burp-UI '
'(defaults to "bui")')
@click.option('-h', '--host', default='::1',
help='Address of the status server (defaults to "::1")')
@click.option('-r', '--redis', default=None,
help='Redis URL to connect to')
@click.option('-d', '--database', default=None,
help='Database to connect to for persistent storage')
@click.option('-p', '--plugins', default=None,
help='Plugins location')
@click.option('-n', '--dry', is_flag=True,
help='Dry mode. Do not edit the files but display changes')
def setup_burp(bconfcli, bconfsrv, client, host, redis, database, plugins, dry):
"""Setup burp client for burp-ui."""
if app.config['BACKEND'] != 'burp2':
click.echo(
click.style(
'Sorry, you can only setup the Burp 2 client',
fg='red'
),
err=True
)
sys.exit(1)
if not app.config['STANDALONE']:
click.echo(
click.style(
'Sorry, only the standalone mode is supported',
fg='red'
),
err=True
)
sys.exit(1)
try:
msg = app.load_modules(True)
except Exception as e:
msg = str(e)
if msg:
_die(msg, 'setup_burp')
from .misc.parser.utils import Config
from .app import get_redis_server
import difflib
import tempfile
parser = app.client.get_parser()
orig = source = None
conf_orig = []
if dry:
try:
with open(app.conf.options.filename) as fil:
conf_orig = fil.readlines()
except:
pass
orig = source = app.conf.options.filename
(_, temp) = tempfile.mkstemp()
app.conf.options.filename = temp
# handle migration of old config files
if app.conf.section_exists('Burp2'):
if app.conf.rename_section('Burp2', 'Burp', source):
click.echo(
click.style(
'Renaming old [Burp2] section',
fg='blue'
)
)
app.conf._refresh(True)
refresh = False
if not app.conf.lookup_section('Burp', source):
refresh = True
if not app.conf.lookup_section('Global', source):
refresh = True
if (database or redis) and not app.conf.lookup_section('Production', source):
refresh = True
if refresh:
app.conf._refresh(True)
def _edit_conf(key, val, attr, section='Burp', obj=app.client):
if val and (((key not in app.conf.options[section]) or
(key in app.conf.options[section] and
val != app.conf.options[section][key])) and
getattr(obj, attr) != val):
app.conf.options[section][key] = val
app.conf.options.write()
click.echo(
click.style(
'Adding new option: "{}={}" to section [{}]'.format(
key,
val,
section
),
fg='blue'
)
)
return True
return False
def _color_diff(line):
if line.startswith('+'):
return click.style(line, fg='green')
elif line.startswith('-'):
return click.style(line, fg='red')
elif line.startswith('^'):
return click.style(line, fg='blue')
return line
refresh = False
refresh |= _edit_conf('bconfcli', bconfcli, 'burpconfcli')
refresh |= _edit_conf('bconfsrv', bconfsrv, 'burpconfsrv')
refresh |= _edit_conf('plugins', plugins, 'plugins', 'Global', app)
if refresh:
app.conf._refresh(True)
if redis:
try:
# detect missing modules
import redis as redis_client # noqa
import celery # noqa
import socket
if ('redis' not in app.conf.options['Production'] or
'redis' in app.conf.options['Production'] and
app.conf.options['Production']['redis'] != redis) and \
app.redis != redis:
app.conf.options['Production']['redis'] = redis
rhost, rport, _ = get_redis_server(app)
ret = -1
for res in socket.getaddrinfo(rhost, rport, socket.AF_UNSPEC, socket.SOCK_STREAM):
if ret == 0:
break
af, socktype, proto, _, sa = res
try:
s = socket.socket(af, socktype, proto)
except socket.error:
continue
try:
ret = s.connect_ex(sa)
except:
continue
if ret == 0:
app.conf.options['Production']['celery'] = 'true'
app.conf.options['Production']['storage'] = 'redis'
app.conf.options['Production']['cache'] = 'redis'
else:
click.echo(
click.style(
'Unable to contact the redis server, disabling it',
fg='yellow'
)
)
app.conf.options['Production']['storage'] = 'default'
app.conf.options['Production']['cache'] = 'default'
if app.use_celery:
app.conf.options['Production']['celery'] = 'false'
app.conf.options.write()
app.conf._refresh(True)
except ImportError:
click.echo(
click.style(
'Unable to activate redis & celery. Did you ran the '
'\'pip install burp-ui[celery]\' and '
'\'pip install burp-ui[gunicorn-extra]\' commands first?',
fg='yellow'
)
)
if database:
try:
from .ext.sql import db # noqa
if ('database' not in app.conf.options['Production'] or
'database' in app.conf.options['Production'] and
app.conf.options['Production']['database'] != database) and \
app.database != database:
app.conf.options['Production']['database'] = database
app.conf.options.write()
app.conf._refresh(True)
except ImportError:
click.echo(
click.style(
'It looks like some dependencies are missing. Did you ran '
'the \'pip install "burp-ui[sql]"\' command first?',
fg='yellow'
)
)
if dry:
temp = app.conf.options.filename
app.conf.options.filename = orig
after = []
try:
if not os.path.exists(temp) or os.path.getsize(temp) == 0:
after = conf_orig
else:
with open(temp) as fil:
after = fil.readlines()
os.unlink(temp)
except:
pass
diff = difflib.unified_diff(conf_orig, after, fromfile=orig, tofile='{}.new'.format(orig))
out = ''
for line in diff:
out += _color_diff(line)
if out:
click.echo_via_pager(out)
bconfcli = bconfcli or app.conf.options['Burp'].get('bconfcli') or \
getattr(app.client, 'burpconfcli')
bconfsrv = bconfsrv or app.conf.options['Burp'].get('bconfsrv') or \
getattr(app.client, 'burpconfsrv')
dest_bconfcli = bconfcli
if not os.path.exists(bconfsrv):
click.echo(
click.style(
'Unable to locate burp-server configuration, aborting!',
fg='red'
),
err=True
)
sys.exit(1)
confsrv = Config(bconfsrv, parser, 'srv')
confsrv.set_default(bconfsrv)
confsrv.parse()
if host not in ['::1', '127.0.0.1']:
bind = confsrv.get('status_address')
if (bind and bind not in [host, '::', '0.0.0.0']) or not bind:
click.echo(
click.style(
'It looks like your burp server is not exposing it\'s '
'status port in a way that is reachable by Burp-UI!',
fg='yellow'
)
)
click.echo(
click.style(
'You may want to set the \'status_address\' setting with '
'either \'{}\', \'::\' or \'0.0.0.0\' in the {} file '
'in order to make Burp-UI work'.format(host, bconfsrv),
fg='blue'
)
)
status_port = confsrv.get('status_port', [4972])
if 'max_status_children' not in confsrv:
click.echo(
click.style(
'We need to set the number of \'max_status_children\'. '
'Setting it to 15.',
fg='blue'
)
)
confsrv['max_status_children'] = 15
status_port = status_port[0]
else:
max_status_children = confsrv.get('max_status_children')
found = False
for idx, value in enumerate(max_status_children):
if value >= 15:
found = True
if idx >= len(status_port):
status_port = status_port[-1]
else:
status_port = status_port[idx]
break
if not found:
click.echo(
click.style(
'We need to raise the number of \'max_status_children\'. '
'Raising it to 15 instead of {}.'.format(max_status_children),
fg='yellow'
)
)
confsrv['max_status_children'][-1] = 15
status_port = status_port[-1]
if 'restore_client' not in confsrv:
confsrv['restore_client'] = client
else:
restore = confsrv.getlist('restore_client')
if client not in restore:
confsrv['restore_client'].append(client)
confsrv['monitor_browse_cache'] = True
ca_client_dir = confsrv.get('ca_csr_dir')
if ca_client_dir and not os.path.exists(ca_client_dir):
try:
os.makedirs(ca_client_dir)
except IOError as exp:
click.echo(
click.style(
'Unable to create "{}" dir: {}'.format(ca_client_dir, exp),
fg='yellow'
),
err=True
)
if confsrv.dirty:
if dry:
(_, dstfile) = tempfile.mkstemp()
else:
dstfile = bconfsrv
confsrv.store(conf=bconfsrv, dest=dstfile, insecure=True)
if dry:
before = []
after = []
try:
with open(bconfsrv) as fil:
before = fil.readlines()
except:
pass
try:
with open(dstfile) as fil:
after = fil.readlines()
os.unlink(dstfile)
except:
pass
diff = difflib.unified_diff(before, after, fromfile=bconfsrv, tofile='{}.new'.format(bconfsrv))
out = ''
for line in diff:
out += _color_diff(line)
if out:
click.echo_via_pager(out)
if confsrv.get('clientconfdir'):
bconfagent = os.path.join(confsrv.get('clientconfdir'), client)
else:
click.echo(
click.style(
'Unable to find "clientconfdir" option, you will have to '
'setup the agent by your own',
fg='yellow'
)
)
bconfagent = os.devnull
if not os.path.exists(bconfcli):
clitpl = """
mode = client
port = 4971
status_port = 4972
server = ::1
password = abcdefgh
cname = {0}
protocol = 1
pidfile = /tmp/burp.client.pid
syslog = 0
stdout = 1
progress_counter = 1
network_timeout = 72000
server_can_restore = 0
cross_all_filesystems=0
ca_burp_ca = /usr/sbin/burp_ca
ca_csr_dir = /etc/burp/CA-client
ssl_cert_ca = /etc/burp/ssl_cert_ca-client-{0}.pem
ssl_cert = /etc/burp/ssl_cert-bui-client.pem
ssl_key = /etc/burp/ssl_cert-bui-client.key
ssl_key_password = password
ssl_peer_cn = burpserver
include = /home
exclude_fs = sysfs
exclude_fs = tmpfs
nobackup = .nobackup
exclude_comp=bz2
exclude_comp=gz
""".format(client)
if dry:
(_, dest_bconfcli) = tempfile.mkstemp()
with open(dest_bconfcli, 'w') as confcli:
confcli.write(clitpl)
parser = app.client.get_parser()
confcli = Config(dest_bconfcli, parser, 'srv')
confcli.set_default(dest_bconfcli)
confcli.parse()
if confcli.get('cname') != client:
confcli['cname'] = client
if confcli.get('server') != host:
confcli['server'] = host
if confcli.get('status_port')[0] != status_port:
c_status_port = confcli.get_raw('status_port')
c_status_port[0] = status_port
if confcli.dirty:
if dry:
(_, dstfile) = tempfile.mkstemp()
else:
dstfile = bconfcli
confcli.store(conf=bconfcli, dest=dstfile, insecure=True)
if dry:
before = []
after = []
try:
with open(bconfcli) as fil:
before = fil.readlines()
except:
pass
try:
with open(dstfile) as fil:
after = fil.readlines()
os.unlink(dstfile)
except:
pass
if dest_bconfcli != bconfcli:
# the file did not exist
os.unlink(dest_bconfcli)
before = []
diff = difflib.unified_diff(before, after, fromfile=bconfcli, tofile='{}.new'.format(bconfcli))
out = ''
for line in diff:
out += _color_diff(line)
if out:
click.echo_via_pager(out)
if not os.path.exists(bconfagent):
agenttpl = """
password = abcdefgh
"""
if not dry:
with open(bconfagent, 'w') as confagent:
confagent.write(agenttpl)
else:
before = []
after = ['{}\n'.format(x) for x in agenttpl.splitlines()]
diff = difflib.unified_diff(before, after, fromfile='None', tofile=bconfagent)
out = ''
for line in diff:
out += _color_diff(line)
if out:
click.echo_via_pager(out)
else:
confagent = Config(bconfagent, parser, 'cli')
confagent.set_default(bconfagent)
confagent.parse()
if confagent.get('password') != confcli.get('password'):
click.echo(
click.style(
'It looks like the passwords in the {} and the {} files '
'mismatch. Burp-UI will not work properly until you fix '
'this'.format(bconfcli, bconfagent),
fg='yellow'
)
)
@app.cli.command()
@click.option('-c', '--client', default='bui',
help='Name of the burp client that will be used by Burp-UI '
'(defaults to "bui")')
@click.option('-h', '--host', default='::1',
help='Address of the status server (defaults to "::1")')
@click.option('-t', '--tips', is_flag=True,
help='Show you some tips')
def diag(client, host, tips):
"""Check Burp-UI is correctly setup."""
if app.config['BACKEND'] != 'burp2':
click.echo(
click.style(
'Sorry, you can only setup the Burp 2 client',
fg='red'
),
err=True
)
sys.exit(1)
if not app.config['STANDALONE']:
click.echo(
click.style(
'Sorry, only the standalone mode is supported',
fg='red'
),
err=True
)
sys.exit(1)
try:
msg = app.load_modules(True)
except Exception as e:
msg = str(e)
if msg:
_die(msg, 'diag')
from .misc.parser.utils import Config
from .app import get_redis_server
if 'Production' in app.conf.options and \
'redis' in app.conf.options['Production']:
try:
# detect missing modules
import redis as redis_client # noqa
import celery # noqa
import socket
rhost, rport, _ = get_redis_server(app)
ret = -1
for res in socket.getaddrinfo(rhost, rport, socket.AF_UNSPEC, socket.SOCK_STREAM):
if ret == 0:
break
af, socktype, proto, _, sa = res
try:
s = socket.socket(af, socktype, proto)
except socket.error:
continue
try:
ret = s.connect_ex(sa)
except:
continue
if ret != 0:
click.echo(
click.style(
'Unable to contact the redis server, disabling it',
fg='yellow'
)
)
except ImportError:
click.echo(
click.style(
'Unable to activate redis & celery. Did you ran the '
'\'pip install "burp-ui[celery]"\' and '
'\'pip install "burp-ui[gunicorn-extra]"\' commands first?',
fg='yellow'
)
)
if 'Production' in app.conf.options and \
'database' in app.conf.options['Production']:
try:
from .ext.sql import db # noqa
except ImportError:
click.echo(
click.style(
'It looks like some dependencies are missing. Did you ran '
'the \'pip install "burp-ui[sql]"\' command first?',
fg='yellow'
)
)
section = 'Burp'
if not app.conf.section_exists(section):
click.echo(
click.style(
'Section [Burp] not found, looking for the old [Burp2] section '
'instead.',
fg='yellow'
)
)
section = 'Burp2'
if not app.conf.section_exists(section):
click.echo(
click.style(
'No [Burp*] section found at all!',
fg='red'
)
)
section = 'Burp'
bconfcli = app.conf.options.get(section, {}).get('bconfcli') or \
getattr(app.client, 'burpconfcli')
bconfsrv = app.conf.options.get(section, {}).get('bconfsrv') or \
getattr(app.client, 'burpconfsrv')
try:
app.client.status()
except Exception as e:
if 'Unable to spawn burp process' in str(e):
try:
app.client._spawn_burp(verbose=True)
except Exception as e:
msg = str(e)
else:
msg = str(e)
if msg:
click.echo(click.style(msg, fg='red'))
if 'could not connect' in msg:
click.echo(
click.style(
'It looks like your burp-client can not reach your '
'burp-server. Please check both your \'server\' setting in '
'your \'{}\' file and \'status_address\' in your \'{}\' '
'file.'.format(bconfcli, bconfsrv),
fg='yellow'
)
)
errors = False
if os.path.exists(bconfcli):
parser = app.client.get_parser()
confcli = Config(bconfcli, parser, 'srv')
confcli.set_default(bconfcli)
confcli.parse()
if confcli.get('cname') != client:
click.echo(
click.style(
'The cname of your burp client does not match: '
'{} != {}'.format(confcli.get('cname'), client),
fg='yellow'
)
)
errors = True
if confcli.get('server') != host:
click.echo(
click.style(
'The burp server address does not match: '
'{} != {}'.format(confcli.get('server'), host),
fg='yellow'
)
)
errors = True
else:
click.echo(
click.style(
'No client conf file found: {} does not exist'.format(bconfcli),
fg='red'
),
err=True
)
errors = True
if os.path.exists(bconfsrv):
parser = app.client.get_parser()
confsrv = Config(bconfsrv, parser, 'srv')
confsrv.set_default(bconfsrv)
confsrv.parse()
if host not in ['::1', '127.0.0.1']:
bind = confsrv.get('status_address')
if (bind and bind not in [host, '::', '0.0.0.0']) or not bind:
click.echo(
click.style(
'It looks like your burp server is not exposing it\'s '
'status port in a way that is reachable by Burp-UI!',
fg='yellow'
)
)
click.echo(
click.style(
'You may want to set the \'status_address\' setting with '
'either \'{}\', \'::\' or \'0.0.0.0\' in the {} file '
'in order to make Burp-UI work'.format(host, bconfsrv),
fg='blue'
)
)
max_status_children = confsrv.get('max_status_children', [-1])
if all([x < 15 for x in max_status_children]):
click.echo(
click.style(
'\'max_status_children\' is to low, you need to set it to '
'15 or more. Please edit your {} file.'.format(bconfsrv),
fg='blue'
)
)
errors = True
restore = []
if 'restore_client' in confsrv:
restore = confsrv.getlist('restore_client')
if client not in restore:
click.echo(
click.style(
'Your burp client is not listed as a \'restore_client\'. '
'You won\'t be able to view other clients stats!',
fg='yellow'
)
)
errors = True
if 'monitor_browse_cache' not in confsrv or not \
confsrv.get('monitor_browse_cache'):
click.echo(
click.style(
'For performance reasons, it is recommended to enable the '
'\'monitor_browse_cache\'.',
fg='yellow'
)
)
errors = True
ca_client_dir = confsrv.get('ca_csr_dir')
if ca_client_dir and not os.path.exists(ca_client_dir):
try:
os.makedirs(ca_client_dir)
except IOError as exp:
click.echo(
click.style(
'Unable to create "{}" dir: {}'.format(ca_client_dir, exp),
fg='yellow'
),
err=True
)
if confsrv.get('clientconfdir'):
bconfagent = os.path.join(confsrv.get('clientconfdir'), client)
else:
click.echo(
click.style(
'Unable to find "clientconfdir" option. Something is wrong '
'with your setup.',
fg='yellow'
)
)
bconfagent = 'ihopethisfiledoesnotexistbecauseitisrelatedtoburpui'
if not os.path.exists(bconfagent) and bconfagent.startswith('/'):
click.echo(
click.style(
'Unable to find the {} file.'.format(bconfagent),
fg='yellow'
)
)
errors = True
else:
confagent = Config(bconfagent, parser, 'cli')
confagent.set_default(bconfagent)
confagent.parse()
if confagent.get('password') != confcli.get('password'):
click.echo(
click.style(
'It looks like the passwords in the {} and the {} files '
'mismatch. Burp-UI will not work properly until you fix '
'this.'.format(bconfcli, bconfagent),
fg='yellow'
)
)
else:
click.echo(
click.style(
'Unable to locate burp-server configuration: {} does not '
'exist.'.format(bconfsrv),
fg='red'
),
err=True
)
errors = True
if errors:
if not tips:
click.echo(
click.style(
'Some errors have been found in your configuration. '
'Please make sure you ran this command with the right flags! '
'(see --help for details).'.format(sys.argv[0], sys.argv[1]),
fg='red'
),
err=True
)
else:
click.echo(
click.style(
'\n'
'Well, if you are sure about your settings, you can run the '
'following command to help you setup your Burp-UI agent. '
'(Note, the \'--dry\' flag is here to show you the '
'modifications that will be applied. Once you are OK with '
'those, you can re-run the command without the \'--dry\' flag):',
fg='blue'
)
)
click.echo(' > bui-manage setup_burp --host="{}" --client="{}" --dry'.format(host, client))
else:
click.echo(
click.style(
'Congratulations! It seems everything is alright. Burp-UI '
'should run without any issue now.',
fg='green'
)
)
@app.cli.command()
@click.option('-v', '--verbose', is_flag=True,
help='Dump parts of the config (Please double check no sensitive'
' data leaked)')
@click.option('-l', '--load', is_flag=True,
help='Load all configured modules for full summary')
def sysinfo(verbose, load):
"""Returns a couple of system informations to help debugging."""
from .desc import __release__, __version__
import platform
msg = None
if load:
try:
msg = app.load_modules(True)
except Exception as e:
msg = str(e)
backend_version = app.config['BACKEND']
colors = {
'True': 'green',
'False': 'red',
}
embedded_ws = str(app.config['WITH_WS'])
available_ws = str(WS_AVAILABLE)
click.echo('Python version: {}.{}.{}'.format(sys.version_info[0], sys.version_info[1], sys.version_info[2]))
click.echo('Burp-UI version: {} ({})'.format(__version__, __release__))
click.echo('OS: {}:{} ({})'.format(platform.system(), platform.release(), os.name))
if platform.system() == 'Linux':
click.echo('Distribution: {} {} {}'.format(*platform.dist()))
click.echo('Single mode: {}'.format(app.config['STANDALONE']))
click.echo('Backend version: {}'.format(backend_version))
click.echo('WebSocket embedded: {}'.format(click.style(embedded_ws, fg=colors[embedded_ws])))
click.echo('WebSocket available: {}'.format(click.style(available_ws, colors[available_ws])))
click.echo('Config file: {}'.format(app.config.conffile))
if load:
if not app.config['STANDALONE'] and not msg:
click.echo('Agents:')
for agent, obj in app.client.servers.items():
client_version = server_version = 'unknown'
try:
app.client.status(agent=agent)
client_version = app.client.get_client_version(agent=agent)
server_version = app.client.get_server_version(agent=agent)
except BUIserverException:
pass
alive = obj.ping()
if alive:
status = click.style('ALIVE', fg='green')
else:
status = click.style('DISCONNECTED', fg='red')
click.echo(' - {} ({})'.format(agent, status))
click.echo(' * client version: {}'.format(client_version))
click.echo(' * server version: {}'.format(server_version))
elif not msg:
server_version = 'unknown'
try:
app.client.status()
server_version = app.client.get_server_version()
except BUIserverException:
pass
click.echo('Burp client version: {}'.format(app.client.client_version))
click.echo('Burp server version: {}'.format(server_version))
if verbose:
click.echo('>>>>> Extra verbose informations:')
click.echo(click.style(
'!!! PLEASE MAKE SURE NO SENSITIVE DATA GET EXPOSED !!!',
fg='red'
))
sections = [
'WebSocket',
'Burp',
'Production',
'Global',
]
sections.reverse()
for section in sections:
if section in app.config.options:
click.echo()
click.echo(' 8<{}BEGIN[{}]'.format('-' * (67 - len(section)), section))
for key, val in app.config.options.get(section, {}).items():
click.echo(' {} = {}'.format(key, val))
click.echo(' 8<{}END[{}]'.format('-' * (69 - len(section)), section))
if load and msg:
_die(msg, 'sysinfo')