mirror of
https://github.com/ziirish/burp-ui.git
synced 2026-05-21 06:45:24 -06:00
improve logger
This commit is contained in:
parent
98daf70198
commit
c763d12306
9 changed files with 130 additions and 63 deletions
|
|
@ -14,7 +14,8 @@ if __name__ == '__main__':
|
|||
Main function
|
||||
"""
|
||||
parser = OptionParser()
|
||||
parser.add_option('-v', '--verbose', dest='log', help='verbose output', action='store_true')
|
||||
parser.add_option('-v', '--verbose', dest='log', help='increase output verbosity (e.g., -vv is more than -v)', action='count')
|
||||
parser.add_option('-l', '--logfile', dest='logfile', help='where to store logs', metavar='LOGFILE')
|
||||
parser.add_option('-c', '--config', dest='config', help='configuration file', metavar='CONFIG')
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
|
@ -42,5 +43,5 @@ if __name__ == '__main__':
|
|||
conf = p
|
||||
break
|
||||
|
||||
agent = Agent(conf, options.log)
|
||||
agent = Agent(conf, options.log, options.logfile)
|
||||
agent.run()
|
||||
|
|
|
|||
109
burpui/agent.py
109
burpui/agent.py
|
|
@ -6,13 +6,18 @@ try:
|
|||
import ujson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
import sys
|
||||
import logging
|
||||
import pickle
|
||||
import traceback
|
||||
import ConfigParser
|
||||
import SocketServer
|
||||
from threading import Thread
|
||||
from logging import Formatter, StreamHandler
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from burpui.misc.utils import BUIlogging
|
||||
|
||||
g_port = '10000'
|
||||
g_bind = '::'
|
||||
|
|
@ -24,13 +29,35 @@ g_timeout = '5'
|
|||
g_password = 'password'
|
||||
|
||||
|
||||
class BUIAgent:
|
||||
def __init__(self, conf=None, debug=False):
|
||||
class BUIAgent(BUIlogging):
|
||||
def __init__(self, conf=None, debug=False, logfile=None):
|
||||
global g_port, g_bind, g_ssl, g_version, g_sslcert, g_sslkey, g_password
|
||||
self.conf = conf
|
||||
self.dbg = debug
|
||||
print 'conf: ' + self.conf
|
||||
print 'debug: ' + str(self.dbg)
|
||||
self.logger = None
|
||||
if debug > logging.NOTSET:
|
||||
levels = [0, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
|
||||
if debug >= len(levels):
|
||||
debug = len(levels) - 1
|
||||
lvl = levels[debug]
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger.setLevel(lvl)
|
||||
if logfile:
|
||||
handler = RotatingFileHandler(logfile, maxBytes=1024 * 1024 * 100, backupCount=20)
|
||||
LOG_FORMAT = '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
|
||||
else:
|
||||
handler = StreamHandler()
|
||||
LOG_FORMAT = (
|
||||
'-' * 80 + '\n' +
|
||||
'%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n' +
|
||||
'%(message)s\n' +
|
||||
'-' * 80
|
||||
)
|
||||
handler.setLevel(lvl)
|
||||
handler.setFormatter(Formatter(LOG_FORMAT))
|
||||
self.logger.addHandler(handler)
|
||||
self.logger.info('conf: ' + self.conf)
|
||||
self.logger.info('level: ' + logging.getLevelName(lvl))
|
||||
if not conf:
|
||||
raise IOError('No configuration file found')
|
||||
|
||||
|
|
@ -42,18 +69,18 @@ class BUIAgent:
|
|||
with open(conf) as fp:
|
||||
config.readfp(fp)
|
||||
try:
|
||||
self.port = config.getint('Global', 'port')
|
||||
self.bind = config.get('Global', 'bind')
|
||||
self.vers = config.getint('Global', 'version')
|
||||
self.timeout = config.getint('Global', 'timeout')
|
||||
self.port = self._safe_config_get(config.getint, 'port', cast=int)
|
||||
self.bind = self._safe_config_get(config.get, 'bind')
|
||||
self.vers = self._safe_config_get(config.getint, 'version', cast=int)
|
||||
self.timeout = self._safe_config_get(config.getint, 'timeout', cast=int)
|
||||
try:
|
||||
self.ssl = config.getboolean('Global', 'ssl')
|
||||
except ValueError:
|
||||
self.app.logger.error("Wrong value for 'ssl' key! Assuming 'false'")
|
||||
self.ssl = False
|
||||
self.sslcert = config.get('Global', 'sslcert')
|
||||
self.sslkey = config.get('Global', 'sslkey')
|
||||
self.password = config.get('Global', 'password')
|
||||
self.sslcert = self._safe_config_get(config.get, 'sslcert')
|
||||
self.sslkey = self._safe_config_get(config.get, 'sslkey')
|
||||
self.password = self._safe_config_get(config.get, 'password')
|
||||
except ConfigParser.NoOptionError, e:
|
||||
raise e
|
||||
|
||||
|
|
@ -62,6 +89,7 @@ class BUIAgent:
|
|||
mod = __import__(module, fromlist=['Burp'])
|
||||
Client = mod.Burp
|
||||
self.backend = Client(conf=conf)
|
||||
self.backend.set_logger(self.logger)
|
||||
except Exception, e:
|
||||
traceback.print_exc()
|
||||
self.debug('Failed loading backend for Burp version %d: %s', self.vers, str(e))
|
||||
|
|
@ -89,22 +117,55 @@ class BUIAgent:
|
|||
|
||||
self.server = AgentServer((self.bind, self.port), AgentTCPHandler, self)
|
||||
|
||||
def _safe_config_get(self, callback, key, sect='Global', cast=None):
|
||||
"""
|
||||
:func:`burpui.agent._safe_config_get` is a wrapper to handle
|
||||
Exceptions throwed by :mod:`ConfigParser`.
|
||||
|
||||
:param callback: Function to wrap
|
||||
:type callback: callable
|
||||
|
||||
:param key: Key to retrieve
|
||||
:type key: str
|
||||
|
||||
:param sect: Section of the config file to read
|
||||
:type sect: str
|
||||
|
||||
:param cast: Cast the returned value if provided
|
||||
:type case: callable
|
||||
|
||||
:returns: The value returned by the `callback`
|
||||
"""
|
||||
try:
|
||||
return callback(sect, key)
|
||||
except ConfigParser.NoOptionError as e:
|
||||
self._logger('error', str(e))
|
||||
except ConfigParser.NoSectionError as e:
|
||||
self._logger('warning', str(e))
|
||||
if key in self.defaults:
|
||||
if cast:
|
||||
return cast(self.defaults[key])
|
||||
return self.defaults[key]
|
||||
return None
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(0)
|
||||
|
||||
def debug(self, msg, *args):
|
||||
if self.dbg:
|
||||
print msg % (args)
|
||||
def _logger(self, level, message):
|
||||
# hide password from logs
|
||||
msg = message
|
||||
if self.logger.getEffectiveLevel() != logging.DEBUG:
|
||||
msg = re.sub(r'"password": \S+', '"password": "*****",', message)
|
||||
super(BUIAgent, self)._logger(level, msg)
|
||||
|
||||
|
||||
class AgentTCPHandler(SocketServer.BaseRequestHandler):
|
||||
"One instance per connection. Override handle(self) to customize action."
|
||||
def handle(self):
|
||||
# self.request is the client connection
|
||||
self.server.agent.debug('===============>')
|
||||
timeout = self.server.agent.timeout
|
||||
try:
|
||||
err = None
|
||||
|
|
@ -114,19 +175,17 @@ class AgentTCPHandler(SocketServer.BaseRequestHandler):
|
|||
lengthbuf = self.request.recv(8)
|
||||
length, = struct.unpack('!Q', lengthbuf)
|
||||
data = self.recvall(length)
|
||||
self.server.agent.debug('####################')
|
||||
self.server.agent.debug('recv: %s', data)
|
||||
self.server.agent.debug('####################')
|
||||
self.server.agent._logger('info','recv: {}'.format(data))
|
||||
j = json.loads(data)
|
||||
_, w, _ = select.select([], [self.request], [], timeout)
|
||||
if not w:
|
||||
raise Exception('Socket timed-out 2')
|
||||
if j['password'] != self.server.agent.password:
|
||||
self.server.agent.debug('-----> Wrong Password <-----')
|
||||
self.server.agent._logger('warning', '-----> Wrong Password <-----')
|
||||
self.request.sendall('KO')
|
||||
return
|
||||
if j['func'] not in self.server.agent.methods:
|
||||
self.server.agent.debug('-----> Wrong method <-----')
|
||||
self.server.agent._logger('warning', '-----> Wrong method <-----')
|
||||
self.request.sendall('KO')
|
||||
return
|
||||
self.request.sendall('OK')
|
||||
|
|
@ -140,9 +199,7 @@ class AgentTCPHandler(SocketServer.BaseRequestHandler):
|
|||
res = json.dumps(self.server.agent.methods[j['func']](**j['args']))
|
||||
else:
|
||||
res = json.dumps(self.server.agent.methods[j['func']]())
|
||||
self.server.agent.debug('####################')
|
||||
self.server.agent.debug('result: %s', res)
|
||||
self.server.agent.debug('####################')
|
||||
self.server.agent._logger('info', 'result: {}'.format(res))
|
||||
_, w, _ = select.select([], [self.request], [], timeout)
|
||||
if not w:
|
||||
raise Exception('Socket timed-out 3')
|
||||
|
|
@ -159,7 +216,7 @@ class AgentTCPHandler(SocketServer.BaseRequestHandler):
|
|||
with open(res, 'rb') as f:
|
||||
buf = f.read(1024)
|
||||
while buf:
|
||||
self.server.agent.debug('sending %d Bytes', len(buf))
|
||||
self.server.agent._logger('info', 'sending {} Bytes'.format(len(buf)))
|
||||
self.request.sendall(buf)
|
||||
buf = f.read(1024)
|
||||
_, w, _ = select.select([], [self.request], [], timeout)
|
||||
|
|
@ -171,9 +228,7 @@ class AgentTCPHandler(SocketServer.BaseRequestHandler):
|
|||
self.request.sendall(res)
|
||||
self.request.close()
|
||||
except Exception as e:
|
||||
self.server.agent.debug('ERROR: %s', str(e))
|
||||
finally:
|
||||
self.server.agent.debug('<===============')
|
||||
self.server.agent._logger('error', '{}'.format(str(e)))
|
||||
|
||||
def recvall(self, length=1024):
|
||||
buf = b''
|
||||
|
|
|
|||
|
|
@ -102,10 +102,12 @@ class Burp(BUIbackend, BUIlogging):
|
|||
if dummy:
|
||||
return
|
||||
self.app = None
|
||||
self.logger = None
|
||||
self.acl_handler = False
|
||||
if server:
|
||||
if hasattr(server, 'app'):
|
||||
self.app = server.app
|
||||
self.set_logger(self.app.logger)
|
||||
self.acl_handler = server.acl_handler
|
||||
self.host = g_burphost
|
||||
self.port = int(g_burpport)
|
||||
|
|
|
|||
|
|
@ -46,10 +46,12 @@ class Burp(Burp1):
|
|||
global g_burpbin, g_stripbin, g_burpconfcli, g_burpconfsrv, g_tmpdir, BURP_MINIMAL_VERSION
|
||||
self.proc = None
|
||||
self.app = None
|
||||
self.logger = None
|
||||
self.acl_handler = False
|
||||
if server:
|
||||
if hasattr(server, 'app'):
|
||||
self.app = server.app
|
||||
self.set_logger(self.app.logger)
|
||||
self.acl_handler = server.acl_handler
|
||||
self.burpbin = g_burpbin
|
||||
self.stripbin = g_stripbin
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ class BUIbackend:
|
|||
self.host = host
|
||||
self.port = port
|
||||
|
||||
def set_logger(self, logger):
|
||||
self.logger = logger
|
||||
|
||||
def status(self, query='\n', agent=None):
|
||||
raise NotImplementedError("Sorry, the current Backend does not implement this method!")
|
||||
|
||||
|
|
|
|||
|
|
@ -17,16 +17,19 @@ except ImportError:
|
|||
import configparser as ConfigParser
|
||||
|
||||
from burpui.misc.backend.interface import BUIbackend, BUIserverException
|
||||
from burpui.misc.utils import BUIlogging
|
||||
|
||||
|
||||
class Burp(BUIbackend):
|
||||
class Burp(BUIbackend,BUIlogging):
|
||||
|
||||
def __init__(self, server=None, conf=None):
|
||||
self.app = None
|
||||
self.logger = None
|
||||
self.acl_handler = False
|
||||
if server:
|
||||
if hasattr(server, 'app'):
|
||||
self.app = server.app
|
||||
self.set_logger(self.app.logger)
|
||||
self.acl_handler = server.acl_handler
|
||||
self.servers = {}
|
||||
self.app.config['SERVERS'] = []
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ class BUIparser(object):
|
|||
def __init__(self, app=None, conf=None):
|
||||
self.app = app
|
||||
self.conf = conf
|
||||
self.logger = None
|
||||
if self.app:
|
||||
self.logger = self.app.logger
|
||||
|
||||
def read_server_conf(self):
|
||||
raise NotImplementedError("Sorry, the current Parser does not implement this method!")
|
||||
|
|
|
|||
|
|
@ -11,22 +11,24 @@ import sys
|
|||
import inspect
|
||||
import zipfile
|
||||
import tarfile
|
||||
import logging
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
long = int
|
||||
|
||||
|
||||
class human_readable(long):
|
||||
""" define a human_readable class to allow custom formatting
|
||||
format specifiers supported :
|
||||
em : formats the size as bits in IEC format i.e. 1024 bits (128 bytes) = 1Kib
|
||||
eM : formats the size as Bytes in IEC format i.e. 1024 bytes = 1KiB
|
||||
sm : formats the size as bits in SI format i.e. 1000 bits = 1kb
|
||||
sM : formats the size as bytes in SI format i.e. 1000 bytes = 1KB
|
||||
cm : format the size as bit in the common format i.e. 1024 bits (128 bytes) = 1Kb
|
||||
cM : format the size as bytes in the common format i.e. 1024 bytes = 1KB
|
||||
"""
|
||||
define a human_readable class to allow custom formatting
|
||||
format specifiers supported :
|
||||
em : formats the size as bits in IEC format i.e. 1024 bits (128 bytes) = 1Kib
|
||||
eM : formats the size as Bytes in IEC format i.e. 1024 bytes = 1KiB
|
||||
sm : formats the size as bits in SI format i.e. 1000 bits = 1kb
|
||||
sM : formats the size as bytes in SI format i.e. 1000 bytes = 1KB
|
||||
cm : format the size as bit in the common format i.e. 1024 bits (128 bytes) = 1Kb
|
||||
cM : format the size as bytes in the common format i.e. 1024 bytes = 1KB
|
||||
|
||||
code from: http://code.activestate.com/recipes/578323-human-readable-filememory-sizes-v2/
|
||||
code from: http://code.activestate.com/recipes/578323-human-readable-filememory-sizes-v2/
|
||||
"""
|
||||
def __format__(self, fmt):
|
||||
# is it an empty format or not a special format for the size class
|
||||
|
|
@ -73,31 +75,24 @@ def currentframe():
|
|||
|
||||
class BUIlogging(object):
|
||||
def _logger(self, level, *args):
|
||||
if self.app:
|
||||
logs = {
|
||||
'info': self.app.logger.info,
|
||||
'error': self.app.logger.error,
|
||||
'debug': self.app.logger.debug,
|
||||
'warning': self.app.logger.warning
|
||||
}
|
||||
if level in logs:
|
||||
if self.logger:
|
||||
"""
|
||||
Try to guess where was call the function
|
||||
"""
|
||||
cf = currentframe()
|
||||
(frame, filename, line_number, function_name, lines, index) = inspect.getouterframes(cf)[1]
|
||||
if cf is not None:
|
||||
cf = cf.f_back
|
||||
"""
|
||||
Try to guess where was call the function
|
||||
Ugly hack to reformat the message
|
||||
"""
|
||||
cf = currentframe()
|
||||
(frame, filename, line_number, function_name, lines, index) = inspect.getouterframes(cf)[1]
|
||||
if cf is not None:
|
||||
cf = cf.f_back
|
||||
"""
|
||||
Ugly hack to reformat the message
|
||||
"""
|
||||
ar = list(args)
|
||||
if isinstance(ar[0], str):
|
||||
ar[0] = filename + ':' + str(cf.f_lineno) + ' => ' + ar[0]
|
||||
else:
|
||||
ar = [filename + ':' + str(cf.f_lineno) + ' => {0}'.format(ar)]
|
||||
args = tuple(ar)
|
||||
logs[level](*args)
|
||||
ar = list(args)
|
||||
if isinstance(ar[0], str):
|
||||
ar[0] = filename + ':' + str(cf.f_lineno) + ' => ' + ar[0]
|
||||
else:
|
||||
ar = [filename + ':' + str(cf.f_lineno) + ' => {0}'.format(ar)]
|
||||
args = tuple(ar)
|
||||
self.logger.log(logging.getLevelName(level.upper()), *args)
|
||||
|
||||
|
||||
class BUIcompress():
|
||||
|
|
|
|||
|
|
@ -90,10 +90,13 @@ class BUIServer:
|
|||
traceback.print_exc()
|
||||
self.app.logger.error('Import Exception, module \'{0}\': {1}'.format(self.auth, str(e)))
|
||||
sys.exit(1)
|
||||
self.acl_engine = self._safe_config_get(config.get, 'acl')
|
||||
else:
|
||||
# I know that's ugly, but hey, I need it!
|
||||
self.app.login_manager._login_disabled = True
|
||||
self.acl_engine = self._safe_config_get(config.get, 'acl')
|
||||
# No login => no ACL
|
||||
self.acl_engine = 'none'
|
||||
|
||||
if self.acl_engine and self.acl_engine.lower() != 'none':
|
||||
try:
|
||||
mod = __import__(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue