| title | Overview |
|---|
Pull the code from the official GitHub repository:
$ git clone https://github.com/its-a-feature/Mythic --depth 1 --single-branch
All configuration is done via the mythic-cli binary. However, to help with GitHub sizes, the mythic-cli binary is no longer distributed with the main Mythic repository. Instead, you will need to make the binary via sudo make from the main Mythic folder. This will create the build container for the mythic-cli, build the binary, and copy it into your main Mythic folder automatically. From there on, you can use the mythic-cli binary like normal. Make sure you use the mythic-cli binary from the main Mythic folder.
If you're running Mythic on macOS, instead of running sudo make run sudo make macos and you'll get an architecture appropriate machO binary to use.
Mythic configuration is all done via Mythic/.env, which means for your configuration you can either add/edit values there or add them to your environment.
If you need to run mythic-cli as root for Docker and you set your environment variables as a user, be sure to run sudo -E ./mythic-cli so that your environment variables are carried over into your sudo call. The following are the default values that Mythic will generate on first execution of sudo ./mythic-cli start unless overridden:
ALLOWED_IP_BLOCKS="0.0.0.0/0,::/0"
COMPOSE_PROJECT_NAME="mythic"
DEBUG_LEVEL="debug"
DEFAULT_OPERATION_NAME="Operation Chimera"
DEFAULT_OPERATION_WEBHOOK_CHANNEL=
DEFAULT_OPERATION_WEBHOOK_URL=
DOCUMENTATION_BIND_LOCALHOST_ONLY="true"
DOCUMENTATION_HOST="mythic_documentation"
DOCUMENTATION_PORT="8090"
DOCUMENTATION_USE_BUILD_CONTEXT="false"
DOCUMENTATION_USE_VOLUME="true"
GLOBAL_DOCKER_LATEST="v0.0.3"
GLOBAL_MANAGER="docker"
GLOBAL_SERVER_NAME="mythic"
HASURA_BIND_LOCALHOST_ONLY="true"
HASURA_CPUS="2"
HASURA_EXPERIMENTAL_FEATURES="streaming_subscriptions"
HASURA_HOST="mythic_graphql"
HASURA_MEM_LIMIT="2gb"
HASURA_PORT="8080"
HASURA_SECRET="random password"
HASURA_USE_BUILD_CONTEXT="false"
HASURA_USE_VOLUME="true"
INSTALLED_SERVICE_CPUS="1"
INSTALLED_SERVICE_MEM_LIMIT=
JUPYTER_BIND_LOCALHOST_ONLY="true"
JUPYTER_CPUS="2"
JUPYTER_HOST="mythic_jupyter"
JUPYTER_MEM_LIMIT=
JUPYTER_PORT="8888"
JUPYTER_TOKEN="mythic"
JUPYTER_USE_BUILD_CONTEXT="false"
JUPYTER_USE_VOLUME="true"
JWT_SECRET="random password"
MYTHIC_ADMIN_PASSWORD="random password"
MYTHIC_ADMIN_USER="mythic_admin"
MYTHIC_API_KEY=
MYTHIC_DEBUG_AGENT_MESSAGE="false"
MYTHIC_REACT_BIND_LOCALHOST_ONLY="true"
MYTHIC_REACT_DEBUG="false"
MYTHIC_REACT_HOST="mythic_react"
MYTHIC_REACT_PORT="3000"
MYTHIC_REACT_USE_BUILD_CONTEXT="false"
MYTHIC_REACT_USE_VOLUME="true"
MYTHIC_SERVER_BIND_LOCALHOST_ONLY="true"
MYTHIC_SERVER_COMMAND=
MYTHIC_SERVER_CPUS="2"
MYTHIC_SERVER_DYNAMIC_PORTS="7000-7010,1080"
MYTHIC_SERVER_DYNAMIC_PORTS_BIND_LOCALHOST_ONLY="false"
MYTHIC_SERVER_GRPC_PORT="17444"
MYTHIC_SERVER_HOST="mythic_server"
MYTHIC_SERVER_MEM_LIMIT=
MYTHIC_SERVER_PORT="17443"
MYTHIC_SERVER_USE_BUILD_CONTEXT="false"
MYTHIC_SERVER_USE_VOLUME="true"
MYTHIC_SYNC_CPUS="2"
MYTHIC_SYNC_MEM_LIMIT=
NGINX_BIND_LOCALHOST_ONLY="false"
NGINX_HOST="mythic_nginx"
NGINX_PORT="7443"
NGINX_USE_BUILD_CONTEXT="false"
NGINX_USE_IPV4="true"
NGINX_USE_IPV6="false"
NGINX_USE_SSL="true"
NGINX_USE_VOLUME="true"
POSTGRES_BIND_LOCALHOST_ONLY="false"
POSTGRES_CPUS="2"
POSTGRES_DB="mythic_db"
POSTGRES_DEBUG="false"
POSTGRES_HOST="mythic_postgres"
POSTGRES_MEM_LIMIT=
POSTGRES_PASSWORD="random password"
POSTGRES_PORT="5432"
POSTGRES_USE_BUILD_CONTEXT="false"
POSTGRES_USE_VOLUME="true"
POSTGRES_USER="mythic_user"
RABBITMQ_BIND_LOCALHOST_ONLY="true"
RABBITMQ_CPUS="2"
RABBITMQ_HOST="mythic_rabbitmq"
RABBITMQ_MEM_LIMIT=
RABBITMQ_PASSWORD="random password"
RABBITMQ_PORT="5672"
RABBITMQ_USE_BUILD_CONTEXT="false"
RABBITMQ_USE_VOLUME="true"
RABBITMQ_USER="mythic_user"
RABBITMQ_VHOST="mythic_vhost"
REBUILD_ON_START="true"
WEBHOOK_DEFAULT_ALERT_CHANNEL=
WEBHOOK_DEFAULT_CALLBACK_CHANNEL=
WEBHOOK_DEFAULT_CUSTOM_CHANNEL=
WEBHOOK_DEFAULT_FEEDBACK_CHANNEL=
WEBHOOK_DEFAULT_STARTUP_CHANNEL=
WEBHOOK_DEFAULT_URL=
A few important notes here:
-
MYTHIC_SERVER_PORTwill be the port opened on the server where you're running Mythic. TheNGINX_PORTis the one that's opened by Nginx and acts as a reverse proxy to all other services. TheNGINX_PORTis the one you'll connect to for your web user interface and should be the only port you need to expose externally (unless you prefer to SSH port forward your web UI port). -
The
allowed_ip_blocksallow you to restrict access to everything within Mythic. This should be set as a series of netblocks with NO host bits set - i.e.127.0.0.0/16,192.168.10.0/24,10.0.0.0/8 -
*_BIND_LOCALHOST_ONLY- these settings determine if the associated container binds the port to127.0.0.1:portor0.0.0.0:port. These are all set to true (except for the nginx container) by default so that you're not exposing these services externally.
When the mythic_server container starts for the first time, it goes through an initialization step where it uses the password and username from Mythic/.env to create the mythic_admin_user user. Once the database exists, the mythic_server container no longer uses that value.
The mythic-cli binary is used to start/stop/configure/install components of Mythic. You can see the help menu at any time with mythic-cli -h, mythic-cli --help or mythic-cli help.
Mythic CLI is a command line interface for managing the Mythic application and associated containers and services.
Commands are grouped by their use and all support '-h' for help.
For a list of available services to install, check out: https://mythicmeta.github.io/overview/
Usage:
mythic-cli [command]
Available Commands:
add Add local service folder to docker compose
backup Backup various volumes/data to a custom location on disk
build Build/rebuild a specific container
build_ui Build/rebuild the React UI
completion Generate the autocompletion script for the specified shell
config Display or adjust the configuration
database Interact with the database
health Check health status of containers
help Help about any command
install Install services via git or local folders
load Load tar versions of Mythic images from ./saved_images/mythic_save.tar
logs Get docker logs from a running service
mythic_sync Install/Uninstall mythic_sync
rabbitmq Interact with the rabbitmq service
remove Remove local service folder from docker compose
remove_container Remove running or exited containers
restart Start all of Mythic
restore Restore various volumes/data from a custom location on disk
save Save tar versions of the specified container's images
services List out installed services
start Start Mythic containers
status Get current Mythic container status
stop Stop all of Mythic
test Test mythic service connections
uninstall uninstall services locally and remove them from disk
update Check for Mythic updates
version Print information about the mythic-cli and Mythic versions
volume Interact with the mythic volumes
Flags:
-h, --help help for mythic-cli
Use "mythic-cli [command] --help" for more information about a command.
By default, Mythic does not come with any Payload Types (agents) or C2 Profiles. This is for a variety of reasons, but one of the big ones being time/space requirements - all Payload Types and C2 Profiles have their own Docker containers, and as such, collectively they could eat up a lot of space on disk. Additionally, having them split out into separate repositories makes it much easier to keep them updated.
Available Mythic Agents can be found on GitHub at https://github.com/MythicAgents
Available Mythic C2 Profiles can be found on GitHub at https://github.com/MythicC2Profiles
To install a Payload Type or C2 Profile, use the mythic-cli binary with:
sudo ./mythic-cli install github <url>
If you have an agent already installed, but want to update it, you can do the same command again. If you supply a -f at the end, then Mythic will automatically overwrite the current version that's installed, otherwise you'll be prompted for each piece.
If you're wanting to enable SIEM-based logging, install the basic_logger via the mythic cli sudo ./mythic-cli install github https://github.com/MythicC2Profiles/basic_logger. This profile listens to the emit_log RabbitMQ queue and allows you to configure how you want to save/modify the logs. By default they just go to stdout, but you can configure it to write out to files or even submit the events to your own SIEM.
file_upload (file staged on mythic as part of tasking with the intent to get sent to the agent)
file_manual_upload (file staged on mythic as part of a user manually hosting it)
file_screenshot (file is a screenshot from the agent)
file_download (file is downloaded from agent to mythic)
artifact_new (new artifact created - think IOC)
eventlog_new (new eventlog message)
eventlog_modified (eventlog was modified, like resolving an issue or changing their message)
payload_new (new payload created)
task_mitre_attack (a task was associated with a new mitre attack technique)
task_new (a new task was created)
task_completed (a task completed)
task_comment (somebody added/removed/edited a comment on a task)
credential_new (a new credential was added to the store)
credential_modified (a credential was modified)
response_new (a new response for the user to see)
keylog_new (a new keylog entry)
callback_new (new callback registered)
Simply modify the code here to change from printing to stdout to whatever it is you want to do, then use sudo ./mythic-cli build basic_logger to rebuild with your updated code.
If you came here right from the previous section, your Mythic instance should already be up and running. Check out the next section to confirm that's the case. If at any time you wish to stop Mythic, simply run sudo ./mythic-cli stop and if you want to start it again run sudo ./mythic-cli start. If Mythic is currently running and you need to make a change, you can run sudo ./mythic-cli restart again without any issue, that command will automatically stop things and then restart them.
The default username is mythic_admin, but that user's password is randomly generated when Mythic is started for the first time. You can find this random value in the Mythic/.env file. Once Mythic has started at least once, this value is no longer needed, so you can edit or remove this entry from the Mythic/.env file.
If something seems off, here's a few places to check:
- Run
sudo ./mythic-cli statusto give a status update on all of the docker containers. They should all be up and running. If one is exited or has only been up for less than 30 seconds, that container might be your issue. All of the Mythic services will also report back a health check which can be useful to determine if a certain container is having issues. The status command gives a lot of information about what services are running, on which ports, and if they're externally accessible or not.
MYTHIC SERVICE WEB ADDRESS BOUND LOCALLY
Nginx (Mythic Web UI) https://127.0.0.1:7443 false
Mythic Backend Server http://127.0.0.1:17443 false
Hasura GraphQL Console http://127.0.0.1:8080 true
Jupyter Console http://127.0.0.1:8888 true
Internal Documentation http://127.0.0.1:8090 true
ADDITIONAL SERVICES IP PORT BOUND LOCALLY
Postgres Database 127.0.0.1 5432 false
React Server 192.168.53.152 3000 true
RabbitMQ 127.0.0.1 5672 false
Mythic Main Services
CONTAINER NAME STATE STATUS PORTS
mythic_documentation running Up 38 seconds (healthy) 8090/tcp -> 127.0.0.1:8090
mythic_graphql running Up 36 seconds (healthy) 8080/tcp -> 127.0.0.1:8080
mythic_jupyter running Up 41 seconds (healthy) 8888/tcp -> 127.0.0.1:8888
mythic_nginx running Up 35 seconds (healthy) 7443/tcp -> :::7443, 7443
mythic_postgres running Up 39 seconds (healthy) 5432/tcp -> :::5432, 5432
mythic_rabbitmq running Up 40 seconds (health: starting) 5672/tcp -> :::5672, 5672
mythic_server running Up 37 seconds (health: starting) 7000/tcp -> :::7000, 7001/tcp -> :::7001, 7002/tcp -> :::7002, 7003/tcp -> :::7003, 7004/tcp -> :::7004, 7005/tcp -> :::7005, 7006/tcp -> :::7006, 7007/tcp -> :::7007, 7008/tcp -> :::7008, 7009/tcp -> :::7009, 7010/tcp -> :::7010, 17443/tcp -> :::17443, 17444/tcp -> :::17444, 7000, 7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 17443, 17444
Installed Services
CONTAINER NAME STATE STATUS PORTS
no_translator running Up 43 seconds
service_wrapper running Up 42 seconds
-
To check the logs of any container, run
sudo ./mythic-cli logs [container_name]. For example, to see the output of mythic_server, runsudo ./mythic-cli logs mythic_server. This will help track down if the last thing that happened was an error of some kind. -
If all of that looks ok, but something still seems off, it's time to check the browser.
-
First open up the developer tools for your browser and see if there are any errors that might indicate what's wrong. If there's no error though, check the network tab to see if there are any 404 errors.
-
If that's not the case, make sure you've selected a current operation (more on this in the Quick Usage section). Mythic uses websockets that pull information about your current operation to provide data. If you're not currently in an active operation (indicated at the top of your screen in big letters), then Mythic cannot provide you any data.
-
If you run into an issue where mythic_nginx is failing to start, you can look at its logs with sudo ./mythic-cli logs mythic_nginx. If you see Address family not supported by protocol, then it likely means that the nginx container is trying to use IPv4 and IPv6, but your host doesn't support one of them. To fix this, you can edit the .env file to adjust the following as necessary:
NGINX_USE_IPV4="true"
NGINX_USE_IPV6="false"
Then restart the container with sudo ./mythic-cli build mythic_nginx and it should come up.
Starting with Mythic 3.2.16, Mythic pre-builds its main service containers and hosts them on GitHub. You might see ghcr.io/itsafeature in the FROM line in your Dockerfiles instead of the itsafeaturemythic/ line which is hosted on DockerHub. When the Mythic repo gets a new tag, these images are pre-built, mythic-cli is updated, and the associated push on GitHub is updated with the new tag version.
When you use the new mythic-cli to start Mythic, the .env variable GLOBAL_DOCKER_LATEST is used to determine which version of the Docker images to use. This variable is written out and saved as part of mythic-cli itself.
so make sure when you do a git pull that you always run sudo make to get the latest mythic-cli as well.
As part of this, there are four new variables for each container:
-
*_USE_BUILD_CONTEXT- This variable changes thedocker-composefile to either set abuild-contextto read the local Dockerfile when building or to not use the local build context and just set the image to use to be the one hosted on GitHub. In most cases, you're fine to leave this asfalseand just use the image hosted on GitHub. If you wanted to use another image version or if you wanted add stuff to the image that gets generated for a container, you can set this totrueand modify the Dockerfile associated with the service. -
*_USE_VOLUME- This variable identifies if the local file system is mounted into the image at run-time or if a custom volume is created and mounted instead. When this is set totrue, then a custom volume is created and mounted into the container at run time so that your local filesystem isn't used. When this isfalse, then your local filesystem is mounted like normal. One reason to mount the local file system instead of using a volume is if you wanted to make changes to something on disk and have it reflected in the container. Similarly, you can set this tofalseso that your database and downloaded files are all contained within theMythicfolder. Setting this totruewill mean that volumes are used, so your saved files and database are in Docker's volume directory and not locally within the Mythic folder. It's just something to consider when it comes time to save things off or if you wanted to pull the files from disk. -
*_INSTALL_LOCATION- This variable gives either the folder path or the "url;branch" that was used to install the agent in the first place. This is used bymythic-clicommands like./mythic-cli update -s [agent name]to determine if the agent has any updates available. -
*_REMOTE_IMAGE- This variable tracks the remote image that's pre-built for the agent (if one exists). This is used in conjunction with the*_INSTALL_LOCATIONto see if there are any updates for the agent.
Mythic is pre-building its containers so that it's faster and easier to get going while still keeping all of the flexibility of Docker images. This cuts down on the install/build time of the containers and reduces the general size of the images due to multi-stage Docker builds.
Agents on GitHub can also do this for free. It's pretty simple (all things considered) and provides a lot of flexibility to how you build your containers. You don't need to configure any special GitHub secrets - you just need to create the necessary yaml file as part of a certain directory of your repository so that things are kicked off on push and on tag.
One of these changes is an automatic update to your config.json so that Mythic can also track the version associated with your agent.
Specifically, you need to create the .github/workflows/[name].yml file so that GitHub will be able to handle your actions. An example from the apollo payload is shown below:
# 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'
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/apollo)
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: Payload_Type/apollo
file: Payload_Type/apollo/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}
- name: Update package.json version
uses: jossef/action-set-json-field@v2.1
with:
file: config.json
field: remote_images.apollo
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: "['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: exitImmediately99% of this example should work for all agents and c2 profiles. Things to change:
-
RELEASE_BRANCH- This might need to change depending on if your branch name ismasterormain. -
Updating the
remote_images.apollo(line 112) toremote_images.[your name]so that yourconfig.jsonis updated appropriately. If you have multiple installs (ex: a payload in the payload_type folder and a c2 profile in the c2_profiles folder), then you should include this action multiple times to add each entry to yourremote_imagesdictionary.