Hi Kernelmode.info!
[PoC] Bypassing UM Hooks By Bruteforcing Intel Syscalls
Results: This PoC is supposed to demonstrate how one can +- reliably completely bypass all user mode hooks (IAT, EAT, (deep)inline, PEB?)
in order to get access to a pristine on-disk ntdll.dll, assuming the on-disk DLL has not been tampered with.
If the read of the ntdll.dll file succeeds you must see a message stating that a certain copy succeeded. Then, if you started the PoC
as an administrator it will terminate the most processes leading to a crash on Windows 7 and to a hang (or so) on Windows 10.
The results were achieved by leveraging my own universal stub for issuing Intel syscalls and creating a pristine system table for looking up these numbers.
The PoC was tested successfully on Windows 7 SP1 x64, WINPE 3.0, Windows 10 x64 and WINPE for Windows 10.
It was tested with and without administrator rights.
Introduction:
Although it might be well known that user mode hooking can be easily detected and avoided by just leveraging a kernel driver,
I have not come across any user mode tools which are capable of bypassing my former user mode rootkit which simply blocks access to
the on-disk ntdll.dll. Neither PC Hunter 64, nor HookShark64, nor GMER were able to counter this (tested on Windows 7 SP1 x64).
If they can't load a pristine image they can't bypass UM hooks residing in there own image and they have not a comparison base for verifying their own
and the other processes images... Uroburos... the snake bites its tail...
An outbreak would be to load a kernel mode driver in order to let the system process open and read a valid ntdll.dll file...but what if NtLoadDriver is also patched?
You can use NtDeviceIoControlFile to send a FLT_LOAD_FILTER request to fltmgr.sys then, but that API could also be hooked. And you need to call NtOpenFile with
a telltale \\??\\fltmgrmsg string. ;)
So I thought if it wouldn't be possible to use direct Intel 0F 05 syscall instructions to essentially construct your own NTAPI stubs
which, of course, are not patched. I saw three possible approaches. The first one was to have hardcoded the needed numbers
into the executable. But I never liked the fact that the numbers are really volatile and DO change with every new Windows service pack
or version. This would, as for now, lead to the program not running anymore on fictive Windows 11.
With these clear shortcomings, I was like "Why not just bruteforcing those numbers? Why would I need to know which number a certain syscall is corresponding to
when I just can feed it with the correct parameters and let the kernel decide for which number all parameters fit so it can perform the wanted operation?"
With that in mind I started initial tests in March...April. I could, however, not achieve promising results since I had to counter many problems.
Not only that I struggled coding a universal system call interface which easily lets you provide any number of parameters
but it was also almost quite impossible to perform bruteforcing the syscall numbers in the shellcode (or assembly) itself.
All sorts of possible, credible, impossible and incredible things happened to my program while trying perform the wanted operations.
When one number opened a handle to a file the next call just closed it again, the 234th call then let my program run into a NtWaitForXxx loop...etc.
This happened since I had no chance to define rules in assembly of when to deem a system call as successful and break the loop.
Hint: Using NTSTATUS values does not work at all since there are a few APIs which almost always return STATUS_SUCCESS such as NtYieldExecution.
And talk about the issues with the stack, the overwritten parameters due to failing syscall attempts, the few nonvolatile registers which must be preserved,
but I just want to use THAT registers to save my arguments and preserving those registers would shift the stack or unalign it...or I would
need to save a sufficient count of parameters in the memory I'm just executing instructions of...
Hence, with the simple, assembly based approach it was just not feasible and I stopped the project.
Now, a few months later, I wanted to fuzz the lower syscall interface (0x0...0x1000) and in order to be able to supply arbitrary parameters I needed a syscall interface of the form:
syscallStub(ULONG syscallNum, ...). I then just reversed what it looked like if I called printf("blah, %x%x%x%x%x%x", var1, var2, var3, var4, var5, var6).
I saw that the compiler does all the hard work and that the va_list or va_xxx stuff is only of interest when operating on these format strings.
So it was no problem to define a universal syscall interface similar to printf(). But I had some small problems on the other (assembly) side because the syscall number
occupied the former rcx register, the registers were all shifted by 1 and the r9 parameter was now on stack. This was quickly solved by moving
rcx to rax, (my syscall number) and then rdx to rcx, and so forth I then shifted the stack, then I issued a 0F 05 instruction
and then backshifted the stack and my job was done.
The small stub is in the auxfuncs.asm file.
Then I thought, why not just using the fuzzer interface to fuzz the system calls with NtOpenFile parameters until I get a handle to ntdll.dll?
That way I would completely bypass any NtOpenFile hooks. I tried it and due to a proper system call stub I succeeded.
But when I tried to query information on the file it failed because the NtClose call appeared before NtQueryInformationFile and thus closed my handle
I just opened. I solved that and some other problems later but basically I could see that it was feasible after all to use syscall bruteforcing in order to
read a pristine ntdll.dll into my VA space and then to use some basic PE math in order to correlate a NtXxx API name to the correct syscall number.
Having now the numbers of both NtCreateSection and NtMapViewOfSection, it would be a walk in the park to again load the ntdll.dll
but this time as an image file and execute almost ALL functions (RtlXxx, LdrXxx, DbgXxx, etc.) of it.
If there are any questions, feel free to ask! The same holds true if you have any suggestions.
The bruteforce part of the main.c file should be commented quite well so it should be pretty self-explaining.
Please take care if you execute the file with admin rights. It will most certainly crash your computer.
Of course, there are many possible improvements, one could for example use the NtOpenFile - NtCreateSection - NtMapViewOfSection approach directly
if that is possible with syscall bruteforcing.
Best Regards
Microwave89
[PoC] Bypassing UM Hooks By Bruteforcing Intel Syscalls
Results: This PoC is supposed to demonstrate how one can +- reliably completely bypass all user mode hooks (IAT, EAT, (deep)inline, PEB?)
in order to get access to a pristine on-disk ntdll.dll, assuming the on-disk DLL has not been tampered with.
If the read of the ntdll.dll file succeeds you must see a message stating that a certain copy succeeded. Then, if you started the PoC
as an administrator it will terminate the most processes leading to a crash on Windows 7 and to a hang (or so) on Windows 10.
The results were achieved by leveraging my own universal stub for issuing Intel syscalls and creating a pristine system table for looking up these numbers.
The PoC was tested successfully on Windows 7 SP1 x64, WINPE 3.0, Windows 10 x64 and WINPE for Windows 10.
It was tested with and without administrator rights.
Introduction:
Although it might be well known that user mode hooking can be easily detected and avoided by just leveraging a kernel driver,
I have not come across any user mode tools which are capable of bypassing my former user mode rootkit which simply blocks access to
the on-disk ntdll.dll. Neither PC Hunter 64, nor HookShark64, nor GMER were able to counter this (tested on Windows 7 SP1 x64).
If they can't load a pristine image they can't bypass UM hooks residing in there own image and they have not a comparison base for verifying their own
and the other processes images... Uroburos... the snake bites its tail...
An outbreak would be to load a kernel mode driver in order to let the system process open and read a valid ntdll.dll file...but what if NtLoadDriver is also patched?
You can use NtDeviceIoControlFile to send a FLT_LOAD_FILTER request to fltmgr.sys then, but that API could also be hooked. And you need to call NtOpenFile with
a telltale \\??\\fltmgrmsg string. ;)
So I thought if it wouldn't be possible to use direct Intel 0F 05 syscall instructions to essentially construct your own NTAPI stubs
which, of course, are not patched. I saw three possible approaches. The first one was to have hardcoded the needed numbers
into the executable. But I never liked the fact that the numbers are really volatile and DO change with every new Windows service pack
or version. This would, as for now, lead to the program not running anymore on fictive Windows 11.
With these clear shortcomings, I was like "Why not just bruteforcing those numbers? Why would I need to know which number a certain syscall is corresponding to
when I just can feed it with the correct parameters and let the kernel decide for which number all parameters fit so it can perform the wanted operation?"
With that in mind I started initial tests in March...April. I could, however, not achieve promising results since I had to counter many problems.
Not only that I struggled coding a universal system call interface which easily lets you provide any number of parameters
but it was also almost quite impossible to perform bruteforcing the syscall numbers in the shellcode (or assembly) itself.
All sorts of possible, credible, impossible and incredible things happened to my program while trying perform the wanted operations.
When one number opened a handle to a file the next call just closed it again, the 234th call then let my program run into a NtWaitForXxx loop...etc.
This happened since I had no chance to define rules in assembly of when to deem a system call as successful and break the loop.
Hint: Using NTSTATUS values does not work at all since there are a few APIs which almost always return STATUS_SUCCESS such as NtYieldExecution.
And talk about the issues with the stack, the overwritten parameters due to failing syscall attempts, the few nonvolatile registers which must be preserved,
but I just want to use THAT registers to save my arguments and preserving those registers would shift the stack or unalign it...or I would
need to save a sufficient count of parameters in the memory I'm just executing instructions of...
Hence, with the simple, assembly based approach it was just not feasible and I stopped the project.
Now, a few months later, I wanted to fuzz the lower syscall interface (0x0...0x1000) and in order to be able to supply arbitrary parameters I needed a syscall interface of the form:
syscallStub(ULONG syscallNum, ...). I then just reversed what it looked like if I called printf("blah, %x%x%x%x%x%x", var1, var2, var3, var4, var5, var6).
I saw that the compiler does all the hard work and that the va_list or va_xxx stuff is only of interest when operating on these format strings.
So it was no problem to define a universal syscall interface similar to printf(). But I had some small problems on the other (assembly) side because the syscall number
occupied the former rcx register, the registers were all shifted by 1 and the r9 parameter was now on stack. This was quickly solved by moving
rcx to rax, (my syscall number) and then rdx to rcx, and so forth I then shifted the stack, then I issued a 0F 05 instruction
and then backshifted the stack and my job was done.
The small stub is in the auxfuncs.asm file.
Then I thought, why not just using the fuzzer interface to fuzz the system calls with NtOpenFile parameters until I get a handle to ntdll.dll?
That way I would completely bypass any NtOpenFile hooks. I tried it and due to a proper system call stub I succeeded.
But when I tried to query information on the file it failed because the NtClose call appeared before NtQueryInformationFile and thus closed my handle
I just opened. I solved that and some other problems later but basically I could see that it was feasible after all to use syscall bruteforcing in order to
read a pristine ntdll.dll into my VA space and then to use some basic PE math in order to correlate a NtXxx API name to the correct syscall number.
Having now the numbers of both NtCreateSection and NtMapViewOfSection, it would be a walk in the park to again load the ntdll.dll
but this time as an image file and execute almost ALL functions (RtlXxx, LdrXxx, DbgXxx, etc.) of it.
If there are any questions, feel free to ask! The same holds true if you have any suggestions.
The bruteforce part of the main.c file should be commented quite well so it should be pretty self-explaining.
Please take care if you execute the file with admin rights. It will most certainly crash your computer.
Of course, there are many possible improvements, one could for example use the NtOpenFile - NtCreateSection - NtMapViewOfSection approach directly
if that is possible with syscall bruteforcing.
Best Regards
Microwave89
Attachments
(250.64 KiB) Downloaded 71 times
pwd: standard pw
(3.59 KiB) Downloaded 48 times
(3.59 KiB) Downloaded 48 times