mirror of
https://github.com/Adaptix-Framework/AdaptixC2
synced 2026-06-08 10:20:39 +00:00
317 lines
9.5 KiB
Go
317 lines
9.5 KiB
Go
package server
|
|
|
|
import (
|
|
"AdaptixServer/core/profile"
|
|
"AdaptixServer/core/utils/tformat"
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
adaptix "github.com/Adaptix-Framework/axc2"
|
|
)
|
|
|
|
func (ts *Teamserver) TsNotifyClient(connected bool, username string) {
|
|
var packet SpNotification
|
|
|
|
if connected {
|
|
message := fmt.Sprintf("Operator '%v' connected to teamserver", username)
|
|
packet = CreateSpNotification(NOTIFY_CLIENT_CONNECT, message)
|
|
} else {
|
|
message := fmt.Sprintf("Operator '%v' disconnected from teamserver", username)
|
|
packet = CreateSpNotification(NOTIFY_CLIENT_DISCONNECT, message)
|
|
}
|
|
|
|
ts.TsSyncAllClientsWithCategory(packet, SyncCategoryNotifications)
|
|
ts.notifications.Put(packet)
|
|
}
|
|
|
|
func (ts *Teamserver) TsNotifyListenerStart(restart bool, listenerName string, listenerType string) {
|
|
var message string
|
|
if restart {
|
|
message = fmt.Sprintf("Listener '%v' reconfigured", listenerName)
|
|
} else {
|
|
message = fmt.Sprintf("Listener '%v' (%v) started", listenerName, listenerType)
|
|
}
|
|
|
|
packet := CreateSpNotification(NOTIFY_LISTENER_START, message)
|
|
ts.TsSyncAllClientsWithCategory(packet, SyncCategoryNotifications)
|
|
ts.notifications.Put(packet)
|
|
}
|
|
|
|
func (ts *Teamserver) TsNotifyListenerStop(listenerName string, listenerType string) {
|
|
message := fmt.Sprintf("Listener '%v' stopped", listenerName)
|
|
packet := CreateSpNotification(NOTIFY_LISTENER_STOP, message)
|
|
ts.TsSyncAllClientsWithCategory(packet, SyncCategoryNotifications)
|
|
ts.notifications.Put(packet)
|
|
}
|
|
|
|
func (ts *Teamserver) TsNotifyAgent(restore bool, agentData adaptix.AgentData) {
|
|
message := "New "
|
|
postMsg := agentData.Computer
|
|
|
|
if len(agentData.Domain) != 0 && strings.ToLower(agentData.Computer) != strings.ToLower(agentData.Domain) {
|
|
postMsg += "." + agentData.Domain + "'"
|
|
} else {
|
|
postMsg += "'"
|
|
}
|
|
if len(agentData.InternalIP) != 0 {
|
|
postMsg = postMsg + " (" + agentData.InternalIP + ")"
|
|
}
|
|
if restore {
|
|
message = "Restore "
|
|
createdAt := time.Unix(agentData.CreateTime, 0).Format("15:04:05 02.01.2006")
|
|
postMsg = postMsg + fmt.Sprintf(" [created '%v']", createdAt)
|
|
}
|
|
|
|
message += fmt.Sprintf("'%v' (%v) executed on '%v @ %v", agentData.Name, agentData.Id, agentData.Username, postMsg)
|
|
|
|
packet := CreateSpNotification(NOTIFY_AGENT_NEW, message)
|
|
ts.TsSyncAllClientsWithCategory(packet, SyncCategoryNotifications)
|
|
ts.notifications.Put(packet)
|
|
|
|
if !restore {
|
|
go ts.TsNotifyCallbackAgent(agentData)
|
|
}
|
|
}
|
|
|
|
func (ts *Teamserver) TsNotifyTunnelAdd(tunnel *Tunnel) {
|
|
if tunnel.Data.Client == "" {
|
|
message := ""
|
|
switch tunnel.Type {
|
|
case adaptix.TUNNEL_TYPE_SOCKS4:
|
|
message = fmt.Sprintf("SOCKS4 server started on '%s:%s'", tunnel.Data.Interface, tunnel.Data.Port)
|
|
case adaptix.TUNNEL_TYPE_SOCKS5:
|
|
message = fmt.Sprintf("SOCKS5 server started on '%s:%s'", tunnel.Data.Interface, tunnel.Data.Port)
|
|
case adaptix.TUNNEL_TYPE_SOCKS5_AUTH:
|
|
message = fmt.Sprintf("SOCKS5 (with Auth) server started on '%s:%s'", tunnel.Data.Interface, tunnel.Data.Port)
|
|
case adaptix.TUNNEL_TYPE_LOCAL_PORT:
|
|
message = fmt.Sprintf("Local port forward started on '%s:%s'", tunnel.Data.Interface, tunnel.Data.Port)
|
|
case adaptix.TUNNEL_TYPE_REVERSE:
|
|
message = fmt.Sprintf("Reverse port forward '%s' to '%s:%s'", tunnel.Data.Port, tunnel.Data.Fhost, tunnel.Data.Fport)
|
|
default:
|
|
return
|
|
}
|
|
|
|
packet := CreateSpNotification(NOTIFY_TUNNEL_START, message)
|
|
ts.TsSyncAllClientsWithCategory(packet, SyncCategoryNotifications)
|
|
ts.notifications.Put(packet)
|
|
}
|
|
}
|
|
|
|
func (ts *Teamserver) TsNotifyTunnelRemove(tunnel *Tunnel) {
|
|
if tunnel.Data.Client == "" {
|
|
message := ""
|
|
switch tunnel.Type {
|
|
case adaptix.TUNNEL_TYPE_SOCKS4:
|
|
message = fmt.Sprintf("SOCKS4 server ':%s' stopped", tunnel.Data.Port)
|
|
case adaptix.TUNNEL_TYPE_SOCKS5:
|
|
message = fmt.Sprintf("SOCKS5 server ':%s' stopped", tunnel.Data.Port)
|
|
case adaptix.TUNNEL_TYPE_SOCKS5_AUTH:
|
|
message = fmt.Sprintf("SOCKS5 (with Auth) server ':%s' stopped", tunnel.Data.Port)
|
|
case adaptix.TUNNEL_TYPE_LOCAL_PORT:
|
|
message = fmt.Sprintf("Local port forward on ':%s' stopped", tunnel.Data.Port)
|
|
case adaptix.TUNNEL_TYPE_REVERSE:
|
|
message = fmt.Sprintf("Remote port forward to '%s:%s' stopped", tunnel.Data.Fhost, tunnel.Data.Fport)
|
|
default:
|
|
return
|
|
}
|
|
|
|
packet := CreateSpNotification(NOTIFY_TUNNEL_STOP, message)
|
|
ts.TsSyncAllClientsWithCategory(packet, SyncCategoryNotifications)
|
|
ts.notifications.Put(packet)
|
|
}
|
|
}
|
|
|
|
/// CALLBACKS
|
|
|
|
func (ts *Teamserver) hasAnyCallback() bool {
|
|
if ts.Profile.Callbacks == nil {
|
|
return false
|
|
}
|
|
if ts.Profile.Callbacks.Telegram.Token != "" && len(ts.Profile.Callbacks.Telegram.ChatsId) > 0 {
|
|
return true
|
|
}
|
|
if ts.Profile.Callbacks.Slack.WebhookURL != "" {
|
|
return true
|
|
}
|
|
for _, webhook := range ts.Profile.Callbacks.Webhooks {
|
|
if webhook.URL != "" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (ts *Teamserver) sendToAllChannels(msg string) {
|
|
if ts.Profile.Callbacks.Telegram.Token != "" {
|
|
for _, chatId := range ts.Profile.Callbacks.Telegram.ChatsId {
|
|
sendTelegram(msg, ts.Profile.Callbacks.Telegram.Token, chatId)
|
|
}
|
|
}
|
|
|
|
if ts.Profile.Callbacks.Slack.WebhookURL != "" {
|
|
sendSlack(msg, ts.Profile.Callbacks.Slack.WebhookURL)
|
|
}
|
|
|
|
for _, webhook := range ts.Profile.Callbacks.Webhooks {
|
|
if webhook.URL != "" {
|
|
sendWebhook(msg, webhook)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ts *Teamserver) TsNotifyCallbackAgent(agentData adaptix.AgentData) {
|
|
if ts.Profile.Callbacks == nil || ts.Profile.Callbacks.NewAgentMessage == "" || !ts.hasAnyCallback() {
|
|
return
|
|
}
|
|
|
|
msg := ts.Profile.Callbacks.NewAgentMessage
|
|
msg = strings.ReplaceAll(msg, "%type%", agentData.Name)
|
|
msg = strings.ReplaceAll(msg, "%id%", agentData.Id)
|
|
msg = strings.ReplaceAll(msg, "%user%", agentData.Username)
|
|
msg = strings.ReplaceAll(msg, "%computer%", agentData.Computer)
|
|
msg = strings.ReplaceAll(msg, "%externalip%", agentData.ExternalIP)
|
|
msg = strings.ReplaceAll(msg, "%internalip%", agentData.InternalIP)
|
|
msg = strings.ReplaceAll(msg, "%domain%", agentData.Domain)
|
|
msg = strings.ReplaceAll(msg, "%elevated%", fmt.Sprintf("%v", agentData.Elevated))
|
|
msg = strings.ReplaceAll(msg, "%pid%", fmt.Sprintf("%v", agentData.Pid))
|
|
|
|
ts.sendToAllChannels(msg)
|
|
}
|
|
|
|
func (ts *Teamserver) TsNotifyCallbackCreds(creds []adaptix.CredsData) {
|
|
if ts.Profile.Callbacks == nil || ts.Profile.Callbacks.NewCredMessage == "" || !ts.hasAnyCallback() {
|
|
return
|
|
}
|
|
|
|
if len(creds) > 4 {
|
|
msg := fmt.Sprintf("Added %d new credentials", len(creds))
|
|
ts.sendToAllChannels(msg)
|
|
return
|
|
}
|
|
|
|
for _, credData := range creds {
|
|
secret := "*****"
|
|
if len(credData.Password) >= 3 {
|
|
secret = credData.Password[:3] + "*****"
|
|
} else if len(credData.Password) > 0 {
|
|
secret = credData.Password[:1] + "*****"
|
|
}
|
|
|
|
msg := ts.Profile.Callbacks.NewCredMessage
|
|
msg = strings.ReplaceAll(msg, "%username%", credData.Username)
|
|
msg = strings.ReplaceAll(msg, "%password%", secret)
|
|
msg = strings.ReplaceAll(msg, "%domain%", credData.Realm)
|
|
msg = strings.ReplaceAll(msg, "%type%", credData.Type)
|
|
msg = strings.ReplaceAll(msg, "%storage%", credData.Storage)
|
|
msg = strings.ReplaceAll(msg, "%host%", credData.Host)
|
|
|
|
ts.sendToAllChannels(msg)
|
|
}
|
|
}
|
|
|
|
func (ts *Teamserver) TsNotifyCallbackDownloads(downloadData adaptix.DownloadData) {
|
|
if ts.Profile.Callbacks == nil || ts.Profile.Callbacks.NewDownloadMessage == "" || !ts.hasAnyCallback() {
|
|
return
|
|
}
|
|
|
|
msg := ts.Profile.Callbacks.NewDownloadMessage
|
|
msg = strings.ReplaceAll(msg, "%user%", downloadData.User)
|
|
msg = strings.ReplaceAll(msg, "%computer%", downloadData.Computer)
|
|
msg = strings.ReplaceAll(msg, "%path%", downloadData.RemotePath)
|
|
msg = strings.ReplaceAll(msg, "%size%", tformat.SizeBytesToFormat(uint64(downloadData.TotalSize)))
|
|
|
|
ts.sendToAllChannels(msg)
|
|
}
|
|
|
|
///
|
|
|
|
func sendTelegram(text, botToken, chatID string) {
|
|
go func() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", botToken)
|
|
payload := map[string]string{
|
|
"chat_id": chatID,
|
|
"text": text,
|
|
}
|
|
data, _ := json.Marshal(payload)
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(data))
|
|
if err != nil {
|
|
return
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer func(Body io.ReadCloser) {
|
|
_ = Body.Close()
|
|
}(resp.Body)
|
|
}()
|
|
}
|
|
|
|
func sendWebhook(text string, webhook profile.WebhookConfig) {
|
|
go func() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
text = strings.ReplaceAll(webhook.Data, "%data%", text)
|
|
|
|
req, err := http.NewRequestWithContext(ctx, webhook.Method, webhook.URL, strings.NewReader(text))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for key, value := range webhook.Headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
|
|
if _, hasContentType := webhook.Headers["Content-Type"]; !hasContentType {
|
|
req.Header.Set("Content-Type", "text/plain")
|
|
}
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer func(Body io.ReadCloser) {
|
|
_ = Body.Close()
|
|
}(resp.Body)
|
|
}()
|
|
}
|
|
|
|
func sendSlack(text, webhookURL string) {
|
|
go func() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
payload := map[string]string{
|
|
"text": text,
|
|
}
|
|
data, _ := json.Marshal(payload)
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", webhookURL, bytes.NewBuffer(data))
|
|
if err != nil {
|
|
return
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer func(Body io.ReadCloser) {
|
|
_ = Body.Close()
|
|
}(resp.Body)
|
|
}()
|
|
}
|