Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mattermost webhook #438

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions profiles/webhook_example.yaotl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ WebHook {
# optional
User = "Havoc" # User name of the webhook bot
}

#
# Mattermost {
# # required
# Url = "..." # replace this with your webhook
#
# # optional
# AvatarUrl = "https://raw.githubusercontent.com/HavocFramework/Havoc/main/assets/Havoc.png" # url to an image to use as an avatar
#
# # optional
# User = "Havoc" # User name of the webhook bot
# }
#

}

Operators {
Expand Down
17 changes: 17 additions & 0 deletions teamserver/cmd/server/teamserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,23 @@ func (t *Teamserver) Start() {
t.WebHooks.SetDiscord(AvatarUrl, UserName, t.Profile.Config.WebHook.Discord.WebHook)
}
}

if t.Profile.Config.WebHook.Mattermost != nil {
var (
AvatarUrl string
UserName string
)
if len(t.Profile.Config.WebHook.Mattermost.AvatarUrl) > 0 {
AvatarUrl = t.Profile.Config.WebHook.Mattermost.AvatarUrl
}
if len(t.Profile.Config.WebHook.Mattermost.UserName) > 0 {
UserName = t.Profile.Config.WebHook.Mattermost.UserName
}

if len(t.Profile.Config.WebHook.Mattermost.WebHook) > 0 {
t.WebHooks.SetMattermost(AvatarUrl, UserName, t.Profile.Config.WebHook.Mattermost.WebHook)
}
}
}

// start teamserver service
Expand Down
33 changes: 20 additions & 13 deletions teamserver/pkg/profile/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,15 @@ type WebHookDiscordConfig struct {
UserName string `yaotl:"User,optional"`
}

type WebHookMattermostConfig struct {
WebHook string `yaotl:"Url"`
AvatarUrl string `yaotl:"AvatarUrl,optional"`
UserName string `yaotl:"User,optional"`
}

type WebHookConfig struct {
Discord *WebHookDiscordConfig `yaotl:"Discord,block"`
Discord *WebHookDiscordConfig `yaotl:"Discord,block"`
Mattermost *WebHookMattermostConfig `yaotl:"Mattermost,block"`
}

type BuildConfig struct {
Expand Down Expand Up @@ -70,7 +77,7 @@ type ListenerHTTP struct {
/* Port used by the agent */
PortConn int `yaotl:"PortConn,optional"`

Methode string `yaotl:"Method,optional"`
Methode string `yaotl:"Method,optional"`

/* optional fields */
UserAgent string `yaotl:"UserAgent,optional"`
Expand Down Expand Up @@ -137,18 +144,18 @@ type ProcessInjectionBlock struct {
}

type Demon struct {
Sleep int `yaotl:"Sleep,optional"`
Jitter int `yaotl:"Jitter,optional"`
IndirectSyscall bool `yaotl:"IndirectSyscall,optional"`
StackDuplication bool `yaotl:"StackDuplication,optional"`
SleepTechnique string `yaotl:"SleepTechnique,optional"`
ProxyLoading string `yaotl:"ProxyLoading,optional"`
AmsiEtwPatching string `yaotl:"AmsiEtwPatching,optional"`
ProcessInjection *ProcessInjectionBlock `yaotl:"Injection,block"`
Sleep int `yaotl:"Sleep,optional"`
Jitter int `yaotl:"Jitter,optional"`
IndirectSyscall bool `yaotl:"IndirectSyscall,optional"`
StackDuplication bool `yaotl:"StackDuplication,optional"`
SleepTechnique string `yaotl:"SleepTechnique,optional"`
ProxyLoading string `yaotl:"ProxyLoading,optional"`
AmsiEtwPatching string `yaotl:"AmsiEtwPatching,optional"`
ProcessInjection *ProcessInjectionBlock `yaotl:"Injection,block"`

DotNetNamePipe string `yaotl:"DotNetNamePipe,optional"`
DotNetNamePipe string `yaotl:"DotNetNamePipe,optional"`

Binary *Binary `yaotl:"Binary,block"`
Binary *Binary `yaotl:"Binary,block"`

TrustXForwardedFor bool `yaotl:"TrustXForwardedFor,optional"`
TrustXForwardedFor bool `yaotl:"TrustXForwardedFor,optional"`
}
7 changes: 7 additions & 0 deletions teamserver/pkg/webhook/mattermost.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package webhook

type Mattermost_Message struct {
AvatarUrl *string `json:"icon_url,omitempty"`
Content *string `json:"text,omitempty"`
Username *string `json:"username,omitempty"`
}
83 changes: 83 additions & 0 deletions teamserver/pkg/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"net/http"
"strconv"
"strings"
)

type WebHook struct {
Expand All @@ -15,6 +16,12 @@ type WebHook struct {
User string
Url string
}

Mattermost struct {
Avatar string
User string
Url string
}
}

func StringPtr(str string) *string {
Expand Down Expand Up @@ -127,6 +134,76 @@ func (w *WebHook) NewAgent(agent map[string]any) error {

return nil
}
if len(w.Mattermost.Url) > 0 {
var (
payload = new(bytes.Buffer)
message Mattermost_Message
tableRows []string
AgentInfo map[string]any
)

AgentInfo, ok := agent["Info"].(map[string]interface{})
if !ok {
return fmt.Errorf("unable to assert AgentInfo as map[string]interface{}")
}

tableRows = append(tableRows, "| Field | Value |")
tableRows = append(tableRows, "| ----- | ----- |")

addRow := func(name, value string) {
tableRows = append(tableRows, fmt.Sprintf("| %s | %s |", name, value))
}

addRow("Agent ID", agent["NameID"].(string))
addRow("Username", AgentInfo["Username"].(string))
addRow("Hostname", AgentInfo["Hostname"].(string))
addRow("Domain", AgentInfo["DomainName"].(string))
addRow("Internal IP", AgentInfo["InternalIP"].(string))
addRow("Process Path", AgentInfo["ProcessPath"].(string))
addRow("Process Name", AgentInfo["ProcessName"].(string))
addRow("Process ID", strconv.Itoa(AgentInfo["ProcessPID"].(int)))
addRow("Process Arch", AgentInfo["ProcessArch"].(string))
addRow("OS Version", AgentInfo["OSVersion"].(string))
addRow("OS Arch", AgentInfo["OSArch"].(string))
addRow("First Callback", AgentInfo["FirstCallIn"].(string))
addRow("Elevated", AgentInfo["Elevated"].(string))
addRow("SleepDelay", strconv.Itoa(AgentInfo["SleepDelay"].(int)))
addRow("SleepJitter", strconv.Itoa(AgentInfo["SleepJitter"].(int)))

content := fmt.Sprintf("**New Havoc Callback!**\n\n%s", strings.Join(tableRows, "\n"))
message.Content = &content

if len(w.Mattermost.Avatar) > 0 {
message.AvatarUrl = &w.Mattermost.Avatar
}

if len(w.Mattermost.User) > 0 {
message.Username = &w.Mattermost.User
}

err := json.NewEncoder(payload).Encode(message)
if err != nil {
return err
}

resp, err := http.Post(w.Mattermost.Url, "application/json", payload)
if err != nil {
return err
}

if resp.StatusCode != 200 && resp.StatusCode != 204 {
defer resp.Body.Close()

responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return err
}

return fmt.Errorf(string(responseBody))
}

return nil
}

return nil
}
Expand All @@ -136,3 +213,9 @@ func (w *WebHook) SetDiscord(AvatarUrl, User, Url string) {
w.Discord.User = User
w.Discord.Url = Url
}

func (w *WebHook) SetMattermost(AvatarUrl, User, Url string) {
w.Mattermost.Avatar = AvatarUrl
w.Mattermost.User = User
w.Mattermost.Url = Url
}