Skip to content

Profile Reference

laptop tester edited this page Sep 6, 2026 · 2 revisions

Profile Reference

The teamserver is configured with an HCL file (a vendored HCL fork called "yaotl", parsed via hclsimple.DecodeFile). Struct definitions: teamserver/pkg/profile/config.go. Examples: profiles/havoc.yaotl, profiles/http_smb.yaotl, data/havoc.yaotl.

Full annotated schema:

Teamserver {                       # → profile.ServerProfile
    Host = "0.0.0.0"               # operator-facing listen address (wss)
    Port = 40056

    Build {                        # → profile.BuildConfig (toolchain for payload builds)
        Compiler64 = "data/x86_64-w64-mingw32-cross-gcc"
        Compiler86 = "data/i686-w64-mingw32-cross-gcc"
        Nasm       = "/usr/bin/nasm"
    }                              # path validation: Compiler64/Compiler86/Nasm are
                                   # interpolated into the compile command and must only
                                   # contain alnum, space, . - _ / \ : ; anything else
                                   # fails the build (shell-injection hardening, #189)
}

Operators {                        # → profile.OperatorsBlock
    user "Neo" {                   # block label = username
        Password = "password1234"  # compared as hex SHA3-256
    }
    user "trinity" {
        Password = "password1234"
    }
}

Demon {                            # → profile.Demon (see notes below; most fields are DEAD)
    Sleep              = 2         # DEAD: parsed but never consumed; per-payload from client only
    Jitter             = 15        # DEAD: parsed but never consumed
    IndirectSyscall    = true      # DEAD: parsed but never consumed
    StackDuplication   = false     # DEAD: parsed but never consumed
    SleepTechnique     = "WaitForSingleObjectEx"   # DEAD: parsed but never consumed
    ProxyLoading       = "None"    # DEAD: parsed but never consumed
    AmsiEtwPatching    = "None"    # DEAD: parsed but never consumed
    DotNetNamePipe     = "\\\\.\\pipe\\mojo.5688.8052.18389493978708887773"
                                   # USED: pipe name for the .NET (execute-assembly) mate pipe
    TrustXForwardedFor = false     # USED: take agent external IP from X-Forwarded-For
                                   # (also enables BehindRedir lenient multi-hop parsing)

    Injection {                    # DEAD: parsed but never consumed
        Spawn64 = "C:\\Windows\\System32\\notepad.exe"
        Spawn32 = "C:\\Windows\\SysWOW64\\notepad.exe"
    }

    Binary {                       # USED: post-build binary patching (applied to every Demon build)
        Header {
            MagicMz-x64    = ""    # hyphenated names are the accepted syntax
            MagicMz-x86    = ""
            CompileTime    = ""
            ImageSize-x64  = ""
            ImageSize-x86  = ""
        }
        ReplaceStrings-x64 = []    # string replacement pairs applied to the x64 payload
        ReplaceStrings-x86 = []    # string replacement pairs applied to the x86 payload
    }
}

Listeners {                        # → profile.Listeners
    Http {                         # → profile.ListenerHTTP (repeatable)
        Name         = "Demon Listener"
        KillDate     = "2024-02-10 14:39:01"      # optional; layout "2006-01-02 15:04:05"
        WorkingHours = ""                          # optional
        Hosts        = ["0.0.0.0", "192.168.2.100"] # callback hosts the agent rotates through
        HostBind     = "0.0.0.0"                   # bind address
        HostRotation = "round-robin"               # or "random"
        PortBind     = 443
        PortConn     = 443                         # optional; port the agent connects to
        Method       = "POST"                      # optional; only POST builds; GET is rejected at build time
        UserAgent    = "Mozilla/5.0 (...)"         # optional
        Headers      = ["Content-type: */*"]       # optional; requests must match
        Uris         = ["/index.php"]              # optional
        HostHeader   = "www.example.com"           # optional; requests whose Host / X-Forwarded-Host
                                                   # do not match receive the fake 404
        Secure       = true                        # optional; TLS listener

        Cert {                         # optional; else self-signed cert is generated
            Cert = "certs/server.crt"
            Key  = "certs/server.key"
        }

        Response {                     # optional; headers added to every response
            Headers = ["Server: nginx/1.18.0 (Ubuntu)"]
        }

        Proxy {                        # optional; agent-side proxy config
            Type     = "http"          # REQUIRED; "http" or "socks5"
            Host     = "..."
            Port     = "..."
            Username = ""              # optional
            Password = ""              # optional
        }
    }

    Smb {                              # → profile.ListenerSMB (named-pipe pivot via an agent)
        Name         = "Smb listener"
        PipeName     = "mojo.5688.8052.1838949397870888777.806"
        KillDate     = ""              # optional
        WorkingHours = ""              # optional
    }

    External {                         # → profile.ListenerExternal (ExternalC2 endpoint)
        Name     = "External Listener"
        Endpoint = "endpoint"          # exposed as POST /<Endpoint> on the teamserver
    }
}

Service {                            # → profile.ServiceConfig (REQUIRED for third-party agents)
    Endpoint = "service-endpoint"    # wss://<Teamserver.Host>:<Port>/service-endpoint
    Password = "service-password"
}

WebHook {                            # → profile.WebHookConfig
    Discord {
        Url       = "https://discord.com/api/webhooks/..."
        AvatarUrl = ""               # optional
        User      = ""               # optional
    }
}

Notes:

  • All blocks except Service and WebHook are effectively required for a normal setup; Demon fields are individually optional (defaults apply).
  • KillDate is parsed with layout "2006-01-02 15:04:05" and converted to a Windows FILETIME-style epoch for the implant.
  • The Demon block is mostly dead config. Only Binary (post-build patching), DotNetNamePipe and TrustXForwardedFor are actually consumed by the teamserver. Sleep, Jitter, IndirectSyscall, StackDuplication, SleepTechnique, ProxyLoading, AmsiEtwPatching and Injection are parsed but never read; build options like sleep obfuscation, AMSI/ETW patching and injection come exclusively from the client's payload-build Config (see Payload-Generation).
  • Multiple Http / Smb / External blocks are allowed; each becomes one listener.
  • Binary.Header / ReplaceStrings field names are hyphenated (MagicMz-x64, ImageSize-x86, ReplaceStrings-x64, …); underscored variants do not parse.
  • Entries in Hosts and HostBind may be network interface names (e.g. tun0) instead of IPs; the teamserver resolves them to the interface's first IPv4 address (common.GetInterfaceIpv4Addr, teamserver/pkg/common/util.go).
  • Entries in Hosts may also be host:port pairs; a per-host port overrides PortConn for that callback host.
  • HTTP listener requests are validated against Headers, Uris, UserAgent and HostHeader (if set: Host / X-Forwarded-Host must match); mismatches receive a fake nginx 404 (with an X-Havoc: true header; see teamserver/pkg/handlers/404.html).
  • With TrustXForwardedFor = true, the HTTP handler enables lenient BehindRedir parsing: multi-hop X-Forwarded-For chains from redirector relays are handled (teamserver/pkg/handlers/http.go).

Clone this wiki locally