514 lines
14 KiB
Go
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})
|
|
}
|