package connector import ( "AdaptixServer/core/profile" "AdaptixServer/core/utils/krypt" "AdaptixServer/core/utils/logs" "AdaptixServer/core/utils/token" "crypto/tls" "errors" "fmt" "net/http" "strings" "time" "github.com/Adaptix-Framework/axc2" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" ) type Teamserver interface { CreateOTP(otpType string, data interface{}) (string, error) ValidateOTP(token string) (string, interface{}, bool) TsClientExists(username string) bool TsClientDisconnect(username string) TsClientConnect(username string, version string, socket *websocket.Conn, clientType uint8, consoleTeamMode bool, subscriptions []string) TsClientSync(username string) TsClientSubscribe(username string, categories []string, consoleTeamMode *bool) TsListenerList() (string, error) TsListenerStart(listenerName string, configType string, config string, createTime int64, watermark string, customData []byte) error TsListenerEdit(listenerName string, configType string, config string) error TsListenerStop(listenerName string, configType string) error TsListenerPause(listenerName string, configType string) error TsListenerResume(listenerName string, configType string) error TsListenerGetProfile(listenerName string) (string, []byte, error) TsListenerInteralHandler(watermark string, data []byte) (string, error) TsAgentList() (string, error) TsAgentIsExists(agentId string) bool TsAgentCreate(agentCrc string, agentId string, beat []byte, listenerName string, ExternalIP string, Async bool) (adaptix.AgentData, error) TsAgentProcessData(agentId string, bodyData []byte) error TsAgentGetHostedAll(agentId string, maxDataSize int) ([]byte, error) TsAgentCommand(agentName string, agentId string, clientName string, hookId string, handlerId string, cmdline string, ui bool, args map[string]any) error TsAgentBuildSyncOnce(agentName string, config string, listenersName []string) ([]byte, string, error) TsAgentUpdateData(newAgentData adaptix.AgentData) error TsAgentUpdateDataPartial(agentId string, updateData interface{}) error TsAgentTerminate(agentId string, terminateTaskId string) error TsAgentRemove(agentId string) error TsAgentConsoleRemove(agentId string) error TsAgentSetTick(agentId string, listenerName string) error TsAgentTickUpdate() TsAgentConsoleOutput(agentId string, messageType int, message string, clearText string, store bool) TsAgentConsoleOutputClient(agentId string, client string, messageType int, message string, clearText string) TsAgentConsoleErrorCommand(agentId string, client string, cmdline string, message string, HookId string, HandlerId string) TsTaskCreate(agentId string, cmdline string, client string, taskData adaptix.TaskData) TsTaskUpdate(agentId string, data adaptix.TaskData) TsTaskGetAvailableAll(agentId string, availableSize int) ([]adaptix.TaskData, error) TsTaskCancel(agentId string, taskId string) error TsTaskDelete(agentId string, taskId string) error TsTaskPostHook(hookData adaptix.TaskData, jobIndex int) error TsTaskSave(hookData adaptix.TaskData) error TsTaskListCompleted(agentId string, limit int, offset int) ([]byte, error) TsChatSendMessage(username string, message string) TsDownloadAdd(agentId string, fileId string, fileName string, fileSize int64) error TsDownloadUpdate(fileId string, state int, data []byte) error TsDownloadClose(fileId string, reason int) error TsDownloadList() (string, error) TsDownloadSync(fileId string) (string, []byte, error) TsDownloadDelete(fileId []string) error TsDownloadGetFilepath(fileId string) (string, error) TsUploadGetFilepath(fileId string) (string, error) TsUploadGetFileContent(fileId string) ([]byte, error) TsScreenshotList() (string, error) TsScreenshotGetImage(screenId string) ([]byte, error) TsScreenshotDelete(screenId string) error TsScreenshotNote(screenId string, note string) error TsCredentilsList() (string, error) TsCredentilsAdd(creds []map[string]interface{}) error TsCredentilsEdit(credId string, username string, password string, realm string, credType string, tag string, storage string, host string) error TsCredentilsDelete(credsId []string) error TsCredentialsSetTag(credsId []string, tag string) error TsTargetsList() (string, error) TsTargetsAdd(targets []map[string]interface{}) error TsTargetsEdit(targetId string, computer string, domain string, address string, os int, osDesk string, tag string, info string, alive bool) error TsTargetDelete(targetsId []string) error TsTargetSetTag(targetsId []string, tag string) error TsTargetRemoveSessions(agentsId []string) error TsClientGuiDisksWindows(taskData adaptix.TaskData, drives []adaptix.ListingDrivesDataWin) TsClientGuiFilesStatus(taskData adaptix.TaskData) TsClientGuiFilesWindows(taskData adaptix.TaskData, path string, files []adaptix.ListingFileDataWin) TsClientGuiFilesUnix(taskData adaptix.TaskData, path string, files []adaptix.ListingFileDataUnix) TsClientGuiProcessWindows(taskData adaptix.TaskData, process []adaptix.ListingProcessDataWin) TsClientGuiProcessUnix(taskData adaptix.TaskData, process []adaptix.ListingProcessDataUnix) TsAgentTerminalCreateChannel(terminalData string, wsconn *websocket.Conn) error TsAgentBuildCreateChannel(buildData string, wsconn *websocket.Conn) error TsTunnelList() (string, error) TsTunnelClientStart(AgentId string, Listen bool, Type int, Info string, Lhost string, Lport int, Client string, Thost string, Tport int, AuthUser string, AuthPass string) (string, error) TsTunnelClientNewChannel(TunnelData string, wsconn *websocket.Conn) error TsTunnelClientStop(TunnelId string, Client string) error TsTunnelStop(TunnelId string) error TsTunnelClientSetInfo(TunnelId string, Info string) error TsTunnelCreateSocks4(AgentId string, Info string, Lhost string, Lport int) (string, error) TsTunnelCreateSocks5(AgentId string, Info string, Lhost string, Lport int, UseAuth bool, Username string, Password string) (string, error) TsTunnelCreateLportfwd(AgentId string, Info string, Lhost string, Lport int, Thost string, Tport int) (string, error) TsTunnelStopSocks(AgentId string, Port int) TsTunnelStopLportfwd(AgentId string, Port int) TsTunnelStopRportfwd(AgentId string, Port int) TsTunnelConnectionClose(channelId int, writeOnly bool) TsTunnelConnectionHalt(channelId int, errorCode byte) TsTunnelConnectionResume(AgentId string, channelId int, ioDirect bool) TsTunnelConnectionData(channelId int, data []byte) TsServiceLoad(configPath string) error TsServiceUnload(serviceName string) error TsServiceCall(serviceName string, operator string, function string, args string) TsServiceList() (string, error) TsAxScriptLoadUser(name string, script string) error TsAxScriptUnloadUser(name string) error TsAxScriptList() (string, error) TsAxScriptCommands() (string, error) TsAxScriptResolveHooks(agentName string, agentId string, listenerRegName string, os int, cmdline string, args map[string]interface{}) (string, string, bool, error) TsAxScriptIsServerHook(id string) bool TsAxScriptParseAndExecute(agentId string, username string, cmdline string) error AxGetAgentContext(agentId string) (agentName string, listenerRegName string, osType int, err error) } type TsConnector struct { Interface string Port int Hash string OnlyHash bool Operators map[string]string Endpoint string Cert string Key string ManagePasswordHash string httpServer *profile.TsHttpServer Engine *gin.Engine teamserver Teamserver apiGroup *gin.RouterGroup publicGroup *gin.RouterGroup dynamicEndpoints map[string]gin.HandlerFunc dynamicPublicEndpoints map[string]gin.HandlerFunc } func tlsVersionFromString(v string) (uint16, error) { s := strings.TrimSpace(strings.ToUpper(v)) s = strings.ReplaceAll(s, "_", "") s = strings.ReplaceAll(s, "-", "") switch s { case "", "DEFAULT": return 0, nil case "TLS10", "TLS1.0": return tls.VersionTLS10, nil case "TLS11", "TLS1.1": return tls.VersionTLS11, nil case "TLS12", "TLS1.2": return tls.VersionTLS12, nil case "TLS13", "TLS1.3": return tls.VersionTLS13, nil default: return 0, errors.New("unsupported TLS version: " + v) } } func tlsCipherSuiteFromString(name string) (uint16, error) { key := strings.TrimSpace(strings.ToUpper(name)) key = strings.ReplaceAll(key, "-", "_") key = strings.ReplaceAll(key, " ", "_") switch key { case "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": return tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, nil case "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": return tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, nil case "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": return tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, nil case "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": return tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, nil case "TLS_RSA_WITH_AES_128_GCM_SHA256": return tls.TLS_RSA_WITH_AES_128_GCM_SHA256, nil case "TLS_RSA_WITH_AES_256_GCM_SHA384": return tls.TLS_RSA_WITH_AES_256_GCM_SHA384, nil default: return 0, errors.New("unsupported cipher suite: " + name) } } func limitTimeoutMiddleware(cfg profile.TsHTTPConfig) gin.HandlerFunc { return func(c *gin.Context) { timeout := time.Duration(cfg.RequestTimeoutSec) * time.Second if timeout <= 0 { timeout = 300 * time.Second } msg := cfg.RequestTimeoutMessage if msg == "" { msg = "504 Gateway Timeout" } handler := http.TimeoutHandler( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c.Next() }), timeout, msg, ) handler.ServeHTTP(c.Writer, c.Request) } } func default404Middleware(httpError profile.TsHttpError) gin.HandlerFunc { return func(c *gin.Context) { if len(c.Errors) > 0 && !c.Writer.Written() { for header, value := range httpError.Headers { c.Header(header, value) } c.String(httpError.Status, httpError.PageContent) c.Abort() return } c.Next() if len(c.Errors) > 0 && !c.Writer.Written() { for header, value := range httpError.Headers { c.Header(header, value) } c.String(httpError.Status, httpError.PageContent) } } } func NewTsConnector(ts Teamserver, tsProfile profile.TsProfile, httpServer profile.TsHttpServer) (*TsConnector, error) { gin.SetMode(gin.ReleaseMode) var connector = new(TsConnector) connector.Engine = gin.New() connector.Engine.Use(gin.Recovery()) connector.teamserver = ts connector.Interface = tsProfile.Interface connector.Port = tsProfile.Port connector.Endpoint = tsProfile.Endpoint connector.Hash = krypt.SHA256([]byte(tsProfile.Password)) connector.OnlyHash = tsProfile.OnlyPassword connector.Operators = make(map[string]string, len(tsProfile.Operators)) for username, password := range tsProfile.Operators { connector.Operators[username] = krypt.SHA256([]byte(password)) } connector.Key = tsProfile.Key connector.Cert = tsProfile.Cert if tsProfile.ManagePassword != "" { connector.ManagePasswordHash = krypt.SHA256([]byte(tsProfile.ManagePassword)) } if httpServer.Error == nil { httpServer.Error = &profile.TsHttpError{} } if httpServer.Error.Status == 0 { httpServer.Error.Status = 404 } if httpServer.Error.Headers == nil { httpServer.Error.Headers = map[string]string{} } if httpServer.HTTP == nil { httpServer.HTTP = &profile.TsHTTPConfig{} } if httpServer.HTTP.MaxHeaderBytes == 0 { httpServer.HTTP.MaxHeaderBytes = 8192 } if httpServer.HTTP.RequestTimeoutSec == 0 { httpServer.HTTP.RequestTimeoutSec = 300 } if httpServer.HTTP.RequestTimeoutMessage == "" { httpServer.HTTP.RequestTimeoutMessage = "504 Gateway Timeout" } if httpServer.TLS == nil { httpServer.TLS = &profile.TsTLSConfig{} } if httpServer.TLS.MinVersion == "" { httpServer.TLS.MinVersion = "TLS1.2" } if httpServer.TLS.MaxVersion == "" { httpServer.TLS.MaxVersion = "TLS1.3" } connector.httpServer = &httpServer connector.dynamicEndpoints = make(map[string]gin.HandlerFunc) connector.dynamicPublicEndpoints = make(map[string]gin.HandlerFunc) httpCfg := *httpServer.HTTP httpErr := *httpServer.Error public_group := connector.Engine.Group(tsProfile.Endpoint) public_group.Use(limitTimeoutMiddleware(httpCfg), default404Middleware(httpErr)) connector.publicGroup = public_group login_group := connector.Engine.Group(tsProfile.Endpoint) login_group.Use(limitTimeoutMiddleware(httpCfg), default404Middleware(httpErr)) { login_group.POST("/login", connector.tcLogin) login_group.POST("/refresh", token.RefreshTokenHandler) } otp_group := connector.Engine.Group(tsProfile.Endpoint) otp_group.Use(connector.validateOTPMiddleware(), default404Middleware(httpErr)) { otp_group.POST("/otp/upload/temp", connector.tcOTP_UploadTemp) otp_group.GET("/otp/download/sync", connector.tcOTP_DownloadSync) otp_group.GET("/connect", connector.tcConnectOTP) otp_group.GET("/channel", connector.tcChannelOTP) } api_group := connector.Engine.Group(tsProfile.Endpoint) api_group.Use(limitTimeoutMiddleware(httpCfg), token.ValidateAccessToken(), default404Middleware(httpErr)) connector.apiGroup = api_group { api_group.POST("/sync", connector.tcSync) api_group.POST("/subscribe", connector.tcSubscribe) api_group.POST("/otp/generate", connector.tcOTP_Generate) api_group.GET("/listener/list", connector.TcListenerList) api_group.POST("/listener/create", connector.TcListenerStart) api_group.POST("/listener/edit", connector.TcListenerEdit) api_group.POST("/listener/stop", connector.TcListenerStop) api_group.POST("/listener/pause", connector.TcListenerPause) api_group.POST("/listener/resume", connector.TcListenerResume) api_group.GET("/agent/list", connector.TcAgentList) api_group.POST("/agent/generate", connector.TcAgentGenerate) api_group.POST("/agent/remove", connector.TcAgentRemove) api_group.POST("/agent/command/file", connector.TcAgentCommandFile) api_group.POST("/agent/command/execute", connector.TcAgentCommandExecute) api_group.POST("/agent/command/raw", connector.TcAgentCommandRaw) api_group.POST("/agent/console/remove", connector.TcAgentConsoleRemove) api_group.POST("/agent/set/tag", connector.TcAgentSetTag) api_group.POST("/agent/set/mark", connector.TcAgentSetMark) api_group.POST("/agent/set/color", connector.TcAgentSetColor) api_group.POST("/agent/update/data", connector.TcAgentUpdateData) api_group.GET("/agent/task/list", connector.TcAgentTaskList) api_group.POST("/agent/task/cancel", connector.TcAgentTaskCancel) api_group.POST("/agent/task/delete", connector.TcAgentTaskDelete) api_group.POST("/agent/task/hook", connector.TcAgentTaskHook) api_group.POST("/agent/task/save", connector.TcAgentTaskSave) api_group.POST("/chat/send", connector.TcChatSendMessage) api_group.GET("/download/list", connector.TcDownloadList) api_group.POST("/download/sync", connector.TcGuiDownloadSync) api_group.POST("/download/delete", connector.TcGuiDownloadDelete) api_group.GET("/screen/list", connector.TcScreenshotList) api_group.GET("/screen/image", connector.TcScreenshotGetImage) api_group.POST("/screen/setnote", connector.TcScreenshotSetNote) api_group.POST("/screen/remove", connector.TcScreenshotRemove) api_group.GET("/creds/list", connector.TcCredentialsList) api_group.POST("/creds/add", connector.TcCredentialsAdd) api_group.POST("/creds/edit", connector.TcCredentialsEdit) api_group.POST("/creds/remove", connector.TcCredentialsRemove) api_group.POST("/creds/set/tag", connector.TcCredentialsSetTag) api_group.GET("/targets/list", connector.TcTargetsList) api_group.POST("/targets/add", connector.TcTargetsAdd) api_group.POST("/targets/edit", connector.TcTargetEdit) api_group.POST("/targets/remove", connector.TcTargetRemove) api_group.POST("/targets/set/tag", connector.TcTargetSetTag) api_group.GET("/tunnel/list", connector.TcTunnelList) api_group.POST("/tunnel/start/socks5", connector.TcTunnelStartSocks5) api_group.POST("/tunnel/start/socks4", connector.TcTunnelStartSocks4) api_group.POST("/tunnel/start/lportfwd", connector.TcTunnelStartLpf) api_group.POST("/tunnel/start/rportfwd", connector.TcTunnelStartRpf) api_group.POST("/tunnel/stop", connector.TcTunnelStop) api_group.POST("/tunnel/set/info", connector.TcTunnelSetIno) api_group.GET("/service/list", connector.TcServiceList) //api_group.POST("/service/load", connector.TcServiceLoad) //api_group.POST("/service/unload", connector.TcServiceUnload) api_group.POST("/service/call", connector.TcServiceCall) //api_group.POST("/axscript/list", connector.TcAxScriptList) //api_group.POST("/axscript/commands", connector.TcAxScriptCommands) //api_group.POST("/axscript/load", connector.TcAxScriptLoad) //api_group.POST("/axscript/unload", connector.TcAxScriptUnload) } connector.Engine.NoRoute(limitTimeoutMiddleware(httpCfg), default404Middleware(httpErr), func(c *gin.Context) { _ = c.Error(errors.New("NoRoute")) }) return connector, nil } func (tc *TsConnector) endpointKey(method string, path string) string { return method + ":" + path } func (tc *TsConnector) RegisterEndpoint(method string, path string, handler func(c *gin.Context)) error { if tc.apiGroup == nil { return errors.New("api group not initialized") } key := tc.endpointKey(method, path) if _, exists := tc.dynamicEndpoints[key]; !exists { dispatcher := func(c *gin.Context) { if h, ok := tc.dynamicEndpoints[key]; ok { h(c) } else { c.JSON(404, gin.H{"error": "endpoint not found"}) } } switch method { case "GET": tc.apiGroup.GET(path, dispatcher) case "POST": tc.apiGroup.POST(path, dispatcher) case "PUT": tc.apiGroup.PUT(path, dispatcher) case "DELETE": tc.apiGroup.DELETE(path, dispatcher) case "PATCH": tc.apiGroup.PATCH(path, dispatcher) default: return errors.New("unsupported HTTP method: " + method) } } tc.dynamicEndpoints[key] = handler return nil } func (tc *TsConnector) UnregisterEndpoint(method string, path string) error { key := tc.endpointKey(method, path) if _, exists := tc.dynamicEndpoints[key]; !exists { return errors.New("endpoint not registered: " + key) } delete(tc.dynamicEndpoints, key) return nil } func (tc *TsConnector) EndpointExists(method string, path string) bool { key := tc.endpointKey(method, path) _, exists := tc.dynamicEndpoints[key] return exists } func (tc *TsConnector) RegisterPublicEndpoint(method string, path string, handler func(c *gin.Context)) error { if tc.publicGroup == nil { return errors.New("public group not initialized") } key := tc.endpointKey(method, path) if _, exists := tc.dynamicPublicEndpoints[key]; !exists { dispatcher := func(c *gin.Context) { if h, ok := tc.dynamicPublicEndpoints[key]; ok { h(c) } else { c.JSON(404, gin.H{"error": "endpoint not found"}) } } switch method { case "GET": tc.publicGroup.GET(path, dispatcher) case "POST": tc.publicGroup.POST(path, dispatcher) case "PUT": tc.publicGroup.PUT(path, dispatcher) case "DELETE": tc.publicGroup.DELETE(path, dispatcher) case "PATCH": tc.publicGroup.PATCH(path, dispatcher) default: return errors.New("unsupported HTTP method: " + method) } } tc.dynamicPublicEndpoints[key] = handler return nil } func (tc *TsConnector) UnregisterPublicEndpoint(method string, path string) error { key := tc.endpointKey(method, path) if _, exists := tc.dynamicPublicEndpoints[key]; !exists { return errors.New("public endpoint not registered: " + key) } delete(tc.dynamicPublicEndpoints, key) return nil } func (tc *TsConnector) PublicEndpointExists(method string, path string) bool { key := tc.endpointKey(method, path) _, exists := tc.dynamicPublicEndpoints[key] return exists } func (tc *TsConnector) Start(finished *chan bool) { host := fmt.Sprintf("%s:%d", tc.Interface, tc.Port) if tc.httpServer == nil || tc.httpServer.HTTP == nil || tc.httpServer.TLS == nil { logs.Error("", "HTTP server configuration is not initialized") return } httpCfg := *tc.httpServer.HTTP tlsCfgProfile := *tc.httpServer.TLS minTLS, err := tlsVersionFromString(tlsCfgProfile.MinVersion) if err != nil { logs.Error("", "Invalid TLS min_version: "+err.Error()) return } maxTLS, err := tlsVersionFromString(tlsCfgProfile.MaxVersion) if err != nil { logs.Error("", "Invalid TLS max_version: "+err.Error()) return } if minTLS != 0 && maxTLS != 0 && minTLS > maxTLS { logs.Error("", "Invalid TLS version range: min_version (%v) must be <= max_version (%v)", tlsCfgProfile.MinVersion, tlsCfgProfile.MaxVersion) return } var cipherSuites []uint16 if tlsCfgProfile.CipherSuites != nil { cipherSuites = make([]uint16, 0, len(tlsCfgProfile.CipherSuites)) for _, cs := range tlsCfgProfile.CipherSuites { id, err := tlsCipherSuiteFromString(cs) if err != nil { logs.Error("", "Invalid TLS cipher_suites: "+err.Error()) return } cipherSuites = append(cipherSuites, id) } } tlsConfig := &tls.Config{ PreferServerCipherSuites: false, } if minTLS != 0 { tlsConfig.MinVersion = minTLS } if maxTLS != 0 { tlsConfig.MaxVersion = maxTLS } if cipherSuites != nil { tlsConfig.CipherSuites = cipherSuites } if tlsCfgProfile.PreferServerCipherSuites != nil { tlsConfig.PreferServerCipherSuites = *tlsCfgProfile.PreferServerCipherSuites } server := &http.Server{ Addr: host, Handler: tc.Engine, TLSConfig: tlsConfig, ReadTimeout: time.Duration(httpCfg.ReadTimeoutSec) * time.Second, WriteTimeout: time.Duration(httpCfg.WriteTimeoutSec) * time.Second, IdleTimeout: time.Duration(httpCfg.IdleTimeoutSec) * time.Second, MaxHeaderBytes: httpCfg.MaxHeaderBytes, } if httpCfg.ReadHeaderTimeoutSec > 0 { server.ReadHeaderTimeout = time.Duration(httpCfg.ReadHeaderTimeoutSec) * time.Second } server.SetKeepAlivesEnabled(!httpCfg.DisableKeepAlives) if httpCfg.EnableHTTP2 != nil && !*httpCfg.EnableHTTP2 { server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) } err = server.ListenAndServeTLS(tc.Cert, tc.Key) //err := tc.Engine.RunTLS(host, tc.Cert, tc.Key) if err != nil { logs.Error("", "Failed to start HTTP Server: "+err.Error()) return } *finished <- true }