From 9fa0ae99516a8d59b73381f826377c1dcc490500 Mon Sep 17 00:00:00 2001 From: mgeeky Date: Thu, 23 Jan 2020 02:34:36 +0100 Subject: [PATCH] Improved malleable_redirector's proxy handling action --- .gitignore | 2 +- optionsparser.py | 3 +-- plugins/malleable_redirector.py | 28 ++++++++++++++++++---------- pluginsloader.py | 10 +++++++++- proxy2.py | 10 +++++++++- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 0d20b64..94487b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -*.pyc +*.pyc diff --git a/optionsparser.py b/optionsparser.py index 0349355..3a99792 100755 --- a/optionsparser.py +++ b/optionsparser.py @@ -102,8 +102,7 @@ def parse_options(opts, version): else: opts['log'] = sys.stdout - if opts['log']: opts['log'] = os.path.normpath(opts['log']) - if opts['plugin']: opts['plugin'] = os.path.normpath(opts['plugin']) + if opts['log'] and opts['log'] != sys.stdout: opts['log'] = os.path.normpath(opts['log']) if opts['cakey']: opts['cakey'] = os.path.normpath(opts['cakey']) if opts['certdir']: opts['certdir'] = os.path.normpath(opts['certdir']) if opts['certkey']: opts['certkey'] = os.path.normpath(opts['certkey']) diff --git a/plugins/malleable_redirector.py b/plugins/malleable_redirector.py index a34e885..bfe75e4 100644 --- a/plugins/malleable_redirector.py +++ b/plugins/malleable_redirector.py @@ -312,20 +312,25 @@ def response_handler(self, req, req_body, res, 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 = req.headers + if req_body != None and len(req_body) > 0: + req_body = '\r\n' + req_body + else: + req_body = '' + + u = urlparse(req.path) + scheme, netloc, path = u.scheme, u.netloc, (u.path + '?' + u.query if u.query else u.path) - req_headers = ProxyRequestHandler.filter_headers(req.headers) - request = '{} {} {}\r\n{}\r\n{}'.format( - req.command, req.path, 'HTTP/1.1', req_headers, req_body + request = '{} {} {}\r\n{}{}'.format( + req.command, path, 'HTTP/1.1', req_headers, req_body ) - self.logger.info('DROPPED REQUEST:\n\n{}'.format(request)) + self.logger.err('== DROPPED INVALID C2 REQUEST ==:\n\n{}'.format(request)) if self.proxyOptions['drop_action'] == 'reset': return DropConnectionException('Not a conformant beacon request.') - if self.proxyOptions['drop_action'] == 'redirect': + elif self.proxyOptions['drop_action'] == 'redirect': if self.is_request: return DontFetchResponseException('Not a conformant beacon request.') @@ -347,10 +352,13 @@ def drop_action(self, req, req_body, res, res_body): return res_body.encode() - if self.proxyOptions['drop_action'] == 'proxy': - req.path = self.proxyOptions['drop_url'] + elif self.proxyOptions['drop_action'] == 'proxy': + self.logger.dbg('Proxying forward...') - return req_body + if self.is_request: + return req_body + + return res_body def drop_check(self, req, req_body): # User-agent conformancy diff --git a/pluginsloader.py b/pluginsloader.py index 9397cb1..fae20a3 100755 --- a/pluginsloader.py +++ b/pluginsloader.py @@ -95,6 +95,14 @@ def load(self, path): self.logger.dbg('Decomposed as: %s' % str(decomposed)) plugin = decomposed['path'].strip() + + if not os.path.isfile(plugin): + _plugin = os.path.normpath(os.path.join(os.path.dirname(__file__), 'plugins/{}'.format(plugin))) + if os.path.isfile(_plugin): + plugin = _plugin + elif os.path.isfile(_plugin+'.py'): + plugin = _plugin + '.py' + name = os.path.basename(plugin).lower().replace('.py', '') if name in self.plugins or name in ['iproxyplugin', '__init__']: @@ -149,4 +157,4 @@ def load(self, path): except ImportError as e: self.logger.err('Couldn\'t load specified plugin: "%s". Error: %s' % (plugin, e)) if self.options['debug']: - raise \ No newline at end of file + raise diff --git a/proxy2.py b/proxy2.py index 12a0ad1..6d83ad5 100755 --- a/proxy2.py +++ b/proxy2.py @@ -299,6 +299,7 @@ class Response(object): logger.err('Exception catched in request_handler: {}'.format(str(e))) req_path_full = '' + if not req.path: req.path = '/' if req.path[0] == '/': if isinstance(self.connection, ssl.SSLSocket): req_path_full = "https://%s%s" % (req.headers['Host'], req.path) @@ -326,7 +327,11 @@ class Response(object): conn = self.tls.conns[origin] if req_body == None: req_body = '' - else: req_body = req_body.decode() + else: + try: + req_body = req_body.decode() + except AttributeError: + pass request = '{} {} {}\r\n{}\r\n{}'.format( self.command, path, 'HTTP/1.1', req_headers, req_body @@ -336,6 +341,7 @@ class Response(object): conn.request(self.command, path.strip(), req_body.strip(), dict(req_headers)) res = conn.getresponse() res_body = res.read() + if type(res_body) == str: res_body = str.encode(res_body) conn.close() except Exception as e: @@ -372,6 +378,7 @@ class Response(object): except: self.wfile.write(b'\r\n') + if type(res_body) == str: res_body = str.encode(res_body) self.wfile.write(res_body) self.wfile.flush() @@ -525,6 +532,7 @@ def _parse_qsl(s): except ValueError: res_body_text = res_body elif content_type.startswith('text/html'): + if type(res_body) == str: res_body = str.encode(res_body) m = re.search(r']*>\s*([^<]+?)\s*', res_body.decode(), re.I) if m: logger.trace("==== HTML TITLE ====\n%s\n" % html.unescape(m.group(1)), color=ProxyLogger.colors_map['cyan'])