├── C2_Profiles ├── .keep └── dns │ ├── dns │ ├── c2_code │ │ ├── .gitkeep │ │ ├── config.json │ │ ├── dnsserver │ │ │ ├── dnsgrpc │ │ │ │ ├── dns.proto │ │ │ │ └── dns.pb.go │ │ │ ├── config.go │ │ │ └── initialize.go │ │ ├── main.go │ │ ├── Makefile │ │ ├── go.mod │ │ └── go.sum │ └── c2functions │ │ └── builder.go │ ├── .gitignore │ ├── Dockerfile │ ├── main.go │ ├── CHANGELOG.md │ ├── Makefile │ ├── go.mod │ └── go.sum ├── Payload_Type └── .keep ├── agent_icons └── .keep ├── documentation-c2 ├── .keep └── DNS │ └── _index.md ├── documentation-payload └── .keep ├── documentation-wrapper └── .keep ├── .gitignore ├── config.json ├── README.md └── .github └── workflows └── docker.yml /C2_Profiles/.keep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Payload_Type/.keep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /agent_icons/.keep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /documentation-c2/.keep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /documentation-payload/.keep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /documentation-wrapper/.keep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2_code/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .idea/ 3 | mythic_dns_server -------------------------------------------------------------------------------- /C2_Profiles/dns/.gitignore: -------------------------------------------------------------------------------- 1 | http/c2_code/mythic_http_server 2 | main 3 | .idea 4 | .env -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2_code/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "instances": [ 3 | { 4 | "port": 53, 5 | "debug": false, 6 | "bind_ip": "0.0.0.0", 7 | "domains": [ 8 | ] 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude_payload_type": false, 3 | "exclude_c2_profiles": false, 4 | "exclude_documentation_payload": false, 5 | "exclude_documentation_c2": false, 6 | "exclude_agent_icons": false, 7 | "remote_images": { 8 | "dns": "ghcr.io/mythicc2profiles/dns:v0.0.0.6" 9 | } 10 | } -------------------------------------------------------------------------------- /C2_Profiles/dns/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.25 AS builder 2 | 3 | WORKDIR /Mythic/ 4 | 5 | COPY [".", "."] 6 | 7 | RUN make build 8 | 9 | FROM alpine 10 | 11 | RUN apk add make 12 | 13 | COPY --from=builder /main /main 14 | COPY --from=builder /mythic_dns_server /mythic_dns_server 15 | 16 | WORKDIR /Mythic/ 17 | 18 | COPY [".", "."] 19 | 20 | CMD make run -------------------------------------------------------------------------------- /C2_Profiles/dns/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | httpfunctions "MyContainer/dns/c2functions" 5 | "github.com/MythicMeta/MythicContainer" 6 | ) 7 | 8 | func main() { 9 | // load up the agent functions directory so all the init() functions execute 10 | httpfunctions.Initialize() 11 | // sync over definitions and listen 12 | MythicContainer.StartAndRunForever([]MythicContainer.MythicServices{ 13 | MythicContainer.MythicServiceC2, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2_code/dnsserver/dnsgrpc/dns.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/MythicC2Profiles/dns/C2_Profiles/dns/dns/c2_code/dnsserver/dnsgrpc"; 3 | package dnsStructs; 4 | 5 | enum Actions { 6 | AgentToServer = 0; 7 | ServerToAgent = 1; 8 | ReTransmit = 2; 9 | MessageLost = 3; 10 | } 11 | message DnsPacket { 12 | Actions Action = 1; 13 | uint32 AgentSessionID = 2; 14 | uint32 MessageID = 3; 15 | uint32 Size = 4; 16 | uint32 Begin = 5; 17 | bytes Data = 6; 18 | } -------------------------------------------------------------------------------- /C2_Profiles/dns/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [v0.0.5] - 2025-11-07 3 | 4 | ### Changed 5 | 6 | - Updated TXT response for action to be an enum value and not the string representation 7 | 8 | ## [v0.0.4] - 2025-11-05 9 | 10 | ### Changed 11 | 12 | - Updated DNS processing to be a bit more resilient to replicated queries before deleting tracking 13 | 14 | ## [v0.0.3] - 2025-11-05 15 | 16 | ### Changed 17 | 18 | - Fixed an issue with missing subdomains for tracked domains causing a panic 19 | 20 | ## [v0.0.2] - 2025-07-21 21 | 22 | ### Changed 23 | 24 | - Started adding tracking for mythic UUID out of DNS AgentSessionIDs -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2_code/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "mythicDNS/dnsserver" 5 | "os" 6 | 7 | "github.com/MythicMeta/MythicContainer/logging" 8 | ) 9 | 10 | func main() { 11 | dnsserver.InitializeLocalConfig() 12 | for _, instance := range dnsserver.Config.Instances { 13 | logging.LogInfo("Initializing dns", "instance", instance) 14 | server := dnsserver.Initialize(instance) 15 | // start serving up API routes 16 | logging.LogInfo("Starting dns server", "instance", instance) 17 | go func() { 18 | err := server.ListenAndServe() 19 | if err != nil { 20 | logging.LogError(err, "stopped listening", "server", instance) 21 | os.Exit(1) 22 | } 23 | }() 24 | } 25 | forever := make(chan bool) 26 | <-forever 27 | 28 | } 29 | -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2_code/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default 2 | default: build_linux ; 3 | 4 | BINARY_NAME?=mythic_dns_server 5 | DEBUG_LEVEL?="debug" 6 | MYTHIC_SERVER_HOST?="127.0.0.1" 7 | MYTHIC_SERVER_PORT?="17443" 8 | RABBITMQ_HOST?="127.0.0.1" 9 | RABBITMQ_PASSWORD?="PqR9XJ957sfHqcxj6FsBMj4p" 10 | 11 | build_protobuf_go: 12 | protoc --go_out=`pwd`/dnsserver/dnsgrpc --go_opt=paths=source_relative \ 13 | --go-grpc_out=`pwd`/dnsserver/dnsgrpc --go-grpc_opt=paths=source_relative \ 14 | --proto_path=`pwd`/dnsserver/dnsgrpc \ 15 | `pwd`/dnsserver/dnsgrpc/*.proto 16 | 17 | build: 18 | CGO_ENABLED=0 go build -o ${BINARY_NAME} . 19 | cp ${BINARY_NAME} / 20 | 21 | build_local: build_protobuf_go 22 | CGO_ENABLED=0 go build -o ${BINARY_NAME} . 23 | 24 | run: 25 | cp /${BINARY_NAME} . 26 | 27 | build_macos: 28 | CGO_ENABLED=0 go build -o ${BINARY_NAME} . 29 | 30 | run_custom: build_local 31 | DEBUG_LEVEL=${DEBUG_LEVEL} \ 32 | MYTHIC_SERVER_HOST=${MYTHIC_SERVER_HOST} \ 33 | MYTHIC_SERVER_PORT=${MYTHIC_SERVER_PORT} \ 34 | RABBITMQ_HOST=${RABBITMQ_HOST} \ 35 | RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} \ 36 | ./${BINARY_NAME} 37 | -------------------------------------------------------------------------------- /C2_Profiles/dns/Makefile: -------------------------------------------------------------------------------- 1 | BINARY_NAME?=main 2 | DEBUG_LEVEL?="debug" 3 | RABBITMQ_HOST?="127.0.0.1" 4 | RABBITMQ_PASSWORD?="PqR9XJ957sfHqcxj6FsBMj4p" 5 | MYTHIC_SERVER_HOST?="127.0.0.1" 6 | MYTHIC_SERVER_GRPC_PORT?="17444" 7 | WEBHOOK_DEFAULT_URL?= 8 | WEBHOOK_DEFAULT_CHANNEL?= 9 | WEBHOOK_DEFAULT_FEEDBACK_CHANNEL?= 10 | WEBHOOK_DEFAULT_CALLBACK_CHANNEL?= 11 | WEBHOOK_DEFAULT_STARTUP_CHANNEL?= 12 | 13 | build: 14 | CGO_ENABLED=0 go build -o ${BINARY_NAME} . 15 | cp ${BINARY_NAME} / 16 | cd dns/c2_code && make build 17 | 18 | run: 19 | cp /${BINARY_NAME} . 20 | cd dns/c2_code && make run 21 | ./${BINARY_NAME} 22 | 23 | local: 24 | CGO_ENABLED=0 go build -o ${BINARY_NAME} . 25 | 26 | run_custom: local 27 | DEBUG_LEVEL=${DEBUG_LEVEL} \ 28 | RABBITMQ_HOST=${RABBITMQ_HOST} \ 29 | RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} \ 30 | MYTHIC_SERVER_HOST=${MYTHIC_SERVER_HOST} \ 31 | MYTHIC_SERVER_GRPC_PORT=${MYTHIC_SERVER_GRPC_PORT} \ 32 | WEBHOOK_DEFAULT_URL=${WEBHOOK_DEFAULT_URL} \ 33 | WEBHOOK_DEFAULT_CHANNEL=${WEBHOOK_DEFAULT_CHANNEL} \ 34 | WEBHOOK_DEFAULT_FEEDBACK_CHANNEL=${WEBHOOK_DEFAULT_FEEDBACK_CHANNEL} \ 35 | WEBHOOK_DEFAULT_CALLBACK_CHANNEL=${WEBHOOK_DEFAULT_CALLBACK_CHANNEL} \ 36 | WEBHOOK_DEFAULT_STARTUP_CHANNEL=${WEBHOOK_DEFAULT_STARTUP_CHANNEL} \ 37 | ./${BINARY_NAME} 38 | -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2_code/dnsserver/config.go: -------------------------------------------------------------------------------- 1 | package dnsserver 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/MythicMeta/MythicContainer/logging" 10 | ) 11 | 12 | type config struct { 13 | Instances []instanceConfig `json:"instances"` 14 | } 15 | type instanceConfig struct { 16 | Port int `json:"port"` 17 | Debug bool `json:"debug"` 18 | BindIP string `json:"bind_ip"` 19 | Domains []string `json:"domains"` 20 | } 21 | 22 | var ( 23 | Config = config{} 24 | ) 25 | 26 | func InitializeLocalConfig() { 27 | if !fileExists(filepath.Join(getCwdFromExe(), "config.json")) { 28 | if _, err := os.Create(filepath.Join(getCwdFromExe(), "config.json")); err != nil { 29 | logging.LogFatalError(err, "[-] config.json doesn't exist and couldn't be created") 30 | } 31 | } 32 | if fileData, err := os.ReadFile("config.json"); err != nil { 33 | logging.LogError(err, "Failed to read in config.json file") 34 | } else if err = json.Unmarshal(fileData, &Config); err != nil { 35 | logging.LogError(err, "Failed to unmarshal config bytes") 36 | } else { 37 | logging.LogInfo("[+] Successfully read in config.json") 38 | } 39 | } 40 | 41 | func getCwdFromExe() string { 42 | exe, err := os.Executable() 43 | if err != nil { 44 | log.Fatalf("[-] Failed to get path to current executable: %v", err) 45 | } 46 | return filepath.Dir(exe) 47 | } 48 | 49 | func fileExists(path string) bool { 50 | info, err := os.Stat(path) 51 | if err != nil { 52 | if os.IsNotExist(err) { 53 | return false 54 | } 55 | } 56 | return !info.IsDir() 57 | } 58 | -------------------------------------------------------------------------------- /C2_Profiles/dns/go.mod: -------------------------------------------------------------------------------- 1 | module MyContainer 2 | 3 | go 1.25.1 4 | 5 | //replace github.com/MythicMeta/MythicContainer => ../../../../MythicMeta/MythicContainer 6 | 7 | require github.com/MythicMeta/MythicContainer v1.6.3 8 | 9 | require ( 10 | github.com/fsnotify/fsnotify v1.9.0 // indirect 11 | github.com/go-viper/mapstructure/v2 v2.4.0 // indirect 12 | github.com/google/uuid v1.6.0 // indirect 13 | github.com/mattn/go-colorable v0.1.14 // indirect 14 | github.com/mattn/go-isatty v0.0.20 // indirect 15 | github.com/mitchellh/mapstructure v1.5.0 // indirect 16 | github.com/pelletier/go-toml/v2 v2.2.4 // indirect 17 | github.com/rabbitmq/amqp091-go v1.10.0 // indirect 18 | github.com/rs/zerolog v1.34.0 // indirect 19 | github.com/sagikazarmark/locafero v0.12.0 // indirect 20 | github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect 21 | github.com/spf13/afero v1.15.0 // indirect 22 | github.com/spf13/cast v1.10.0 // indirect 23 | github.com/spf13/pflag v1.0.10 // indirect 24 | github.com/spf13/viper v1.21.0 // indirect 25 | github.com/subosito/gotenv v1.6.0 // indirect 26 | go.uber.org/multierr v1.11.0 // indirect 27 | go.yaml.in/yaml/v3 v3.0.4 // indirect 28 | golang.org/x/net v0.48.0 // indirect 29 | golang.org/x/sys v0.39.0 // indirect 30 | golang.org/x/text v0.32.0 // indirect 31 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 // indirect 32 | google.golang.org/grpc v1.77.0 // indirect 33 | google.golang.org/protobuf v1.36.11 // indirect 34 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect 35 | gopkg.in/yaml.v3 v3.0.1 // indirect 36 | ) 37 | -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2_code/go.mod: -------------------------------------------------------------------------------- 1 | module mythicDNS 2 | 3 | go 1.25.1 4 | 5 | require ( 6 | github.com/rs/zerolog v1.34.0 // indirect 7 | github.com/spf13/viper v1.21.0 // indirect 8 | ) 9 | 10 | require ( 11 | github.com/MythicMeta/MythicContainer v1.6.3 12 | github.com/golang/protobuf v1.5.4 13 | github.com/google/uuid v1.6.0 14 | github.com/miekg/dns v1.1.69 15 | google.golang.org/protobuf v1.36.11 16 | ) 17 | 18 | require ( 19 | github.com/fsnotify/fsnotify v1.9.0 // indirect 20 | github.com/go-viper/mapstructure/v2 v2.4.0 // indirect 21 | github.com/mattn/go-colorable v0.1.14 // indirect 22 | github.com/mattn/go-isatty v0.0.20 // indirect 23 | github.com/mitchellh/mapstructure v1.5.0 // indirect 24 | github.com/pelletier/go-toml/v2 v2.2.4 // indirect 25 | github.com/rabbitmq/amqp091-go v1.10.0 // indirect 26 | github.com/sagikazarmark/locafero v0.12.0 // indirect 27 | github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect 28 | github.com/spf13/afero v1.15.0 // indirect 29 | github.com/spf13/cast v1.10.0 // indirect 30 | github.com/spf13/pflag v1.0.10 // indirect 31 | github.com/subosito/gotenv v1.6.0 // indirect 32 | go.uber.org/multierr v1.11.0 // indirect 33 | go.yaml.in/yaml/v3 v3.0.4 // indirect 34 | golang.org/x/mod v0.31.0 // indirect 35 | golang.org/x/net v0.48.0 // indirect 36 | golang.org/x/sync v0.19.0 // indirect 37 | golang.org/x/sys v0.39.0 // indirect 38 | golang.org/x/text v0.32.0 // indirect 39 | golang.org/x/tools v0.40.0 // indirect 40 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 // indirect 41 | google.golang.org/grpc v1.77.0 // indirect 42 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect 43 | gopkg.in/yaml.v3 v3.0.1 // indirect 44 | ) 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DNS 2 | 3 | This is a Mythic C2 Profile called dns. It implements a DNS Server that listens for TXT Queries from the agent. This profiles includes: 4 | 5 | * Kill Dates 6 | * Sleep Intervals 7 | * Custom DNS Server 8 | 9 | The c2 profile has `mythic_c2_container==0.0.22` PyPi package installed and reports to Mythic as version "3". 10 | 11 | ## Implementing DNS Profile for Custom Agents: 12 | 13 | 14 | 15 | ## How to install an agent in this format within Mythic 16 | 17 | When it's time for you to test out your install or for another user to install your c2 profile, it's pretty simple. Within Mythic you can run the `mythic-cli` binary to install this in one of three ways: 18 | 19 | * `sudo ./mythic-cli install github https://github.com/user/repo` to install the main branch 20 | * `sudo ./mythic-cli install github https://github.com/user/repo branchname` to install a specific branch of that repo 21 | * `sudo ./mythic-cli install folder /path/to/local/folder/cloned/from/github` to install from an already cloned down version of an agent repo 22 | 23 | Now, you might be wondering _when_ should you or a user do this to properly add your agent to their Mythic instance. There's no wrong answer here, just depends on your preference. The three options are: 24 | 25 | * Mythic is already up and going, then you can run the install script and just direct that agent's containers to start (i.e. `sudo ./start_payload_types.sh agentName` and if that agent has its own special C2 containers, you'll need to start them too via `sudo ./start_c2_profiles.sh c2profileName`). 26 | * Mythic is already up and going, but you want to minimize your steps, you can just install the agent and run `sudo ./start_mythic.sh`. That script will first _stop_ all of your containers, then start everything back up again. This will also bring in the new agent you just installed. 27 | * Mythic isn't running, you can install the script and just run `sudo ./start_mythic.sh`. 28 | 29 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | # Pulled from Thanatos (https://github.com/MythicAgents/thanatos/blob/rewrite/.github/workflows/image.yml) - MEhrn00 2 | 3 | # Name for the Github actions workflow 4 | name: Build and push container images 5 | 6 | on: 7 | # Only run workflow when there is a new release published in Github 8 | #release: 9 | # types: [published] 10 | push: 11 | branches: 12 | - 'master' 13 | tags: 14 | - "v*.*.*" 15 | 16 | # Variables holding configuration settings 17 | env: 18 | # Container registry the built container image will be pushed to 19 | REGISTRY: ghcr.io 20 | 21 | # Set the container image name to the Github repository name. (MythicAgents/apfell) 22 | AGENT_IMAGE_NAME: ${{ github.repository }} 23 | 24 | # Description label for the package in Github 25 | IMAGE_DESCRIPTION: ${{ github.repository }} container for use with Mythic 26 | 27 | # Source URL for the package in Github. This links the Github repository packages list 28 | # to this container image 29 | IMAGE_SOURCE: ${{ github.server_url }}/${{ github.repository }} 30 | 31 | # License for the container image 32 | IMAGE_LICENSE: BSD-3-Clause 33 | 34 | # Set the container image version to the Github release tag 35 | VERSION: ${{ github.ref_name }} 36 | #VERSION: ${{ github.event.head_commit.message }} 37 | 38 | RELEASE_BRANCH: master 39 | 40 | jobs: 41 | # Builds the base container image and pushes it to the container registry 42 | agent_build: 43 | runs-on: ubuntu-latest 44 | permissions: 45 | contents: write 46 | packages: write 47 | steps: 48 | - name: Checkout the repository 49 | uses: actions/checkout@v4 # ref: https://github.com/marketplace/actions/checkout 50 | - name: Log in to the container registry 51 | uses: docker/login-action@v3 # ref: https://github.com/marketplace/actions/docker-login 52 | with: 53 | registry: ${{ env.REGISTRY }} 54 | username: ${{ github.actor }} 55 | password: ${{ secrets.GITHUB_TOKEN }} 56 | - name: Set up QEMU 57 | uses: docker/setup-qemu-action@v3 58 | with: 59 | platforms: 'arm64,arm' 60 | - name: Set up Docker Buildx 61 | id: buildx 62 | uses: docker/setup-buildx-action@v3 63 | # the following are unique to this job 64 | - name: Lowercase the server container image name 65 | run: echo "AGENT_IMAGE_NAME=${AGENT_IMAGE_NAME,,}" >> ${GITHUB_ENV} 66 | - name: Build and push the server container image 67 | uses: docker/build-push-action@v5 # ref: https://github.com/marketplace/actions/build-and-push-docker-images 68 | with: 69 | context: C2_Profiles/dns 70 | file: C2_Profiles/dns/Dockerfile 71 | tags: | 72 | ${{ env.REGISTRY }}/${{ env.AGENT_IMAGE_NAME }}:${{ env.VERSION }} 73 | ${{ env.REGISTRY }}/${{ env.AGENT_IMAGE_NAME }}:latest 74 | push: ${{ github.ref_type == 'tag' }} 75 | # These container metadata labels allow configuring the package in Github 76 | # packages. The source will link the package to this Github repository 77 | labels: | 78 | org.opencontainers.image.source=${{ env.IMAGE_SOURCE }} 79 | org.opencontainers.image.description=${{ env.IMAGE_DESCRIPTION }} 80 | org.opencontainers.image.licenses=${{ env.IMAGE_LICENSE }} 81 | platforms: linux/amd64,linux/arm64 82 | 83 | update_files: 84 | runs-on: ubuntu-latest 85 | needs: 86 | - agent_build 87 | permissions: 88 | contents: write 89 | packages: write 90 | 91 | steps: 92 | # Pull in the repository code 93 | - name: Checkout the repository 94 | uses: actions/checkout@v4 # ref: https://github.com/marketplace/actions/checkout 95 | 96 | # update names to lowercase 97 | - name: Lowercase the container image name 98 | run: echo "AGENT_IMAGE_NAME=${AGENT_IMAGE_NAME,,}" >> ${GITHUB_ENV} 99 | 100 | - name: Update package.json version 101 | uses: jossef/action-set-json-field@v2.1 102 | with: 103 | file: config.json 104 | field: remote_images.dns 105 | value: ${{env.REGISTRY}}/${{env.AGENT_IMAGE_NAME}}:${{env.VERSION}} 106 | 107 | # Push the changes to the Dockerfile 108 | - name: Push the updated base Dockerfile image reference changes 109 | if: ${{ github.ref_type == 'tag' }} 110 | uses: EndBug/add-and-commit@v9 # ref: https://github.com/marketplace/actions/add-commit 111 | with: 112 | # Only add the Dockerfile changes. Nothing else should have been modified 113 | add: "['config.json']" 114 | # Use the Github actions bot for the commit author 115 | default_author: github_actions 116 | committer_email: github-actions[bot]@users.noreply.github.com 117 | 118 | # Set the commit message 119 | message: "Bump Dockerfile tag to match release '${{ env.VERSION }}'" 120 | 121 | # Overwrite the current git tag with the new changes 122 | tag: '${{ env.VERSION }} --force' 123 | 124 | # Push the new changes with the tag overwriting the current one 125 | tag_push: '--force' 126 | 127 | # Push the commits to the branch marked as the release branch 128 | push: origin HEAD:${{ env.RELEASE_BRANCH }} --set-upstream 129 | 130 | # Have the workflow fail in case there are pathspec issues 131 | pathspec_error_handling: exitImmediately 132 | -------------------------------------------------------------------------------- /documentation-c2/DNS/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "DNS" 3 | chapter = false 4 | weight = 5 5 | +++ 6 | 7 | ## Overview 8 | This C2 Profile uses DNS requests to communicate between the agent and the C2 container, where messages are aggregated and forwarded to the Mythic API. 9 | The DNS Requests use a format similar to the Sliver C2 Framework's DNS C2 (https://github.com/BishopFox/sliver/blob/master/server/c2/dns.go) and (https://sliver.sh/docs?name=DNS+C2). 10 | However, Mythic's version is a bit different and slightly less complex (this is subject to change as this is currently in beta). 11 | 12 | To configure DNS properly, you'll want a setup like the following: 13 | 14 | If your DNS name is `mydomain.com`, then you'll configure DNS with the following three entries: 15 | 16 | * Type: A Record, Host: `ns1`, Value: IP address of where Mythic's DNS C2 Profile is running or redirector that'll forward DNS traffic 17 | * Type: A Record, Host: `mydomain.com`, Value: same as above 18 | * Type: NS Record, Host: `dns`, Value: `ns1.mydomain.com.` (note the trailing `.`, it might get auto-added by your provider) 19 | * This `host` value can be whatever you want and will be the main subdomain for your DNS traffic. Naturally, the longer this is the less room you have for messages. 20 | 21 | When building your payload, you'd configure your domain as `dns.mydomain.com`. The comms will then be in the format of `[stuff].dns.mydomain.com`. 22 | 23 | 24 | To provide metadata about message transfers and ordering while still minimizing space, Mythic's DNS uses the following protobuf: 25 | ```protobuf 26 | syntax = "proto3"; 27 | enum Actions { 28 | AgentToServer = 0; 29 | ServerToAgent = 1; 30 | ReTransmit = 2; 31 | MessageLost = 3; 32 | } 33 | message DnsPacket { 34 | Actions Action = 1; 35 | uint32 AgentSessionID = 2; 36 | uint32 MessageID = 3; 37 | uint32 Size = 4; 38 | uint32 Begin = 5; 39 | string Data = 6; 40 | } 41 | ``` 42 | Agents will put a chunk of their normal base64 message in the "Data" field; this message is then marshalled and converted to base32 format (without padding). 43 | This resulting base32 message is what gets sent via a DNS Query message to the Mythic server. 44 | 45 | Each request gets a reply with 4 answers with the following order (indicated by the TTL for the Answer): 46 | 1. AgentSessionID 47 | 2. MessageID 48 | 3. Begin Byte Number 49 | 4. Action 50 | 51 | The AgentSessionID is generated by the payload when it first executes and is a random uint32 value. This never changes and is NOT your Mythic UUIDs. 52 | The MessageID is a random uint32 value generated by the payload _for each message_ that it's trying to send to Mythic (not each DnsPacket, but each normal Mythic message). 53 | The Begin value is the uint32 value of the where this set of Data chunks lives within the overall message being sent. 54 | The Action indicates to the agent some context. When sending messages from Agent to Server, this will be AgentToServer for every message except for the last one where the server will reply with ServerToAgent to indicate that it got everything and Mythic has a reply. 55 | A ReTransmit action asks the agent to stop and retransmit the entire message (all chunks) again - this typically means something happened on the DNS server side and it lost track of the message. 56 | A MessageLost action happens when the agent is requesting a message from the server and typically happens if the server component restarts during message transmission. In this case, the server no longer has the message to send and the agent should consider it lost. 57 | 58 | For example: 59 | Sending a Mythic Checkin message will be the standard base64(uuid + message) format. That message is chunked up and put into a series of these DnsPacket objects. 60 | The number of chunks depends on the length of the DNS domain being used. Let's assume there's two chunks with 100 Bytes of data in the first chunk and 50 Bytes in the second. 61 | The agent would generate one MessageID, and that MessageID would be the same for both chunks. Size would be the total size, 150 Bytes for both messages. Begin for the first message would be 0, but would be 100 for the second. 62 | 63 | 64 | ### C2 Workflow 65 | {{}} 66 | sequenceDiagram 67 | 68 | 69 | {{< /mermaid >}} 70 | 71 | ## Configuration Options 72 | The profile reads a `config.json` file and binds to ports to receive DNS requests. 73 | 74 | ```JSON 75 | { 76 | "instances": [ 77 | { 78 | "port": 53, 79 | "debug": false, 80 | "bind_ip": "0.0.0.0", 81 | "domains": ["dns.localhost", "fake.localhost", "this.is.something.longer.localhost"] 82 | } 83 | ] 84 | } 85 | 86 | 87 | ``` 88 | 89 | ## Profile Options 90 | 91 | #### domains 92 | A list of domains to use for DNS Queries. The longer the domain, the less room there will be to transfer data. 93 | 94 | #### killdate 95 | Date for the agent to automatically exit, typically after an assessment is finished. 96 | 97 | #### encrypted_exchange_check 98 | True or False for if you want to perform a key exchange with the Mythic Server. When this is true, the agent uses the key specified by the base64 32Byte key to send an initial message to the Mythic server with a newly generated RSA public key. If this is set to `F`, then the agent tries to just use the base64 of the key as a static AES key for encryption. If that key is also blanked out, then the requests will all be in plaintext. 99 | 100 | #### callback_interval 101 | A number to indicate how many seconds the agent should wait in between tasking requests. 102 | 103 | #### callback_jitter 104 | Percentage of jitter effect for callback interval. 105 | 106 | #### Domain Rotation 107 | This indicates how you want your domains to be used (only really matters if you specify more than one domain). `fail-over` will use the first domain until it fails `failover_threshold` times, then it moves to the next one. `round-robin` will just keep using the next one in sequence for each message. `random` will just randomly use them. 108 | 109 | #### AESPSK 110 | Indicate if you want to use no crypto (i.e. plaintext) or if you want to use Mythic's aes256_hmac. Using no crypto is really helpful for agent development so that it's easier to see messages and get started faster, but for actual operations you should leave the default to aes256_hmac. 111 | 112 | #### failover_threshold 113 | How many times a domain should fail before moving on to the next domain when the domain rotation is `fail-over`. 114 | 115 | #### dns_server 116 | What is the DNS Server IP and Port (i.e: `8.8.8.8:53`) to use when issuing DNS requests. 117 | 118 | #### record_type 119 | What kinds of requests should the agent make and receive. A, AAAA, or TXT requests. 120 | 121 | #### max_query_length 122 | What is the maximum length of a query from the agent to Mythic. The hard limit is 255 by DNS protocol standards, but that could also stand out in environments. The smaller this number, the less data can be sent per request. 123 | 124 | ## Development 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2_code/dnsserver/dnsgrpc/dns.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.36.10 4 | // protoc v6.33.0 5 | // source: dns.proto 6 | 7 | package dnsgrpc 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | unsafe "unsafe" 15 | ) 16 | 17 | const ( 18 | // Verify that this generated code is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 20 | // Verify that runtime/protoimpl is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 22 | ) 23 | 24 | type Actions int32 25 | 26 | const ( 27 | Actions_AgentToServer Actions = 0 28 | Actions_ServerToAgent Actions = 1 29 | Actions_ReTransmit Actions = 2 30 | Actions_MessageLost Actions = 3 31 | ) 32 | 33 | // Enum value maps for Actions. 34 | var ( 35 | Actions_name = map[int32]string{ 36 | 0: "AgentToServer", 37 | 1: "ServerToAgent", 38 | 2: "ReTransmit", 39 | 3: "MessageLost", 40 | } 41 | Actions_value = map[string]int32{ 42 | "AgentToServer": 0, 43 | "ServerToAgent": 1, 44 | "ReTransmit": 2, 45 | "MessageLost": 3, 46 | } 47 | ) 48 | 49 | func (x Actions) Enum() *Actions { 50 | p := new(Actions) 51 | *p = x 52 | return p 53 | } 54 | 55 | func (x Actions) String() string { 56 | return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) 57 | } 58 | 59 | func (Actions) Descriptor() protoreflect.EnumDescriptor { 60 | return file_dns_proto_enumTypes[0].Descriptor() 61 | } 62 | 63 | func (Actions) Type() protoreflect.EnumType { 64 | return &file_dns_proto_enumTypes[0] 65 | } 66 | 67 | func (x Actions) Number() protoreflect.EnumNumber { 68 | return protoreflect.EnumNumber(x) 69 | } 70 | 71 | // Deprecated: Use Actions.Descriptor instead. 72 | func (Actions) EnumDescriptor() ([]byte, []int) { 73 | return file_dns_proto_rawDescGZIP(), []int{0} 74 | } 75 | 76 | type DnsPacket struct { 77 | state protoimpl.MessageState `protogen:"open.v1"` 78 | Action Actions `protobuf:"varint,1,opt,name=Action,proto3,enum=dnsStructs.Actions" json:"Action,omitempty"` 79 | AgentSessionID uint32 `protobuf:"varint,2,opt,name=AgentSessionID,proto3" json:"AgentSessionID,omitempty"` 80 | MessageID uint32 `protobuf:"varint,3,opt,name=MessageID,proto3" json:"MessageID,omitempty"` 81 | Size uint32 `protobuf:"varint,4,opt,name=Size,proto3" json:"Size,omitempty"` 82 | Begin uint32 `protobuf:"varint,5,opt,name=Begin,proto3" json:"Begin,omitempty"` 83 | Data []byte `protobuf:"bytes,6,opt,name=Data,proto3" json:"Data,omitempty"` 84 | unknownFields protoimpl.UnknownFields 85 | sizeCache protoimpl.SizeCache 86 | } 87 | 88 | func (x *DnsPacket) Reset() { 89 | *x = DnsPacket{} 90 | mi := &file_dns_proto_msgTypes[0] 91 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 92 | ms.StoreMessageInfo(mi) 93 | } 94 | 95 | func (x *DnsPacket) String() string { 96 | return protoimpl.X.MessageStringOf(x) 97 | } 98 | 99 | func (*DnsPacket) ProtoMessage() {} 100 | 101 | func (x *DnsPacket) ProtoReflect() protoreflect.Message { 102 | mi := &file_dns_proto_msgTypes[0] 103 | if x != nil { 104 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 105 | if ms.LoadMessageInfo() == nil { 106 | ms.StoreMessageInfo(mi) 107 | } 108 | return ms 109 | } 110 | return mi.MessageOf(x) 111 | } 112 | 113 | // Deprecated: Use DnsPacket.ProtoReflect.Descriptor instead. 114 | func (*DnsPacket) Descriptor() ([]byte, []int) { 115 | return file_dns_proto_rawDescGZIP(), []int{0} 116 | } 117 | 118 | func (x *DnsPacket) GetAction() Actions { 119 | if x != nil { 120 | return x.Action 121 | } 122 | return Actions_AgentToServer 123 | } 124 | 125 | func (x *DnsPacket) GetAgentSessionID() uint32 { 126 | if x != nil { 127 | return x.AgentSessionID 128 | } 129 | return 0 130 | } 131 | 132 | func (x *DnsPacket) GetMessageID() uint32 { 133 | if x != nil { 134 | return x.MessageID 135 | } 136 | return 0 137 | } 138 | 139 | func (x *DnsPacket) GetSize() uint32 { 140 | if x != nil { 141 | return x.Size 142 | } 143 | return 0 144 | } 145 | 146 | func (x *DnsPacket) GetBegin() uint32 { 147 | if x != nil { 148 | return x.Begin 149 | } 150 | return 0 151 | } 152 | 153 | func (x *DnsPacket) GetData() []byte { 154 | if x != nil { 155 | return x.Data 156 | } 157 | return nil 158 | } 159 | 160 | var File_dns_proto protoreflect.FileDescriptor 161 | 162 | const file_dns_proto_rawDesc = "" + 163 | "\n" + 164 | "\tdns.proto\x12\n" + 165 | "dnsStructs\"\xbc\x01\n" + 166 | "\tDnsPacket\x12+\n" + 167 | "\x06Action\x18\x01 \x01(\x0e2\x13.dnsStructs.ActionsR\x06Action\x12&\n" + 168 | "\x0eAgentSessionID\x18\x02 \x01(\rR\x0eAgentSessionID\x12\x1c\n" + 169 | "\tMessageID\x18\x03 \x01(\rR\tMessageID\x12\x12\n" + 170 | "\x04Size\x18\x04 \x01(\rR\x04Size\x12\x14\n" + 171 | "\x05Begin\x18\x05 \x01(\rR\x05Begin\x12\x12\n" + 172 | "\x04Data\x18\x06 \x01(\fR\x04Data*P\n" + 173 | "\aActions\x12\x11\n" + 174 | "\rAgentToServer\x10\x00\x12\x11\n" + 175 | "\rServerToAgent\x10\x01\x12\x0e\n" + 176 | "\n" + 177 | "ReTransmit\x10\x02\x12\x0f\n" + 178 | "\vMessageLost\x10\x03BOZMgithub.com/MythicC2Profiles/dns/C2_Profiles/dns/dns/c2_code/dnsserver/dnsgrpcb\x06proto3" 179 | 180 | var ( 181 | file_dns_proto_rawDescOnce sync.Once 182 | file_dns_proto_rawDescData []byte 183 | ) 184 | 185 | func file_dns_proto_rawDescGZIP() []byte { 186 | file_dns_proto_rawDescOnce.Do(func() { 187 | file_dns_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_dns_proto_rawDesc), len(file_dns_proto_rawDesc))) 188 | }) 189 | return file_dns_proto_rawDescData 190 | } 191 | 192 | var file_dns_proto_enumTypes = make([]protoimpl.EnumInfo, 1) 193 | var file_dns_proto_msgTypes = make([]protoimpl.MessageInfo, 1) 194 | var file_dns_proto_goTypes = []any{ 195 | (Actions)(0), // 0: dnsStructs.Actions 196 | (*DnsPacket)(nil), // 1: dnsStructs.DnsPacket 197 | } 198 | var file_dns_proto_depIdxs = []int32{ 199 | 0, // 0: dnsStructs.DnsPacket.Action:type_name -> dnsStructs.Actions 200 | 1, // [1:1] is the sub-list for method output_type 201 | 1, // [1:1] is the sub-list for method input_type 202 | 1, // [1:1] is the sub-list for extension type_name 203 | 1, // [1:1] is the sub-list for extension extendee 204 | 0, // [0:1] is the sub-list for field type_name 205 | } 206 | 207 | func init() { file_dns_proto_init() } 208 | func file_dns_proto_init() { 209 | if File_dns_proto != nil { 210 | return 211 | } 212 | type x struct{} 213 | out := protoimpl.TypeBuilder{ 214 | File: protoimpl.DescBuilder{ 215 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 216 | RawDescriptor: unsafe.Slice(unsafe.StringData(file_dns_proto_rawDesc), len(file_dns_proto_rawDesc)), 217 | NumEnums: 1, 218 | NumMessages: 1, 219 | NumExtensions: 0, 220 | NumServices: 0, 221 | }, 222 | GoTypes: file_dns_proto_goTypes, 223 | DependencyIndexes: file_dns_proto_depIdxs, 224 | EnumInfos: file_dns_proto_enumTypes, 225 | MessageInfos: file_dns_proto_msgTypes, 226 | }.Build() 227 | File_dns_proto = out.File 228 | file_dns_proto_goTypes = nil 229 | file_dns_proto_depIdxs = nil 230 | } 231 | -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2functions/builder.go: -------------------------------------------------------------------------------- 1 | package c2functions 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "slices" 9 | 10 | c2structs "github.com/MythicMeta/MythicContainer/c2_structs" 11 | "github.com/MythicMeta/MythicContainer/logging" 12 | ) 13 | 14 | type config struct { 15 | Instances []instanceConfig `json:"instances"` 16 | } 17 | type instanceConfig struct { 18 | Port int `json:"port"` 19 | Debug bool `json:"debug"` 20 | BindIP string `json:"bind_ip"` 21 | Domains []string `json:"domains"` 22 | } 23 | 24 | func getC2JsonConfig() (*config, error) { 25 | currentConfig := config{} 26 | configBytes, err := os.ReadFile(filepath.Join(".", "dns", "c2_code", "config.json")) 27 | if err != nil { 28 | return nil, err 29 | } 30 | err = json.Unmarshal(configBytes, ¤tConfig) 31 | if err != nil { 32 | logging.LogError(err, "Failed to unmarshal config bytes") 33 | return nil, err 34 | } 35 | return ¤tConfig, nil 36 | } 37 | func writeC2JsonConfig(cfg *config) error { 38 | jsonBytes, err := json.MarshalIndent(*cfg, "", " ") 39 | if err != nil { 40 | return err 41 | } 42 | return os.WriteFile(filepath.Join(".", "dns", "c2_code", "config.json"), jsonBytes, 644) 43 | } 44 | 45 | var version = "0.0.6" 46 | var dnsc2definition = c2structs.C2Profile{ 47 | Name: "dns", 48 | Author: "@its_a_feature_", 49 | Description: fmt.Sprintf("Uses DNS A, AAAA, and TXT queries for connectivity\nCURRENTLY IN BETA! USE WITH CAUTION!"), 50 | IsP2p: false, 51 | IsServerRouted: true, 52 | SemVer: version, 53 | ServerBinaryPath: filepath.Join(".", "dns", "c2_code", "mythic_dns_server"), 54 | ConfigCheckFunction: func(message c2structs.C2ConfigCheckMessage) c2structs.C2ConfigCheckMessageResponse { 55 | response := c2structs.C2ConfigCheckMessageResponse{ 56 | Success: true, 57 | Message: fmt.Sprintf("Called config check\n%v", message), 58 | } 59 | currentConfig, err := getC2JsonConfig() 60 | if err != nil { 61 | response.Success = false 62 | response.Message = err.Error() 63 | return response 64 | } 65 | domains, err := message.GetArrayArg("domains") 66 | if err != nil { 67 | response.Success = false 68 | response.Message = err.Error() 69 | return response 70 | } 71 | ports := []int{} 72 | for i, _ := range currentConfig.Instances { 73 | ports = append(ports, currentConfig.Instances[i].Port) 74 | for _, domain := range domains { 75 | if !slices.Contains(currentConfig.Instances[i].Domains, domain) { 76 | currentConfig.Instances[i].Domains = append(currentConfig.Instances[i].Domains, domain) 77 | } 78 | } 79 | } 80 | err = writeC2JsonConfig(currentConfig) 81 | if err != nil { 82 | response.Success = false 83 | response.Message = err.Error() 84 | return response 85 | } 86 | response.Success = true 87 | response.Message = fmt.Sprintf("All domains are included and ready!\nBe sure to connect or redirect via one of the following ports:\n%v\n", ports) 88 | response.RestartInternalServer = true 89 | return response 90 | }, 91 | GetRedirectorRulesFunction: func(message c2structs.C2GetRedirectorRuleMessage) c2structs.C2GetRedirectorRuleMessageResponse { 92 | response := c2structs.C2GetRedirectorRuleMessageResponse{ 93 | Success: false, 94 | Message: "Function not supported yet", 95 | } 96 | return response 97 | }, 98 | OPSECCheckFunction: func(message c2structs.C2OPSECMessage) c2structs.C2OPSECMessageResponse { 99 | response := c2structs.C2OPSECMessageResponse{ 100 | Success: true, 101 | Message: fmt.Sprintf("Called opsec check:\n%v", message), 102 | } 103 | return response 104 | }, 105 | GetIOCFunction: func(message c2structs.C2GetIOCMessage) c2structs.C2GetIOCMessageResponse { 106 | response := c2structs.C2GetIOCMessageResponse{Success: true} 107 | domains, err := message.GetArrayArg("domains") 108 | if err != nil { 109 | response.Success = false 110 | return response 111 | } 112 | for _, domain := range domains { 113 | response.IOCs = append(response.IOCs, c2structs.IOC{ 114 | Type: "Domain", 115 | IOC: domain, 116 | }) 117 | } 118 | return response 119 | }, 120 | SampleMessageFunction: func(message c2structs.C2SampleMessageMessage) c2structs.C2SampleMessageResponse { 121 | response := c2structs.C2SampleMessageResponse{Success: true, Message: "Function not supported yet"} 122 | 123 | return response 124 | }, 125 | HostFileFunction: func(message c2structs.C2HostFileMessage) c2structs.C2HostFileMessageResponse { 126 | return c2structs.C2HostFileMessageResponse{ 127 | Success: false, 128 | RestartInternalServer: false, 129 | Error: "Function not supported yet", 130 | } 131 | }, 132 | } 133 | var dnsc2parameters = []c2structs.C2Parameter{ 134 | { 135 | Name: "domains", 136 | Description: "Series of domains to use", 137 | DefaultValue: []string{"domain.com"}, 138 | ParameterType: c2structs.C2_PARAMETER_TYPE_ARRAY, 139 | Required: true, 140 | }, 141 | { 142 | Name: "killdate", 143 | Description: "Kill Date", 144 | DefaultValue: 365, 145 | ParameterType: c2structs.C2_PARAMETER_TYPE_DATE, 146 | Required: false, 147 | }, 148 | { 149 | Name: "encrypted_exchange_check", 150 | Description: "Perform Key Exchange", 151 | DefaultValue: true, 152 | ParameterType: c2structs.C2_PARAMETER_TYPE_BOOLEAN, 153 | Required: false, 154 | }, 155 | { 156 | Name: "callback_jitter", 157 | Description: "Callback Jitter in percent", 158 | DefaultValue: 23, 159 | ParameterType: c2structs.C2_PARAMETER_TYPE_NUMBER, 160 | Required: false, 161 | VerifierRegex: "^[0-9]+$", 162 | }, 163 | { 164 | Name: "AESPSK", 165 | Description: "Encryption Type", 166 | DefaultValue: "aes256_hmac", 167 | ParameterType: c2structs.C2_PARAMETER_TYPE_CHOOSE_ONE, 168 | Required: false, 169 | IsCryptoType: true, 170 | Choices: []string{ 171 | "aes256_hmac", 172 | "none", 173 | }, 174 | }, 175 | { 176 | Name: "callback_interval", 177 | Description: "Callback Interval in seconds", 178 | DefaultValue: 10, 179 | ParameterType: c2structs.C2_PARAMETER_TYPE_NUMBER, 180 | Required: false, 181 | VerifierRegex: "^[0-9]+$", 182 | }, 183 | { 184 | Name: "domain_rotation", 185 | Description: "Domain rotation pattern. Fail-over uses each one in order until it can't communicate with it successfully and moves on. Round-robin makes each request to the next host in the list.", 186 | ParameterType: c2structs.C2_PARAMETER_TYPE_CHOOSE_ONE, 187 | Choices: []string{ 188 | "round-robin", 189 | "random", 190 | "fail-over", 191 | }, 192 | }, 193 | { 194 | Name: "failover_threshold", 195 | Description: "Domain fail-over threshold for how many times to keep trying one host before moving onto the next", 196 | DefaultValue: 5, 197 | ParameterType: c2structs.C2_PARAMETER_TYPE_NUMBER, 198 | }, 199 | { 200 | Name: "dns_server", 201 | Description: "IP:Port of DNS server to use", 202 | DefaultValue: "8.8.8.8:53", 203 | ParameterType: c2structs.C2_PARAMETER_TYPE_STRING, 204 | }, 205 | { 206 | Name: "record_type", 207 | Description: "What type of DNS responses to use - A, AAAA, or TXT", 208 | DefaultValue: "A", 209 | Choices: []string{ 210 | "A", "AAAA", "TXT", 211 | }, 212 | ParameterType: c2structs.C2_PARAMETER_TYPE_CHOOSE_ONE, 213 | }, 214 | { 215 | Name: "max_query_length", 216 | Description: "Maximum DNS Query length (must be <= 255 per DNS)", 217 | DefaultValue: 255, 218 | ParameterType: c2structs.C2_PARAMETER_TYPE_NUMBER, 219 | }, 220 | } 221 | 222 | func Initialize() { 223 | c2structs.AllC2Data.Get("dns").AddC2Definition(dnsc2definition) 224 | c2structs.AllC2Data.Get("dns").AddParameters(dnsc2parameters) 225 | } 226 | -------------------------------------------------------------------------------- /C2_Profiles/dns/go.sum: -------------------------------------------------------------------------------- 1 | github.com/MythicMeta/MythicContainer v1.4.21 h1:g8/u09yfpb3Qn+Y0RiTpXV/z8dzgUEml+GgFIktmGh8= 2 | github.com/MythicMeta/MythicContainer v1.4.21/go.mod h1:BnUYftqQ9KsGxBd6RlyRcAHBrqV1CUcrRCjktWwc2Do= 3 | github.com/MythicMeta/MythicContainer v1.6.3 h1:iSWYf+4m0qAEFql8rXNI++8wM5wWxez50H5D22r4cSo= 4 | github.com/MythicMeta/MythicContainer v1.6.3/go.mod h1:bHB40wZf9txJKNc2x5H5g+3CJ2NCJlT9t5zCZBbVXYE= 5 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 9 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 10 | github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= 11 | github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 12 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 13 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 14 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 15 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 16 | github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= 17 | github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 18 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 19 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 20 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 21 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 22 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 23 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 24 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 25 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 26 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 27 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 28 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 29 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 30 | github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= 31 | github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 32 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 33 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 34 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 35 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 36 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 37 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 38 | github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= 39 | github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= 40 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 41 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 42 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 43 | github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= 44 | github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= 45 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 46 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 47 | github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= 48 | github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= 49 | github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= 50 | github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= 51 | github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= 52 | github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= 53 | github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= 54 | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= 55 | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= 56 | github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= 57 | github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= 58 | github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= 59 | github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= 60 | github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= 61 | github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= 62 | github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= 63 | github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= 64 | github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= 65 | github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= 66 | github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 67 | github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= 68 | github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 69 | github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= 70 | github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= 71 | github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= 72 | github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= 73 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 74 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 75 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 76 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 77 | go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 78 | go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 79 | go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= 80 | go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= 81 | go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= 82 | go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= 83 | go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= 84 | go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= 85 | go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= 86 | go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= 87 | go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= 88 | go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= 89 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 90 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 91 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 92 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 93 | go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= 94 | go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 95 | golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= 96 | golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= 97 | golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= 98 | golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= 99 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 100 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 101 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 102 | golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= 103 | golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 104 | golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= 105 | golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 106 | golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= 107 | golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= 108 | golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= 109 | golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= 110 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10= 111 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= 112 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 h1:2I6GHUeJ/4shcDpoUlLs/2WPnhg7yJwvXtqcMJt9liA= 113 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= 114 | google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= 115 | google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= 116 | google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= 117 | google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= 118 | google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 119 | google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 120 | google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= 121 | google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 122 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 123 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 124 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 125 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 126 | gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= 127 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 128 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 129 | -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2_code/go.sum: -------------------------------------------------------------------------------- 1 | github.com/MythicMeta/MythicContainer v1.4.21 h1:g8/u09yfpb3Qn+Y0RiTpXV/z8dzgUEml+GgFIktmGh8= 2 | github.com/MythicMeta/MythicContainer v1.4.21/go.mod h1:BnUYftqQ9KsGxBd6RlyRcAHBrqV1CUcrRCjktWwc2Do= 3 | github.com/MythicMeta/MythicContainer v1.6.2 h1:y1PBY0EsjfKCTzgFsGyHlrnDBEvhhCHSJREKHtyxQXM= 4 | github.com/MythicMeta/MythicContainer v1.6.2/go.mod h1:bHB40wZf9txJKNc2x5H5g+3CJ2NCJlT9t5zCZBbVXYE= 5 | github.com/MythicMeta/MythicContainer v1.6.3 h1:iSWYf+4m0qAEFql8rXNI++8wM5wWxez50H5D22r4cSo= 6 | github.com/MythicMeta/MythicContainer v1.6.3/go.mod h1:bHB40wZf9txJKNc2x5H5g+3CJ2NCJlT9t5zCZBbVXYE= 7 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 11 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 12 | github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= 13 | github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 14 | github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= 15 | github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 16 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 17 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 18 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 19 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 20 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 21 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 22 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 23 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 24 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 25 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 26 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 27 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 28 | github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= 29 | github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 30 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 31 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 32 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 33 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 34 | github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0= 35 | github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= 36 | github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc= 37 | github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g= 38 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 39 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 40 | github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= 41 | github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= 42 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 43 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 44 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 45 | github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= 46 | github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= 47 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 48 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 49 | github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= 50 | github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= 51 | github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= 52 | github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= 53 | github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= 54 | github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= 55 | github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= 56 | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= 57 | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= 58 | github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= 59 | github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= 60 | github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= 61 | github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= 62 | github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= 63 | github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= 64 | github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= 65 | github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= 66 | github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= 67 | github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= 68 | github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 69 | github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= 70 | github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 71 | github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= 72 | github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= 73 | github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= 74 | github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= 75 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 76 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 77 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 78 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 79 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 80 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 81 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 82 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 83 | go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= 84 | go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 85 | golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= 86 | golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= 87 | golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= 88 | golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= 89 | golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= 90 | golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= 91 | golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= 92 | golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= 93 | golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= 94 | golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 95 | golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= 96 | golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 97 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 98 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 99 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 100 | golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= 101 | golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 102 | golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= 103 | golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 104 | golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= 105 | golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= 106 | golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= 107 | golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= 108 | golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= 109 | golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= 110 | golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= 111 | golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= 112 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= 113 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= 114 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= 115 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 h1:2I6GHUeJ/4shcDpoUlLs/2WPnhg7yJwvXtqcMJt9liA= 116 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= 117 | google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= 118 | google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= 119 | google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= 120 | google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= 121 | google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= 122 | google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 123 | google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 124 | google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= 125 | google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 126 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 127 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 128 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 129 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 130 | gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= 131 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 132 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 133 | -------------------------------------------------------------------------------- /C2_Profiles/dns/dns/c2_code/dnsserver/initialize.go: -------------------------------------------------------------------------------- 1 | package dnsserver 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "encoding/base32" 7 | "encoding/base64" 8 | "fmt" 9 | "io" 10 | "mythicDNS/dnsserver/dnsgrpc" 11 | "net" 12 | "net/http" 13 | "slices" 14 | "sort" 15 | "strings" 16 | "sync" 17 | "time" 18 | 19 | mythicConfig "github.com/MythicMeta/MythicContainer/config" 20 | "github.com/MythicMeta/MythicContainer/logging" 21 | "github.com/MythicMeta/MythicContainer/mythicrpc" 22 | "github.com/golang/protobuf/proto" 23 | "github.com/google/uuid" 24 | "github.com/miekg/dns" 25 | ) 26 | 27 | type DnsServer struct { 28 | server *dns.Server 29 | connections *sync.Map 30 | connectionMutex *sync.RWMutex 31 | domains []string 32 | TTL uint32 33 | debug bool 34 | CacheTimeout time.Duration 35 | } 36 | 37 | // DnsMessageStream tracks the progress of a message in chunk transfer 38 | type DnsMessageStream struct { 39 | Size uint32 40 | TotalReceived uint32 41 | Messages map[uint32]*dnsgrpc.DnsPacket 42 | StartBytes []uint32 43 | } 44 | 45 | // DnsConnection tracks all the messages in/out for a callback/payload 46 | type DnsConnection struct { 47 | outgoingMsgIDs []uint32 48 | outgoingBuffers map[uint32][]byte 49 | outgoingMsgIDsToClear []uint32 50 | outgoingMutex *sync.RWMutex 51 | 52 | incomingMessages map[uint32]*DnsMessageStream 53 | incomingMutex *sync.Mutex 54 | } 55 | type AgentMessageMythicUITracking struct { 56 | AgentCallbackID string `json:"agentCallbackId"` 57 | ExtraInfo string `json:"extraInfo"` 58 | LastUpdateTime time.Time 59 | PerformedUpdate bool 60 | } 61 | 62 | var AgentIDToMythicIDMap = make(map[uint32]AgentMessageMythicUITracking) 63 | var AgentIDToMythicIDLock sync.RWMutex 64 | 65 | func Initialize(configInstance instanceConfig) *DnsServer { 66 | server := &DnsServer{ 67 | server: &dns.Server{Addr: fmt.Sprintf("%s:%d", configInstance.BindIP, configInstance.Port), Net: "udp"}, 68 | connections: &sync.Map{}, 69 | connectionMutex: &sync.RWMutex{}, 70 | TTL: 0, 71 | debug: configInstance.Debug, 72 | domains: configInstance.Domains, 73 | } 74 | // add our own handlefunc per dns instance we make 75 | instanceHandler := dns.NewServeMux() 76 | instanceHandler.HandleFunc(".", func(writer dns.ResponseWriter, req *dns.Msg) { 77 | server.HandleDNSRequest(writer, req) 78 | }) 79 | server.server.Handler = instanceHandler 80 | return server 81 | } 82 | 83 | func (s *DnsServer) ListenAndServe() error { 84 | return s.server.ListenAndServe() 85 | } 86 | func (s *DnsServer) getParentDomain(reqDomain string) string { 87 | longestParent := "" 88 | for _, parentDomain := range s.domains { 89 | if dns.IsSubDomain(parentDomain+".", reqDomain) { 90 | //logging.LogInfo("found parent domain", "parent", parentDomain) 91 | if len(parentDomain) > len(longestParent) { 92 | longestParent = parentDomain 93 | } 94 | } 95 | } 96 | if longestParent == "" { 97 | logging.LogError(nil, "Got request with parent domain not tracked", "reqDomain", reqDomain) 98 | return "" 99 | } 100 | return longestParent + "." 101 | } 102 | func (s *DnsServer) HandleDNSRequest(writer dns.ResponseWriter, req *dns.Msg) { 103 | if req == nil { 104 | return 105 | } 106 | if len(req.Question) < 1 { 107 | logging.LogError(nil, "No questions in DNS request") 108 | return 109 | } 110 | //logging.LogInfo("got dns request", "question", req.Question, "name", req.Question[0].Name) 111 | domain := s.getParentDomain(req.Question[0].Name) 112 | if domain == "" { 113 | logging.LogError(nil, "Not a tracked domain", "domain", req.Question[0].Name) 114 | return 115 | } 116 | resp := s.handleMessage(domain, req) 117 | //logging.LogInfo("sending final message back", "response", resp) 118 | err := writer.WriteMsg(resp) 119 | if err != nil { 120 | logging.LogError(err, "Failed to write response", "domain", domain) 121 | } 122 | } 123 | func (s *DnsServer) handleMessage(domain string, req *dns.Msg) *dns.Msg { 124 | if len(req.Question[0].Name) == len(domain) { 125 | logging.LogError(nil, "no subdomain data given") 126 | return s.nameErrorResp(req, dns.RcodeNameError) 127 | } 128 | subdomain := req.Question[0].Name[:len(req.Question[0].Name)-len(domain)-1] 129 | //logging.LogInfo("processing req", "sub domain", subdomain, "domain", domain) 130 | msg, err := s.parseData(subdomain) 131 | if err != nil { 132 | logging.LogError(err, "failed to process message as a Mythic DNS message, returning generic name error", "message", subdomain) 133 | return s.nameErrorResp(req, dns.RcodeNameError) 134 | } 135 | // Msg Type -> Handler 136 | switch msg.Action { 137 | case dnsgrpc.Actions_AgentToServer: 138 | // agent is sending a Mythic message 139 | return s.AgentToServer(domain, msg, req) 140 | case dnsgrpc.Actions_ServerToAgent: 141 | // server is responding with a Mythic message response 142 | return s.ServerToAgent(domain, msg, req) 143 | case dnsgrpc.Actions_ReTransmit: 144 | // agent asking server to retransmit a message 145 | //return s.ReTransmit(domain, msg, req) 146 | } 147 | return nil 148 | } 149 | func (s *DnsServer) GetConnection(msg *dnsgrpc.DnsPacket) *DnsConnection { 150 | var dnsConnection *DnsConnection 151 | s.connectionMutex.Lock() 152 | loadSession, ok := s.connections.Load(msg.AgentSessionID) 153 | if !ok { 154 | // we haven't seen this AgentSessionID before, start tracking it 155 | dnsConnection = s.TrackNewConnection(msg) 156 | } else { 157 | dnsConnection = loadSession.(*DnsConnection) 158 | } 159 | s.connectionMutex.Unlock() 160 | return dnsConnection 161 | } 162 | func (s *DnsServer) AgentToServer(domain string, msg *dnsgrpc.DnsPacket, req *dns.Msg) *dns.Msg { 163 | //logging.LogInfo("got message from AgentToServer", "msg", msg) 164 | dnsConnection := s.GetConnection(msg) 165 | respAction := dnsConnection.AddIncomingMessage(msg) 166 | resp := new(dns.Msg) 167 | resp.Authoritative = true 168 | s.addResponseAction(resp, msg, req, domain, respAction) 169 | dnsConnection.UpdateTransferStatus(msg.AgentSessionID, msg.Action, msg.Begin, msg.Size) 170 | return resp 171 | } 172 | func (s *DnsServer) ServerToAgent(domain string, msg *dnsgrpc.DnsPacket, req *dns.Msg) *dns.Msg { 173 | //logging.LogInfo("got message from CheckForMessage", "msg", msg) 174 | dnsConnection := s.GetConnection(msg) 175 | resp := new(dns.Msg) 176 | 177 | dnsConnection.outgoingMutex.Lock() 178 | defer dnsConnection.outgoingMutex.Unlock() 179 | if _, ok := dnsConnection.outgoingBuffers[msg.MessageID]; !ok { 180 | logging.LogError(nil, "request for message that doesn't exist", "msg", msg) 181 | s.addResponseAction(resp, msg, req, domain, dnsgrpc.Actions_MessageLost) 182 | return resp 183 | } 184 | s.addResponseAction(resp, msg, req, domain, dnsgrpc.Actions_ServerToAgent) 185 | finishedMessage := false 186 | size := uint32(len(dnsConnection.outgoingBuffers[msg.MessageID])) 187 | end := msg.Begin 188 | if req.Question[0].Qtype == dns.TypeA { 189 | end += 128 // in TypeA, need 1B for order, 3B for data per Answer. 1 Answer for Action 190 | } else if req.Question[0].Qtype == dns.TypeAAAA { 191 | end += 630 // in TypeAAAA, need 1B for order, 15 for data per Answer. 1 Answer for Action 192 | } else if req.Question[0].Qtype == dns.TypeTXT { 193 | end += 1000 // arbitrary 1k instead of 128 Bytes per response 194 | } 195 | if end >= size { 196 | end = size 197 | finishedMessage = true 198 | } 199 | chunk := make([]byte, end-msg.Begin) 200 | copy(chunk, dnsConnection.outgoingBuffers[msg.MessageID][msg.Begin:end]) 201 | responsePacket := &dnsgrpc.DnsPacket{ 202 | Action: dnsgrpc.Actions_ServerToAgent, 203 | AgentSessionID: msg.AgentSessionID, 204 | MessageID: msg.MessageID, 205 | Size: size, 206 | Begin: msg.Begin, 207 | Data: chunk, 208 | } 209 | s.AddPacketToResponse(msg, responsePacket, resp, req, domain) 210 | dnsConnection.UpdateTransferStatus(responsePacket.AgentSessionID, responsePacket.Action, responsePacket.Begin, responsePacket.Size) 211 | if finishedMessage { 212 | if slices.Contains(dnsConnection.outgoingMsgIDsToClear, msg.MessageID) { 213 | logging.LogInfo("finished sending message again", "message id", msg.MessageID, "chunk", chunk) 214 | return resp 215 | } 216 | logging.LogInfo("finished sending message", "message id", msg.MessageID) 217 | if len(dnsConnection.outgoingMsgIDsToClear) > 0 { 218 | priorMessageID := dnsConnection.outgoingMsgIDsToClear[0] 219 | //logging.LogInfo("finished message, removing prior buffer", "message id", priorMessageID) 220 | delete(dnsConnection.outgoingBuffers, priorMessageID) 221 | msgIndex := slices.Index(dnsConnection.outgoingMsgIDs, priorMessageID) 222 | endIndex := msgIndex + 1 223 | if endIndex > len(dnsConnection.outgoingMsgIDs) { 224 | endIndex = len(dnsConnection.outgoingMsgIDs) 225 | } 226 | dnsConnection.outgoingMsgIDs = slices.Delete(dnsConnection.outgoingMsgIDs, msgIndex, endIndex) 227 | dnsConnection.outgoingMsgIDsToClear = dnsConnection.outgoingMsgIDsToClear[1:] 228 | } 229 | dnsConnection.outgoingMsgIDsToClear = append(dnsConnection.outgoingMsgIDsToClear, msg.MessageID) 230 | } 231 | //logging.LogInfo("added response to reply", "resp", resp) 232 | return resp 233 | } 234 | func getChunks(data []byte, chunkSize int) [][]byte { 235 | chunks := make([][]byte, 0) 236 | index := 0 237 | remaining := chunkSize 238 | endReached := false 239 | for i := 0; i < len(data); i += chunkSize { 240 | end := i + chunkSize 241 | if end > len(data) { 242 | endReached = true 243 | remaining = end - len(data) // how many extra bytes there are 244 | end = len(data) 245 | } 246 | chunk := make([]byte, chunkSize+1) 247 | copy(chunk[1:], data[i:end]) 248 | chunk[0] = byte(index + 1) 249 | if endReached { 250 | for j := len(chunk) - 1; j > chunkSize-remaining; j-- { 251 | chunk[j] = uint8(remaining) 252 | } 253 | } 254 | //logging.LogInfo("adding chunk", "chunk[0]", chunk[0]) 255 | chunks = append(chunks, chunk) 256 | if !endReached && i+chunkSize >= len(data) { 257 | chunkPadding := make([]byte, chunkSize+1) 258 | for j := 1; j <= chunkSize; j++ { 259 | chunkPadding[j] = byte(remaining) 260 | } 261 | chunkPadding[0] = byte(index + 2) 262 | chunks = append(chunks, chunkPadding) 263 | } 264 | index++ 265 | } 266 | return chunks 267 | } 268 | func getTxtChunks(data []byte) []string { 269 | chunks := []string{} 270 | for i := 0; i < len(data); i += 255 { 271 | end := i + 255 272 | if end > len(data) { 273 | end = len(data) 274 | } 275 | chunks = append(chunks, string(data[i:end])) 276 | } 277 | return chunks 278 | } 279 | func (s *DnsServer) AddPacketToResponse(msg *dnsgrpc.DnsPacket, responsePacket *dnsgrpc.DnsPacket, resp *dns.Msg, req *dns.Msg, domain string) { 280 | msgToSend, err := proto.Marshal(responsePacket) 281 | if err != nil { 282 | logging.LogError(err, "failed to marshal response packet") 283 | return 284 | } 285 | // add a message id 286 | for _, q := range req.Question { 287 | switch q.Qtype { 288 | case dns.TypeA: 289 | chunks := getChunks(msgToSend, net.IPv4len-1) 290 | for _, chunk := range chunks { 291 | resp.Answer = append(resp.Answer, 292 | &dns.A{ 293 | Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}, 294 | A: chunk, 295 | }, 296 | ) 297 | } 298 | case dns.TypeAAAA: 299 | chunks := getChunks(msgToSend, net.IPv6len-1) 300 | for _, chunk := range chunks { 301 | resp.Answer = append(resp.Answer, 302 | &dns.AAAA{ 303 | Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}, 304 | AAAA: chunk, 305 | }, 306 | ) 307 | } 308 | case dns.TypeTXT: 309 | chunks := getTxtChunks([]byte(base64.StdEncoding.EncodeToString(msgToSend))) 310 | resp.Answer = append(resp.Answer, 311 | &dns.TXT{ 312 | Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}, 313 | Txt: chunks, 314 | }, 315 | ) 316 | } 317 | } 318 | } 319 | func (s *DnsServer) addResponseAction(resp *dns.Msg, msg *dnsgrpc.DnsPacket, req *dns.Msg, domain string, action dnsgrpc.Actions) { 320 | resp = resp.SetReply(req) 321 | resp.Authoritative = true 322 | for _, q := range req.Question { 323 | switch q.Qtype { 324 | case dns.TypeA: 325 | actionRespBuf := make([]byte, net.IPv4len) 326 | actionRespBuf[net.IPv4len-1] = byte(action) 327 | resp.Answer = append(resp.Answer, 328 | &dns.A{ 329 | Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}, 330 | A: actionRespBuf, 331 | }, 332 | ) 333 | case dns.TypeAAAA: 334 | actionRespBuf := make([]byte, net.IPv6len) 335 | actionRespBuf[net.IPv6len-1] = byte(action) 336 | resp.Answer = append(resp.Answer, 337 | &dns.AAAA{ 338 | Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}, 339 | AAAA: actionRespBuf, 340 | }, 341 | ) 342 | case dns.TypeTXT: 343 | resp.Answer = append(resp.Answer, 344 | &dns.TXT{ 345 | Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}, 346 | Txt: []string{ 347 | fmt.Sprintf("%d", action), 348 | }, 349 | }) 350 | } 351 | } 352 | //logging.LogInfo("setting reply", "resp", resp) 353 | } 354 | func (s *DnsServer) nameErrorResp(req *dns.Msg, errCode int) *dns.Msg { 355 | resp := new(dns.Msg) 356 | resp.SetRcode(req, errCode) 357 | resp.Authoritative = true 358 | return resp 359 | } 360 | func (s *DnsServer) parseData(subdomain string) (*dnsgrpc.DnsPacket, error) { 361 | subdata := strings.Join(strings.Split(subdomain, "."), "") 362 | //logging.LogInfo("parseData", "raw data", subdata) 363 | data, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(strings.ToUpper(subdata)) 364 | if err != nil { 365 | logging.LogError(err, "failed to decode subdata") 366 | return nil, err 367 | } 368 | msg := &dnsgrpc.DnsPacket{} 369 | err = proto.Unmarshal(data, msg) 370 | if err != nil { 371 | logging.LogError(err, "failed to unmarshal data", "data", data) 372 | return nil, err 373 | } 374 | //logging.LogInfo("parseData", "msg", msg) 375 | return msg, nil 376 | } 377 | func (s *DnsServer) TrackNewConnection(msg *dnsgrpc.DnsPacket) *DnsConnection { 378 | dnsConn := &DnsConnection{ 379 | outgoingMsgIDs: []uint32{}, // message IDs that need to be picked up 380 | outgoingBuffers: map[uint32][]byte{}, // message IDs to data to send to agent 381 | outgoingMutex: &sync.RWMutex{}, 382 | incomingMessages: map[uint32]*DnsMessageStream{}, 383 | incomingMutex: &sync.Mutex{}, 384 | } 385 | s.connections.Store(msg.AgentSessionID, dnsConn) 386 | return dnsConn 387 | } 388 | 389 | var tr = &http.Transport{ 390 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 391 | MaxIdleConns: 1, 392 | MaxConnsPerHost: 1, 393 | DisableKeepAlives: true, 394 | } 395 | var client = &http.Client{ 396 | Transport: tr, 397 | } 398 | 399 | func (con *DnsConnection) AddIncomingMessage(msg *dnsgrpc.DnsPacket) dnsgrpc.Actions { 400 | con.incomingMutex.Lock() 401 | defer con.incomingMutex.Unlock() 402 | if con.incomingMessages[msg.MessageID] == nil { 403 | con.incomingMessages[msg.MessageID] = &DnsMessageStream{ 404 | TotalReceived: 0, 405 | Messages: make(map[uint32]*dnsgrpc.DnsPacket), 406 | Size: msg.Size, 407 | StartBytes: []uint32{}, 408 | } 409 | } 410 | if slices.Contains(con.incomingMessages[msg.MessageID].StartBytes, msg.Begin) { 411 | return msg.Action // this is a duplicate, we've seen this one before 412 | } 413 | con.incomingMessages[msg.MessageID].StartBytes = append(con.incomingMessages[msg.MessageID].StartBytes, msg.Begin) 414 | con.incomingMessages[msg.MessageID].TotalReceived += uint32(len(msg.Data)) 415 | con.incomingMessages[msg.MessageID].Messages[msg.Begin] = msg 416 | if con.incomingMessages[msg.MessageID].TotalReceived == msg.Size { 417 | totalBuffer := make([]byte, msg.Size) 418 | // sort all the start bytes to be in order 419 | sort.Slice(con.incomingMessages[msg.MessageID].StartBytes, func(i, j int) bool { return i < j }) 420 | // iterate over the start bytes and add the corresponding string data together 421 | for i := 0; i < len(con.incomingMessages[msg.MessageID].StartBytes); i++ { 422 | copy(totalBuffer[con.incomingMessages[msg.MessageID].Messages[con.incomingMessages[msg.MessageID].StartBytes[i]].Begin:], con.incomingMessages[msg.MessageID].Messages[con.incomingMessages[msg.MessageID].StartBytes[i]].Data) 423 | //totalBuffer += con.incomingMessages[msg.MessageID].Messages[con.incomingMessages[msg.MessageID].StartBytes[i]].Data 424 | } 425 | // remove the tracking of this msg.MessageID because we got the whole message 426 | delete(con.incomingMessages, msg.MessageID) 427 | go con.UpdateMythicIDTracking(msg.AgentSessionID, totalBuffer) 428 | finalBuffer := base64.StdEncoding.EncodeToString(totalBuffer) 429 | // send this totalBuffer off to Mythic for processing 430 | logging.LogInfo("sending full message to mythic", "message id", msg.MessageID, "agent session id", msg.AgentSessionID) 431 | requestURL := fmt.Sprintf("http://%s:%d/agent_message", mythicConfig.MythicConfig.MythicServerHost, mythicConfig.MythicConfig.MythicServerPort) 432 | req, err := http.NewRequest("POST", requestURL, bytes.NewBuffer([]byte(finalBuffer))) 433 | if err != nil { 434 | logging.LogError(err, "failed to create request") 435 | } 436 | req.Header.Set("mythic", "dns") 437 | resp, err := client.Do(req) 438 | if err != nil { 439 | logging.LogError(err, "failed to send request") 440 | if resp != nil && resp.Body != nil { 441 | resp.Body.Close() 442 | } 443 | return dnsgrpc.Actions_ReTransmit 444 | } 445 | body, err := io.ReadAll(resp.Body) 446 | resp.Body.Close() 447 | if resp.StatusCode != http.StatusOK { 448 | logging.LogError(nil, "bad response from server", "statuscode", resp.StatusCode) 449 | return dnsgrpc.Actions_ReTransmit 450 | } 451 | // add the response to the outgoing buffers for the agent to pick up next 452 | //logging.LogInfo("received response from server", "body", body) 453 | finalResponse, err := base64.StdEncoding.DecodeString(string(body)) 454 | if err != nil { 455 | logging.LogError(err, "failed to decode response", "response", string(body)) 456 | return dnsgrpc.Actions_ReTransmit 457 | } 458 | con.outgoingBuffers[msg.MessageID] = finalResponse 459 | con.outgoingMsgIDs = append(con.outgoingMsgIDs, msg.MessageID) 460 | return dnsgrpc.Actions_ServerToAgent 461 | } 462 | return msg.Action 463 | } 464 | func (con *DnsConnection) UpdateMythicIDTracking(AgentSessionID uint32, MythicMessage []byte) { 465 | callbackUUID := "" 466 | if len(MythicMessage) > 40 { 467 | callbackUUIDParsed, err := uuid.Parse(string(MythicMessage[:36])) 468 | if err != nil { 469 | logging.LogError(err, "failed to parse callback UUID") 470 | return 471 | } 472 | callbackUUID = callbackUUIDParsed.String() 473 | } else { 474 | return 475 | } 476 | AgentIDToMythicIDLock.RLock() 477 | agentData, ok := AgentIDToMythicIDMap[AgentSessionID] 478 | AgentIDToMythicIDLock.RUnlock() 479 | if ok { 480 | // we've seen this AgentSessionID before, but see if it's different and update if necessary 481 | if agentData.AgentCallbackID != callbackUUID && callbackUUID != "" { 482 | agentData.AgentCallbackID = callbackUUID 483 | AgentIDToMythicIDLock.Lock() 484 | AgentIDToMythicIDMap[AgentSessionID] = agentData 485 | AgentIDToMythicIDLock.Unlock() 486 | return 487 | } 488 | return 489 | } 490 | 491 | agentData = AgentMessageMythicUITracking{ 492 | AgentCallbackID: callbackUUID, 493 | ExtraInfo: "", 494 | LastUpdateTime: time.Now(), 495 | PerformedUpdate: false, 496 | } 497 | AgentIDToMythicIDLock.Lock() 498 | AgentIDToMythicIDMap[AgentSessionID] = agentData 499 | AgentIDToMythicIDLock.Unlock() 500 | 501 | } 502 | func (con *DnsConnection) UpdateTransferStatus(AgentSessionID uint32, Action dnsgrpc.Actions, currentChunk uint32, size uint32) { 503 | AgentIDToMythicIDLock.RLock() 504 | agentStatus, ok := AgentIDToMythicIDMap[AgentSessionID] 505 | AgentIDToMythicIDLock.RUnlock() 506 | if !ok { 507 | return 508 | } 509 | if agentStatus.LastUpdateTime.Add(time.Duration(5) * time.Second).After(time.Now()) { 510 | return 511 | } 512 | newMessage := "" 513 | updateLastCheckinTime := true 514 | updateLastCheckinTimeC2 := "dns" 515 | if currentChunk == 0 { 516 | if agentStatus.ExtraInfo != "" || !agentStatus.PerformedUpdate { 517 | resp, err := mythicrpc.SendMythicRPCCallbackUpdate(mythicrpc.MythicRPCCallbackUpdateMessage{ 518 | AgentCallbackID: &agentStatus.AgentCallbackID, 519 | ExtraInfo: &newMessage, 520 | UpdateLastCheckinTime: &updateLastCheckinTime, 521 | UpdateLastCheckinTimeViaC2Profile: &updateLastCheckinTimeC2, 522 | }) 523 | if err != nil { 524 | logging.LogError(err, "failed to send callback update") 525 | } else if !resp.Success { 526 | logging.LogError(nil, "failed to send callback update", "response", resp.Error, "agent session id", AgentSessionID, "agent callback id", agentStatus.AgentCallbackID) 527 | } 528 | } 529 | AgentIDToMythicIDLock.Lock() 530 | agentStatus.ExtraInfo = newMessage 531 | agentStatus.LastUpdateTime = time.Now() 532 | agentStatus.PerformedUpdate = true 533 | AgentIDToMythicIDMap[AgentSessionID] = agentStatus 534 | AgentIDToMythicIDLock.Unlock() 535 | return 536 | } 537 | if Action == dnsgrpc.Actions_ServerToAgent { 538 | newMessage = fmt.Sprintf("Mythic->Agent: Sending") 539 | } else if Action == dnsgrpc.Actions_AgentToServer { 540 | newMessage = fmt.Sprintf("Agent->Mythic: Sending") 541 | } 542 | percentage := (float32(currentChunk) / float32(size)) * 100 543 | newMessage += fmt.Sprintf(" %d/%d (%.2f%%) Bytes...", currentChunk, size, percentage) 544 | resp, err := mythicrpc.SendMythicRPCCallbackUpdate(mythicrpc.MythicRPCCallbackUpdateMessage{ 545 | AgentCallbackID: &agentStatus.AgentCallbackID, 546 | ExtraInfo: &newMessage, 547 | UpdateLastCheckinTime: &updateLastCheckinTime, 548 | UpdateLastCheckinTimeViaC2Profile: &updateLastCheckinTimeC2, 549 | }) 550 | if err != nil { 551 | logging.LogError(err, "failed to send callback update") 552 | } else if !resp.Success { 553 | logging.LogError(nil, "failed to send callback update", "response", resp.Error, "agent session id", AgentSessionID, "agent callback id", agentStatus.AgentCallbackID) 554 | } 555 | AgentIDToMythicIDLock.Lock() 556 | agentStatus.ExtraInfo = newMessage 557 | agentStatus.LastUpdateTime = time.Now() 558 | agentStatus.PerformedUpdate = true 559 | AgentIDToMythicIDMap[AgentSessionID] = agentStatus 560 | AgentIDToMythicIDLock.Unlock() 561 | } 562 | --------------------------------------------------------------------------------