 Hello everyone, my name is Ron Benitzchak and I'm glad to be speaking here at DEF CON for the second year in a row. By the way, thanks for attending my talk at 12pm on the last day of this conference. Last year I talked about a new cadential dumping technique and today I'm presenting my new research about how to abuse the Windows filtering platform for privileged escalation. A little bit about me, I'm a security researcher at DP Instinct. I'm interested in Indian bypass techniques and I enjoy playing volleyball and row climbing. Here is what we're going to talk about today. I'll start with an overview of non-privileged escalation techniques and the technical background to understand them. Then I'll show the process of reverse engineering the execution of an RPC method throughout the entire Windows filtering platform. Once we understand how this platform works, we'll be able to abuse it to gain high-privileged tokens. The final part will be about how to detect these attacks and I'll also share some interesting findings that you can continue and dig into and maybe you'll find your attacks yourself. Privileged escalation is the ability to execute code in a higher level of permissions on the system. It is useful when compromising a weak process. Let's say if an attacker gains the ability to execute code in a process that runs as a local service account. This user is limited in the operations it can perform on the system and escalating to higher privileges is necessary. Here is an example for that. On the left we can see the token of a process running as anti-authority system. And on the right is the process of a token running as the local service account. The token of this user is limited in the privileges it holds and it prevents the attacker from further compromising the machine. Anti-authority system is the most privileged user in the Windows OS and it is required for performing attacks like reading the same registry hive as a stinker ring and dumping Kerberos tickets with the tool Rubeus. To understand the techniques we'll talk about soon and this token in general we first need to understand what a token is. So I'll quote MSDN. Access token is an object that describes the security context of a process or a thread. It identifies the user when a thread interacts with a secure object or performs a privileged task. A token details the identity of a process and it is composed of several things. The user that executed it, the groups and the log on session it belongs to and the privileges of the process. For example if it can read the memory of other processes, load a driver, shadow on the system and more. When a thread tries to access an object let's say a named pipe the security identifiers of the token are checked to see if the access is allowed. There are two types of tokens. Primary tokens describe the security context of the user account associated with the process and impersonation tokens describe the security context of a client process. We'll talk more about that in just a bit. To visualize this whole concept we can look at a software called token viewer. Here we can see information about the token belonging to a user named user and this is a primary token and the session ID is one. Let's talk more about impersonation. It gives us the ability to execute under a different security context than the owning process. Let's say I'm writing an application for a file server. The process will be executed as the user network service probably but it will need to check the access of the client's connecting tweets. This is the purpose of impersonation. A new thread will impersonate the client and try to access the file. There are several impersonation levels and I won't go into details about each one but the level most offensive tools attempt to gain is the third one, impersonation. Which means the thread can impersonate the client security context on the local system. And again we can look at an example with the token viewer. This is another token belonging to the user named user and this time it is an impersonation token and the level is identification. Now let's talk about actual techniques. The first one is based on the API duplicate token which duplicates the primary token of another process and we can combine that with the ability to launch a child process with a different token than ours. These are the steps for that. Let's say we have our offensive tool on the left and a process running as anti-authority system on the right. We start by calling open process token which gives us a handle to the other process primary token. Then we'll call duplicate token which gives us a new token with the same security identifiers that we can use with the call to create process with token to launch a new child process as anti-authority system. This is a very simple technique and it is implemented in PowerSploit and Rubeus. There is another technique based on the API duplicate handle. The tokens held by a process are listed in the handle table and here we can see the handle table of Elsas. It holds several tokens and other processes can gain access to them. Let's see how. Again we have our tool on the left and let's say Elsas on the right. We start by calling anti-query information process. This gives us the handle table we just saw. Then we start going over this table and calling duplicate handle on each object. This will give us access to that object and we can query it until we find the token of anti-authority system. Then once again we can call create process with token. This is a very common technique and it is implemented in Metropeter and Mimicats. The final technique is based on the impersonation concept. It is a feature that gets abused by many offensive tools. They manipulate common RPC servers to connect one of those threads that will then impersonate system. Let's see how they do it. The tool launches a new thread. The thread creates a named pipe. Then a new service is created that interacts with this pipe. This gives the thread the ability to call impersonate named pipe client that will change the thread security context to anti-authority system. This is another technique implemented in Metropeter and also many potato tools manipulate services in various ways. These techniques are known for a long time and detecting them is easy. We can look for events of duplicate token or duplicate handle that return a token of anti-authority system through a process with lower privileges or a thread that impersonates system after calling one of these APIs. Many tools are very similar and will be detected by the same logic. Now that we are familiar with privileged escalation techniques, let's talk about how this research started. It all began when my teammate San Obsieller developed a tool for mapping RPC methods. The purpose of it was to find ways to manipulate benign services. For example, to call as a service to change the protection of a page in another process or to encrypt a file. All RPC servers on the system were mapped and methods were marked if the parameters that will be sent to the WinAPI are controlled by the RPC clients. The WinAPI could be called directly by the RPC method or after several internal calls. RPC methods were also marked if the specific keywords appear in their names. Let's take a look at the output of the tool. Here we can see it founds RPC methods that lead to APIs like create remote thread or create processes user. It found the calls after one, two or three hops. An example of a call will look like this. The RPC method server pass message is exposed by the DLL D3D10 warp. It will call an internal non-exported function called build, which will then lead to read process memory. Now let's look at one of the interesting results this tool produced. The DLL called BFE caught my attention because it exposes many RPC methods and I looked for one with an interesting name. This script goes through each RPC interface exposed by this DLL and looks for the word token in the name of the procedure. This led to a method called BFE RPC open token. It sounded interesting to me, so I started looking into this DLL and found out it is part of the windows filtering platform. This platform is a native part of the Windows OS and it can be interacted with by using dedicated APIs. It controls network traffic. It can block or allow it based on several fields like the path of the application, user, address, port and more. It does that by hooking the network stack and that allows developing security products like host firewall and IDS. That platform consists of several components. The first one are caller drivers. These are user-defined drivers that can be loaded and integrated with the platform to extend its capabilities. They receive network data and can process it in custom ways that the platform doesn't offer like the packet inspection, packet modification or just perform some custom logging. The second component is the filter engine. This component is designed to filter network data by using multiple layers from the OS network stack and the layers are set in user and kernel mode. The user-mode component filters RPC and IPsec network data and the kernel-mode component performs filtering at the network and transport layers of the TCP-IP stack. It also sends the network data to the caller drivers. The third component is the base filtering engine or BFE. This is a user-mode service that is implemented in the DLL BFE that also exports management functions for user interaction. It accepts commands to add or remove filters offer data and statistics about the platform and force configuration settings to other components in the system. This is how it all comes together. The Windows firewall and other security products use an RPC client implemented by a DLL. It invokes methods in the RPC service which then sends device hour requests to the filter engine in the kernel. Also, the various layers in the TCP-IP driver use the filter engine to classify network data which will then go through the relevant filters and caller drivers. Okay, we are done with the technical background. Time to move on to the reverse engineering process. Like I said in the overview of the platform, there is a DLL that acts as an RPC client. It has exported and documented functions that trap RPC calls to the BFE servers. This is the function that interests us. It has several parameters and most of them are clear. The engine handle can be gained from another WinAPI and we get back an access token that we can specify the desired access to. There is one parameter I couldn't understand, the modified ID. So I looked into the code to find out what it is. We're going to dig into several components until we'll have the full picture for this query. The first step is an RPC client, then the server, and the last one is a kernel driver. All the pseudocode snippets I'll show you today are a bit simplified. I removed variables declaration and error handling to make them shorter. This is the function in the RPC client, and it calls NDR client call, which is the first step in an RPC call. Based on the index that is specified here, we can tell that the method that will be invoked in the server is BFE RPC OpenToken. This method receives the modified ID and sends back a process ID and a source handle. The source handle is then duplicated from another process into the current process and stored in the access token variable. So the modified ID is still unclear. Let's look at the server now. This is the method from the RPC server, and it is undocumented, but the parameters for the RPC client make this call clearer. It starts by calling driver token query, and if this call is successful, we get back the token handle. So let's look inside driver token query. What happens here is that a device I requested sent and the handle to the device is stored in a global variable. Based on cross-references, we can tell that this device is called WFP-ALE. So what is this device? I searched for this string in the driver's folder, and it led me to the file tcpip.cs. This driver has a lot of functionalities, and it registers many devices. But for now, we're going to focus on the ALE device. I mapped the control codes and the functions they invoke. All of them are undocumented, and they are listed here for future use. Now let's look at the code of the driver. This is the function the BFE service will invoke. It uses an undocumented structure I named token entry. It will try to find it based on the earlier ID it received, which is the modified ID that we sent. If it is found, duplicate token is called. This is when I realized I found something interesting. Invoking APIs in the kernel means bypassing user-mode hooks. The desired access is hardcoded to bid token duplicates, and also the token type is hardcoded to bid token primary. If the duplication is successful, we get back the new token handle in the output buffer. But how an entry is found? Here we can see that the hash value is calculated based on the earlier ID and then sent to another function. Okay, this is the last step I swear. The driver iterates over a hash table and tries to find the entry that matches the parameters it receives. If the entry is found, it is returned to the caller. So this is the recap of the whole process. We use an RPC client that invokes a method in the BFE service. This service sends a device request to the TCP-IP driver, and after several internal calls, a hash table is looked into. Time to understand what this hash table is. The table is used by over 30 functions and stores various undocumented stocks. We can see here names like processing information, peer information, and connection context. The function that inserts tokens appear on the screen, it's too long, I'm not going to read it. The next thing I did is try to debug it and see how it is invoked. But it isn't called at all. Not during the boot process and not by simpler interactions with the machine. What we can tell about the insertion function is that it is called by another function named ALE Process Token Reference. Looking back at the control code table, we can see that it is invoked by device R request. But again, there is a complication. The function in the BFE service that sends this request isn't exposed directly by RPC. It will be invoked under very specific circumstances. We want to find a way to trigger this device R request so that token entry will be inserted into the table and we can retrieve it. Let's see how this insertion is done. The function receives a very simple structure as the input buffer. It is made of a process ID and a handle to a token. Note that any process ID can be set by the caller. One process can specify the ID of another process. This design can be easily abused. The driver changes into the context of the process specified by the PID by obtaining the address of the E process structure and then calling stack attached process. Then, the token specified by the input buffer is duplicated, a new token entry is created based on the LEID of the new token and this entry is inserted into the hash table. The LEID is returned to the caller. We finish reverse engineering the process of adding and retrieving a token from the TCP-IP driver. Now let's see how we can abuse the Windows filtering platform. To recap the last step, we had a problem. There is no RPC call we can make that will add a token to the table. But if we'll send this device, our request ourselves will bypass this limitation. We want to gain access to the device WFP-ALE. But guess what? Opening a handle to this device results in error access denied. This is because the device is created with a security descriptor by the TCP-IP driver. Here we can see the creation of the device. It is pretty standard. IOCRAY device and then IOCRAY symbolic link. But there is another call to a function named allowBFE generic call. And if we look at the token of the BFE service, we can see it has a unique security identifier that allows it to access this device. So what can we do? The BFE service has an open handle to this device and we can see it in the process handle table. What we can do is to duplicate this process or duplicate this handle to another process. This will give us access to the device and this action is allowed because the security descriptor doesn't block the duplication of the handle, only creating a new one. The next image shows how well the security descriptor is protecting the device. One thing to note, there are a few requirements for this action. The first one are debug privileges which are pretty standard for most offensive tools and also will need a handle to the BFE service with the permissions of duplicating handles and querying information. Gaining this handle isn't suspicious to any DR. Process Hacker does the same thing. That's how it shows us the information about the handle table. And also this type of handle is opened by the RPC client. So overall, it's a pretty legitimate action. Another benefit for sending the device our request directly is avoiding detections. Like I said before, specific calls to duplicate handle or duplicate token might be detected if they retain the token of anti-authority system to a process with lower privileges and using the RPC client will trigger those detections. This is why. We duplicate the handle to the token from the BFE service to the current process here at these points. And the only permission we can have for it is token duplicates because that how it is set in the driver. We can see it right here. This means that we'll also need to call the API duplicate token to get a new token that can be used to launch a new process. Sending the device our request directly will bypass these detections because the TCP-IP driver will duplicate the token for the current process instead of the BFE service. I will say that the initial permissions will still be token duplicate. So we'll need to call the API duplicate handle to change it. But that call will most likely be ignored by NDR because the source and the destination process are the current process. This object already belongs to us. Time to put all the pieces together. Our goal is to store token in the hash table and then retrieve it. We can rely on the technique we talked about before, the one that is based on the API duplicate handle. Like before we have our offensive tool and the process running as anti-assorty system, but now we also have the TCP-IP driver and the hash table. We'll start by calling anti-query information process to gain the handle table, but instead of calling duplicate handle we'll call ALE process token reference. This will cause the driver to store a token in the hash table and send us the LERID. Then we use this value with the call ALE query token by ID to get back this token. Time to see a demo. All demos were recorded on a fully-patched Windows 11 machine and these PowerShell commands show us the token held by the WinLogon process. We can see that the first token belongs to anti-authority system. We launch our tool from an elevated command prompt running as the local admin and we get a new console as a system. Let's conclude this attack. It has a few advantages. We avoid a suspicious caller because a handle to a token isn't duplicated from one process to another. Also, the requirements aren't suspicious. Duplicating a handle to the ALE device should not be detected by security products. Another thing is that I compared this technique to the techniques we talked about before and the token of several services can be duplicated only by this method, services like the LSM, WinManagement, Task Scheduler, and many more. At this point I was happy. I found a new privilege escalation technique, but it was based on a known one. I wanted to find something truly novel, something that will be accepted to DEFCON. So I looked for additional cross-references to the token insertion function and I found one that is related to IPsec. So maybe using IPsec will insert the token. But what is it? Internet protocol security, or IPsec, is a set of protocols that allows private and secure communication between two machines. It creates an encrypted tunnel that supports almost TCP-IP protocols and it also protects against several attacks. The first one is network sniffers because the payload of the packet is encrypted. It protects against data modification because each packet contains a cryptographic checksum. So if the data of the packet is modified, the checksum will be altered. It also protects against identity spoofing because the IPsec communication is established after its mutual authentication. This means that only trusted systems can communicate with one another. The final protection is against denial of service. The IPsec filtering methodology will draw packets that don't match the address, protocol, or port of the policy. This means that in an application, one process requests from unsecured sources. Before exchanging data, a secure connection must be set up. This is done by the Internet Key Exchange Service. Machines can authenticate with one another based on Kerberos, certificates, or what we're going to use a pre-shared key. There is another protocol called OTH-IP, which expands the Internet Key Exchange with more authentication options, most notably NTLM, and the IPsec policy can be configured with the Microsoft Management Console or the documented APIs. Here we can see an example for it. On the left is the Management Console and on the right are the APIs. So now our goal is to establish an IPsec connection. To do that, I used an example from MSDN. Again, the parameters are pretty clear. The engine handle is already familiar to us, the policy name is just a string, and the addresses are known structures. The first parameter I couldn't understand is the provider key. I didn't know which one I should specify, so I just tried to use every provider key on the system until I found out that the Internet Key Exchange provider didn't return a neural code. The second parameter I didn't understand was the pre-shared key. I wasn't familiar with the structure named Byblob, but I found out that the API GetAppID from file name returns this type of structure. There is one small problem, it only accepted file names. So I reversed engineered the algorithm and made my own function that accepts any type of string. Finally, I had a function in code. What I did is to set a breakpoint in the kernel on the insertion function, configured the IPsec policy on two machines, and started praying. I connected from one machine to another and discovered that establishing IPsec connections results in inserting tokens through the hash table. This is because metadata is kept on IPsec connections, and this metadata includes the token of the process. But I was curious, why is it kept? What is the token used for? So I dug into the documentation and tried testing several configurations, but the closest answer I have is this line from MSDN. IPsec impersonates the security context under which the socket is created. I couldn't find the code in the driver that does this impersonation, but nonetheless, we have what we want. Now our goal is to manipulate the service to create a socket that matches the IPsec policy. And this time we'll target a specific service, spooler. We'll start by configuring an IPsec policy from and to the local host. Then we'll call the method RPCOpenPrinter, which is a documented RPC method in the spooler service that will cause it to connect to the local host. Then the TCPIP driver will duplicate the token of the service and store it in the hash table. There is another problem. We don't know the earlier idea of the new token, but the value ranges from 1 to 4096. So we can just bootforce it. We'll start guessing the value until we find the right one, and the token will be sent to us. Let's see another demo. So we start recording events on the machine, and again, we launch our tool from an elevated command point. Now we can see that we caused the spooler service to create a socket. Then we guess the earlier idea of the new token, and got back a new console as a system. Let's conclude this attack as well. IPsec is used by admins of enterprise networks or for just ensuring secure communication with a server. So configuring an IPsec policy on a machine is totally legitimate. Also, the policy doesn't alter the communication on the local host. No service should be affected by it. And EDRs will most likely ignore local host communication and not slog it anywhere. Another benefit is that the driver automatically adds the token to the hash table, so we don't need to call ALE process token reference. And the final thing is that the interaction with the spooler service isn't suspicious. We don't retrieve the handle table of spooler or open a handle to its primary token. The call to RPC open printer isn't a known way to manipulate the service for privilege escalation. So now I was really satisfied. This technique is truly different. But you know the saying, give them a finger and they'll take the whole hand. So can we manipulate more services and gain other tokens? Yes, we can. A certain new goal. To steal the token of another user logged on to the machine. And it can lead to lateral movement if, let's say, the domain admin is connected to the same machine as us. I looked for RPC servers running as logged on users and noticed the local service account or anti-authority system. This script looks for processes running as a domain admin, then checking if they expose an RPC interface and what happened is that I started going over this DLL until I found out the DLL that had what I needed, sync controller. Once two or more users are logged into the machine, each session launches the one sync SVC service with the user's permissions. Here we can see there are two instance sets of the service, each one with a unique suffix. This service loads the sync controller DLL. And this DLL implements an undocumented RPC method. Again, the name is too long, I'm not going to read it, but it receives a string that causes it to connect to any address we want. Time for one final attack. We'll go over the services and find the instance of one sync SVC that runs in another session. Since the interface of sync controller is exposed by several services, we need to find the unique LPC port opened by the service that we are targeting. We can see it in the process handle table. Then we'll start a listener thread on the port 443 and call the RPC method. This will cause the service to connect to us, and while the socket is active, we can bootforce the early idea of the token stored in the hash table. Let's see a demo for that. Okay, so we'll look for instances of one sync SVC. There are two, one running as the local admin and one running as the domain admin. Then we'll look at the handle table of the service and find the LPC port we want to connect to. We'll start recording events on the machine, and we can see that the local admin cannot access the shared drives on the domain controller. We'll tell the tool to target the second session, and we can see we caused the service to connect to us. Then we get the new console running as a domain admin, and now we can access the shared drives on the domain controller. Let's conclude this final attack. No other tool abused one sync SVC before. I didn't see any mentions to the service online, so calling this RPC method should not trigger EDR solutions. Also, like I said before, it can achieve lateral movements. There's another thing. It is possible to be notified about a new login session and then execute this attack. I might add this ability in the future to this tool. So these are all the ways I found to abuse the Windows filtering platform. We can gain tokens of system as well as other logged-on users. Now to the final part of this talk. I submitted my findings to the Microsoft Security Response Center. I explained every attack I found and sent them POC code, and the response was, we determined that this behavior is considered to be by design. Well, what can you do? Let's move on to detection. I tried to make these attacks as stealthy as possible, but they can still be detected new IPsec policies that don't match the known network configuration or even correlating RPC calls with the existence of an IPsec policy. Calling many times to ALE query token by ID is also a strong indication for this attack, but the best way, in my opinion, is detecting device IO requests to the ALE device by processes other than the BFE service. For those who prefer log-based detection, let's see if it's possible. The Windows filtering platform generates logs for events, but most of them are about packet drops or other types of failures. It is possible to log a loud packet, but it is generally not recommended because it will generate a lot of noise. I looked at the logs generated during the attack, and saw they are not indicative enough. Let's look at them. This log shows that the spooler service was allowed to connect to the port 135 on the local host. There is no mention of an IPsec policy disconnection, but there is a filter ID. Quaring this filter returns the following information. Again, it's just allowing local host communication, no mention of IPsec or any data relevance. Let's move to further research. During this research, I had a feeling that the TCP-IP driver is a gold mine, and I'm sure that with the community can find more ways to abuse it. There are more devices than just ALE, with various functionalities and a lot of methods to invoke. Here is just a partial list of them. Although we talked a lot about the hash table, it's worth digging into even more. It is used for managing data about various operations and it stores a lot more than just tokens. Some of the data can be valuable for attackers, and there is one thing that caught my attention. Credential information. The TCP-IP driver stores something called process explicit credentials. In much like tokens, it can be retrieved through RPC. Unfortunately, the exported function in the RPC client is undocumented, so this query is even more obscure. I tried calling the RPC method with arbitrary values, but got back the error code access denied. My immediate response was to execute the code again as system, which worked, but if we don't want to do that, we can just skip over this check. This check is done by the BFE service, so we can just send the device our request directly. The same device that you can see here. I dug into explicit credentials some more and found out that much like token entries, none are inserted into the table by default. I tried to find configuration that will trigger it, but found none. So I did the same thing as before, looked at cross-references. Cross-references to the insertion function show names like security and early process circuit options, so maybe the API WSA set security is somehow relevant. The Internet Key Exchange service uses the query function and cross-references in this service show names like initialize SSPI and create SSPI IKE, so like you might guess it is somehow related to SSPI. This is everything I found on explicit credentials stored in the TCP-IP driver. I encourage the community to dig into this subject and finish this research. Time to conclude this entire talk. I showed the process of reverse engineering an RPC call and achieving lateral movement and privileged escalation. We looked at the various components of the Windows filtering platform, analyzed them and bypassed the security mechanisms, protecting them. Also I shared some leads with you that you can continue and look into. Several attacks were developed and their advantages are that they avoid Win API that are likely to be monitored. They can execute code as system and other logged-on user, compared to many tools that only elevate the system. They are very sturdy, there is barely any evidence and logs and I tested them against some security products and no detection was generated. This is the code for the tool I'll leave the link up a few seconds so we can look at it. Thank you. Any questions? You can just talk to me right here.