mirror of
https://github.com/MythicC2Profiles/http
synced 2026-06-08 11:56:36 +00:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b5dfcf3de | |||
| 44fbb4ead7 | |||
| 239a2a6d1a | |||
| f9139c89f7 | |||
| 5c5341bcb3 | |||
| b1e40fdd95 | |||
| 20c9d37f76 | |||
| b91bd3eae2 | |||
| 200bb3f339 | |||
| 7dbe513591 | |||
| 68f4c32f00 | |||
| 95ff364a78 | |||
| 4d11b7b43d | |||
| cf4f25a54a | |||
| 0a830a84e8 | |||
| 7fc135b895 | |||
| 3a0e033149 | |||
| 0e74377fea | |||
| 9fcd3c6de4 | |||
| 6667cec568 | |||
| 1d93e22f64 | |||
| 8fbedcc51c | |||
| da88f63aac | |||
| 26e119f5df | |||
| 4eaa51303f | |||
| ef8101e909 | |||
| 39a75fae3f | |||
| 72192a418f | |||
| c3b8ade832 | |||
| bc2238ae50 | |||
| 60062ebdae | |||
| 1e15fe3d03 | |||
| f0a5457a85 | |||
| 65d50c6a8e | |||
| 1ec59eb1b6 | |||
| 1b53c59fd4 | |||
| a362a4a1a0 | |||
| 79787ffef9 | |||
| 60880efa14 | |||
| 530e64c714 | |||
| 49c68ede9a | |||
| 3006f05bea | |||
| 571e4b441b | |||
| 408d6930ba | |||
| 541c84458f | |||
| f18a44083b | |||
| 7201a6abc9 | |||
| 0252a76b21 | |||
| 8015cf7bf6 | |||
| e06f0e5e52 | |||
| e921fe0f11 | |||
| 9bd96bccc5 | |||
| bb29774780 | |||
| bf689569e6 | |||
| 38a9b45688 | |||
| ffb18d6067 | |||
| 13d7fbd0e3 | |||
| 3ae4842771 | |||
| 4ed4dea0ba | |||
| 29aba4e07d | |||
| 829c871477 | |||
| 16f9f7dd0f | |||
| 27da7a9849 | |||
| 5b0b5c52c8 | |||
| 0f95c909b4 | |||
| b406ffcb80 | |||
| 1887e13b71 | |||
| 2a2e6175be | |||
| 6fa00183f2 | |||
| 4014cc1211 | |||
| 60871375e7 | |||
| 2553b0d442 |
@@ -0,0 +1,139 @@
|
||||
# Pulled from Thanatos (https://github.com/MythicAgents/thanatos/blob/rewrite/.github/workflows/image.yml) - MEhrn00
|
||||
|
||||
# Name for the Github actions workflow
|
||||
name: Build and push container images
|
||||
|
||||
on:
|
||||
# Only run workflow when there is a new release published in Github
|
||||
#release:
|
||||
# types: [published]
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'Mythic3.3'
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
# Variables holding configuration settings
|
||||
env:
|
||||
# Container registry the built container image will be pushed to
|
||||
REGISTRY: ghcr.io
|
||||
|
||||
# Set the container image name to the Github repository name. (MythicAgents/apfell)
|
||||
AGENT_IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
# Description label for the package in Github
|
||||
IMAGE_DESCRIPTION: ${{ github.repository }} container for use with Mythic
|
||||
|
||||
# Source URL for the package in Github. This links the Github repository packages list
|
||||
# to this container image
|
||||
IMAGE_SOURCE: ${{ github.server_url }}/${{ github.repository }}
|
||||
|
||||
# License for the container image
|
||||
IMAGE_LICENSE: BSD-3-Clause
|
||||
|
||||
# Set the container image version to the Github release tag
|
||||
VERSION: ${{ github.ref_name }}
|
||||
#VERSION: ${{ github.event.head_commit.message }}
|
||||
|
||||
RELEASE_BRANCH: master
|
||||
|
||||
jobs:
|
||||
# Builds the base container image and pushes it to the container registry
|
||||
agent_build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4 # ref: https://github.com/marketplace/actions/checkout
|
||||
- name: Log in to the container registry
|
||||
uses: docker/login-action@v3 # ref: https://github.com/marketplace/actions/docker-login
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
with:
|
||||
platforms: 'arm64,arm'
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
# the following are unique to this job
|
||||
- name: Lowercase the server container image name
|
||||
run: echo "AGENT_IMAGE_NAME=${AGENT_IMAGE_NAME,,}" >> ${GITHUB_ENV}
|
||||
- name: Build and push the server container image
|
||||
uses: docker/build-push-action@v5 # ref: https://github.com/marketplace/actions/build-and-push-docker-images
|
||||
with:
|
||||
context: C2_Profiles/http
|
||||
file: C2_Profiles/http/.docker/Dockerfile
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.AGENT_IMAGE_NAME }}:${{ env.VERSION }}
|
||||
${{ env.REGISTRY }}/${{ env.AGENT_IMAGE_NAME }}:latest
|
||||
push: ${{ github.ref_type == 'tag' }}
|
||||
# These container metadata labels allow configuring the package in Github
|
||||
# packages. The source will link the package to this Github repository
|
||||
labels: |
|
||||
org.opencontainers.image.source=${{ env.IMAGE_SOURCE }}
|
||||
org.opencontainers.image.description=${{ env.IMAGE_DESCRIPTION }}
|
||||
org.opencontainers.image.licenses=${{ env.IMAGE_LICENSE }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
update_files:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- agent_build
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
# Pull in the repository code
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4 # ref: https://github.com/marketplace/actions/checkout
|
||||
|
||||
# update names to lowercase
|
||||
- name: Lowercase the container image name
|
||||
run: echo "AGENT_IMAGE_NAME=${AGENT_IMAGE_NAME,,}" >> ${GITHUB_ENV}
|
||||
|
||||
# The Dockerfile which Mythic uses to pull in the base container image needs to be
|
||||
# updated to reference the newly built container image
|
||||
- name: Fix the server Dockerfile reference to reference the new release tag
|
||||
working-directory: C2_Profiles/http
|
||||
run: |
|
||||
sed -i "s|^FROM ghcr\.io.*$|FROM ${REGISTRY}/${AGENT_IMAGE_NAME}:${VERSION}|" Dockerfile
|
||||
|
||||
- name: Update package.json version
|
||||
uses: jossef/action-set-json-field@v2.1
|
||||
with:
|
||||
file: config.json
|
||||
field: remote_images.http
|
||||
value: ${{env.REGISTRY}}/${{env.AGENT_IMAGE_NAME}}:${{env.VERSION}}
|
||||
|
||||
# Push the changes to the Dockerfile
|
||||
- name: Push the updated base Dockerfile image reference changes
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
uses: EndBug/add-and-commit@v9 # ref: https://github.com/marketplace/actions/add-commit
|
||||
with:
|
||||
# Only add the Dockerfile changes. Nothing else should have been modified
|
||||
add: "['C2_Profiles/http/Dockerfile', 'config.json']"
|
||||
# Use the Github actions bot for the commit author
|
||||
default_author: github_actions
|
||||
committer_email: github-actions[bot]@users.noreply.github.com
|
||||
|
||||
# Set the commit message
|
||||
message: "Bump Dockerfile tag to match release '${{ env.VERSION }}'"
|
||||
|
||||
# Overwrite the current git tag with the new changes
|
||||
tag: '${{ env.VERSION }} --force'
|
||||
|
||||
# Push the new changes with the tag overwriting the current one
|
||||
tag_push: '--force'
|
||||
|
||||
# Push the commits to the branch marked as the release branch
|
||||
push: origin HEAD:${{ env.RELEASE_BRANCH }} --set-upstream
|
||||
|
||||
# Have the workflow fail in case there are pathspec issues
|
||||
pathspec_error_handling: exitImmediately
|
||||
@@ -1 +1,7 @@
|
||||
__pycache__/
|
||||
.DS_Store
|
||||
main
|
||||
.idea/
|
||||
C2_Profiles/http/http/c2_code/fullchain.pem
|
||||
|
||||
C2_Profiles/http/http/c2_code/privkey.pem
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
FROM golang:1.22 as builder
|
||||
|
||||
WORKDIR /Mythic/
|
||||
|
||||
COPY [".", "."]
|
||||
|
||||
RUN make build
|
||||
|
||||
FROM alpine
|
||||
|
||||
RUN apk add make
|
||||
|
||||
COPY --from=builder /main /main
|
||||
COPY --from=builder /mythic_http_server /mythic_http_server
|
||||
|
||||
WORKDIR /Mythic/
|
||||
|
||||
COPY [".", "."]
|
||||
|
||||
CMD make run
|
||||
@@ -0,0 +1,4 @@
|
||||
http/c2_code/mythic_http_server
|
||||
main
|
||||
.idea
|
||||
.env
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/PoseidonContainer.iml" filepath="$PROJECT_DIR$/.idea/PoseidonContainer.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Executable → Regular
+20
-1
@@ -1 +1,20 @@
|
||||
FROM itsafeaturemythic/python38_sanic_c2profile:0.0.6
|
||||
FROM golang:1.22 as builder
|
||||
|
||||
WORKDIR /Mythic/
|
||||
|
||||
COPY [".", "."]
|
||||
|
||||
RUN make build
|
||||
|
||||
FROM alpine
|
||||
|
||||
RUN apk add make
|
||||
|
||||
COPY --from=builder /main /main
|
||||
COPY --from=builder /mythic_http_server /mythic_http_server
|
||||
|
||||
WORKDIR /Mythic/
|
||||
|
||||
COPY [".", "."]
|
||||
|
||||
CMD make run
|
||||
@@ -0,0 +1,37 @@
|
||||
BINARY_NAME?=main
|
||||
DEBUG_LEVEL?="debug"
|
||||
RABBITMQ_HOST?="127.0.0.1"
|
||||
RABBITMQ_PASSWORD?="PqR9XJ957sfHqcxj6FsBMj4p"
|
||||
MYTHIC_SERVER_HOST?="127.0.0.1"
|
||||
MYTHIC_SERVER_GRPC_PORT?="17444"
|
||||
WEBHOOK_DEFAULT_URL?=
|
||||
WEBHOOK_DEFAULT_CHANNEL?=
|
||||
WEBHOOK_DEFAULT_FEEDBACK_CHANNEL?=
|
||||
WEBHOOK_DEFAULT_CALLBACK_CHANNEL?=
|
||||
WEBHOOK_DEFAULT_STARTUP_CHANNEL?=
|
||||
|
||||
build:
|
||||
CGO_ENABLED=0 go build -o ${BINARY_NAME} .
|
||||
cp ${BINARY_NAME} /
|
||||
cd http/c2_code && make build
|
||||
|
||||
run:
|
||||
cp /${BINARY_NAME} .
|
||||
cd http/c2_code && make run
|
||||
./${BINARY_NAME}
|
||||
|
||||
local:
|
||||
CGO_ENABLED=0 go build -o ${BINARY_NAME} .
|
||||
|
||||
run_custom: local
|
||||
DEBUG_LEVEL=${DEBUG_LEVEL} \
|
||||
RABBITMQ_HOST=${RABBITMQ_HOST} \
|
||||
RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} \
|
||||
MYTHIC_SERVER_HOST=${MYTHIC_SERVER_HOST} \
|
||||
MYTHIC_SERVER_GRPC_PORT=${MYTHIC_SERVER_GRPC_PORT} \
|
||||
WEBHOOK_DEFAULT_URL=${WEBHOOK_DEFAULT_URL} \
|
||||
WEBHOOK_DEFAULT_CHANNEL=${WEBHOOK_DEFAULT_CHANNEL} \
|
||||
WEBHOOK_DEFAULT_FEEDBACK_CHANNEL=${WEBHOOK_DEFAULT_FEEDBACK_CHANNEL} \
|
||||
WEBHOOK_DEFAULT_CALLBACK_CHANNEL=${WEBHOOK_DEFAULT_CALLBACK_CHANNEL} \
|
||||
WEBHOOK_DEFAULT_STARTUP_CHANNEL=${WEBHOOK_DEFAULT_STARTUP_CHANNEL} \
|
||||
./${BINARY_NAME}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"instances": [
|
||||
{
|
||||
"ServerHeaders": {
|
||||
"Server": "NetDNA-cache/2.2",
|
||||
"Cache-Control": "max-age=0, no-cache",
|
||||
"Pragma": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Content-Type": "application/javascript; charset=utf-8"
|
||||
},
|
||||
"port": 80,
|
||||
"key_path": "privkey.pem",
|
||||
"cert_path": "fullchain.pem",
|
||||
"debug": false,
|
||||
"use_ssl": false,
|
||||
"payloads": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Page Not Found!</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,223 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import html, redirect, text, raw
|
||||
from sanic.exceptions import NotFound
|
||||
import sys
|
||||
import asyncio
|
||||
import ssl
|
||||
from pathlib import Path
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
import aiohttp
|
||||
from OpenSSL import crypto, SSL
|
||||
import random
|
||||
|
||||
config = {}
|
||||
|
||||
async def print_flush(message):
|
||||
print(message)
|
||||
sys.stdout.flush()
|
||||
|
||||
session = aiohttp.ClientSession()
|
||||
|
||||
def server_error_handler(request, exception):
|
||||
if request is None:
|
||||
print("Invalid HTTP Method - Likely HTTPS trying to talk to HTTP")
|
||||
sys.stdout.flush()
|
||||
return html("Error: Failed to process request", status=500, headers={})
|
||||
return html("Error: Requested URL {} not found".format(request.url), status=404, headers=config[request.app.name]['headers'])
|
||||
|
||||
|
||||
async def download_file(request, **kwargs):
|
||||
try:
|
||||
if config[request.app.name]['debug']:
|
||||
await print_flush("agent_message request from: {} with {} and {}".format(request.url, request.cookies, request.headers))
|
||||
await print_flush(config[request.app.name]['payloads'])
|
||||
await print_flush(request.path)
|
||||
if config[request.app.name]['debug']:
|
||||
await print_flush(f"forwarding to : {config['mythic_base_address'] + '/api/v1.4/files/download/{}'.format(config[request.app.name]['payloads'][request.path])}")
|
||||
async with session.get(config['mythic_base_address'] + "/api/v1.4/files/download/{}".format(config[request.app.name]['payloads'][request.path]), ssl=False, headers={"Mythic": "http", **request.headers}) as resp:
|
||||
return raw(await resp.read(), status=resp.status, headers=config[request.app.name]['headers'])
|
||||
except Exception as e:
|
||||
await print_flush(str(e))
|
||||
|
||||
async def agent_message(request, **kwargs):
|
||||
global config
|
||||
forwarded_headers = {
|
||||
"x-forwarded-for": request.headers["x-forwarded-for"] if "x-forwarded-for" in request.headers else request.ip,
|
||||
"x-forwarded-url": request.url,
|
||||
"x-forwarded-query": request.query_string,
|
||||
"x-forwarded-port": str(request.server_port),
|
||||
"x-forwarded-cookies": str(request.cookies)
|
||||
}
|
||||
try:
|
||||
if config[request.app.name]['debug']:
|
||||
await print_flush("agent_message request from: {} with {} and {}".format(request.url, request.cookies, request.headers))
|
||||
await print_flush(" and URI: {}".format(request.query_string))
|
||||
if config[request.app.name]['debug']:
|
||||
await print_flush("Forwarding along to: {}".format(config['mythic_address']))
|
||||
if request.method == "POST":
|
||||
# manipulate the request if needed
|
||||
#from mythic_c2_container.MythicRPC import MythicRPC
|
||||
#msg = await MythicRPC().execute("create_encrypted_message", message={"action": "get_tasking"}, target_uuid="ba3f9b0a-e073-4cbc-afe7-1c94f86cc76a", c2_profile_name="http")
|
||||
#resp = await MythicRPC().execute("create_event_message", message=msg.response, warning=False)
|
||||
#msg = await MythicRPC().execute("create_decrypted_message", message=msg.response, c2_profile_name="http")
|
||||
#resp = await MythicRPC().execute("create_event_message", message=msg.response, warning=False)
|
||||
async with session.post(config['mythic_address'], data=request.body, ssl=False, headers={"Mythic": "http", **request.headers, **forwarded_headers}) as resp:
|
||||
return raw(await resp.read(), status=resp.status, headers=config[request.app.name]['headers'])
|
||||
else:
|
||||
# manipulate the request if needed
|
||||
async with session.get(config['mythic_address'] + "?{}".format(request.query_string), data=request.body, ssl=False, headers={"Mythic": "http", **request.headers, **forwarded_headers}) as resp:
|
||||
return raw(await resp.read(), status=resp.status, headers=config[request.app.name]['headers'])
|
||||
except Exception as e:
|
||||
if request is None:
|
||||
await print_flush("Invalid HTTP Method - Likely HTTPS trying to talk to HTTP")
|
||||
return server_error_handler(request, e)
|
||||
if config[request.app.name]['debug']:
|
||||
await print_flush("error in agent_message: {}".format(str(e)))
|
||||
return server_error_handler(request, e)
|
||||
|
||||
def random_mixed():
|
||||
random.seed()
|
||||
length = random.randint(5,20)
|
||||
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
rnd = ''.join(random.choice(letters) for i in range(length))
|
||||
return rnd
|
||||
|
||||
# from https://stackoverflow.com/questions/27164354/create-a-self-signed-x509-certificate-in-python
|
||||
def cert_gen(
|
||||
emailAddress: str = random_mixed(),
|
||||
commonName: str = random_mixed(),
|
||||
countryName: str = "US",
|
||||
localityName: str = random_mixed(),
|
||||
stateOrProvinceName: str = random_mixed(),
|
||||
organizationName: str = random_mixed(),
|
||||
organizationUnitName: str = random_mixed(),
|
||||
serialNumber: int = 0,
|
||||
validityStartInSeconds: int = 0,
|
||||
validityEndInSeconds: int = 10*365*24*60*60,
|
||||
key_file: str = "privkey.pem",
|
||||
cert_file: str = "fullchain.pem"):
|
||||
|
||||
#can look at generated file using openssl:
|
||||
#openssl x509 -inform pem -in selfsigned.crt -noout -text
|
||||
# create a key pair
|
||||
k = crypto.PKey()
|
||||
k.generate_key(crypto.TYPE_RSA, 4096)
|
||||
# create a self-signed cert
|
||||
cert = crypto.X509()
|
||||
cert.get_subject().C = countryName
|
||||
cert.get_subject().ST = stateOrProvinceName
|
||||
cert.get_subject().L = localityName
|
||||
cert.get_subject().O = organizationName
|
||||
cert.get_subject().OU = organizationUnitName
|
||||
cert.get_subject().CN = commonName
|
||||
cert.get_subject().emailAddress = emailAddress
|
||||
cert.set_serial_number(serialNumber)
|
||||
cert.gmtime_adj_notBefore(0)
|
||||
cert.gmtime_adj_notAfter(validityEndInSeconds)
|
||||
cert.set_issuer(cert.get_subject())
|
||||
cert.set_pubkey(k)
|
||||
cert.sign(k, 'sha512')
|
||||
with open(cert_file, "wt") as f:
|
||||
f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode("utf-8"))
|
||||
with open(key_file, "wt") as f:
|
||||
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode("utf-8"))
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.path.append("/Mythic/mythic")
|
||||
config_file = open("config.json", 'rb')
|
||||
main_config = json.loads(config_file.read().decode('utf-8'))
|
||||
print("Opening config and starting instances...")
|
||||
sys.stdout.flush()
|
||||
# basic mapping of the general endpoints to the real endpoints
|
||||
try:
|
||||
config['mythic_address'] = os.environ['MYTHIC_ADDRESS']
|
||||
config['mythic_base_address'] = config['mythic_address'].split('/api')[0]
|
||||
except Exception as e:
|
||||
print("failed to find MYTHIC_ADDRESS environment variable")
|
||||
sys.stdout.flush()
|
||||
sys.exit(1)
|
||||
# now look at the specific instances to start
|
||||
for inst in main_config['instances']:
|
||||
config[str(inst['port'])] = {'debug': inst['debug'],
|
||||
'headers': inst['ServerHeaders'],
|
||||
'payloads': {}}
|
||||
if inst['debug']:
|
||||
print("Debugging statements are enabled. This gives more context, but might be a performance hit")
|
||||
else:
|
||||
print("Debugging statements are disabled")
|
||||
sys.stdout.flush()
|
||||
# now to create an app instance to handle responses
|
||||
app = Sanic(str(inst['port']))
|
||||
app.config['REQUEST_MAX_SIZE'] = 1000000000
|
||||
app.config['REQUEST_TIMEOUT'] = 600
|
||||
app.config['RESPONSE_TIMEOUT'] = 600
|
||||
if "payloads" in inst and isinstance(inst["payloads"], dict):
|
||||
for k, v in inst["payloads"].items():
|
||||
config[str(inst["port"])]["payloads"][f"{k}"] = v
|
||||
app.add_route(download_file, f"{k}", methods=["GET"])
|
||||
app.add_route(agent_message, "/<uri:path>", methods=['GET','POST'])
|
||||
app.add_route(agent_message, "/", methods=['GET','POST'])
|
||||
app.error_handler.add(Exception, server_error_handler)
|
||||
keyfile = Path(inst['key_path'])
|
||||
certfile = Path(inst['cert_path'])
|
||||
if (not keyfile.is_file() or not certfile.is_file()) and inst["use_ssl"]:
|
||||
# we need to generate the certs
|
||||
cert_gen(key_file=inst['key_path'], cert_file=inst['cert_path'])
|
||||
if keyfile.is_file() and certfile.is_file() and inst['use_ssl']:
|
||||
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
|
||||
context.load_cert_chain(inst['cert_path'], keyfile=inst['key_path'])
|
||||
if inst['debug']:
|
||||
server = app.create_server(host="0.0.0.0", port=inst['port'], ssl=context, debug=False, return_asyncio_server=True, access_log=True)
|
||||
else:
|
||||
server = app.create_server(host="0.0.0.0", port=inst['port'], ssl=context, debug=False, return_asyncio_server=True, access_log=False)
|
||||
print("++++++++++++++++++++++++++++++++++++++++++")
|
||||
print("++++++++++++++++++++++++++++++++++++++++++")
|
||||
print("Successfully using SSL for port {}".format(inst['port']))
|
||||
print("++++++++++++++++++++++++++++++++++++++++++")
|
||||
print("++++++++++++++++++++++++++++++++++++++++++")
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
if (inst["key_path"] != "" or inst["cert_path"] != "") and inst["use_ssl"]:
|
||||
print("-----------------------------------------------------------------------------")
|
||||
print("-----------------------------------------------------------------------------")
|
||||
print("key_path or cert_path is not empty and use_ssl is true, but at least one of the files cannot be found.")
|
||||
print("If you're using the http profile in a Docker container, place your cert and key in Mythic/C2_Profiles/http/c2_code/")
|
||||
print(" and set the key_path and cert_path values to just the names of the files")
|
||||
print("If you're using the http profile outside of Docker, make sure the paths are absolute and correct")
|
||||
print("NOT using SSL for port {}".format(inst['port']))
|
||||
print("-----------------------------------------------------------------------------")
|
||||
print("-----------------------------------------------------------------------------")
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
print("**************************************************************")
|
||||
print("**************************************************************")
|
||||
print("NOT using SSL for port {}".format(inst['port']))
|
||||
print("**************************************************************")
|
||||
print("**************************************************************")
|
||||
sys.stdout.flush()
|
||||
if inst['debug']:
|
||||
server = app.create_server(host="0.0.0.0", port=inst['port'], debug=False, return_asyncio_server=True, access_log=True)
|
||||
else:
|
||||
server = app.create_server(host="0.0.0.0", port=inst['port'], debug=False, return_asyncio_server=True, access_log=False)
|
||||
task = asyncio.ensure_future(server)
|
||||
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
def callback(fut):
|
||||
try:
|
||||
fetch_count = fut.result()
|
||||
except:
|
||||
print("port already in use")
|
||||
sys.stdout.flush()
|
||||
sys.exit()
|
||||
task.add_done_callback(callback)
|
||||
loop.run_forever()
|
||||
except:
|
||||
sys.exit()
|
||||
loop.stop()
|
||||
@@ -0,0 +1,41 @@
|
||||
module MyContainer
|
||||
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.22.2
|
||||
|
||||
//replace github.com/MythicMeta/MythicContainer => ../../../../MythicMeta/MythicContainer
|
||||
|
||||
require github.com/MythicMeta/MythicContainer v1.4.8
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
||||
google.golang.org/grpc v1.67.1 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -0,0 +1,151 @@
|
||||
github.com/MythicMeta/MythicContainer v1.4.0 h1:vy8LsoSu5JBIEMWZn9ZnwQUqFvR6/35kxOHUu9rmA4E=
|
||||
github.com/MythicMeta/MythicContainer v1.4.0/go.mod h1:KMFetMj5A+s3IKt+oSgIFyQRXashXZzCABRnaETzPeY=
|
||||
github.com/MythicMeta/MythicContainer v1.4.1 h1:/2jixKxwrPjN+qT14ZZNogXMHp9bfLfxVuEsEfqsCaE=
|
||||
github.com/MythicMeta/MythicContainer v1.4.1/go.mod h1:f9nIBe8teDUW+NP/jYw9Otor1RZHXpC/4cxMv88+KQE=
|
||||
github.com/MythicMeta/MythicContainer v1.4.5 h1:bnXH3eA3NXpENk5ljUi9j91M2nfupztzfPxLArsVwng=
|
||||
github.com/MythicMeta/MythicContainer v1.4.5/go.mod h1:f9nIBe8teDUW+NP/jYw9Otor1RZHXpC/4cxMv88+KQE=
|
||||
github.com/MythicMeta/MythicContainer v1.4.6 h1:d+fD/WuhpWAl+MbaXB/c+OUxxT26AWUjtpCwCAo/42o=
|
||||
github.com/MythicMeta/MythicContainer v1.4.6/go.mod h1:f9nIBe8teDUW+NP/jYw9Otor1RZHXpC/4cxMv88+KQE=
|
||||
github.com/MythicMeta/MythicContainer v1.4.8 h1:/x8TJHQp5NKqmv3AK9zFBHi1L+PBHCoBx2ztjd54WRg=
|
||||
github.com/MythicMeta/MythicContainer v1.4.8/go.mod h1:BnUYftqQ9KsGxBd6RlyRcAHBrqV1CUcrRCjktWwc2Do=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
||||
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
|
||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw=
|
||||
golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5 h1:SbSDUWW1PAO24TNpLdeheoYPd7kllICcLU52x6eD4kQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
|
||||
google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -0,0 +1,26 @@
|
||||
.PHONY: default
|
||||
default: build_linux ;
|
||||
|
||||
BINARY_NAME?=mythic_http_server
|
||||
DEBUG_LEVEL?="debug"
|
||||
MYTHIC_SERVER_HOST?="127.0.0.1"
|
||||
MYTHIC_SERVER_PORT?="17443"
|
||||
|
||||
build:
|
||||
CGO_ENABLED=0 go build -o ${BINARY_NAME} .
|
||||
cp ${BINARY_NAME} /
|
||||
|
||||
build_local:
|
||||
CGO_ENABLED=0 go build -o ${BINARY_NAME} .
|
||||
|
||||
run:
|
||||
cp /${BINARY_NAME} .
|
||||
|
||||
build_macos:
|
||||
CGO_ENABLED=0 go build -o ${BINARY_NAME} .
|
||||
|
||||
run_custom: build_local
|
||||
DEBUG_LEVEL=${DEBUG_LEVEL} \
|
||||
MYTHIC_SERVER_HOST=${MYTHIC_SERVER_HOST} \
|
||||
MYTHIC_SERVER_PORT=${MYTHIC_SERVER_PORT} \
|
||||
./${BINARY_NAME}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"instances": [
|
||||
{
|
||||
"port": 80,
|
||||
"key_path": "privkey.pem",
|
||||
"cert_path": "fullchain.pem",
|
||||
"debug": true,
|
||||
"use_ssl": false,
|
||||
"ServerHeaders": {
|
||||
"Cache-Control": "max-age=0, no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Content-Type": "application/javascript; charset=utf-8",
|
||||
"Pragma": "no-cache",
|
||||
"Server": "NetDNA-cache/2.2"
|
||||
},
|
||||
"error_file_path": "",
|
||||
"error_status_code": 404,
|
||||
"payloads": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
module mythicHTTP
|
||||
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.22.2
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
)
|
||||
|
||||
require github.com/MythicMeta/MythicContainer v1.4.6
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.12.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.11.0 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/grpc v1.65.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -0,0 +1,201 @@
|
||||
github.com/MythicMeta/MythicContainer v1.4.0 h1:vy8LsoSu5JBIEMWZn9ZnwQUqFvR6/35kxOHUu9rmA4E=
|
||||
github.com/MythicMeta/MythicContainer v1.4.0/go.mod h1:KMFetMj5A+s3IKt+oSgIFyQRXashXZzCABRnaETzPeY=
|
||||
github.com/MythicMeta/MythicContainer v1.4.1 h1:/2jixKxwrPjN+qT14ZZNogXMHp9bfLfxVuEsEfqsCaE=
|
||||
github.com/MythicMeta/MythicContainer v1.4.1/go.mod h1:f9nIBe8teDUW+NP/jYw9Otor1RZHXpC/4cxMv88+KQE=
|
||||
github.com/MythicMeta/MythicContainer v1.4.5 h1:bnXH3eA3NXpENk5ljUi9j91M2nfupztzfPxLArsVwng=
|
||||
github.com/MythicMeta/MythicContainer v1.4.5/go.mod h1:f9nIBe8teDUW+NP/jYw9Otor1RZHXpC/4cxMv88+KQE=
|
||||
github.com/MythicMeta/MythicContainer v1.4.6 h1:d+fD/WuhpWAl+MbaXB/c+OUxxT26AWUjtpCwCAo/42o=
|
||||
github.com/MythicMeta/MythicContainer v1.4.6/go.mod h1:f9nIBe8teDUW+NP/jYw9Otor1RZHXpC/4cxMv88+KQE=
|
||||
github.com/bytedance/sonic v1.11.9 h1:LFHENlIY/SLzDWverzdOvgMztTxcfcF+cqNsz9pK5zg=
|
||||
github.com/bytedance/sonic v1.11.9/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg=
|
||||
github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
|
||||
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
||||
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
|
||||
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
||||
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
||||
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
|
||||
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
|
||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw=
|
||||
golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"mythicHTTP/webserver"
|
||||
"github.com/MythicMeta/MythicContainer/logging"
|
||||
)
|
||||
|
||||
func main() {
|
||||
webserver.InitializeLocalConfig()
|
||||
for index, instance := range webserver.Config.Instances {
|
||||
logging.LogInfo("Initializing webserver", "instance", index+1)
|
||||
router := webserver.Initialize(instance)
|
||||
// start serving up API routes
|
||||
logging.LogInfo("Starting webserver", "instance", index+1)
|
||||
webserver.StartServer(router, instance)
|
||||
}
|
||||
forever := make(chan bool)
|
||||
<-forever
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/MythicMeta/MythicContainer/logging"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Instances []instanceConfig `json:"instances"`
|
||||
}
|
||||
type instanceConfig struct {
|
||||
Port int `json:"port"`
|
||||
KeyPath string `json:"key_path"`
|
||||
CertPath string `json:"cert_path"`
|
||||
Debug bool `json:"debug"`
|
||||
UseSSL bool `json:"use_ssl"`
|
||||
Headers map[string]string `json:"ServerHeaders"`
|
||||
PayloadHostPaths map[string]string `json:"payloads"`
|
||||
BindIP string `json:"bind_ip"`
|
||||
ErrorFilePath string `json:"error_file_path"`
|
||||
ErrorStatusCode int `json:"error_status_code"`
|
||||
}
|
||||
|
||||
var (
|
||||
Config = config{}
|
||||
)
|
||||
|
||||
func InitializeLocalConfig() {
|
||||
if !fileExists(filepath.Join(getCwdFromExe(), "config.json")) {
|
||||
if _, err := os.Create(filepath.Join(getCwdFromExe(), "config.json")); err != nil {
|
||||
logging.LogFatalError(err, "[-] config.json doesn't exist and couldn't be created")
|
||||
}
|
||||
}
|
||||
if fileData, err := os.ReadFile("config.json"); err != nil {
|
||||
logging.LogError(err, "Failed to read in config.json file")
|
||||
} else if err = json.Unmarshal(fileData, &Config); err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal config bytes")
|
||||
} else {
|
||||
logging.LogInfo("[+] Successfully read in config.json")
|
||||
}
|
||||
}
|
||||
|
||||
func getCwdFromExe() string {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Fatalf("[-] Failed to get path to current executable: %v", err)
|
||||
}
|
||||
return filepath.Dir(exe)
|
||||
}
|
||||
|
||||
func fileExists(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return !info.IsDir()
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
mythicConfig "github.com/MythicMeta/MythicContainer/config"
|
||||
"github.com/MythicMeta/MythicContainer/logging"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Initialize(configInstance instanceConfig) *gin.Engine {
|
||||
//if mythicConfig.MythicConfig.DebugLevel == "warning" {
|
||||
// gin.SetMode(gin.ReleaseMode)
|
||||
//} else {
|
||||
gin.DisableConsoleColor()
|
||||
gin.SetMode(gin.DebugMode)
|
||||
//}
|
||||
r := gin.New()
|
||||
|
||||
// Global middleware
|
||||
r.Use(InitializeGinLogger(configInstance))
|
||||
// Recovery middleware recovers from any panics and writes a 500 if there was one.
|
||||
r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
|
||||
if err, ok := recovered.(string); ok {
|
||||
logging.LogError(nil, err)
|
||||
}
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
}))
|
||||
r.RedirectFixedPath = true
|
||||
r.HandleMethodNotAllowed = true
|
||||
r.RemoveExtraSlash = true
|
||||
r.MaxMultipartMemory = 8 << 20 // 8 MB
|
||||
// set up the routes to use
|
||||
setRoutes(r, configInstance)
|
||||
return r
|
||||
}
|
||||
|
||||
func StartServer(r *gin.Engine, configInstance instanceConfig) {
|
||||
logging.LogInfo("Starting webserver", "config", configInstance)
|
||||
if configInstance.UseSSL {
|
||||
if err := checkCerts(configInstance.CertPath, configInstance.KeyPath); err != nil {
|
||||
// certs don't exist, so generate them
|
||||
if err = generateCerts(configInstance); err != nil {
|
||||
logging.LogFatalError(err, "Failed to generate certs")
|
||||
}
|
||||
}
|
||||
if configInstance.BindIP != "" {
|
||||
go backgroundRunTLS(r, fmt.Sprintf("%s:%d", configInstance.BindIP, configInstance.Port), configInstance.CertPath, configInstance.KeyPath)
|
||||
} else {
|
||||
go backgroundRunTLS(r, fmt.Sprintf("%s:%d", "0.0.0.0", configInstance.Port), configInstance.CertPath, configInstance.KeyPath)
|
||||
}
|
||||
} else {
|
||||
if configInstance.BindIP != "" {
|
||||
go backgroundRun(r, fmt.Sprintf("%s:%d", configInstance.BindIP, configInstance.Port))
|
||||
} else {
|
||||
go backgroundRun(r, fmt.Sprintf("%s:%d", "0.0.0.0", configInstance.Port))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func backgroundRun(r *gin.Engine, address string) {
|
||||
if err := r.Run(address); err != nil {
|
||||
logging.LogFatalError(err, "Failed to run webserver")
|
||||
}
|
||||
}
|
||||
func backgroundRunTLS(r *gin.Engine, address string, certPath string, keyPath string) {
|
||||
if err := r.RunTLS(address, certPath, keyPath); err != nil {
|
||||
logging.LogFatalError(err, "Failed to run webserver")
|
||||
}
|
||||
}
|
||||
|
||||
func InitializeGinLogger(configInstance instanceConfig) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// Start timer
|
||||
start := time.Now()
|
||||
path := c.Request.URL.Path
|
||||
raw := c.Request.URL.RawQuery
|
||||
//logging.LogDebug("got new request")
|
||||
// Process request
|
||||
c.Next()
|
||||
param := gin.LogFormatterParams{
|
||||
Request: c.Request,
|
||||
Keys: c.Keys,
|
||||
}
|
||||
|
||||
// Stop timer
|
||||
param.TimeStamp = time.Now()
|
||||
param.Latency = param.TimeStamp.Sub(start)
|
||||
|
||||
param.ClientIP = c.ClientIP()
|
||||
param.Method = c.Request.Method
|
||||
param.StatusCode = c.Writer.Status()
|
||||
param.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
|
||||
|
||||
param.BodySize = c.Writer.Size()
|
||||
|
||||
if raw != "" {
|
||||
path = path + "?" + raw
|
||||
}
|
||||
|
||||
param.Path = path
|
||||
if configInstance.Debug {
|
||||
logging.LogInfo("WebServer Logging",
|
||||
"ClientIP", param.ClientIP,
|
||||
"method", param.Method,
|
||||
"path", param.Path,
|
||||
"protocol", param.Request.Proto,
|
||||
"statusCode", param.StatusCode,
|
||||
"latency", param.Latency,
|
||||
"error", param.ErrorMessage)
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func setRoutes(r *gin.Engine, configInstance instanceConfig) {
|
||||
// define generic get/post routes
|
||||
director := func(req *http.Request) {
|
||||
req.Header.Add("mythic", "http")
|
||||
req.Header.Add("X-forwarded-user-agent", req.Header.Get("User-Agent"))
|
||||
req.Header.Add("x-forwarded-url", req.URL.RequestURI())
|
||||
req.Header.Add("x-forwarded-for", req.RemoteAddr)
|
||||
req.Header.Add("x-forwarded-host", req.Host)
|
||||
req.URL.Scheme = "http"
|
||||
req.URL.Host = fmt.Sprintf("%s:%d", mythicConfig.MythicConfig.MythicServerHost, mythicConfig.MythicConfig.MythicServerPort)
|
||||
req.Host = fmt.Sprintf("%s:%d", mythicConfig.MythicConfig.MythicServerHost, mythicConfig.MythicConfig.MythicServerPort)
|
||||
req.URL.Path = "/agent_message"
|
||||
}
|
||||
modifyResponse := func(resp *http.Response) error {
|
||||
//logging.LogInfo("hitting modify response", "responseCode", resp.StatusCode)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if configInstance.ErrorFilePath != "" {
|
||||
statusCode := 200
|
||||
if configInstance.ErrorStatusCode > 0 {
|
||||
statusCode = configInstance.ErrorStatusCode
|
||||
}
|
||||
file, err := os.Open(configInstance.ErrorFilePath)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to get error_file_path")
|
||||
return err
|
||||
}
|
||||
fileStat, err := file.Stat()
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to stat error_file_path")
|
||||
return err
|
||||
}
|
||||
resp.Body = io.NopCloser(file)
|
||||
resp.Header["Content-Length"] = []string{fmt.Sprint(fileStat.Size())}
|
||||
resp.StatusCode = statusCode
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
proxy := &httputil.ReverseProxy{
|
||||
Director: director,
|
||||
ModifyResponse: modifyResponse,
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 10,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}}
|
||||
// each route is "/someWord" with an optional "/somethingElse" afterwards
|
||||
r.GET("/:val/*action", getRequest(configInstance, proxy))
|
||||
r.POST("/:val/*action", postRequest(configInstance, proxy))
|
||||
r.GET("/:val", getRequest(configInstance, proxy))
|
||||
r.POST("/:val", postRequest(configInstance, proxy))
|
||||
r.GET("/", getRequest(configInstance, proxy))
|
||||
r.POST("/", postRequest(configInstance, proxy))
|
||||
if len(configInstance.PayloadHostPaths) > 0 {
|
||||
for path, value := range configInstance.PayloadHostPaths {
|
||||
localVal := value
|
||||
directorForFiles := func(req *http.Request) {
|
||||
req.Header.Add("mythic", "http")
|
||||
req.Header.Add("X-forwarded-user-agent", req.Header.Get("User-Agent"))
|
||||
req.Header.Add("x-forwarded-url", req.URL.RequestURI())
|
||||
req.Header.Add("x-forwarded-for", req.RemoteAddr)
|
||||
req.Header.Add("x-forwarded-host", req.Host)
|
||||
req.URL.Scheme = "http"
|
||||
req.URL.Host = fmt.Sprintf("%s:%d", mythicConfig.MythicConfig.MythicServerHost, mythicConfig.MythicConfig.MythicServerPort)
|
||||
req.Host = fmt.Sprintf("%s:%d", mythicConfig.MythicConfig.MythicServerHost, mythicConfig.MythicConfig.MythicServerPort)
|
||||
req.URL.Path = fmt.Sprintf("/direct/download/%s", localVal)
|
||||
}
|
||||
proxyForFiles := httputil.ReverseProxy{
|
||||
Director: directorForFiles,
|
||||
ModifyResponse: modifyResponse,
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 10,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}}
|
||||
r.GET(path, generateServeFile(configInstance, fmt.Sprintf("%s", localVal), &proxyForFiles))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateServeFile(configInstance instanceConfig, fileUUID string, proxyForFiles *httputil.ReverseProxy) gin.HandlerFunc {
|
||||
|
||||
if configInstance.Debug {
|
||||
logging.LogInfo("debug route", "host", mythicConfig.MythicConfig.MythicServerHost, "path", "/direct/download/"+fileUUID)
|
||||
}
|
||||
return func(c *gin.Context) {
|
||||
if c.Request.Header.Get("X-Forwarded-For") == "" {
|
||||
c.Request.Header.Set("X-Forwarded-For", c.ClientIP())
|
||||
}
|
||||
proxyForFiles.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
|
||||
func getRequest(configInstance instanceConfig, proxy *httputil.ReverseProxy) gin.HandlerFunc {
|
||||
if configInstance.Debug {
|
||||
logging.LogInfo("debug route", "host", mythicConfig.MythicConfig.MythicServerHost, "path", "/agent_message")
|
||||
}
|
||||
return func(c *gin.Context) {
|
||||
for header, val := range configInstance.Headers {
|
||||
c.Header(header, val)
|
||||
}
|
||||
if c.Request.Header.Get("X-Forwarded-For") == "" {
|
||||
c.Request.Header.Set("X-Forwarded-For", c.ClientIP())
|
||||
}
|
||||
proxy.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
|
||||
func postRequest(configInstance instanceConfig, proxy *httputil.ReverseProxy) gin.HandlerFunc {
|
||||
if configInstance.Debug {
|
||||
logging.LogInfo("debug route", "host", mythicConfig.MythicConfig.MythicServerHost, "path", "/agent_message")
|
||||
}
|
||||
return func(c *gin.Context) {
|
||||
for header, val := range configInstance.Headers {
|
||||
c.Header(header, val)
|
||||
}
|
||||
if c.Request.Header.Get("X-Forwarded-For") == "" {
|
||||
c.Request.Header.Set("X-Forwarded-For", c.ClientIP())
|
||||
}
|
||||
proxy.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
|
||||
// code to generate self-signed certs pulled from github.com/kabukky/httpscerts
|
||||
// and from http://golang.org/src/crypto/tls/generate_cert.go.
|
||||
// only modifications were to use a specific elliptic curve cipher
|
||||
func checkCerts(certPath string, keyPath string) error {
|
||||
if _, err := os.Stat(certPath); os.IsNotExist(err) {
|
||||
return err
|
||||
} else if _, err := os.Stat(keyPath); os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func generateCerts(configInstance instanceConfig) error {
|
||||
|
||||
logging.LogInfo("[*] generating certs now...")
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to generate private key")
|
||||
return err
|
||||
}
|
||||
notBefore := time.Now()
|
||||
oneYear := 365 * 24 * time.Hour
|
||||
notAfter := notBefore.Add(oneYear)
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to generate serial number")
|
||||
return err
|
||||
}
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Mythic C2"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to create certificate")
|
||||
return err
|
||||
}
|
||||
certOut, err := os.Create(configInstance.CertPath)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to open "+configInstance.CertPath+" for writing")
|
||||
return err
|
||||
}
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
keyOut, err := os.OpenFile(configInstance.KeyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to open "+configInstance.KeyPath+" for writing")
|
||||
return err
|
||||
}
|
||||
marshalKey, err := x509.MarshalECPrivateKey(priv)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Unable to marshal ECDSA private key")
|
||||
return err
|
||||
}
|
||||
pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: marshalKey})
|
||||
keyOut.Close()
|
||||
logging.LogInfo("Successfully generated new SSL certs\n")
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,548 @@
|
||||
package c2functions
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
c2structs "github.com/MythicMeta/MythicContainer/c2_structs"
|
||||
"github.com/MythicMeta/MythicContainer/logging"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Instances []instanceConfig `json:"instances"`
|
||||
}
|
||||
type instanceConfig struct {
|
||||
Port int `json:"port"`
|
||||
KeyPath string `json:"key_path"`
|
||||
CertPath string `json:"cert_path"`
|
||||
Debug bool `json:"debug"`
|
||||
UseSSL bool `json:"use_ssl"`
|
||||
BindIP string `json:"bind_ip"`
|
||||
ServerHeaders map[string]interface{} `json:"ServerHeaders"`
|
||||
ErrorFilePath string `json:"error_file_path"`
|
||||
ErrorStatusCode int `json:"error_status_code"`
|
||||
PayloadHostPaths map[string]string `json:"payloads"`
|
||||
}
|
||||
|
||||
func getC2JsonConfig() (*config, error) {
|
||||
currentConfig := config{}
|
||||
if configBytes, err := os.ReadFile(filepath.Join(".", "http", "c2_code", "config.json")); err != nil {
|
||||
return nil, err
|
||||
} else if err = json.Unmarshal(configBytes, ¤tConfig); err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal config bytes")
|
||||
return nil, err
|
||||
} else {
|
||||
for i, _ := range currentConfig.Instances {
|
||||
if currentConfig.Instances[i].ErrorStatusCode == 0 {
|
||||
currentConfig.Instances[i].ErrorStatusCode = 404
|
||||
}
|
||||
}
|
||||
return ¤tConfig, nil
|
||||
}
|
||||
}
|
||||
func writeC2JsonConfig(cfg *config) error {
|
||||
jsonBytes, err := json.MarshalIndent(*cfg, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join(".", "http", "c2_code", "config.json"), jsonBytes, 644)
|
||||
}
|
||||
|
||||
var httpc2definition = c2structs.C2Profile{
|
||||
Name: "http",
|
||||
Author: "@its_a_feature_",
|
||||
Description: "Uses HTTP Get/Post messages for connectivity",
|
||||
IsP2p: false,
|
||||
IsServerRouted: true,
|
||||
ServerBinaryPath: filepath.Join(".", "http", "c2_code", "mythic_http_server"),
|
||||
ConfigCheckFunction: func(message c2structs.C2ConfigCheckMessage) c2structs.C2ConfigCheckMessageResponse {
|
||||
response := c2structs.C2ConfigCheckMessageResponse{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("Called config check\n%v", message),
|
||||
}
|
||||
if suppliedPort, ok := message.Parameters["callback_port"]; !ok {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get callback_port attribute"
|
||||
return response
|
||||
} else if suppliedHost, ok := message.Parameters["callback_host"]; !ok {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get callback_host attribute"
|
||||
return response
|
||||
} else if currentConfig, err := getC2JsonConfig(); err != nil {
|
||||
response.Success = false
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
possibleSSLPorts := []int{}
|
||||
possiblePorts := []int{}
|
||||
parameterPort := int(suppliedPort.(float64))
|
||||
parameterHost := suppliedHost.(string)
|
||||
for _, instance := range currentConfig.Instances {
|
||||
if instance.UseSSL {
|
||||
possibleSSLPorts = append(possibleSSLPorts, instance.Port)
|
||||
} else {
|
||||
possiblePorts = append(possiblePorts, instance.Port)
|
||||
}
|
||||
if instance.Port == parameterPort {
|
||||
// we found a match for our port and a configured port
|
||||
if strings.HasPrefix(parameterHost, "https") && !instance.UseSSL {
|
||||
// callback_host of https:// on port, but port isn't configured with ssl
|
||||
message := fmt.Sprintf("C2 Profile container is configured to NOT use SSL on port %d, but the callback host for the agent is using https, %s.\n\n",
|
||||
instance.Port, parameterHost)
|
||||
message += "This means there should be the following connectivity for success:\n"
|
||||
message += fmt.Sprintf("Agent via SSL to %s on port %d, then redirection to C2 Profile container WITHOUT SSL on port %d",
|
||||
parameterHost, parameterPort, parameterPort)
|
||||
response.Error = message
|
||||
//response.Success = false
|
||||
//return response
|
||||
} else if !strings.HasPrefix(parameterHost, "https") && instance.UseSSL {
|
||||
// callback_host of http:// on port, but port is configured with ssl
|
||||
message := fmt.Sprintf("C2 Profile container is configured to use SSL on port %d, but the callback host for the agent is using http, %s.\n\n",
|
||||
instance.Port, parameterHost)
|
||||
message += "This means there should be the following connectivity for success:\n"
|
||||
message += fmt.Sprintf("Agent via NO SSL to %s on port %d, then redirection to C2 Profile container WITH SSL on port %d",
|
||||
parameterHost, parameterPort, parameterPort)
|
||||
response.Error = message
|
||||
//response.Success = false
|
||||
//return response
|
||||
} else {
|
||||
// either http:// on port without ssl or https:// on port with ssl, all good
|
||||
response.Message = fmt.Sprintf("C2 Profile container and agent configuration match port, %d, and SSL expectations (%v)\n",
|
||||
instance.Port, instance.UseSSL)
|
||||
return response
|
||||
}
|
||||
}
|
||||
}
|
||||
message := "Specified use of SSL and ports indicate the use of a redirector, or a mismatch in expected connectivity.\n\n"
|
||||
message += "This means there should be the following connectivity for success:\n"
|
||||
if strings.HasPrefix(parameterHost, "https") {
|
||||
message += fmt.Sprintf("Agent via HTTPS on port %d to %s (should be a redirector).\n",
|
||||
parameterPort, parameterHost)
|
||||
} else {
|
||||
message += fmt.Sprintf("Agent via HTTP on port %d to %s (should be a redirector).\n",
|
||||
parameterPort, parameterHost)
|
||||
}
|
||||
if len(possibleSSLPorts) > 0 {
|
||||
message += fmt.Sprintf("Redirector then forwards request to C2 Profile container WITH SSL on one of the following ports: %v\n",
|
||||
possibleSSLPorts)
|
||||
}
|
||||
if len(possiblePorts) > 0 {
|
||||
if len(possibleSSLPorts) > 0 {
|
||||
message += fmt.Sprintf("Alternatively, redirector could forward request to C2 Profile container WITHOUT SSL on one of the following ports: %v\n",
|
||||
possiblePorts)
|
||||
} else {
|
||||
message += fmt.Sprintf("Redirector then forwards request to C2 Profile container WITHOUT SSL on one of the following ports: %v\n",
|
||||
possiblePorts)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(parameterHost, "https") {
|
||||
message += "\nAlternatively, this might mean that you want to do SSL but are not using SSL within your C2 Profile container.\n"
|
||||
message += "To add SSL to your C2 profile:\n"
|
||||
message += "\t1. Go to the C2 Profile page\n"
|
||||
message += "\t2. Click configure for the http profile\n"
|
||||
message += fmt.Sprintf(
|
||||
"\t3. Change 'use_ssl' to 'true' and make sure the port is %d\n",
|
||||
parameterPort)
|
||||
message += "\t4. Click to stop the profile and then start it again\n"
|
||||
}
|
||||
response.Message = message
|
||||
return response
|
||||
}
|
||||
},
|
||||
GetRedirectorRulesFunction: func(message c2structs.C2GetRedirectorRuleMessage) c2structs.C2GetRedirectorRuleMessageResponse {
|
||||
response := c2structs.C2GetRedirectorRuleMessageResponse{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("Called redirector status check:\n%v", message),
|
||||
}
|
||||
output := "#mod_rewrite rules generated from @AndrewChiles' project https://github.com/threatexpress/mythic2modrewrite:\n"
|
||||
errors := ""
|
||||
ua := ""
|
||||
uris := []string{}
|
||||
if headersInterface, ok := message.Parameters["headers"]; !ok {
|
||||
errors += "#[!] Headers c2 profile parameter not found\n"
|
||||
} else {
|
||||
headers := headersInterface.(map[string]interface{})
|
||||
if userAgent, ok := headers["User-Agent"]; !ok {
|
||||
errors += "#[!] User-Agent not found in headers\n"
|
||||
} else {
|
||||
ua = userAgent.(string)
|
||||
}
|
||||
}
|
||||
if getURI, ok := message.Parameters["get_uri"]; !ok {
|
||||
errors += "#[!] No GET URI found\n"
|
||||
} else {
|
||||
uris = append(uris, "/"+getURI.(string))
|
||||
}
|
||||
if postURI, ok := message.Parameters["post_uri"]; !ok {
|
||||
errors += "#[!] No POST URI found\n"
|
||||
} else {
|
||||
uris = append(uris, "/"+postURI.(string))
|
||||
}
|
||||
// Create UA in modrewrite syntax. No regex needed in UA string matching, but () characters must be escaped
|
||||
uaString := strings.ReplaceAll(ua, "(", "\\(")
|
||||
uaString = strings.ReplaceAll(uaString, ")", "\\)")
|
||||
// Create URI string in modrewrite syntax. "*" are needed in regex to support GET and uri-append parameters on the URI
|
||||
urisString := strings.Join(uris, ".*|") + ".*"
|
||||
c2RewriteOutput := []string{}
|
||||
if currentConfig, err := getC2JsonConfig(); err != nil {
|
||||
logging.LogError(err, "Failed to get current json configuration")
|
||||
response.Error = "Failed to get current json configuration"
|
||||
response.Success = false
|
||||
return response
|
||||
} else {
|
||||
c2RewriteTemplate := "RewriteRule ^.*$ \"%s%%{REQUEST_URI}\" [P,L]"
|
||||
for _, instance := range currentConfig.Instances {
|
||||
if instance.UseSSL {
|
||||
serverURL := fmt.Sprintf("https://C2_SERVER_HERE:%d", instance.Port)
|
||||
c2RewriteOutput = append(c2RewriteOutput, fmt.Sprintf(c2RewriteTemplate, serverURL))
|
||||
} else {
|
||||
serverURL := fmt.Sprintf("http://C2_SERVER_HERE:%d", instance.Port)
|
||||
c2RewriteOutput = append(c2RewriteOutput, fmt.Sprintf(c2RewriteTemplate, serverURL))
|
||||
}
|
||||
}
|
||||
}
|
||||
output += "#\tReplace 'C2_SERVER_HERE' with the IP/Domain address of where matching traffic should go\n"
|
||||
|
||||
htaccessTemplate := `
|
||||
########################################
|
||||
## .htaccess START
|
||||
RewriteEngine On
|
||||
## C2 Traffic (HTTP-GET, HTTP-POST, HTTP-STAGER URIs)
|
||||
## Logic: If a requested URI AND the User-Agent matches, proxy the connection to the Teamserver
|
||||
## Consider adding other HTTP checks to fine tune the check. (HTTP Cookie, HTTP Referer, HTTP Query String, etc)
|
||||
## Refer to http://httpd.apache.org/docs/current/mod/mod_rewrite.html
|
||||
%s
|
||||
## Redirect all other traffic here
|
||||
RewriteRule ^.*$ redirect/? [L,R=302]
|
||||
## .htaccess END
|
||||
########################################
|
||||
`
|
||||
htaccessConditionTemplate := `
|
||||
## Only allow GET and POST methods to pass to the C2 server
|
||||
RewriteCond %%{REQUEST_METHOD} ^(GET|POST) [NC]
|
||||
## Profile URIs
|
||||
RewriteCond %%{REQUEST_URI} ^(%s)$
|
||||
## Profile UserAgent
|
||||
RewriteCond %%{HTTP_USER_AGENT} "%s"`
|
||||
htaccessConditions := fmt.Sprintf(htaccessConditionTemplate, urisString, uaString)
|
||||
allHtaccessConditions := ""
|
||||
for _, c2Entry := range c2RewriteOutput {
|
||||
allHtaccessConditions += htaccessConditions + "\n" + c2Entry + "\n"
|
||||
}
|
||||
htaccess := fmt.Sprintf(htaccessTemplate, allHtaccessConditions)
|
||||
output += "#\tReplace 'redirect' with the http(s) address of where non-matching traffic should go, ex: https://redirect.com\n"
|
||||
output += "\n" + htaccess
|
||||
response.Message = output
|
||||
return response
|
||||
},
|
||||
OPSECCheckFunction: func(message c2structs.C2OPSECMessage) c2structs.C2OPSECMessageResponse {
|
||||
response := c2structs.C2OPSECMessageResponse{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("Called opsec check:\n%v", message),
|
||||
}
|
||||
if callbackHost, ok := message.Parameters["callback_host"]; !ok {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get callback_host attribute"
|
||||
return response
|
||||
} else if callbackPort, ok := message.Parameters["callback_port"]; !ok {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get callback_port attribute"
|
||||
return response
|
||||
} else if callbackHost.(string) == "https://domain.com" {
|
||||
response.Success = false
|
||||
response.Error = "Callback Host is set to default of https://domain.com!\n"
|
||||
return response
|
||||
} else if len(strings.Split(callbackHost.(string), ":")) != 2 {
|
||||
response.Success = false
|
||||
response.Error = fmt.Sprintf("callback host is improperly configured! %v shouldn't specify a port, that should be in the callback_port field", callbackHost)
|
||||
return response
|
||||
} else if strings.HasPrefix(callbackHost.(string), "https") {
|
||||
standardHttpsPorts := []int{443, 8443, 7443}
|
||||
for _, port := range standardHttpsPorts {
|
||||
if port == int(callbackPort.(float64)) {
|
||||
return response
|
||||
}
|
||||
}
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("Callback port, %d, is unusual for https scheme", int(callbackPort.(float64)))
|
||||
return response
|
||||
} else {
|
||||
response.Message = "No immediate issues with configuration"
|
||||
return response
|
||||
}
|
||||
},
|
||||
GetIOCFunction: func(message c2structs.C2GetIOCMessage) c2structs.C2GetIOCMessageResponse {
|
||||
response := c2structs.C2GetIOCMessageResponse{Success: true}
|
||||
callbackHost, err := message.GetStringArg("callback_host")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get callback_host"
|
||||
return response
|
||||
}
|
||||
callbackPort, err := message.GetNumberArg("callback_port")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get callback_port"
|
||||
return response
|
||||
}
|
||||
getURI, err := message.GetStringArg("get_uri")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get get_uri"
|
||||
return response
|
||||
}
|
||||
postURI, err := message.GetStringArg("post_uri")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get post_uri"
|
||||
return response
|
||||
}
|
||||
queryPathForGet, err := message.GetStringArg("query_path_name")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get query_path_name"
|
||||
return response
|
||||
}
|
||||
|
||||
response.IOCs = append(response.IOCs, c2structs.IOC{
|
||||
Type: "url",
|
||||
IOC: fmt.Sprintf("%s:%v", callbackHost, callbackPort),
|
||||
})
|
||||
response.IOCs = append(response.IOCs, c2structs.IOC{
|
||||
Type: "url",
|
||||
IOC: fmt.Sprintf("%s:%v/%s?%s=", callbackHost, callbackPort, getURI, queryPathForGet),
|
||||
})
|
||||
response.IOCs = append(response.IOCs, c2structs.IOC{
|
||||
Type: "url",
|
||||
IOC: fmt.Sprintf("%s:%v/%s", callbackHost, callbackPort, postURI),
|
||||
})
|
||||
return response
|
||||
},
|
||||
SampleMessageFunction: func(message c2structs.C2SampleMessageMessage) c2structs.C2SampleMessageResponse {
|
||||
response := c2structs.C2SampleMessageResponse{Success: true}
|
||||
getURI, err := message.GetStringArg("get_uri")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get get_uri"
|
||||
return response
|
||||
}
|
||||
postURI, err := message.GetStringArg("post_uri")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get post_uri"
|
||||
return response
|
||||
}
|
||||
queryPathForGet, err := message.GetStringArg("query_path_name")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get query_path_name"
|
||||
return response
|
||||
}
|
||||
headers, err := message.GetDictionaryArg("headers")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get headers"
|
||||
return response
|
||||
}
|
||||
callbackHost, err := message.GetStringArg("callback_host")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get callback host"
|
||||
return response
|
||||
}
|
||||
callbackPort, err := message.GetNumberArg("callback_port")
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = "Failed to get callback port"
|
||||
return response
|
||||
}
|
||||
sampleRawBytes, _ := base64.URLEncoding.DecodeString("MjQ1M2Q2NjQtYmZhNC00ZTI5LTgzMjEtNTgxYzQwNDBjYWM5Iv_gaPq1yVK76sNsMwCgtIOOQPWJ_fO0YBZGtyvdGIcDXnaTmlG6GLJ-ZV9NdhfNKxlM4u7JOHQeB4zJmQiNf1mqokqvhh1Vm9dYRc8O87J8oIv-H1sIENR-NDW1mirT")
|
||||
base64URLEncoding := base64.URLEncoding.EncodeToString(sampleRawBytes)
|
||||
base64StdEncoding := base64.StdEncoding.EncodeToString(sampleRawBytes)
|
||||
sampleCURLGet := "curl "
|
||||
sampleCURLPost := fmt.Sprintf("curl -X POST -d \"%s\" ", base64StdEncoding)
|
||||
for key, value := range headers {
|
||||
sampleCURLGet += fmt.Sprintf("-H \"%s: %s\" ", key, value)
|
||||
sampleCURLPost += fmt.Sprintf("-H \"%s: %s\" ", key, value)
|
||||
}
|
||||
sampleCURLGet += fmt.Sprintf("%s:%d/%s?%s=%s", callbackHost, int(callbackPort), getURI, queryPathForGet, base64URLEncoding)
|
||||
sampleCURLPost += fmt.Sprintf("%s:%d/%s", callbackHost, int(callbackPort), postURI)
|
||||
response.Message = fmt.Sprintf("GET:\n%s\n\nPOST:\n%s\n\n", sampleCURLGet, sampleCURLPost)
|
||||
return response
|
||||
},
|
||||
HostFileFunction: func(message c2structs.C2HostFileMessage) c2structs.C2HostFileMessageResponse {
|
||||
config, err := getC2JsonConfig()
|
||||
if err != nil {
|
||||
return c2structs.C2HostFileMessageResponse{
|
||||
Success: false,
|
||||
Error: err.Error(),
|
||||
}
|
||||
}
|
||||
for i, _ := range config.Instances {
|
||||
if config.Instances[i].PayloadHostPaths == nil {
|
||||
config.Instances[i].PayloadHostPaths = make(map[string]string)
|
||||
}
|
||||
if message.Remove {
|
||||
for j, _ := range config.Instances[i].PayloadHostPaths {
|
||||
if config.Instances[i].PayloadHostPaths[j] == message.FileUUID {
|
||||
delete(config.Instances[i].PayloadHostPaths, j)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
config.Instances[i].PayloadHostPaths[message.HostURL] = message.FileUUID
|
||||
}
|
||||
|
||||
}
|
||||
err = writeC2JsonConfig(config)
|
||||
if err != nil {
|
||||
return c2structs.C2HostFileMessageResponse{
|
||||
Success: false,
|
||||
Error: err.Error(),
|
||||
}
|
||||
}
|
||||
return c2structs.C2HostFileMessageResponse{
|
||||
Success: true,
|
||||
RestartInternalServer: true,
|
||||
}
|
||||
},
|
||||
}
|
||||
var httpc2parameters = []c2structs.C2Parameter{
|
||||
{
|
||||
Name: "callback_port",
|
||||
Description: "Callback Port",
|
||||
DefaultValue: 80,
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_NUMBER,
|
||||
Required: false,
|
||||
VerifierRegex: "^[0-9]+$",
|
||||
},
|
||||
{
|
||||
Name: "killdate",
|
||||
Description: "Kill Date",
|
||||
DefaultValue: 365,
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_DATE,
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "encrypted_exchange_check",
|
||||
Description: "Perform Key Exchange",
|
||||
DefaultValue: true,
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_BOOLEAN,
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "callback_jitter",
|
||||
Description: "Callback Jitter in percent",
|
||||
DefaultValue: 23,
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_NUMBER,
|
||||
Required: false,
|
||||
VerifierRegex: "^[0-9]+$",
|
||||
},
|
||||
{
|
||||
Name: "headers",
|
||||
Description: "HTTP Headers",
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_DICTIONARY,
|
||||
Required: false,
|
||||
DictionaryChoices: []c2structs.C2ParameterDictionary{
|
||||
{
|
||||
Name: "User-Agent",
|
||||
DefaultShow: true,
|
||||
DefaultValue: "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
|
||||
},
|
||||
{
|
||||
Name: "Host",
|
||||
DefaultShow: false,
|
||||
DefaultValue: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "AESPSK",
|
||||
Description: "Encryption Type",
|
||||
DefaultValue: "aes256_hmac",
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_CHOOSE_ONE,
|
||||
Required: false,
|
||||
IsCryptoType: true,
|
||||
Choices: []string{
|
||||
"aes256_hmac",
|
||||
"none",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "callback_host",
|
||||
Description: "Callback Host",
|
||||
DefaultValue: "https://domain.com",
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_STRING,
|
||||
Required: true,
|
||||
VerifierRegex: "^(http|https):\\/\\/[^\\/:]*$",
|
||||
},
|
||||
{
|
||||
Name: "get_uri",
|
||||
Description: "GET request URI (don't include leading /)",
|
||||
DefaultValue: "index",
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_STRING,
|
||||
Required: false,
|
||||
VerifierRegex: "^[^\\/].*",
|
||||
},
|
||||
{
|
||||
Name: "post_uri",
|
||||
Description: "POST request URI (don't include leading /)",
|
||||
DefaultValue: "data",
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_STRING,
|
||||
Required: false,
|
||||
VerifierRegex: "^[^\\/].*",
|
||||
},
|
||||
{
|
||||
Name: "query_path_name",
|
||||
Description: "Name of the query parameter for GET requests",
|
||||
DefaultValue: "q",
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_STRING,
|
||||
Required: false,
|
||||
VerifierRegex: "^[^\\/].*",
|
||||
},
|
||||
{
|
||||
Name: "proxy_host",
|
||||
Description: "Proxy Host",
|
||||
DefaultValue: "",
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_STRING,
|
||||
Required: false,
|
||||
VerifierRegex: "^$|^(http|https):\\/\\/[a-zA-Z0-9]+",
|
||||
},
|
||||
{
|
||||
Name: "proxy_port",
|
||||
Description: "Proxy Port",
|
||||
DefaultValue: "",
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_STRING,
|
||||
Required: false,
|
||||
VerifierRegex: "^$|^[0-9]+$",
|
||||
},
|
||||
{
|
||||
Name: "proxy_user",
|
||||
Description: "Proxy Username",
|
||||
DefaultValue: "",
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_STRING,
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "proxy_pass",
|
||||
Description: "Proxy Password",
|
||||
DefaultValue: "",
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_STRING,
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "callback_interval",
|
||||
Description: "Callback Interval in seconds",
|
||||
DefaultValue: 10,
|
||||
ParameterType: c2structs.C2_PARAMETER_TYPE_NUMBER,
|
||||
Required: false,
|
||||
VerifierRegex: "^[0-9]+$",
|
||||
},
|
||||
}
|
||||
|
||||
func Initialize() {
|
||||
c2structs.AllC2Data.Get("http").AddC2Definition(httpc2definition)
|
||||
c2structs.AllC2Data.Get("http").AddParameters(httpc2parameters)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
httpfunctions "MyContainer/http/c2functions"
|
||||
"github.com/MythicMeta/MythicContainer"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// load up the agent functions directory so all the init() functions execute
|
||||
httpfunctions.Initialize()
|
||||
// sync over definitions and listen
|
||||
MythicContainer.StartAndRunForever([]MythicContainer.MythicServices{
|
||||
MythicContainer.MythicServiceC2,
|
||||
})
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
from mythic_c2_container.MythicRPC import *
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
import netifaces
|
||||
|
||||
# request is a dictionary: {"action": func_name, "message": "the input", "task_id": task id num}
|
||||
# must return an RPCResponse() object and set .status to an instance of RPCStatus and response to str of message
|
||||
async def test(request):
|
||||
response = RPCResponse()
|
||||
response.status = RPCStatus.Success
|
||||
response.response = "hello"
|
||||
resp = await MythicRPC().execute("create_event_message", message="Test message", warning=False)
|
||||
return response
|
||||
|
||||
|
||||
# The opsec function is called when a payload is created as a check to see if the parameters supplied are good
|
||||
# The input for "request" is a dictionary of:
|
||||
# {
|
||||
# "action": "opsec",
|
||||
# "parameters": {
|
||||
# "param_name": "param_value",
|
||||
# "param_name2: "param_value2",
|
||||
# }
|
||||
# }
|
||||
# This function should return one of two things:
|
||||
# For success: {"status": "success", "message": "your success message here" }
|
||||
# For error: {"status": "error", "error": "your error message here" }
|
||||
async def opsec(request):
|
||||
if request["parameters"]["callback_host"] == "https://domain.com":
|
||||
return {"status": "error", "error": "Callback Host is set to default of https://domain.com!\n"}
|
||||
if request["parameters"]["callback_host"].count(":") == 2:
|
||||
return {"status": "error", "error": f"Callback Host specifies a port ({request['parameters']['callback_host']})! This should be omitted and specified in the Callback Port parameter.\n"}
|
||||
if "https" in request["parameters"]["callback_host"] and request["parameters"]["callback_port"] not in ["443", "8443", "7443"]:
|
||||
return {"status": "success", "message": f"Mismatch in callback host: HTTPS specified, but port {request['parameters']['callback_port']}, is not standard HTTPS port\n"}
|
||||
return {"status": "success", "message": "Basic OPSEC Check Passed\n"}
|
||||
|
||||
|
||||
# The config_check function is called when a payload is created as a check to see if the parameters supplied
|
||||
# to the agent match up with what's in the C2 profile
|
||||
# The input for "request" is a dictionary of:
|
||||
# {
|
||||
# "action": "config_check",
|
||||
# "parameters": {
|
||||
# "param_name": "param_value",
|
||||
# "param_name2: "param_value2",
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# This function should return one of two things:
|
||||
# For success: {"status": "success", "message": "your success message here" }
|
||||
# For error: {"status": "error", "error": "your error message here" }
|
||||
async def config_check(request):
|
||||
try:
|
||||
with open("../c2_code/config.json") as f:
|
||||
config = json.load(f)
|
||||
possible_ports = []
|
||||
for inst in config["instances"]:
|
||||
possible_ports.append({"port": inst["port"], "use_ssl": inst["use_ssl"]})
|
||||
if str(inst["port"]) == str(request["parameters"]["callback_port"]):
|
||||
if "https" in request["parameters"]["callback_host"] and not inst["use_ssl"]:
|
||||
message = f"C2 Profile container is configured to NOT use SSL on port {inst['port']}, but the callback host for the agent is using https, {request['parameters']['callback_host']}.\n\n"
|
||||
message += "This means there should be the following connectivity for success:\n"
|
||||
message += f"Agent via SSL to {request['parameters']['callback_host']} on port {inst['port']}, then redirection to C2 Profile container WITHOUT SSL on port {inst['port']}"
|
||||
return {"status": "error", "error": message}
|
||||
elif "https" not in request["parameters"]["callback_host"] and inst["use_ssl"]:
|
||||
message = f"C2 Profile container is configured to use SSL on port {inst['port']}, but the callback host for the agent is using http, {request['parameters']['callback_host']}.\n\n"
|
||||
message += "This means there should be the following connectivity for success:\n"
|
||||
message += f"Agent via NO SSL to {request['parameters']['callback_host']} on port {inst['port']}, then redirection to C2 Profile container WITH SSL on port {inst['port']}"
|
||||
return {"status": "error", "error": message}
|
||||
else:
|
||||
message = f"C2 Profile container and agent configuration match port, {inst['port']}, and SSL expectations.\n"
|
||||
return {"status": "success", "message": message}
|
||||
message = f"Failed to find port, {request['parameters']['callback_port']}, in C2 Profile configuration\n"
|
||||
message += f"This could indicate the use of a redirector, or a mismatch in expected connectivity.\n\n"
|
||||
message += f"This means there should be the following connectivity for success:\n"
|
||||
if "https" in request["parameters"]["callback_host"]:
|
||||
message += f"Agent via HTTPS on port {request['parameters']['callback_port']} to {request['parameters']['callback_host']} (should be a redirector).\n"
|
||||
else:
|
||||
message += f"Agent via HTTP on port {request['parameters']['callback_port']} to {request['parameters']['callback_host']} (should be a redirector).\n"
|
||||
if len(possible_ports) == 1:
|
||||
message += f"Redirector then forwards request to C2 Profile container on port, {possible_ports[0]['port']}, {'WITH SSL' if possible_ports[0]['use_ssl'] else 'WITHOUT SSL'}"
|
||||
else:
|
||||
message += f"Redirector then forwards request to C2 Profile container on one of the following ports: {json.dumps(possible_ports)}\n"
|
||||
if "https" in request["parameters"]["callback_host"]:
|
||||
message += f"\nAlternatively, this might mean that you want to do SSL but are not using SSL within your C2 Profile container.\n"
|
||||
message += f"To add SSL to your C2 profile:\n"
|
||||
message += f"\t1. Go to the C2 Profile page\n"
|
||||
message += f"\t2. Click configure for the http profile\n"
|
||||
message += f"\t3. Change 'use_ssl' to 'true' and make sure the port is {request['parameters']['callback_port']}\n"
|
||||
message += f"\t4. Click to stop the profile and then start it again\n"
|
||||
return {"status": "success", "message": message}
|
||||
except Exception as e:
|
||||
return {"status": "error", "error": str(e)}
|
||||
|
||||
|
||||
# The redirect_rules function is called on demand by an operator to generate redirection rules for a specific payload
|
||||
# The input for "request" is a dictionary of:
|
||||
# {
|
||||
# "action": "redirect_rules",
|
||||
# "parameters": {
|
||||
# "param_name": "param_value",
|
||||
# "param_name2: "param_value2",
|
||||
# }
|
||||
# }
|
||||
# This function should return one of two things:
|
||||
# For success: {"status": "success", "message": "your success message here" }
|
||||
# For error: {"status": "error", "error": "your error message here" }
|
||||
async def redirect_rules(request):
|
||||
output = "mod_rewrite rules generated from @AndrewChiles' project https://github.com/threatexpress/mythic2modrewrite:\n"
|
||||
# Get User-Agent
|
||||
errors = ""
|
||||
ua = ''
|
||||
uris = []
|
||||
if "headers" in request['parameters']:
|
||||
for header in request['parameters']["headers"]:
|
||||
if header["key"] == "User-Agent":
|
||||
ua = header["value"]
|
||||
else:
|
||||
errors += "[!] User-Agent Not Found\n"
|
||||
# Get all profile URIs
|
||||
if "get_uri" in request['parameters']:
|
||||
uris.append("/" + request['parameters']["get_uri"])
|
||||
else:
|
||||
errors += "[!] No GET URI found\n"
|
||||
if "post_uri" in request['parameters']:
|
||||
uris.append("/" + request['parameters']["post_uri"])
|
||||
else:
|
||||
errors += "[!] No POST URI found\n"
|
||||
# Create UA in modrewrite syntax. No regex needed in UA string matching, but () characters must be escaped
|
||||
ua_string = ua.replace('(', '\(').replace(')', '\)')
|
||||
# Create URI string in modrewrite syntax. "*" are needed in regex to support GET and uri-append parameters on the URI
|
||||
uris_string = ".*|".join(uris) + ".*"
|
||||
try:
|
||||
interface = netifaces.gateways()['default'][netifaces.AF_INET][1]
|
||||
address = netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr']
|
||||
c2_rewrite_template = """RewriteRule ^.*$ "{c2server}%{{REQUEST_URI}}" [P,L]"""
|
||||
c2_rewrite_output = []
|
||||
with open("../c2_code/config.json") as f:
|
||||
config = json.load(f)
|
||||
for inst in config["instances"]:
|
||||
c2_rewrite_output.append(c2_rewrite_template.format(
|
||||
c2server=f"https://{address}:{inst['port']}" if inst["use_ssl"] else f"http://{address}:{inst['port']}"
|
||||
))
|
||||
except Exception as e:
|
||||
errors += "[!] Failed to get C2 Profile container IP address. Replace 'c2server' in HTACCESS rules with correct IP\n"
|
||||
c2_rewrite_output = ["""RewriteRule ^.*$ "{c2server}%{{REQUEST_URI}}" [P,L]"""]
|
||||
htaccess_template = '''
|
||||
########################################
|
||||
## .htaccess START
|
||||
RewriteEngine On
|
||||
## C2 Traffic (HTTP-GET, HTTP-POST, HTTP-STAGER URIs)
|
||||
## Logic: If a requested URI AND the User-Agent matches, proxy the connection to the Teamserver
|
||||
## Consider adding other HTTP checks to fine tune the check. (HTTP Cookie, HTTP Referer, HTTP Query String, etc)
|
||||
## Refer to http://httpd.apache.org/docs/current/mod/mod_rewrite.html
|
||||
## Only allow GET and POST methods to pass to the C2 server
|
||||
RewriteCond %{{REQUEST_METHOD}} ^(GET|POST) [NC]
|
||||
## Profile URIs
|
||||
RewriteCond %{{REQUEST_URI}} ^({uris})$
|
||||
## Profile UserAgent
|
||||
RewriteCond %{{HTTP_USER_AGENT}} "{ua}"
|
||||
{c2servers}
|
||||
## Redirect all other traffic here
|
||||
RewriteRule ^.*$ {redirect}/? [L,R=302]
|
||||
## .htaccess END
|
||||
########################################
|
||||
'''
|
||||
htaccess = htaccess_template.format(uris=uris_string, ua=ua_string, c2servers="\n".join(c2_rewrite_output), redirect="redirect")
|
||||
output += "\tReplace 'redirect' with the http(s) address of where non-matching traffic should go, ex: https://redirect.com\n"
|
||||
output += f"\n{htaccess}"
|
||||
if errors != "":
|
||||
return {"status": "error", "error": errors}
|
||||
else:
|
||||
return {"status": "success", "message": output}
|
||||
@@ -1,132 +0,0 @@
|
||||
from mythic_c2_container.C2ProfileBase import *
|
||||
|
||||
|
||||
class HTTP(C2Profile):
|
||||
name = "http"
|
||||
description = "Uses HTTP(S) connections with a simple query parameter or basic POST messages. For more configuration options use dynamicHTTP."
|
||||
author = "@its_a_feature_"
|
||||
is_p2p = False
|
||||
is_server_routed = False
|
||||
parameters = [
|
||||
C2ProfileParameter(
|
||||
name="callback_port",
|
||||
description="Callback Port",
|
||||
default_value="80",
|
||||
verifier_regex="^[0-9]+$",
|
||||
required=False,
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="killdate",
|
||||
description="Kill Date",
|
||||
parameter_type=ParameterType.Date,
|
||||
default_value=365,
|
||||
required=False,
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="encrypted_exchange_check",
|
||||
description="Perform Key Exchange",
|
||||
choices=["T", "F"],
|
||||
required=False,
|
||||
parameter_type=ParameterType.ChooseOne,
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="callback_jitter",
|
||||
description="Callback Jitter in percent",
|
||||
default_value="23",
|
||||
verifier_regex="^[0-9]+$",
|
||||
required=False,
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="headers",
|
||||
description="HTTP Headers",
|
||||
required=False,
|
||||
parameter_type=ParameterType.Dictionary,
|
||||
default_value=[
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"max": 1,
|
||||
"default_value": "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
|
||||
"default_show": True,
|
||||
},
|
||||
{
|
||||
"name": "Host",
|
||||
"max": 1,
|
||||
"default_value": "",
|
||||
"default_show": False,
|
||||
},
|
||||
{
|
||||
"name": "*",
|
||||
"max": -1,
|
||||
"default_value": "",
|
||||
"default_show": False
|
||||
}
|
||||
]
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="AESPSK",
|
||||
description="Crypto type",
|
||||
default_value="aes256_hmac",
|
||||
parameter_type=ParameterType.ChooseOne,
|
||||
choices=["aes256_hmac", "none"],
|
||||
required=False,
|
||||
crypto_type=True
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="callback_host",
|
||||
description="Callback Host",
|
||||
default_value="https://domain.com",
|
||||
verifier_regex="^(http|https):\/\/[a-zA-Z0-9]+",
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="get_uri",
|
||||
description="GET request URI (don't include leading /)",
|
||||
default_value="index",
|
||||
required=False,
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="post_uri",
|
||||
description="POST request URI (don't include leading /)",
|
||||
default_value="data",
|
||||
required=False,
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="query_path_name",
|
||||
description="Name of the query parameter for GET requests",
|
||||
default_value="q",
|
||||
required=False,
|
||||
verifier_regex="^[^\/]",
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="proxy_host",
|
||||
description="Proxy Host",
|
||||
default_value="",
|
||||
required=False,
|
||||
verifier_regex="^$|^(http|https):\/\/[a-zA-Z0-9]+",
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="proxy_port",
|
||||
description="Proxy Port",
|
||||
default_value="",
|
||||
verifier_regex="^$|^[0-9]+$",
|
||||
required=False,
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="proxy_user",
|
||||
description="Proxy Username",
|
||||
default_value="",
|
||||
required=False,
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="proxy_pass",
|
||||
description="Proxy Password",
|
||||
default_value="",
|
||||
required=False,
|
||||
),
|
||||
C2ProfileParameter(
|
||||
name="callback_interval",
|
||||
description="Callback Interval in seconds",
|
||||
default_value="10",
|
||||
verifier_regex="^[0-9]+$",
|
||||
required=False,
|
||||
),
|
||||
]
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd /Mythic/mythic
|
||||
|
||||
export PYTHONPATH=/Mythic:/Mythic/mythic
|
||||
|
||||
python3.8 mythic_service.py
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
from mythic_c2_container import mythic_service
|
||||
mythic_service.start_service_and_heartbeat(debug=False)
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"username": "mythic_user",
|
||||
"password": "mythic_password",
|
||||
"virtual_host": "mythic_vhost",
|
||||
"host": "127.0.0.1",
|
||||
"name": "hostname",
|
||||
"container_files_path": "/Mythic/"
|
||||
}
|
||||
+9
-6
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"exclude_payload_type": false,
|
||||
"exclude_c2_profiles": false,
|
||||
"exclude_documentation_payload": false,
|
||||
"exclude_documentation_c2": false,
|
||||
"exclude_agent_icons": false
|
||||
}
|
||||
"exclude_payload_type": false,
|
||||
"exclude_c2_profiles": false,
|
||||
"exclude_documentation_payload": false,
|
||||
"exclude_documentation_c2": false,
|
||||
"exclude_agent_icons": false,
|
||||
"remote_images": {
|
||||
"http": "ghcr.io/mythicc2profiles/http:v0.0.2.9"
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ Legend:
|
||||
- Dotted line is a message within that connection
|
||||
|
||||
## Configuration Options
|
||||
The profile reads a `config.json` file for a set of instances of `Sanic` webservers to stand up (`80` by default) and redirects the content.
|
||||
The profile reads a `config.json` file for a set of instances of `gin` webservers to stand up (`80` by default) and redirects the content.
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -42,15 +42,20 @@ The profile reads a `config.json` file for a set of instances of `Sanic` webserv
|
||||
"key_path": "privkey.pem",
|
||||
"cert_path": "fullchain.pem",
|
||||
"debug": true,
|
||||
"use_ssl": false
|
||||
"use_ssl": false,
|
||||
"error_file_path": "",
|
||||
"error_status_code": 404,
|
||||
"payloads": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You can specify the headers that the profile will set on Server responses. If there's an error, the server will return a `404` message based on the `fake.html` file contents in `C2_Profiles/HTTP/c2_code`.
|
||||
You can specify the headers that the profile will set on Server responses.
|
||||
If there's an error, the server will return a `error_status_code` message based on the `error_file_path` file contents in `C2_Profiles/HTTP/c2_code`.
|
||||
|
||||
If you want to use SSL within this container specifically, then you can put your key and cert in the `C2_Profiles/HTTP/c2_code` folder and update the `key_path` and `cert_path` variables to have the `names` of those files. Alternatively, if you specify `use_ssl` as true and you don't have any certs already placed on disk, then the profile will automatically generate some self-signed certs for you to use.
|
||||
If you want to use SSL within this container specifically, then you can put your key and cert in the `C2_Profiles/HTTP/c2_code` folder and update the `key_path` and `cert_path` variables to have the `names` of those files.
|
||||
Alternatively, if you specify `use_ssl` as true and you don't have any certs already placed on disk, then the profile will automatically generate some self-signed certs for you to use.
|
||||
You should get a notification when the server starts with information about the configuration:
|
||||
|
||||
```
|
||||
@@ -115,5 +120,3 @@ If you need to manually specify a proxy endpoint, this is where you specify the
|
||||
This profile doesn't do any randomization of network components outside of allowing operators to specify internals/jitter. Every GET request for tasking will be the same. This is important to take into consideration for profiling/beaconing analytics.
|
||||
|
||||
## Development
|
||||
|
||||
All of the code for the server is Python3 using `Sanic` and located in `C2_Profiles/HTTP/c2_code/server`. It loops through the `instances` in the `config.json` file and stands up those individual web servers.
|
||||
|
||||
Reference in New Issue
Block a user