diff --git a/GetWebDAVStatus_BOF/GetWebDAVStatus.cna b/GetWebDAVStatus_BOF/GetWebDAVStatus.cna new file mode 100644 index 0000000..7ee2c84 --- /dev/null +++ b/GetWebDAVStatus_BOF/GetWebDAVStatus.cna @@ -0,0 +1,62 @@ +# GetWebDAVStatus.cna +# CNA author @nickvourd + +beacon_command_register( + "GetWebDAVStatus", + "Determine if the WebClient Service (WebDAV) is running on remote systems", + "Synopsis: GetWebDAVStatus [target2] [target3] ...\n\n" . + "Usage: GetWebDAVStatus [additional targets...]\n\n" . + "Description:\n" . + " Checks if the WebClient service (WebDAV) is running on one or more\n" . + " remote systems by attempting to connect to the DAV RPC SERVICE pipe.\n\n" . + "Arguments:\n" . + " target IP address or hostname of the remote system(s)\n" . + " Multiple targets can be specified separated by spaces\n\n" . + "Examples:\n" . + " GetWebDAVStatus 192.168.1.100\n" . + " GetWebDAVStatus dc01.domain.local\n" . + " GetWebDAVStatus dc01 ws01 srv01\n" . + " GetWebDAVStatus 192.168.1.10 192.168.1.20 192.168.1.30", + "bof" +); + +alias GetWebDAVStatus { + local('$bid $handle $data $arg_data $i $target $numTargets'); + + $bid = $1; + + # Check if we have at least one target + if (size(@_) < 2) { + berror($bid, "Specify at least one ip or hostname"); + berror($bid, "Usage: GetWebDAVStatus [target2] [target3] ..."); + return; + } + + # Read in the right BOF file + $handle = openf(script_resource("GetWebDAVStatus." . barch($bid) . ".o")); + $data = readb($handle, -1); + closef($handle); + + if (strlen($data) == 0) { + berror($bid, "Could not load BOF file: GetWebDAVStatus." . barch($bid) . ".o"); + return; + } + + # Calculate number of targets + $numTargets = size(@_) - 1; + + # Pack: first the count (i), then each target as wide string (Z) + $arg_data = bof_pack($bid, "i", $numTargets); + + for ($i = 1; $i < size(@_); $i++) { + $target = @_[$i]; + if ($target ne "") { + $arg_data = $arg_data . bof_pack($bid, "Z", $target); + } + } + + btask($bid, "GetWebDAVStatus BOF by @G0ldenGunSec && @nickvourd"); + btask($bid, "Checking WebDAV status on $numTargets host(s)..."); + + beacon_inline_execute($bid, $data, "go", $arg_data); +} diff --git a/GetWebDAVStatus_BOF/GetWebDAVStatus.x64.o b/GetWebDAVStatus_BOF/GetWebDAVStatus.x64.o new file mode 100644 index 0000000..6ebf0be Binary files /dev/null and b/GetWebDAVStatus_BOF/GetWebDAVStatus.x64.o differ diff --git a/GetWebDAVStatus_BOF/GetWebDAVStatus.x86.o b/GetWebDAVStatus_BOF/GetWebDAVStatus.x86.o new file mode 100644 index 0000000..5171e2c Binary files /dev/null and b/GetWebDAVStatus_BOF/GetWebDAVStatus.x86.o differ diff --git a/GetWebDAVStatus_BOF/GetWebDAVStatus_x64.o b/GetWebDAVStatus_BOF/GetWebDAVStatus_x64.o deleted file mode 100644 index f61b004..0000000 Binary files a/GetWebDAVStatus_BOF/GetWebDAVStatus_x64.o and /dev/null differ diff --git a/GetWebDAVStatus_BOF/GetWebDavStatus-Checksum.txt b/GetWebDAVStatus_BOF/GetWebDavStatus-Checksum.txt new file mode 100644 index 0000000..40bb483 --- /dev/null +++ b/GetWebDAVStatus_BOF/GetWebDavStatus-Checksum.txt @@ -0,0 +1,2 @@ +GetWebDAVStatus.x64.o B8ED7B5C95EC7511D4CE307550BA0305 +GetWebDAVStatus.x86.o 49C859A3FF0D7E18E01576075A934899 \ No newline at end of file diff --git a/GetWebDAVStatus_BOF/Source/GetWebDAVStatus.c b/GetWebDAVStatus_BOF/Source/GetWebDAVStatus.c new file mode 100644 index 0000000..f39f5f9 --- /dev/null +++ b/GetWebDAVStatus_BOF/Source/GetWebDAVStatus.c @@ -0,0 +1,117 @@ +#include +#include "beacon.h" + +WINBASEAPI BOOL WINAPI KERNEL32$WaitNamedPipeW(LPCWSTR lpNamedPipeName, DWORD nTimeOut); +WINBASEAPI HANDLE WINAPI KERNEL32$GetProcessHeap(void); +WINBASEAPI LPVOID WINAPI KERNEL32$HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes); +WINBASEAPI BOOL WINAPI KERNEL32$HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); + +void go(char* args, int length) +{ + datap parser; + wchar_t* host = NULL; + wchar_t* fullPipeName = NULL; + BOOL pipeStatus = 0; + HANDLE hHeap = NULL; + int totalHosts = 0; + int enabledCount = 0; + int disabledCount = 0; + int hostLen = 0; + int i = 0; + int numHosts = 0; + + BeaconDataParse(&parser, args, length); + hHeap = KERNEL32$GetProcessHeap(); + + // First argument is the number of hosts + numHosts = BeaconDataInt(&parser); + + if (numHosts <= 0) + { + BeaconPrintf(CALLBACK_ERROR, "[!] No targets specified"); + return; + } + + BeaconPrintf(CALLBACK_OUTPUT, "=== GetWebDAVStatus Check ===\n"); + + for (totalHosts = 0; totalHosts < numHosts; totalHosts++) + { + host = (wchar_t*)BeaconDataExtract(&parser, NULL); + + if (host == NULL || host[0] == L'\0') + { + break; + } + + // Calculate length manually + hostLen = 0; + while (host[hostLen] != L'\0') + { + hostLen++; + } + + // Allocate buffer for full pipe name + // \\host\pipe\DAV RPC SERVICE = 2 + hostLen + 21 + 1 + fullPipeName = (wchar_t*)KERNEL32$HeapAlloc(hHeap, HEAP_ZERO_MEMORY, (2 + hostLen + 21 + 1) * sizeof(wchar_t)); + + if (fullPipeName == NULL) + { + BeaconPrintf(CALLBACK_ERROR, "[!] Memory allocation failed for %S", host); + continue; + } + + // Build pipe name manually + // Copy "\\\\" + fullPipeName[0] = L'\\'; + fullPipeName[1] = L'\\'; + + // Copy host + for (i = 0; i < hostLen; i++) + { + fullPipeName[2 + i] = host[i]; + } + + // Copy "\\pipe\\DAV RPC SERVICE" + fullPipeName[2 + hostLen] = L'\\'; + fullPipeName[3 + hostLen] = L'p'; + fullPipeName[4 + hostLen] = L'i'; + fullPipeName[5 + hostLen] = L'p'; + fullPipeName[6 + hostLen] = L'e'; + fullPipeName[7 + hostLen] = L'\\'; + fullPipeName[8 + hostLen] = L'D'; + fullPipeName[9 + hostLen] = L'A'; + fullPipeName[10 + hostLen] = L'V'; + fullPipeName[11 + hostLen] = L' '; + fullPipeName[12 + hostLen] = L'R'; + fullPipeName[13 + hostLen] = L'P'; + fullPipeName[14 + hostLen] = L'C'; + fullPipeName[15 + hostLen] = L' '; + fullPipeName[16 + hostLen] = L'S'; + fullPipeName[17 + hostLen] = L'E'; + fullPipeName[18 + hostLen] = L'R'; + fullPipeName[19 + hostLen] = L'V'; + fullPipeName[20 + hostLen] = L'I'; + fullPipeName[21 + hostLen] = L'C'; + fullPipeName[22 + hostLen] = L'E'; + fullPipeName[23 + hostLen] = L'\0'; + + pipeStatus = KERNEL32$WaitNamedPipeW(fullPipeName, 3000); + + if (pipeStatus == 0) + { + BeaconPrintf(CALLBACK_OUTPUT, "[-] %S - WebClient NOT running or unreachable", host); + disabledCount++; + } + else + { + BeaconPrintf(CALLBACK_OUTPUT, "[+] %S - WebClient ENABLED", host); + enabledCount++; + } + + KERNEL32$HeapFree(hHeap, 0, fullPipeName); + fullPipeName = NULL; + } + + BeaconPrintf(CALLBACK_OUTPUT, "\n[*] Summary: %d hosts checked, %d enabled, %d disabled/unreachable", + totalHosts, enabledCount, disabledCount); +} diff --git a/GetWebDAVStatus_BOF/Source/Makefile b/GetWebDAVStatus_BOF/Source/Makefile new file mode 100644 index 0000000..6bc9e4a --- /dev/null +++ b/GetWebDAVStatus_BOF/Source/Makefile @@ -0,0 +1,19 @@ +SRC = $(wildcard *.c) +OBJS = $(patsubst %.c, %.o, $(SRC)) +CC_x86 := i686-w64-mingw32-gcc +CC_x64 := x86_64-w64-mingw32-gcc +STRIP_x86 := i686-w64-mingw32-strip +STRIP_x64 := x86_64-w64-mingw32-strip +CFLAGS := -masm=intel + +all: $(OBJS) + +%.o: %.c + $(CC_x64) $(CFLAGS) -o ../$*.x64.o -c $< + $(STRIP_x64) --strip-unneeded ../$*.x64.o + + $(CC_x86) $(CFLAGS) -o ../$*.x86.o -DWOW64 -fno-leading-underscore -c $< + $(STRIP_x86) --strip-unneeded ../$*.x86.o + +clean: + rm ../*.o diff --git a/GetWebDAVStatus_BOF/src/beacon.h b/GetWebDAVStatus_BOF/Source/beacon.h similarity index 100% rename from GetWebDAVStatus_BOF/src/beacon.h rename to GetWebDAVStatus_BOF/Source/beacon.h diff --git a/GetWebDAVStatus_BOF/src/Source.c b/GetWebDAVStatus_BOF/src/Source.c deleted file mode 100644 index a75517e..0000000 --- a/GetWebDAVStatus_BOF/src/Source.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include "beacon.h" - -WINBASEAPI BOOL WINAPI KERNEL32$WaitNamedPipeA(LPCSTR lpNamedPipeName, DWORD nTimeOut); -WINBASEAPI void* WINAPI MSVCRT$malloc(SIZE_T); -WINBASEAPI SIZE_T WINAPI MSVCRT$strlen(const char* str); -WINBASEAPI void* WINAPI MSVCRT$strcpy(const char* dest, const char* source); -WINBASEAPI void* WINAPI MSVCRT$strcat(const char* dest, const char* source); -WINBASEAPI void* WINAPI MSVCRT$strtok(char* str, const char* delim); -WINBASEAPI void* WINAPI MSVCRT$free(void*); - -void go(char* args, int length) -{ - char* pipeNameHead = "\\\\"; - char* pipeNameTail = "\\pipe\\DAV RPC SERVICE"; - BOOL pipeStatus = 0; - char* singleHost = MSVCRT$strtok(args, ","); - - while (singleHost != NULL) - { - char* fullPipeName = MSVCRT$malloc(MSVCRT$strlen(singleHost) + MSVCRT$strlen(pipeNameHead) + MSVCRT$strlen(pipeNameTail) + 1); - MSVCRT$strcpy(fullPipeName, pipeNameHead); - MSVCRT$strcat(fullPipeName, singleHost); - MSVCRT$strcat(fullPipeName, pipeNameTail); - pipeStatus = KERNEL32$WaitNamedPipeA(fullPipeName, 3000); - if (pipeStatus == 0) - { - BeaconPrintf(CALLBACK_OUTPUT, "[x] Unable to hit DAV pipe on %s, system is either unreachable or does not have WebClient service running", singleHost); - } - else - { - BeaconPrintf(CALLBACK_OUTPUT, "[+] WebClient service is active on %s", singleHost); - } - MSVCRT$free((void*)fullPipeName); - singleHost = MSVCRT$strtok(NULL, ","); - } -} \ No newline at end of file diff --git a/README.md b/README.md index e75ce43..36ac380 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,24 @@ Small project to determine if the Web Client service (WebDAV) is running on a remote system by checking for the presence of the DAV RPC SERVICE named pipe. Does not require admin privileges on the remote system, but does require some form of valid credentials (no anonymous access). Both a BOF and C# version of the project are included, the C# version is multi-threaded so would be better suited for scanning a large number of systems. ## Usage -Both the BOF and C# versions take a comma-seperated list of systems to scan. The C# version also has an optional arg of "--tc" that allows the operator to control the max amount of threads to be used (default: 5). +The C# versions take a comma-seperated list of systems to scan. The C# version also has an optional arg of "--tc" that allows the operator to control the max amount of threads to be used (default: 5). The BOF vesrion take only one argument. -BOF: `inline-execute C:\scripts\GetWebDAVStatus_x64.o server01,server02` +BOF: `GetWebDAVStatus server01 dc01 ws01 192.168.1.55 ca.domain.local` C#: `execute-assembly C:\assemblies\GetWebDAVStatus.exe server01,server02 --tc 1` ## Building -The C# project should be a pretty standard build, x64 + Release is the recommended build configuration. BOF can be built with the following command from the Developer Command Prompt for VS: +The C# project should be a pretty standard build, x64 + Release is the recommended build configuration. BOF can be built with the following command from the Makefile: -`cl.exe /c /GS- Source.c /Fo./GetWebDAVStatus_x64.o` +`make` + +Prerequisites for compiling the BOF: +- i686-w64-mingw32-gcc +- x86_64-w64-mingw32-gcc ## Credits [@tifkin_](https://twitter.com/tifkin_) originally posted about this method of remotely identifying WebDAV [here](https://twitter.com/tifkin_/status/1419806476353298442). +Special thanks to [@nickvourd](https://twitter.com/nickvourd) for his contributions. + Originally heard about the above tweet on [@flangvik](https://twitter.com/Flangvik)'s [twitch stream](https://www.twitch.tv/flangvik). Would definitely recommend checking out.