Skip to content

Commit

Permalink
initial SO-CON 2024 release
Browse files Browse the repository at this point in the history
  • Loading branch information
its-a-feature committed Mar 11, 2024
1 parent d9d640a commit 34a00d5
Show file tree
Hide file tree
Showing 82 changed files with 4,046 additions and 1 deletion.
139 changes: 139 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -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'
- 'main'
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: main

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: Payload_Type/bloodhound
file: Payload_Type/bloodhound/.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: Payload_Type/bloodhound
run: |
sed -i "s|^FROM ghcr\.io.*$|FROM ${REGISTRY}/${AGENT_IMAGE_NAME}:${VERSION}|" Dockerfile
- name: Update package.json version
uses: jossef/[email protected]
with:
file: config.json
field: remote_images.bloodhound
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: "['Payload_Type/bloodhound/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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
__pycache__/
*.py[cod]
*$py.class

.idea/
.DS_Store
rabbitmq_config.json
# C extensions
*.so

Expand Down
Empty file added C2_Profiles/.keep
Empty file.
28 changes: 28 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

Copyright (c) 2024, its-a-feature
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of bloodhound, mythic, nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 changes: 22 additions & 0 deletions Payload_Type/bloodhound/.docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM python:3.11-slim-bookworm as builder

COPY [".docker/requirements.txt", "requirements.txt"]
RUN apt-get -y update && \
apt-get -y upgrade && \
apt-get install --no-install-recommends \
software-properties-common apt-utils make build-essential libssl-dev zlib1g-dev libbz2-dev \
xz-utils tk-dev libffi-dev liblzma-dev libsqlite3-dev protobuf-compiler \
binutils-aarch64-linux-gnu libc-dev-arm64-cross -y
RUN python3 -m pip wheel --wheel-dir /wheels -r requirements.txt

FROM python:3.11-slim-bookworm

COPY --from=builder /wheels /wheels

RUN pip install --no-cache /wheels/*

WORKDIR /Mythic/

COPY [".", "."]

CMD ["python3", "main.py"]
7 changes: 7 additions & 0 deletions Payload_Type/bloodhound/.docker/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
aio-pika==9.0.4
dynaconf==3.1.11
ujson==5.7.0
aiohttp==3.8.3
psutil==5.9.4
mythic-container==0.4.13
requests
22 changes: 22 additions & 0 deletions Payload_Type/bloodhound/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM python:3.11-slim-bookworm as builder

COPY [".docker/requirements.txt", "requirements.txt"]
RUN apt-get -y update && \
apt-get -y upgrade && \
apt-get install --no-install-recommends \
software-properties-common apt-utils make build-essential libssl-dev zlib1g-dev libbz2-dev \
xz-utils tk-dev libffi-dev liblzma-dev libsqlite3-dev protobuf-compiler \
binutils-aarch64-linux-gnu libc-dev-arm64-cross -y
RUN python3 -m pip wheel --wheel-dir /wheels -r requirements.txt

FROM python:3.11-slim-bookworm

COPY --from=builder /wheels /wheels

RUN pip install --no-cache /wheels/*

WORKDIR /Mythic/

COPY [".", "."]

CMD ["python3", "main.py"]
121 changes: 121 additions & 0 deletions Payload_Type/bloodhound/bloodhound/BloodhoundRequests/BloodhoundAPI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from mythic_container.MythicCommandBase import *
from bloodhound.BloodhoundRequests.BloodhoundAPIClasses import *
from mythic_container.MythicRPC import *

cachedAssetGroupOwnedID = None
BLOODHOUND_API_KEY = "BLOODHOUND_API_KEY"
BLOODHOUND_API_ID = "BLOODHOUND_API_ID"


def checkValidValues(token_id, token_key, url) -> bool:
if token_id == "" or token_id is None:
return False
if token_key == "" or token_key is None:
return False
if url == "" or url is None:
return False
return True


async def query_bloodhound(taskData: PTTaskMessageAllData, uri: str, method: str = 'GET', body: bytes = None, ) -> \
(int, dict):
token_id = None
token_key = None
url = None
for buildParam in taskData.BuildParameters:
if buildParam.Name == "URL":
url = buildParam.Value
if BLOODHOUND_API_KEY in taskData.Secrets:
token_key = taskData.Secrets[BLOODHOUND_API_KEY]
if BLOODHOUND_API_ID in taskData.Secrets:
token_id = taskData.Secrets[BLOODHOUND_API_ID]
if not checkValidValues(token_id, token_key, url):
if token_id == "" or token_id is None:
return 500, "Missing BLOODHOUND_API_ID in user's secrets"
if token_key == "" or token_key is None:
return 500, "Missing BLOODHOUND_API_KEY in user's secrets"
if url == "" or url is None:
return 500, "Missing URL from build parameters"

try:
credentials = Credentials(token_id=token_id, token_key=token_key)
client = Client(url=url, credentials=credentials)
response = client.Request(method=method, uri=uri, body=body)
logger.info(f"Bloodhound Query: {uri}")
#logger.info(response.status_code)
#logger.info(response.text)
if 200 <= response.status_code < 300:
try:
payload = response.json()
return response.status_code, payload
except Exception as mid_exception:
logger.error(mid_exception)
return response.status_code, response.text
else:
return response.status_code, response.text
except Exception as e:
logger.exception(f"[-] Failed to query Bloodhound: \n{e}\n")
raise Exception(f"[-] Failed to query Bloodhound: \n{e}\n")


async def get_owned_id(taskData: PTTaskMessageAllData) -> int:
global cachedAssetGroupOwnedID

if cachedAssetGroupOwnedID is not None:
return cachedAssetGroupOwnedID
uri = f"/api/v2/asset-groups"
try:
response_code, response_data = await query_bloodhound(taskData, method='GET', uri=uri)
if response_code == 200:
asset_groups = response_data["data"]
if len(asset_groups) == 0:
raise Exception("no asset groups")
for x in asset_groups["asset_groups"]:
if x["system_group"] and x["tag"] == "owned":
cachedAssetGroupOwnedID = x["id"]
return cachedAssetGroupOwnedID
raise Exception("no owned asset_group")
raise Exception("Failed to query")
except Exception as e:
raise e


async def get_whoami(taskData: PTTaskMessageAllData) -> str:
uri = f"/api/v2/self"
try:
response_code, response_data = await query_bloodhound(taskData, method='GET', uri=uri)
if response_code == 200:
return response_data["data"]["id"]
raise Exception("Failed to query self")
except Exception as e:
raise e


async def get_saved_queries(taskData: PTTaskMessageAllData) -> list[dict]:
try:
user_id = await get_whoami(taskData=taskData)
uri = f"/api/v2/saved-queries?sort_by=name&user_id={user_id}"
response_code, response_data = await query_bloodhound(taskData, method='GET', uri=uri)
if response_code == 200:
return response_data["data"]
raise Exception("Failed to query")
except Exception as e:
raise e


async def process_standard_response(response_code: int, response_data: any,
taskData: PTTaskMessageAllData, response: PTTaskCreateTaskingMessageResponse) -> \
PTTaskCreateTaskingMessageResponse:
if 200 <= response_code < 300:
await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage(
TaskID=taskData.Task.ID,
Response=json.dumps(response_data["data"]).encode("UTF8"),
))
response.Success = True
else:
await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage(
TaskID=taskData.Task.ID,
Response=f"{response_data}".encode("UTF8"),
))
response.TaskStatus = "Error: Bloodhound Query Error"
return response
Loading

0 comments on commit 34a00d5

Please sign in to comment.