├── README.md ├── dist └── remote_process_commandline.cna └── src ├── Makefile ├── beacon.h └── remote_process_commandline.c /README.md: -------------------------------------------------------------------------------- 1 | # Read Remote Process Commandline BOF 2 | `BOF` to read the startup arguments of a remote process, when provided a process ID (PID) 3 | 4 | #### Why: 5 | A few use-cases that immediately come to mind: 6 | - Secondary selection for process injection 7 | - Inspection of remote commandline arguments to identify possible configuration paths for applications 8 | 9 | #### Building 10 | ```bash 11 | cd src 12 | make 13 | ``` 14 | 15 | #### Usage 16 | - Load the `remote_process_commandline.cna` file from the `dist` folder. 17 | - Within a `beacon`: `remote_process_commandline process_id_number` 18 | 19 | #### Images 20 | ![Usage](https://i.ibb.co/0ns6ZkT/remote-cli.png) 21 | 22 | -------------------------------------------------------------------------------- /dist/remote_process_commandline.cna: -------------------------------------------------------------------------------- 1 | beacon_command_register( 2 | "remote_process_commandline", 3 | "Attempts to retrieve the remote commandline of a provided process ID.", 4 | "Synopsis: cReadRemoteProcessCmdline " 5 | ); 6 | 7 | alias remote_process_commandline 8 | { 9 | if(size(@_) == 2) 10 | { 11 | local('$barch $handle $data $args $target_pid'); 12 | $barch = barch($1); 13 | $handle = openf(script_resource("remote_process_commandline. $+ $barch $+ .o")); 14 | $data = readb($handle, -1); 15 | 16 | closef($handle); 17 | 18 | $args = bof_pack($1, "i", $2); 19 | 20 | beacon_inline_execute($1, $data, "go", $args); 21 | } 22 | else 23 | { 24 | berror($1, "You must provide a process ID number only."); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | BOFNAME := remote_process_commandline 2 | CC_x64 := x86_64-w64-mingw32-gcc 3 | 4 | all: 5 | $(CC_x64) -Wno-unused-variable -Wno-int-to-pointer-cast -o ../dist/$(BOFNAME).x64.o -c $(BOFNAME).c -masm=intel 6 | 7 | clean: 8 | rm -f ../dist/$(BOFNAME).x64.o 9 | -------------------------------------------------------------------------------- /src/beacon.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | typedef struct { 5 | char* original; /* the original buffer [so we can free it] */ 6 | char* buffer; /* current pointer into our buffer */ 7 | int length; /* remaining length of data */ 8 | int size; /* total size of this buffer */ 9 | } datap; 10 | 11 | DECLSPEC_IMPORT void BeaconDataParse(datap* parser, char* buffer, int size); 12 | DECLSPEC_IMPORT int BeaconDataInt(datap* parser); 13 | DECLSPEC_IMPORT short BeaconDataShort(datap* parser); 14 | DECLSPEC_IMPORT int BeaconDataLength(datap* parser); 15 | DECLSPEC_IMPORT char* BeaconDataExtract(datap* parser, int* size); 16 | 17 | /* format API */ 18 | typedef struct { 19 | char* original; /* the original buffer [so we can free it] */ 20 | char* buffer; /* current pointer into our buffer */ 21 | int length; /* remaining length of data */ 22 | int size; /* total size of this buffer */ 23 | } formatp; 24 | 25 | DECLSPEC_IMPORT void BeaconFormatAlloc(formatp* format, int maxsz); 26 | DECLSPEC_IMPORT void BeaconFormatReset(formatp* format); 27 | DECLSPEC_IMPORT void BeaconFormatFree(formatp* format); 28 | DECLSPEC_IMPORT void BeaconFormatAppend(formatp* format, char* text, int len); 29 | DECLSPEC_IMPORT void BeaconFormatPrintf(formatp* format, char* fmt, ...); 30 | DECLSPEC_IMPORT char* BeaconFormatToString(formatp* format, int* size); 31 | DECLSPEC_IMPORT void BeaconFormatInt(formatp* format, int value); 32 | 33 | /* Output Functions */ 34 | #define CALLBACK_OUTPUT 0x0 35 | #define CALLBACK_OUTPUT_OEM 0x1e 36 | #define CALLBACK_ERROR 0x0d 37 | #define CALLBACK_OUTPUT_UTF8 0x20 38 | #define CALLBACK_FILE 0x02 39 | #define CALLBACK_FILE_WRITE 0x08 40 | #define CALLBACK_FILE_CLOSE 0x09 41 | 42 | DECLSPEC_IMPORT void BeaconPrintf(int type, char* fmt, ...); 43 | DECLSPEC_IMPORT void BeaconOutput(int type, char* data, int len); 44 | 45 | /* Token Functions */ 46 | DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); 47 | DECLSPEC_IMPORT void BeaconRevertToken(); 48 | DECLSPEC_IMPORT BOOL BeaconIsAdmin(); 49 | 50 | /* Spawn+Inject Functions */ 51 | DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char* buffer, int length); 52 | DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char* arg, int a_len); 53 | DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len); 54 | DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION* pInfo); 55 | 56 | /* Utility Functions */ 57 | DECLSPEC_IMPORT BOOL toWideChar(char* src, wchar_t* dst, int max); -------------------------------------------------------------------------------- /src/remote_process_commandline.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "beacon.h" 6 | 7 | DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtClose(HANDLE ProcessHandle); 8 | DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientID); 9 | DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryInformationProcess(HANDLE ProcessHandle,PROCESSINFOCLASS ProcessInformationClass,PVOID ProcessInformation,ULONG ProcessInformationLength,PULONG ReturnLength); 10 | DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtReadVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG Size, PULONG TotalBytesRead); 11 | DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$HeapAlloc (HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes); 12 | DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$GetProcessHeap (VOID); 13 | DECLSPEC_IMPORT WINBOOL WINAPI KERNEL32$HeapFree (HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); 14 | 15 | 16 | VOID dumpFormatAllocation(formatp* formatAllocationData, LPVOID heapaddr) 17 | { 18 | char* outputString = NULL; 19 | int sizeOfObject = 0; 20 | 21 | outputString = BeaconFormatToString(formatAllocationData, &sizeOfObject); 22 | BeaconOutput(CALLBACK_OUTPUT, outputString, sizeOfObject); 23 | 24 | if (heapaddr != NULL) 25 | { 26 | KERNEL32$HeapFree(KERNEL32$GetProcessHeap(), 0, heapaddr); 27 | } 28 | 29 | BeaconFormatFree(formatAllocationData); 30 | 31 | return; 32 | } 33 | 34 | 35 | HANDLE GetProcessHandle(DWORD dwPid) 36 | { 37 | NTSTATUS status; 38 | HANDLE hProcess = NULL; 39 | 40 | OBJECT_ATTRIBUTES ObjectAttributes; 41 | 42 | InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 43 | CLIENT_ID uPid = { 0 }; 44 | 45 | uPid.UniqueProcess = (PVOID)dwPid; 46 | uPid.UniqueThread = 0; 47 | 48 | status = NTDLL$NtOpenProcess(&hProcess, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, &ObjectAttributes, &uPid); 49 | if (hProcess == NULL) { 50 | return NULL; 51 | } 52 | 53 | return hProcess; 54 | } 55 | 56 | 57 | WCHAR* returnData(HANDLE hProcessHandle, formatp* fpObject) 58 | { 59 | PROCESS_BASIC_INFORMATION pbi = { 0 }; 60 | RTL_USER_PROCESS_PARAMETERS rtlPP = { 0 }; 61 | 62 | NTSTATUS ntResult = NTDLL$NtQueryInformationProcess(hProcessHandle, 0, &pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL); 63 | 64 | if (NT_SUCCESS(ntResult)) 65 | { 66 | BeaconFormatPrintf(fpObject, "%-20s 0x%p\n", "Remote PEB:", pbi.PebBaseAddress); 67 | 68 | PEB pebData = { 0 }; 69 | ULONG pebSize = 0; 70 | 71 | NTDLL$NtReadVirtualMemory(hProcessHandle, pbi.PebBaseAddress, &pebData, sizeof(PEB), &pebSize); 72 | 73 | if (pebSize == (ULONG)sizeof(PEB)) 74 | { 75 | ULONG ulRtlParameters = 0; 76 | NTDLL$NtReadVirtualMemory(hProcessHandle, pebData.ProcessParameters, &rtlPP, sizeof(RTL_USER_PROCESS_PARAMETERS), &ulRtlParameters); 77 | 78 | if (ulRtlParameters == sizeof(RTL_USER_PROCESS_PARAMETERS)) 79 | { 80 | WCHAR* wcCommandLineBuffer = (WCHAR*)KERNEL32$HeapAlloc(KERNEL32$GetProcessHeap(), HEAP_ZERO_MEMORY, rtlPP.CommandLine.Length * sizeof(WCHAR)); 81 | 82 | if (wcCommandLineBuffer != NULL) 83 | { 84 | ULONG ulReadCommandline = 0; 85 | BeaconFormatPrintf(fpObject, "%-20s 0x%p\n", "Command Addr:", wcCommandLineBuffer); 86 | BeaconFormatPrintf(fpObject, "%-20s %hu\n", "Size:", rtlPP.CommandLine.Length), 87 | 88 | NTDLL$NtReadVirtualMemory(hProcessHandle, rtlPP.CommandLine.Buffer, wcCommandLineBuffer, rtlPP.CommandLine.Length, &ulReadCommandline); 89 | BeaconFormatPrintf(fpObject, "%-20s %S\n", "Commandline:", (wchar_t*)wcCommandLineBuffer); 90 | 91 | return wcCommandLineBuffer; 92 | } 93 | } 94 | else 95 | { 96 | return NULL; 97 | } 98 | 99 | } 100 | else 101 | { 102 | return NULL; 103 | } 104 | } 105 | else 106 | { 107 | return NULL; 108 | } 109 | } 110 | 111 | 112 | void go(char* args, int len) 113 | { 114 | datap parser; 115 | formatp fpObject; 116 | 117 | BeaconDataParse(&parser, args, len); 118 | BeaconFormatAlloc(&fpObject, 64 * 1024); 119 | 120 | DWORD dwProcessID = (DWORD)BeaconDataInt(&parser); 121 | 122 | BeaconFormatPrintf(&fpObject, "%-20s %lu\n", "Process ID:", dwProcessID); 123 | 124 | HANDLE hProcess = GetProcessHandle(dwProcessID); 125 | 126 | if (hProcess) 127 | { 128 | WCHAR* heapAllocatedData = returnData(hProcess, &fpObject); 129 | dumpFormatAllocation(&fpObject, heapAllocatedData); 130 | } 131 | else 132 | { 133 | BeaconFormatPrintf(&fpObject, "%-20s %s\n", "Error:", "Unable to obtain process handle."); 134 | 135 | } 136 | 137 | NTDLL$NtClose(hProcess); 138 | } --------------------------------------------------------------------------------