Skip to content

Commit

Permalink
Added 'remove_these_response_headers' parameter fixing issue with Cob…
Browse files Browse the repository at this point in the history
…alt Strike 4.7+ unable to process response body.
  • Loading branch information
mgeeky committed Oct 7, 2022
1 parent 3013ef1 commit accad35
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 5 deletions.
6 changes: 3 additions & 3 deletions RedWarden.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@
# 0.9 added support for RedELK logs generation.
#
# Author:
# Mariusz Banach / mgeeky, '16-'21
# Mariusz Banach / mgeeky, '16-'22
# <[email protected]>
#
# (originally based on: @inaz2 implementation: https://github.com/futuresimple/proxy2)
# (now obsoleted)
#

VERSION = '0.9.1'
VERSION = '0.9.3'

import sys, os

Expand Down Expand Up @@ -174,7 +174,7 @@ def main():
:: RedWarden - Keeps your malleable C2 packets slipping through AVs,
EDRs, Blue Teams and club bouncers like nothing else!
by Mariusz Banach / mgeeky, '19-'21
by Mariusz Banach / mgeeky, '19-'22
<mb [at] binary-offensive.com>
v{}
Expand Down
11 changes: 11 additions & 0 deletions example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,17 @@ policy:
#repair_these_headers:
# - Accept-Encoding

#
# This option specifies which headers coming from Teamserver responses should be removed before
# reaching Beacon process. With Cobalt Strike 4.7+ I noticed that Teamserver removes Content-Encoding header
# automatically without any notice, thus violating our malleable http-(get|post).server contract.
# Since RedWarden followed the contract, Beacon was either dropping responses or decompressing them incorrectly.
#
# Either way, with Cobalt Strike 4.7+ we need to remove Content-Encoding header from responses to keep on going.
#
remove_these_response_headers:
- Content-Encoding


#
# Malleable Redirector plugin can act as a basic oracle API responding to calls
Expand Down
23 changes: 21 additions & 2 deletions lib/proxyhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,7 @@ class Response(object):

origin = (scheme, inbound_origin)
neworigin = (scheme, netloc)
res_msg_hdrs = {}

class MyResponse(http.client.HTTPResponse):
def __init__(self, req, origreq):
Expand Down Expand Up @@ -722,6 +723,10 @@ def __init__(self, req, origreq):

res = MyResponse(myreq, self)
self.response_headers = res.headers
res_msg_hdrs = res.msg.copy()

if len(res.msg) == 0 and len(res.headers) > 0:
res_msg_hdrs = res.headers.copy()

res_body = ""
if ignore_response_decompression_errors:
Expand Down Expand Up @@ -768,8 +773,8 @@ def __init__(self, req, origreq):
if options['debug']: raise
return

setattr(res, 'headers', res.msg)
self.response_headers = res.msg
setattr(res, 'headers', res_msg_hdrs)
self.response_headers = res_msg_hdrs

content_encoding = res.headers.get('Content-Encoding', 'identity')
if ignore_response_decompression_errors:
Expand All @@ -789,6 +794,20 @@ def __init__(self, req, origreq):
newuri = self.request.uri
self.request.uri = origuri

reskeys = [x.lower() for x in res.headers.keys()]

# if content_encoding == 'identity' and 'content-encoding' in reskeys:
# self.logger.dbg('Removed Content-Encoding response header.')
# del res.headers['Content-Encoding']

if plugins.IProxyPlugin.proxy2_metadata_headers['remove_response_headers'] in reskeys:
hdrs = res.headers[IProxyPlugin.proxy2_metadata_headers['remove_response_headers']]
self.logger.dbg('Removing these response headers: ' + hdrs)

hdrsList = hdrs.split(',')
for h in hdrsList:
del res.headers[h]

if type(modified) == bool:
modified |= (newuri != origuri)

Expand Down
1 change: 1 addition & 0 deletions plugins/IProxyPlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'override_host_header' : 'X-Proxy2-Override-Host-Header',
'domain_front_host_header' : 'X-Proxy2-Domain-Front-Host-Header',
'keep_alive_this_connection' : 'X-Proxy2-Keep-Alive',
'remove_response_headers' : 'X-Proxy2-Remove-Response-Headers',
}

class DropConnectionException(Exception):
Expand Down
6 changes: 6 additions & 0 deletions plugins/malleable_redirector.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ def parse(self, path):
except Exception as e:
self.logger.fatal(f'Could not process line as ([set] parameter ["value", ...] :\n\n\t{line}\n\nMake sure your line doesnt include apostrophes, or other characters breaking compregexes["parameter-value"] regex.')


if values == []:
values = ''
elif len(values) == 1:
Expand Down Expand Up @@ -412,6 +413,7 @@ class AlterHostHeader(Exception):
'mitigate_replay_attack': False,
'whitelisted_ip_addresses' : [],
'protect_these_headers_from_tampering' : [],
'remove_these_response_headers' : [],
'verify_peer_ip_details': True,
'malleable_redirector_hidden_api_endpoint' : '',
'remove_superfluous_headers': True,
Expand Down Expand Up @@ -964,6 +966,10 @@ def redirect(self, req, _target, malleable_meta):
req.headers[proxy2_metadata_headers['ignore_response_decompression_errors']] = "1"
req.headers[proxy2_metadata_headers['override_host_header']] = newhost

if 'remove_these_response_headers' in self.proxyOptions.keys() and len(self.proxyOptions['remove_these_response_headers']) > 0:
removeThese = ','.join([x.lower() for x in self.proxyOptions['remove_these_response_headers']])
req.headers[proxy2_metadata_headers['remove_response_headers']] = removeThese

if 'host' in malleable_meta.keys() and len(malleable_meta['host']) > 0:
req.headers[proxy2_metadata_headers['domain_front_host_header']] = malleable_meta['host']

Expand Down

0 comments on commit accad35

Please sign in to comment.