diff --git a/optionsparser.py b/optionsparser.py index d7e45f5..0349355 100755 --- a/optionsparser.py +++ b/optionsparser.py @@ -64,10 +64,10 @@ def parse_options(opts, version): opts.update(vars(params)) if params.list_plugins: - with os.scandir(os.path.normpath(os.path.join(os.getcwd(), 'plugins/'))) as it: - for entry in it: - if entry.name.endswith(".py") and entry.is_file(): - print('[+] Plugin: {}'.format(entry.name)) + files = sorted([f for f in os.scandir(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'plugins/'))], key = lambda f: f.name) + for _, entry in enumerate(files): + if entry.name.endswith(".py") and entry.is_file() and entry.name.lower() not in ['iproxyplugin.py', '__init__.py']: + print('[+] Plugin: {}'.format(entry.name)) sys.exit(0) @@ -76,7 +76,7 @@ def parse_options(opts, version): decomposed = PluginsLoader.decompose_path(opt) if not os.path.isfile(decomposed['path']): opt = opt.replace('.py', '') - opt2 = os.path.normpath(os.path.join(os.getcwd(), 'plugins/{}.py'.format(opt))) + opt2 = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'plugins/{}.py'.format(opt))) if not os.path.isfile(opt2): raise Exception('Specified plugin: "%s" does not exist.' % decomposed['path']) else: @@ -111,10 +111,10 @@ def parse_options(opts, version): def feed_with_plugin_options(opts, parser): logger = ProxyLogger() plugins = [] - with os.scandir(os.path.join(os.getcwd(), 'plugins/')) as it: - for entry in it: - if entry.name.endswith(".py") and entry.is_file(): - plugins.append(entry.path) + files = sorted([f for f in os.scandir(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'plugins/'))], key = lambda f: f.name) + for _, entry in enumerate(files): + if entry.name.endswith(".py") and entry.is_file() and entry.name.lower() not in ['iproxyplugin.py', '__init__.py']: + plugins.append(entry.path) options = opts.copy() options['plugins'] = plugins diff --git a/plugins/malleable_redirector.py b/plugins/malleable_redirector.py index db7c89f..a34e885 100644 --- a/plugins/malleable_redirector.py +++ b/plugins/malleable_redirector.py @@ -279,6 +279,10 @@ def help(parser): help='If someone who is not a beacon hits the proxy, where to redirect him (or where to proxy his request). Default: https://google.com', default = 'https://google.com' ) + parser.add_argument('--log-dropped', + help='Logs full dropped requests bodies.', + action = 'store_true' + ) def request_handler(self, req, req_body): self.is_request = True @@ -307,6 +311,17 @@ def response_handler(self, req, req_body, res, res_body): return res_body def drop_action(self, req, req_body, res, res_body): + if self.proxyOptions['log_dropped'] == True: + if req_body == None: req_body = '' + else: req_body = req_body.decode() + + req_headers = ProxyRequestHandler.filter_headers(req.headers) + request = '{} {} {}\r\n{}\r\n{}'.format( + req.command, req.path, 'HTTP/1.1', req_headers, req_body + ) + + self.logger.info('DROPPED REQUEST:\n\n{}'.format(request)) + if self.proxyOptions['drop_action'] == 'reset': return DropConnectionException('Not a conformant beacon request.') diff --git a/proxy2.py b/proxy2.py index 8d89331..12a0ad1 100755 --- a/proxy2.py +++ b/proxy2.py @@ -54,6 +54,8 @@ ssl._create_default_https_context = ssl._create_unverified_context +normpath = lambda p: os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), p)) + # Global options dictonary, that will get modified after parsing # program arguments. Below state represents default values. options = { @@ -66,10 +68,10 @@ 'proxy_self_url': 'http://proxy2.test/', 'timeout': 5, 'no_ssl': False, - 'cakey': os.path.normpath(os.path.join(os.getcwd(), 'ca-cert/ca.key')), - 'cacert': os.path.normpath(os.path.join(os.getcwd(), 'ca-cert/ca.crt')), - 'certkey': os.path.normpath(os.path.join(os.getcwd(), 'ca-cert/cert.key')), - 'certdir': os.path.normpath(os.path.join(os.getcwd(), 'certs/')), + 'cakey': normpath('ca-cert/ca.key'), + 'cacert': normpath('ca-cert/ca.crt'), + 'certkey': normpath('ca-cert/cert.key'), + 'certdir': normpath('certs/'), 'cacn': 'proxy2 CA', 'plugins': set(), 'plugin_class_name': 'ProxyPlugin', @@ -313,7 +315,7 @@ class Response(object): assert scheme in ('http', 'https') #if netloc: # req.headers['Host'] = netloc - req_headers = self.filter_headers(req.headers) + req_headers = ProxyRequestHandler.filter_headers(req.headers) if not origin in self.tls.conns: logger.dbg('Connecting with {}'.format(outbound_origin)) @@ -359,7 +361,7 @@ class Response(object): logger.info('[RESPONSE] HTTP {} {}, length: {}'.format(res.status, res.reason, len(res_body)), color=ProxyLogger.colors_map['yellow']) - res_headers = self.filter_headers(res.headers) + res_headers = ProxyRequestHandler.filter_headers(res.headers) o = "%s %d %s\r\n" % (self.protocol_version, res.status, res.reason) self.wfile.write(o.encode()) for k, v in res_headers.items(): @@ -385,7 +387,8 @@ class Response(object): do_TRACE = do_GET do_PATCH = do_GET - def filter_headers(self, headers): + @staticmethod + def filter_headers(headers): # http://tools.ietf.org/html/rfc2616#section-13.5.1 hop_by_hop = ('connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailers', 'transfer-encoding', 'upgrade') for k in hop_by_hop: