2026-04-06 00:20:51 -05:00

514 lines
14 KiB
Go

package connector
import (
"AdaptixServer/core/utils/logs"
"AdaptixServer/core/utils/std"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func (tc *TsConnector) TcAgentList(ctx *gin.Context) {
jsonAgents, err := tc.teamserver.TsAgentList()
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false})
return
}
ctx.Data(http.StatusOK, "application/json; charset=utf-8", []byte(jsonAgents))
}
type AgentConfig struct {
ListenerName []string `json:"listener_name"`
AgentName string `json:"agent"`
Config string `json:"config"`
}
func (tc *TsConnector) TcAgentGenerate(ctx *gin.Context) {
var (
agentConfig AgentConfig
err error
fileContent []byte
fileName string
)
err = ctx.ShouldBindJSON(&agentConfig)
if err != nil {
_ = ctx.Error(errors.New("invalid agent config"))
return
}
fileContent, fileName, err = tc.teamserver.TsAgentBuildSyncOnce(agentConfig.AgentName, agentConfig.Config, agentConfig.ListenerName)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false})
return
}
encodedContent := base64.StdEncoding.EncodeToString([]byte(fileName)) + ":" + base64.StdEncoding.EncodeToString(fileContent)
ctx.JSON(http.StatusOK, gin.H{"message": encodedContent, "ok": true})
}
type CommandData struct {
AgentId string `json:"id"`
UI bool `json:"ui"`
CmdLine string `json:"cmdline"`
Data string `json:"data"`
HookId string `json:"ax_hook_id"`
HandlerId string `json:"ax_handler_id"`
WaitAnswer bool `json:"wait_answer"`
}
func (tc *TsConnector) resolveFileRefs(args map[string]any) error {
for key, val := range args {
m, ok := val.(map[string]any)
if !ok {
continue
}
ref, ok := m["__file_ref"].(string)
if !ok || ref == "" {
continue
}
data, err := tc.teamserver.TsUploadGetFileContent(ref)
if err != nil {
return fmt.Errorf("failed to resolve file ref '%s' for arg '%s': %w", ref, key, err)
}
args[key] = base64.StdEncoding.EncodeToString(data)
}
return nil
}
func (tc *TsConnector) dispatchAgentCommand(ctx *gin.Context, username string, commandData *CommandData, args map[string]any) {
agentName, listenerRegName, agentOs, ctxErr := tc.teamserver.AxGetAgentContext(commandData.AgentId)
if ctxErr != nil {
ctx.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("agent not found: %v", ctxErr), "ok": false})
return
}
/// Resolve __file_ref markers: read temp files, base64-encode, replace in args
if err := tc.resolveFileRefs(args); err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false})
return
}
/// Resolve server-side hooks if client did not provide any
if commandData.HookId == "" && commandData.HandlerId == "" {
srvHookId, srvHandlerId, preHookHandled, hookErr := tc.teamserver.TsAxScriptResolveHooks(agentName, commandData.AgentId, listenerRegName, agentOs, commandData.CmdLine, args)
if hookErr != nil {
tc.teamserver.TsAgentConsoleErrorCommand(commandData.AgentId, username, commandData.CmdLine, std.ExtractJsErrorMessage(hookErr), "", "")
ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true})
return
}
if preHookHandled {
ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true})
return
}
commandData.HookId = srvHookId
commandData.HandlerId = srvHandlerId
}
if commandData.WaitAnswer {
err := tc.teamserver.TsAgentCommand(agentName, commandData.AgentId, username, commandData.HookId, commandData.HandlerId, commandData.CmdLine, commandData.UI, args)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false})
return
}
} else {
go func(agentName, agentId, clientName, hookId, handlerId, cmdline string, ui bool, a map[string]any) {
err := tc.teamserver.TsAgentCommand(agentName, agentId, clientName, hookId, handlerId, cmdline, ui, a)
if err != nil {
tc.teamserver.TsAgentConsoleErrorCommand(agentId, clientName, cmdline, err.Error(), hookId, handlerId)
}
}(agentName, commandData.AgentId, username, commandData.HookId, commandData.HandlerId, commandData.CmdLine, commandData.UI, args)
}
ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true})
}
func (tc *TsConnector) TcAgentCommandExecute(ctx *gin.Context) {
var (
username string
commandData CommandData
args map[string]any
ok bool
err error
)
value, exists := ctx.Get("username")
if !exists {
ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false})
return
}
username, ok = value.(string)
if !ok {
ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false})
return
}
err = ctx.ShouldBindJSON(&commandData)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false})
return
}
err = json.Unmarshal([]byte(commandData.Data), &args)
if err != nil {
logs.Debug("", "Error parsing commands JSON: %s\n", err.Error())
}
tc.dispatchAgentCommand(ctx, username, &commandData, args)
}
type CommandData2 struct {
ObjectId string `json:"object_id"`
}
func (tc *TsConnector) TcAgentCommandFile(ctx *gin.Context) {
var (
username string
commandData CommandData
commandData2 CommandData2
ok bool
err error
)
err = ctx.ShouldBindJSON(&commandData2)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false})
return
}
value, exists := ctx.Get("username")
if !exists {
ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false})
return
}
username, ok = value.(string)
if !ok {
ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false})
return
}
content, err := tc.teamserver.TsUploadGetFileContent(commandData2.ObjectId)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false})
return
}
err = json.Unmarshal(content, &commandData)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false})
return
}
var args map[string]any
err = json.Unmarshal([]byte(commandData.Data), &args)
if err != nil {
logs.Debug("", "Error parsing commands JSON: %s\n", err.Error())
}
tc.dispatchAgentCommand(ctx, username, &commandData, args)
}
type CommandDataRaw struct {
AgentId string `json:"id"`
CmdLine string `json:"cmdline"`
}
func (tc *TsConnector) TcAgentCommandRaw(ctx *gin.Context) {
var (
username string
rawData CommandDataRaw
ok bool
err error
)
value, exists := ctx.Get("username")
if !exists {
ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false})
return
}
username, ok = value.(string)
if !ok {
ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false})
return
}
err = ctx.ShouldBindJSON(&rawData)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false})
return
}
if rawData.AgentId == "" || rawData.CmdLine == "" {
ctx.JSON(http.StatusOK, gin.H{"message": "id and cmdline are required", "ok": false})
return
}
err = tc.teamserver.TsAxScriptParseAndExecute(rawData.AgentId, username, rawData.CmdLine)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false})
return
}
ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true})
}
type AgentRemove struct {
AgentIdArray []string `json:"agent_id_array"`
}
func (tc *TsConnector) TcAgentConsoleRemove(ctx *gin.Context) {
var (
agentRemove AgentRemove
err error
)
err = ctx.ShouldBindJSON(&agentRemove)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false})
return
}
var errorsSlice []string
for _, agentId := range agentRemove.AgentIdArray {
err = tc.teamserver.TsAgentConsoleRemove(agentId)
if err != nil {
errorsSlice = append(errorsSlice, err.Error())
}
}
if len(errorsSlice) > 0 {
message := ""
for i, errorMessage := range errorsSlice {
message += fmt.Sprintf("%d. %s\n", i+1, errorMessage)
}
ctx.JSON(http.StatusOK, gin.H{"message": message, "ok": false})
return
}
ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true})
}
func (tc *TsConnector) TcAgentRemove(ctx *gin.Context) {
var (
agentRemove AgentRemove
err error
)
err = ctx.ShouldBindJSON(&agentRemove)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false})
return
}
_ = tc.teamserver.TsTargetRemoveSessions(agentRemove.AgentIdArray)
var errorsSlice []string
for _, agentId := range agentRemove.AgentIdArray {
err = tc.teamserver.TsAgentRemove(agentId)
if err != nil {
errorsSlice = append(errorsSlice, err.Error())
}
}
if len(errorsSlice) > 0 {
message := ""
for i, errorMessage := range errorsSlice {
message += fmt.Sprintf("%d. %s\n", i+1, errorMessage)
}
ctx.JSON(http.StatusOK, gin.H{"message": message, "ok": false})
return
}
ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true})
}
type AgentTag struct {
AgentIdArray []string `json:"agent_id_array"`
Tag string `json:"tag"`
}
func (tc *TsConnector) TcAgentSetTag(ctx *gin.Context) {
var (
agentTag AgentTag
err error
)
err = ctx.ShouldBindJSON(&agentTag)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false})
return
}
var errorsSlice []string
for _, agentId := range agentTag.AgentIdArray {
updateData := struct {
Tags *string `json:"tags"`
}{Tags: &agentTag.Tag}
err = tc.teamserver.TsAgentUpdateDataPartial(agentId, updateData)
if err != nil {
errorsSlice = append(errorsSlice, err.Error())
}
}
if len(errorsSlice) > 0 {
message := ""
for i, errorMessage := range errorsSlice {
message += fmt.Sprintf("%d. %s\n", i+1, errorMessage)
}
ctx.JSON(http.StatusOK, gin.H{"message": message, "ok": false})
return
}
ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true})
}
type AgentMark struct {
AgentIdArray []string `json:"agent_id_array"`
Mark string `json:"mark"`
}
func (tc *TsConnector) TcAgentSetMark(ctx *gin.Context) {
var (
agentMark AgentMark
err error
)
err = ctx.ShouldBindJSON(&agentMark)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false})
return
}
var errorsSlice []string
for _, agentId := range agentMark.AgentIdArray {
updateData := struct {
Mark *string `json:"mark"`
}{Mark: &agentMark.Mark}
err = tc.teamserver.TsAgentUpdateDataPartial(agentId, updateData)
if err != nil {
errorsSlice = append(errorsSlice, err.Error())
}
}
if len(errorsSlice) > 0 {
message := ""
for i, errorMessage := range errorsSlice {
message += fmt.Sprintf("%d. %s\n", i+1, errorMessage)
}
ctx.JSON(http.StatusOK, gin.H{"message": message, "ok": false})
return
}
ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true})
}
type AgentColor struct {
AgentIdArray []string `json:"agent_id_array"`
Background string `json:"bc"`
Foreground string `json:"fc"`
Reset bool `json:"reset"`
}
func (tc *TsConnector) TcAgentSetColor(ctx *gin.Context) {
var (
agentColor AgentColor
err error
)
err = ctx.ShouldBindJSON(&agentColor)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false})
return
}
newcolor := ""
if !agentColor.Reset {
newcolor = agentColor.Background + "-" + agentColor.Foreground
}
var errorsSlice []string
for _, agentId := range agentColor.AgentIdArray {
updateData := struct {
Color *string `json:"color"`
}{Color: &newcolor}
err = tc.teamserver.TsAgentUpdateDataPartial(agentId, updateData)
if err != nil {
errorsSlice = append(errorsSlice, err.Error())
}
}
if len(errorsSlice) > 0 {
message := ""
for i, errorMessage := range errorsSlice {
message += fmt.Sprintf("%d. %s\n", i+1, errorMessage)
}
ctx.JSON(http.StatusOK, gin.H{"message": message, "ok": false})
return
}
ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true})
}
type AgentUpdateData struct {
AgentId string `json:"agent_id"`
InternalIP *string `json:"internal_ip,omitempty"`
ExternalIP *string `json:"external_ip,omitempty"`
GmtOffset *int `json:"gmt_offset,omitempty"`
ACP *int `json:"acp,omitempty"`
OemCP *int `json:"oemcp,omitempty"`
Pid *string `json:"pid,omitempty"`
Tid *string `json:"tid,omitempty"`
Arch *string `json:"arch,omitempty"`
Elevated *bool `json:"elevated,omitempty"`
Process *string `json:"process,omitempty"`
Os *int `json:"os,omitempty"`
OsDesc *string `json:"os_desc,omitempty"`
Domain *string `json:"domain,omitempty"`
Computer *string `json:"computer,omitempty"`
Username *string `json:"username,omitempty"`
Impersonated *string `json:"impersonated,omitempty"`
Tags *string `json:"tags,omitempty"`
Mark *string `json:"mark,omitempty"`
Color *string `json:"color,omitempty"`
}
func (tc *TsConnector) TcAgentUpdateData(ctx *gin.Context) {
var (
agentUpdateData AgentUpdateData
err error
)
err = ctx.ShouldBindJSON(&agentUpdateData)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false})
return
}
if agentUpdateData.AgentId == "" {
ctx.JSON(http.StatusOK, gin.H{"message": "agent_id is required", "ok": false})
return
}
err = tc.teamserver.TsAgentUpdateDataPartial(agentUpdateData.AgentId, agentUpdateData)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false})
return
}
ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true})
}