-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0c4f6d4
commit b3b94d2
Showing
33 changed files
with
1,575 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/nemesis | ||
file: Payload_Type/nemesis/.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/nemesis | ||
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.nemesis | ||
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/nemesis/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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,9 @@ | |
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
.idea/ | ||
.DS_Store | ||
rabbitmq_config.json | ||
|
||
# C extensions | ||
*.so | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 nemesis, 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. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import mythic_container | ||
import asyncio | ||
# import the nemesis agent | ||
import nemesis | ||
|
||
mythic_container.mythic_service.start_and_run_forever() |
142 changes: 142 additions & 0 deletions
142
Payload_Type/nemesis/nemesis/NemesisRequests/NemesisAPI.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
from mythic_container.MythicCommandBase import * | ||
from nemesis.NemesisRequests.NemesisAPIClasses import * | ||
from mythic_container.MythicRPC import * | ||
from gql import gql | ||
from datetime import datetime, timedelta | ||
|
||
NEMESIS_USERNAME = "NEMESIS_USERNAME" | ||
NEMESIS_PASSWORD = "NEMESIS_PASSWORD" | ||
|
||
|
||
def check_valid_values(username, password, url) -> bool: | ||
if username == "" or username is None: | ||
logger.error("missing username") | ||
return False | ||
if password == "" or password is None: | ||
logger.error("missing password") | ||
return False | ||
if url == "" or url is None: | ||
logger.error("missing url") | ||
return False | ||
return True | ||
|
||
|
||
def convert_timestamp(timestamp, days_to_add=0): | ||
""" | ||
Strips off the microseconds from a timestamp and reformats to our unified format. | ||
**Parameters** | ||
``timestamp`` | ||
The timestamp string to reformat. | ||
**Returns** | ||
A reformatted timestamp string. | ||
""" | ||
|
||
dt = datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ") | ||
|
||
if days_to_add != 0: | ||
dt = dt + timedelta(days=days_to_add) | ||
|
||
return dt.strftime("%Y-%m-%dT%H:%M:%S.000Z") | ||
|
||
|
||
async def query_graphql(taskData: PTTaskMessageAllData, query: gql, uri: str = '/hasura/v1/graphql', | ||
variable_values: dict = None) -> (int, dict): | ||
username = None | ||
password = None | ||
url = None | ||
for buildParam in taskData.BuildParameters: | ||
if buildParam.Name == "URL": | ||
url = buildParam.Value | ||
if NEMESIS_USERNAME in taskData.Secrets: | ||
username = taskData.Secrets[NEMESIS_USERNAME] | ||
if NEMESIS_PASSWORD in taskData.Secrets: | ||
password = taskData.Secrets[NEMESIS_PASSWORD] | ||
if not check_valid_values(username, password, url): | ||
return 500, f"Missing {NEMESIS_USERNAME} or {NEMESIS_PASSWORD} in User settings or missing Nemesis URL" | ||
try: | ||
credentials = Credentials(username=username, password=password) | ||
client = NemesisClient(url=url.rstrip("/") + uri, credentials=credentials) | ||
response = await client.graphql_query(query=query, variable_values=variable_values) | ||
logger.info(f"Nemesis Query: {uri}") | ||
if response is not None: | ||
return 200, response | ||
else: | ||
return 500, client.last_error | ||
except Exception as e: | ||
logger.exception(f"[-] Failed to query Nemesis: \n{e}\n") | ||
raise Exception(f"[-] Failed to query Nemesis: \n{e}\n") | ||
|
||
|
||
async def post_data_api(taskData: PTTaskMessageAllData, data: dict) -> (int, dict): | ||
username = None | ||
password = None | ||
url = None | ||
for buildParam in taskData.BuildParameters: | ||
if buildParam.Name == "URL": | ||
url = buildParam.Value | ||
if NEMESIS_USERNAME in taskData.Secrets: | ||
username = taskData.Secrets[NEMESIS_USERNAME] | ||
if NEMESIS_PASSWORD in taskData.Secrets: | ||
password = taskData.Secrets[NEMESIS_PASSWORD] | ||
if not check_valid_values(username, password, url): | ||
return 500, f"Missing {NEMESIS_USERNAME} or {NEMESIS_PASSWORD} in User settings or missing Nemesis URL" | ||
try: | ||
credentials = Credentials(username=username, password=password) | ||
client = NemesisClient(url=url.rstrip("/"), credentials=credentials, graphql=False) | ||
response = await client.nemesis_post_data(data=data) | ||
if response is not None: | ||
return 200, response | ||
else: | ||
return 500, client.last_error | ||
except Exception as e: | ||
logger.exception(f"[-] Failed to post data to Nemesis: \n{e}\n") | ||
raise Exception(f"[-] Failed to post data to Nemesis: \n{e}\n") | ||
|
||
|
||
async def post_file_api(taskData: PTTaskMessageAllData, file_bytes: bytes) -> (int, str): | ||
username = None | ||
password = None | ||
url = None | ||
for buildParam in taskData.BuildParameters: | ||
if buildParam.Name == "URL": | ||
url = buildParam.Value | ||
if NEMESIS_USERNAME in taskData.Secrets: | ||
username = taskData.Secrets[NEMESIS_USERNAME] | ||
if NEMESIS_PASSWORD in taskData.Secrets: | ||
password = taskData.Secrets[NEMESIS_PASSWORD] | ||
if not check_valid_values(username, password, url): | ||
return 500, f"Missing {NEMESIS_USERNAME} or {NEMESIS_PASSWORD} in User settings or missing Nemesis URL" | ||
try: | ||
credentials = Credentials(username=username, password=password) | ||
client = NemesisClient(url=url.rstrip("/"), credentials=credentials, graphql=False) | ||
response = await client.nemesis_post_file(file_bytes=file_bytes) | ||
if response is not None: | ||
return 200, response | ||
else: | ||
return 500, client.last_error | ||
except Exception as e: | ||
logger.exception(f"[-] Failed to post file to Nemesis: \n{e}\n") | ||
raise Exception(f"[-] Failed to post file to Nemesis: \n{e}\n") | ||
|
||
|
||
async def process_standard_response(response_code: int, response_data: any, | ||
taskData: PTTaskMessageAllData, response: PTTaskCreateTaskingMessageResponse) -> \ | ||
PTTaskCreateTaskingMessageResponse: | ||
if response_code == 200: | ||
await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( | ||
TaskID=taskData.Task.ID, | ||
Response=json.dumps(response_data).encode("UTF8"), | ||
)) | ||
response.Success = True | ||
else: | ||
await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( | ||
TaskID=taskData.Task.ID, | ||
Response=f"{response_data}".encode("UTF8"), | ||
)) | ||
response.TaskStatus = "Error: Nemesis Query Error" | ||
response.Success = False | ||
return response |
Oops, something went wrong.