Compare commits

...

61 Commits

Author SHA1 Message Date
github-actions 95ff364a78 Bump Dockerfile tag to match release 'v0.0.2.3' 2024-09-10 20:07:09 +00:00
its-a-feature 4d11b7b43d updating to return custom error code/files
added error_file_path and error_status_code options
2024-09-10 15:00:31 -05:00
github-actions cf4f25a54a Bump Dockerfile tag to match release 'v0.0.2.2' 2024-09-05 18:23:24 +00:00
its-a-feature 0a830a84e8 updating to add mythic header for file requests 2024-09-05 14:17:25 -04:00
github-actions 7fc135b895 Bump Dockerfile tag to match release 'v0.0.2.1' 2024-08-28 19:07:25 +00:00
Cody Thomas 3a0e033149 Merge pull request #19 from MythicC2Profiles/Mythic3.3
Mythic3.3
2024-08-28 13:15:08 -05:00
its-a-feature 0e74377fea updating go modules and release branch 2024-08-28 10:47:32 -05:00
github-actions 9fcd3c6de4 Bump Dockerfile tag to match release 'v0.0.2.0' 2024-07-09 23:22:30 +00:00
its-a-feature 6667cec568 Update for Mythic3.3 2024-07-09 17:57:36 -05:00
github-actions 1d93e22f64 Bump Dockerfile tag to match release 'v0.0.1.7' 2024-05-02 18:51:06 +00:00
its-a-feature 8fbedcc51c removing default ssl certs so they're generated each time 2024-05-02 13:44:43 -05:00
github-actions da88f63aac Bump Dockerfile tag to match release 'v0.0.1.6' 2024-04-25 16:50:25 +00:00
its-a-feature 26e119f5df update profile to set X-Forwarded-For if it's not set 2024-04-25 11:41:49 -05:00
github-actions 4eaa51303f Bump Dockerfile tag to match release 'v0.0.1.5' 2024-04-24 17:05:46 +00:00
its-a-feature ef8101e909 updating gomod 2024-04-24 11:52:21 -05:00
github-actions 39a75fae3f Bump Dockerfile tag to match release 'v0.0.1.4' 2024-02-13 15:58:15 +00:00
its-a-feature 72192a418f updating MythicContainer version 2024-02-13 09:51:17 -06:00
github-actions c3b8ade832 Bump Dockerfile tag to match release 'v0.0.1.3' 2024-02-09 00:28:21 +00:00
its-a-feature bc2238ae50 Merge branch 'master' of https://github.com/MythicC2Profiles/http 2024-02-08 18:21:22 -06:00
its-a-feature 60062ebdae removing color codes from debug output because it's hard to read 2024-02-08 18:21:18 -06:00
github-actions 1e15fe3d03 Bump Dockerfile tag to match release 'v0.0.1.2' 2024-02-08 21:55:29 +00:00
its-a-feature f0a5457a85 updating dockerfiles for slimmer versions 2024-02-08 15:42:15 -06:00
github-actions 65d50c6a8e Bump Dockerfile tag to match release 'v0.0.1.1' 2024-01-29 20:15:12 +00:00
its-a-feature 1ec59eb1b6 updating regex checkers 2024-01-29 12:09:56 -08:00
github-actions 1b53c59fd4 Bump Dockerfile tag to match release 'v0.0.1' 2024-01-21 00:42:43 +00:00
Cody Thomas a362a4a1a0 Merge pull request #16 from MythicC2Profiles/docker_testing
Docker testing
2024-01-20 18:36:53 -06:00
its-a-feature 79787ffef9 updating docker flow 2024-01-20 18:36:34 -06:00
github-actions 60880efa14 Bump Dockerfile tag to match release 'v0.0.1-rc1' 2024-01-20 22:53:42 +00:00
its-a-feature 530e64c714 testing new docker 2024-01-20 16:43:33 -06:00
its-a-feature 49c68ede9a Fixed bug in hosted file serves
added ability to specify `bind_ip` to not bind to 0.0.0.0
2024-01-10 14:28:56 -06:00
its-a-feature 3006f05bea update http server to exclusively bind to ipv4 2023-10-19 10:35:08 -05:00
its-a-feature 571e4b441b fix configuration modification to not remove ServerHeaders 2023-10-12 08:42:16 +02:00
its-a-feature 408d6930ba updated to support HostFile functionality 2023-10-03 11:15:55 -05:00
its-a-feature 541c84458f updated MythicContainer golang package
updated package fixes a mis-reporting of internal service status
2023-07-17 17:03:48 -05:00
its-a-feature f18a44083b updating go.mod 2023-06-09 09:27:10 -05:00
its-a-feature 7201a6abc9 updating MythicContainer library
adding in GetIOC and GetSampleMessage functions
2023-06-09 09:23:27 -05:00
its-a-feature 0252a76b21 updating configuration check message 2023-06-07 12:30:19 -05:00
its-a-feature 8015cf7bf6 updated to not bail out on config checks 2023-06-07 11:05:38 -05:00
its-a-feature e06f0e5e52 updating mod-rewrite to auto provide http(s) and :port 2023-05-11 13:59:19 -05:00
its-a-feature e921fe0f11 removing extra {} in output 2023-05-11 13:36:25 -05:00
its-a-feature 9bd96bccc5 updating comments to appear better formatted 2023-05-11 13:28:00 -05:00
its-a-feature bb29774780 updating modrewrite generation to not try to get public ip 2023-05-11 13:23:25 -05:00
Cody Thomas bf689569e6 Merge pull request #10 from MythicC2Profiles/v3.0.0
V3.0.0
2023-05-10 09:25:53 -05:00
its-a-feature 38a9b45688 fix working directory 2023-05-09 11:25:50 -05:00
its-a-feature ffb18d6067 minimize build image
update build image to be smaller and update MythicContainer version
2023-05-09 11:23:19 -05:00
its-a-feature 13d7fbd0e3 updating Dockerfile image 2023-05-04 16:37:10 -05:00
its-a-feature 3ae4842771 updating MythicContainer package 2023-04-21 17:10:58 -05:00
its-a-feature 4ed4dea0ba fix some bad brackets in mod rewrite rules 2023-04-20 08:38:46 -05:00
its-a-feature 29aba4e07d adding .gitignore 2023-04-19 21:08:44 -05:00
its-a-feature 829c871477 updating regex for callback_host 2023-04-17 14:31:28 -05:00
its-a-feature 16f9f7dd0f bumping the MythicContainer version 2023-04-11 08:11:44 -05:00
its-a-feature 27da7a9849 update c2 profile description 2023-04-03 13:57:35 -05:00
its-a-feature 5b0b5c52c8 bump docker container version 2023-03-30 15:26:00 -05:00
its-a-feature 0f95c909b4 Merge remote-tracking branch 'origin/v3.0.0' into v3.0.0 2023-03-30 14:33:30 -05:00
its-a-feature b406ffcb80 added config, opsec check, and modrewrite generation 2023-03-30 14:33:10 -05:00
Cody Thomas 1887e13b71 Merge pull request #9 from checkymander/patch-1
Update builder.go
2023-03-24 12:24:00 -05:00
Cody Thomas 2a2e6175be Merge pull request #7 from MEhrn00/fix/v3.0.0-proxy-port-desc
Fix description for proxy_port C2 profile parameter in v3.0.0
2023-03-24 12:22:30 -05:00
checkymander 6fa00183f2 Update builder.go 2023-03-24 13:21:52 -04:00
checkymander 4014cc1211 Update builder.go 2023-03-24 13:18:44 -04:00
Matt Ehrnschwender 60871375e7 Fix description for proxy_port 2023-03-12 21:46:32 -04:00
its-a-feature 2553b0d442 initial update for Mythic v3.0.0 2023-03-03 21:02:53 -06:00
33 changed files with 1653 additions and 589 deletions
+139
View File
@@ -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
+6
View File
@@ -1 +1,7 @@
__pycache__/
.DS_Store
main
.idea/
C2_Profiles/http/http/c2_code/fullchain.pem
C2_Profiles/http/http/c2_code/privkey.pem
+20
View File
@@ -0,0 +1,20 @@
FROM golang:1.21 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
+4
View File
@@ -0,0 +1,4 @@
http/c2_code/mythic_http_server
main
.idea
.env
+8
View File
@@ -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
View File
@@ -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>
+8
View File
@@ -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
View File
@@ -1 +1,20 @@
FROM itsafeaturemythic/python38_sanic_c2profile:0.0.6
FROM golang:1.21 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
+37
View File
@@ -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}
build_macos:
CGO_ENABLED=0 go build -o ${BINARY_NAME} .
run_custom: build_macos
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}
-19
View File
@@ -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": {}
}
]
}
-10
View File
@@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Page Not Found!</title>
</head>
<body>
</body>
</html>
-223
View File
@@ -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()
+41
View File
@@ -0,0 +1,41 @@
module MyContainer
go 1.21.0
toolchain go1.22.2
//replace github.com/MythicMeta/MythicContainer => ../../../../MythicMeta/MythicContainer
require github.com/MythicMeta/MythicContainer v1.4.5
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-20240904232852-e7e105dedf7e // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.0 // indirect
google.golang.org/protobuf v1.34.2 // 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
)
+131
View File
@@ -0,0 +1,131 @@
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/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/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/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/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=
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/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/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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=
+26
View File
@@ -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:
DEBUG_LEVEL=${DEBUG_LEVEL} \
MYTHIC_SERVER_HOST=${MYTHIC_SERVER_HOST} \
MYTHIC_SERVER_PORT=${MYTHIC_SERVER_PORT} \
./${BINARY_NAME}
+21
View File
@@ -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": {}
}
]
}
+58
View File
@@ -0,0 +1,58 @@
module mythicHTTP
go 1.21.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.1
require (
github.com/bytedance/sonic v1.12.2 // 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.0 // indirect
github.com/goccy/go-json v0.10.3 // 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/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.9.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
google.golang.org/protobuf v1.34.2 // 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
)
+168
View File
@@ -0,0 +1,168 @@
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/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/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/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/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/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/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/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/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/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/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=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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=
+20
View File
@@ -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,317 @@
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.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"
req.Header.Add("mythic", "http")
}
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.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)
req.Header.Add("mythic", "http")
}
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,525 @@
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"`
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, &currentConfig); err != nil {
logging.LogError(err, "Failed to unmarshal config bytes")
return nil, err
} else {
return &currentConfig, 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
## 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"
%s
## Redirect all other traffic here
RewriteRule ^.*$ redirect/? [L,R=302]
## .htaccess END
########################################
`
htaccess := fmt.Sprintf(htaccessTemplate, urisString, uaString, strings.Join(c2RewriteOutput, "\n"))
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)
}
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,
}
},
}
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)
}
+15
View File
@@ -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,
),
]
-7
View File
@@ -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
View File
@@ -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.3"
}
}
+9 -6
View File
@@ -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.