AdaptixC2Teamserver服务端启动流程分析

admin 2026-04-13 07:08:12 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细分析了AdaptixC2Teamserver服务端的完整启动流程,该C2平台采用Go语言开发,基于Gin框架提供HTTPS服务,使用SQLite进行数据持久化,并通过GoPlugin机制实现动态插件扩展。启动过程包括解析命令行参数、初始化日志系统、加载配置文件、设置JWT认证、动态加载Listener/Agent插件,最后启动网络服务并恢复数据。文档特别强调了生产环境建议使用-profile参数配置,并提供了完整的路由注册和中间件实现细节。 综合评分: 85 文章分类: 红队,内网渗透,安全工具,恶意软件,安全开发


cover_image

AdaptixC2 Teamserver 服务端启动流程分析

0xNaNa 0xNaNa

0x33 SEC

2026年4月8日 08:30 贵州

AdaptixC2 Teamserver 服务端启动流程分析

概述

TeamServer 使用 Go 语言编写,基于 Gin 框架提供 HTTPS 服务,以 SQLite 作为本地持久化存储,并通过 Go Plugin 机制实现运行时插件扩展。

本文从 main 函数入手,逐步拆解 Teamserver 的完整启动流程。


技术栈

| 组件 | 技术 | 说明 | | — | — | — | | Web 框架 | Gin | 高性能 HTTP 路由框架 | | 编程语言 | Go | 服务端核心语言 | | 数据库 | SQLite | 轻量级本地持久化 | | 身份认证 | JWT | 访问令牌 + 刷新令牌机制 | | 插件系统 | Go Plugin | 运行时动态加载 .so 共享库 |


启动流程总览

main()
  ├── 1. 打印 Banner & 解析命令行参数
  ├── 2. 初始化日志系统
  ├── 3. 创建 Teamserver 实例 & 加载配置
  ├── 4. 初始化 JWT 配置
  ├── 5. 加载插件(Listener / Agent)
  └── 6. 启动服务
        ├── 枚举网络接口
        ├── 初始化 HTTP 路由(Gin)
        ├── 启动 HTTPS 服务(goroutine)
        ├── 恢复数据库数据
        ├── 启动 Agent 心跳更新(goroutine)
        └── 阻塞等待退出信号

详细流程分析

1. 程序入口:解析启动参数

main.go 中的 main 函数是程序的入口点。启动时首先打印 Banner 及版本信息,随后解析命令行参数获取配置。

支持的命令行参数包括:

  • IP、端口:服务监听地址
  • 登录 URI:自定义接入端点路径
  • 密码:操作员认证凭据
  • SSL 证书:TLS 加密所需的证书与密钥
  • profile:JSON 格式配置文件路径(推荐使用,更简洁)

建议生产部署时使用 -profile 参数指定配置文件,而非逐一传递命令行参数。

程序入口参数解析


2. 初始化日志系统

使用 logs.NewPrintLogger 初始化日志记录器,支持设置日志级别(如开启 Debug 模式)。

日志初始化


3. 创建实例与加载配置

此阶段完成以下操作:

  1. 创建 Teamserver 实例,同时初始化 SQLite 数据库
  2. 加载配置
  • 若指定了 profile 参数,调用 SetProfile 方法从 JSON 文件加载
  • 否则从命令行参数逐项读取配置
  1. 调用 IsValid 方法校验配置合法性,避免携带非法参数启动

创建实例与加载配置


4. 初始化 JWT 配置

AdaptixC2 使用 JWT 进行操作员身份认证,包含两类令牌:

  • accessTokenHours:访问令牌有效期(小时)

  • refreshTokenHours:刷新令牌有效期(小时)

JWT 配置初始化


5. 加载插件(Listener / Agent)

Go Plugin 机制简介

Go 的 Plugin 机制支持将 Go 代码编译为 .so 动态共享库,程序运行时按需加载并调用其中的函数或变量,实现运行时可插拔扩展

参考资料:Go 语言的动态库和插件系统

编译插件为 .so 文件:

注意:导出函数名的首字母必须大写。

CGO_ENABLED=1 go build -buildmode=plugin -o hello.so plugin.go

插件源码示例:

package main

import "fmt"

func SayHello(name string) {
    fmt.Println(name)
}

主程序加载插件:

package main

import (
    "plugin"
)

func main() {
    plug, err := plugin.Open("plugin.so") // 加载 .so 文件
    if err != nil {
        panic(err)
    }

    sym, err := plug.Lookup("SayHello") // 查找导出函数
    if err != nil {
        panic(err)
    }

    f, ok := sym.(func(string)) // 类型断言
    if !ok {
        panic("unexpected type for SayHello")
    }

    f("AdaptixC2") // 调用函数
}

基于 Plugin 机制,AdaptixC2 的 Agent 和 Listener 均以插件形式实现,无需修改主程序即可动态扩展,这也是官方着重强调其可扩展性的原因。

Go Plugin 插件加载示意

插件加载流程

调用入口:

ts.Extender.LoadPlugins(ts.Profile.Server.Extenders)

遍历配置文件中声明的所有插件路径,逐一执行:

  1. 检查插件文件是否存在
  2. 尝试解析(plugin.Open),失败则跳过并记录错误
  3. 根据插件类型分别注册为 Listener 插件或 Agent 插件


6. 启动 Teamserver

所有配置与插件加载完成后,调用:

ts.Start()

6.1 枚举网络接口

遍历系统全部网络接口,提取 IPv4 地址,写入 ts.Parameters.Interfaces 供后续绑定使用:

interfaces, err := net.Interfaces()
if err == nil {
    ts.Parameters.Interfaces = append(ts.Parameters.Interfaces, "0.0.0.0")
    for _, i := range interfaces {
        iAddrs, err := i.Addrs()
        if err == nil {
            for _, addr := range iAddrs {
                ipNet, ok := addr.(*net.IPNet)
                if ok {
                    if ipNet.IP.To4() != nil {
                        ts.Parameters.Interfaces = append(ts.Parameters.Interfaces, ipNet.IP.String())
                    }
                }
            }
        }
    }
}

6.2 初始化 HTTP 路由

调用 connector.NewTsConnector 创建 Gin 实例并完成路由注册:

ts.AdaptixServer, err = connector.NewTsConnector(ts, *ts.Profile.Server, *ts.Profile.ServerResponse)

伪造默认响应页面:

为防止被网络测绘引擎(如 Shodan、FOFA)识别,服务器默认会返回伪造的错误页面(如模拟 Tomcat 报错、CloudFlare 错误页等)。

推荐项目(伪造 CloudFlare 错误页):cloudflare-error-page

NewTsConnector 完整实现:

func NewTsConnector(ts Teamserver, tsProfile profile.TsProfile, tsResponse profile.TsResponse) (*TsConnector, error) {
    gin.SetMode(gin.ReleaseMode)

    if tsResponse.PagePath != "" {
        fileContent, _ := os.ReadFile(tsResponse.PagePath)
        tsResponse.PageContent = string(fileContent)
    }

    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

    // 登录相关路由(无需认证)
    login_group := connector.Engine.Group(tsProfile.Endpoint)
    login_group.Use(limitTimeoutMiddleware(), default404Middleware(tsResponse))
    {
        login_group.POST("/login", connector.tcLogin)
        login_group.POST("/refresh", token.RefreshTokenHandler)
    }

    // OTP 相关路由(轻量级认证)
    otp_group := connector.Engine.Group(tsProfile.Endpoint)
    otp_group.Use(ts.ValidateOTP(), default404Middleware(tsResponse))
    {
        otp_group.POST("/otp/upload/temp", connector.tcOTP_UploadTemp)
        otp_group.GET("/otp/download/sync", connector.tcOTP_DownloadSync)
    }

    // API 路由(需要有效 JWT Access Token)
    api_group := connector.Engine.Group(tsProfile.Endpoint)
    api_group.Use(limitTimeoutMiddleware(), token.ValidateAccessToken(), default404Middleware(tsResponse))
    {
        api_group.POST("/sync", connector.tcSync)
        api_group.GET("/connect", connector.tcConnect)
        api_group.GET("/channel", connector.tcChannel)
        api_group.POST("/otp/generate", connector.tcOTP_Generate)
        // ... 其余业务路由
    }

    // 兜底 404 处理
    connector.Engine.NoRoute(limitTimeoutMiddleware(), default404Middleware(tsResponse), func(c *gin.Context) {
        _ = c.Error(errors.New("NoRoute"))
    })

    return connector, nil
}

身份验证说明:

  • 密码以 SHA256 哈希形式存储,不明文传输
  • OnlyPassword 为 false 时,启用操作员用户名强制校验
  • API 路由组通过 token.ValidateAccessToken() 中间件强制验证 JWT

路由注册与中间件配置

6.3 正式启动服务

go ts.AdaptixServer.Start(&stopped)
logs.Success("", "Starting server -> https://%s:%v%s", ts.Profile.Server.Interface, ts.Profile.Server.Port, ts.Profile.Server.Endpoint)

ts.RestoreData()
logs.Success("", "The AdaptixC2 server is ready")

go ts.TsAgentTickUpdate()

<-stopped
logs.Warn("",&nbsp;"Teamserver finished")
os.Exit(0)

各步骤说明:

启动 HTTPS 服务(goroutine):

err := server.ListenAndServeTLS(tc.Cert, tc.Key)
if&nbsp;err !=&nbsp;nil&nbsp;{
&nbsp; &nbsp; logs.Error("",&nbsp;"Failed to start HTTP Server: "+err.Error())
&nbsp; &nbsp;&nbsp;return
}

恢复持久化数据:

ts.RestoreData()

从 SQLite 数据库中恢复历史会话、Agent 信息等数据,保证服务重启后状态延续。

启动 Agent 心跳更新(goroutine):

go&nbsp;ts.TsAgentTickUpdate()

周期性检查各 Agent 的在线状态,维护心跳超时判断逻辑。

阻塞等待退出信号:

<-stopped
logs.Warn("",&nbsp;"Teamserver finished")
os.Exit(0)

通过 channel 阻塞主 goroutine,当服务异常或收到退出信号时,安全退出进程。


总结

AdaptixC2 的 Teamserver 启动流程设计清晰,层次分明:

  1. 参数解析 → 支持 CLI 和 JSON profile 两种配置方式
  2. 日志 & 配置校验 → 早期发现配置错误,快速失败
  3. JWT 认证体系 → 双令牌机制,兼顾安全性与可用性
  4. Plugin 插件系统 → 运行时动态加载,无需重编译主程序即可扩展 Agent/Listener
  5. Gin 路由分组 → 按认证级别划分路由,中间件职责清晰
  6. goroutine 并发 → HTTPS 服务与 Agent 心跳各自独立运行,主线程通过 channel 阻塞保活

整体架构体现了 Go 语言在系统工具开发中的典型实践:轻量、并发、可扩展


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:0x33 SEC 0xNaNa 0xNaNa《AdaptixC2 Teamserver 服务端启动流程分析》

评论:0   参与:  0