GoGoGadget – kernel exploitation helper class

The aim of this class is to facilitate building kernel shell-codes and ROP chains (at least before RFG kicks in sometime next year) by providing easy access to some of the available kernel information leaks. Under the hood I am using well known NtQuerySystemInformation with following classes: SystemExtendedHandleInformation, SystemModuleInformation. This piece of code came to live as a part of an exploit that I was developing some time ago, but I can’t release it yet. Therefore I decided to publish it as a separate project, so everyone (or just future me) can reuse it for their own purposes. Code is written in C++ and should compile with Visual Studio 2015, for now it only supports x64 platform and some of the functionality is limited to Windows 10 (obtaining EPROCESS address of some of the system processes). I advise compiling Release build, since Debug is very slow due to extensive use of STL

GitHub: https://github.com/rwfpl/rewolf-gogogadget

Demo

I’ll first show simple example how to use it, and then I’m going to describe the API.

#include <Windows.h>
#include "GoGoGadget.h"
 
int main()
{
	GoGoGadget gogo;
 
	gogo.addApi("ntoskrnl.exe", "PsGetCurrentProcess", "getCurProc");
	gogo.addApi("ntoskrnl.exe", "PsGetCurrentThreadId");
 
	gogo.addGadget(GadgetType::PopRax);
	gogo.addGadget(GadgetType::PopRcx);
	gogo.addGadget(GadgetType::PopRdx);
	gogo.addGadget(GadgetType::PopR14);
	gogo.addGadget(GadgetType::AddRaxRcx);
	gogo.addGadget(GadgetType::MovRaxPtrRax);
	gogo.addGadget("MovPtrRcx38Eax", { 0x89, 0x41, 0x38, 0xc3 });
 
	gogo.addModule("ntoskrnl.exe");
	gogo.addModule("ndis.sys");
 
	gogo.addProcess(GetCurrentProcessId());
	gogo.addProcess("explorer.exe");
 
	//gogo.stats();
 
	gogo.go();
 
	printf("\nChecking results:\n");
	printf("%0I64X\n", gogo.getSymbol("getCurProc"));
	printf("%0I64X\n", gogo.getSymbol("ntoskrnl.exe", "PsGetCurrentThreadId"));
	printf("%0I64X\n", gogo.getSymbol(GadgetType::PopRax));
	printf("%0I64X\n", gogo.getSymbol(GadgetType::PopRcx));
	printf("%0I64X\n", gogo.getSymbol(GadgetType::PopRdx));
	printf("%0I64X\n", gogo.getSymbol("MovPtrRcx38Eax"));
	printf("%0I64X\n", gogo.getSymbol("ntoskrnl.exe"));
	printf("%0I64X\n", gogo.getSymbol("ndis.sys"));
	printf("%0I64X\n", gogo.getSymbol(GetCurrentProcessId()));
	printf("%0I64X\n", gogo.getSymbol("GoGoGadget.exe"));
	printf("%0I64X\n", gogo.getSymbol("explorer.exe"));
	printf("%0I64X\n", gogo.getSymbol(SystemProcessType::System));
	printf("%0I64X\n", gogo.getSymbol(SystemProcessType::Services));
	printf("%0I64X\n", gogo.getSymbol(SystemProcessType::Wininit));
	printf("%0I64X\n", gogo.getSymbol(SystemProcessType::Lsass));
	return 0;
}

The main idea is simple, in the first step GoGoGadget takes all the information that are required by the exploit, then go() function is called and all symbols can be retrieved with series of calls to getSymbol() function. There is one additional function named stats(), which collects number of occurrences of each gadget, it can be useful to decide which gadgets have higher chances to be found on a different version of the OS.

addApi()

void addApi(const char* moduleName, const char* apiName, const char* symbolName = nullptr);

This api is used to obtain address of any function exported by any kernel driver.

addGadget()

void addGadget(GadgetType type);
void addGadget(const char* gadgetName, const uint8_t* gadgetBody, size_t gadgetSize);
void addGadget(const char* gadgetName, const std::vector<uint8_t>& gadgetBody);

This set of apis is used to look for a specific gadgets, either from the predefined set (it is very limited, feel free to extend it on your own and send me the patches) or by defining them in place (like MovPtrRcx38Eax in above example).

addModule()

void addModule(const char* moduleName);

Used to obtain base address of any loaded kernel module.

addProcess()

void addProcess(const char* processName);
void addProcess(uint32_t pid);

Used to obtain EPROCESS address of any process that can be opened with SYNCHRONIZE flag (usually it will work only with processes that belongs to the same user as the running exploit). To obtain EPROCESS of some of the privileged processes, one can use getSymbol() with SystemProcessType (it implements the technique described in my previous post).

go()

Issuing go() command starts the whole machinery. First it goes through all EPROCESSes, then it proceeds with API and gadget lookup in each of the loaded kernel modules. The library maps all kernel modules into user-space and apply proper relocations to limit possible gadget false positives/negatives.

stats()

As mentioned earlier, this function prints the number of occurrences for each found gadget.

getSymbol()

uint64_t getSymbol(GadgetType type) const;
uint64_t getSymbol(uint32_t pid) const;
uint64_t getSymbol(const char* symbolName) const;
uint64_t getSymbol(const std::string& symbolName) const;
uint64_t getSymbol(const char* moduleName, const char* apiName) const;
uint64_t getSymbol(SystemProcessType processType) const;

When the go() method finish, all gathered symbols can be obtained with above set of functions.

That would be all, so happy ROPping and enjoy. Pull requests are always welcome.

GitHub: https://github.com/rwfpl/rewolf-gogogadget

Big thanks to @gim for the final code review and some corrections (however I did not take into account all of them, cheers!)

4 Comments

  1. […] The aim of this class is to facilitate building kernel shell-codes and ROP chains (at least before RFG kicks in sometime next year) by providing easy access to some of the available kernel information leaks. Under the hood I am using well known NtQuerySystemInformation with following classes: SystemExtendedHandleInformation, SystemModuleInformation.  […]

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *