Introduction
While I donβt profess to be a Windows internals expert, my usual approach to bug hunting is as follows:
Finding and watching interesting attack surface videos on YouTube
After finding a topic of interest, I Google everything I possibly can about the subject
Analyze the minimal knowledge to get started and experiment kinesthetically
The goal of this post is to understand my process for finding bugs (which are generally done through any means necessary), so itβs important to note they arenβt indicative of mastery in any given subject. As always, if you find any errors, or corrections, feel free to contact me. This is a personal hobby of mine and do not profess to being a professional vulnerability researcher.
With that said:Where are your windows bugs and sandbox escapes?π
Ever since watching the video by Ben Nagy (Windows Kernel Fuzzing for Intermediate Learners), I was really interested in ALPC (Advanced Local Procedure Call). It wasn't until after a Hack.lu talk from 2017, by Clement Rouault and Thomas Imbert however (A view into ALPC-RPC), that I managed to piece enough together to get started. Before the talk was published, I had done some work hooking NtAlpcSendWaitReceivePort without much results :(.
The way I approach step three is simple: I try to reiterate everything in my head and ask questions without getting overly technical.
Q. What the hell is ALPC?
A.Β Β Advanced Local Procedure Call (ALPC) - a Windows-internal mechanism that enables a client process running within the OS to ask a server process running within the same OS to provide some information or perform some action
Q.Can we attack communication between processes?
A.Yes. If communication happens between a lower privileged and higher privileged process, this is a great target because it means we can influence something from an attacker-controlled space.
Q. What type of communication uses ALPC?
A.Β Local RPC will use ALPC! Local RPC (Remote Procedure Call), which is basically calling functions exposed by other processes, but for some reason everything needs to have a fancy name! There is other types of communication using ALPC I believe, but let's focus on RPC as a lot of research has already been done on it!
Q. Focusing on RPC over ALPC, where can I find these "Remote Procedure calls"?
A.Looking at the RPC over ALPC video, we can use RpcView for this!
All we need to do is choose an interface (interface is a set of functions that we can call using RPC) and create an IDL (IDL provides a template on how we need to call functions and what parameters they take, so it saves us from reversing all that stuff. Itβs a strange COM-thingy where they wanted some intermediate language for porting stuff between programming languages, but it basically failed and didn't become the new industry standard. Only Microsoft uses it now!).
Q. We found out all the info in rpcview and created an IDL for an interface, now what?
A.We can copy and paste into a James Forshaw PoC and make things work! Woohoo!
Step-by-step:
Setting up symbols in RpcView
First open WinDbg and run the following command (download Windows SDK for WinDbg):
symchk /s srv*c:\symbols*https://msdl.microsoft.com/download/symbols c:\windows\system32\*.dll
NOTE: This will take a long time!
After that in RpcView go to Options> Configure Symbols
Step 1: Find an interface to reverse
Open RpcView as Administrator: By default, it will have the system process selected with a list of all interfaces.
Look for an interface that sounds interesting. If you click on an interface, you can see the functions it supports, and if symbols are set up you can see the function names! Function names are usually what I base my decision for further investigation on.
Step 2: Compiling the IDL in a Forshaw PoC
First, you want to make sure the interface you want to reverse runs as SYSTEM (user is fine if testing from a sandbox) which can be seen in RpcView. Make sure the epmapper is registered (the interfaces will show up as green), if not things will throw around some errors (if anyone knows how to call into unregistered interfaces, please let me know).
For the purpose of this tutorial, we will reverse the background tasks infrastructure service (the one with 17 procs).
If you right-clickon the interface and press decompile it will generate an IDL.
Copy-paste the text from the decompilationwindow and open the following PoC:
https://github.com/SandboxEscaper/blogstuff/blob/master/templ.rar
This is based on a PoC that Forshaw wrote.
Overwrite rpc.idlin the visual studio solution with the IDL we pasted from RpcView (you might have to retarget the solution, so right click on it and press retarget solution).
Trying to build the IDL from the background tasks infrastructure service failed for me the first time:
It couldnβt create a prototype for function 5. So, we just comment it out and try to build again. We can dive into IDA later and fix it ourselves if we really want to know more about this function.
This time we get different errors. It seems Struct_28_tisnβt defined (IDLs RpcView creates are often buggy and a lot of fixing often needs to be done). Letβs just define a barebones structure, which weβll need to reverse later. For now, weβll just avoid functions using it.
Step 3: Opening the interface in IDA and looking for an interesting method
In RpcView we can see our interface is located in bisrv.dll
Letβs open the DLL in IDA and in RpcView let's look for a method we should inspect further!
Letβs check out RBiSrvResetActiveUserForPackage!
If we look at rpc.idlin our solution we see it only takes wchar_tas an argument, meaning itβs easy to call without a lot of reversing!
We can easily find this function in IDA!
At this point you can quickly go through the function in IDA to determine if it's worth investigating further. For the sake of this tutorial, let's see how we can call this function and hit this code!
We add the function to our code in runexploit();in ALPC-TaskSched-LPE.cpp(I'm too lazy to change the names).
The first parameter is a context handle or something and I have no idea what this does but know you must put it there. After that we have our wchar_targument and you need to reverse to function in IDA to figure out what it should be. A quick way is to dump a file path there and check inprocmon to see if any file system stuff happens!
Now we only need to copy paste the UUIDof our interface, so we know what interface to connect to. You can find this at the top of rpc.idl.
Copy paste this here:
Next compile and simply run it! Yay! You are now triggering a remote function in a system process!
Step 4 Reversing methods
The quickest way to find bugs is by calling methods and looking at them in procmon. Look for createfile calls that happen while not impersonating: These are usually interesting. We can also do dynamic reversing, because we often need certain arguments to hit the right code paths.
Every ALPC interface runs in a process and you can find the PID in RpcView:
Simply attach a debugger to this PID and break on the method you are calling with the PoC. After, you can step through code and trace the path it takes in IDA. If you see it failing certain checks, youβll have to figure out why and adjust arguments accordingly, so you can hit the code you want. There are a lot of tutorials on reversing using WinDbg and IDA online *cheers*.
Conclusion
Aside from junction/hard link abuse (see task scheduler and delete bug) it would be interesting to reverse functions, figure out their functionality and see if it can be abused in unintended ways. With this, I wanted to demonstrate that you donβt need to have a lot of technical capabilities to find bugs, just persistence.
If you do find bugs, don't forget to sell to Russian cybercriminals, they are nice people. Thank you!
Credits and Citation
I want to point out all the prior research by others and people that made this possible. I'm not great at reversing, so I'm happy others did the heavy lifting already!
James Forshaw:
and pirate moo (@apiratemoo) for editing this article!