Will WebClient Start
Aug 19 2025
By: Steven Flores • 31 min read
TL;DR WebClient is a common targeted service for NTLM relay attacks. In this post we will cover if it is possible to start the service remotely as a low privileged user. This will also dive into what is happening when the service is started and the associated protocols and technologies used.
“Can you enable the WebClient service remotely as a low privileged user?” For years now, I’ve wondered and finally decided to do a deep dive into this specific question. If this is possible, it would allow attackers to go from opportunistic to targeted NTLM Relay via WebClient. If you are curious about the answer and don’t care for the details discovered along the way you can go straight to the conclusion.
Background (i.e., Why do we care about WebClient?)
The WebClient service lets users expand NTLM relay opportunities with further tradecraft. If you are unaware, NTLM relay is a machine-in-the-middle (MitM) type of attack that takes advantage of the NTLM authentication protocol. Historically, relay attacks targeted the SMB protocol and typically relied on poisoning traffic or files. Over time, that has shifted to WebClient being a likely bigger target for relaying due to common protections like SMB signing. In addition, with protections like Message Integrity Code (MIC), relaying from SMB to something like LDAP is not possible (unless unpatched). This is where WebClient is great, because it affords us the ability to relay easily cross protocol to something like LDAP(S). That’s because it’s easy to strip out just the authentication portion of NTLM authentication over HTTP.
There are protections available to prevent relaying from HTTP to LDAP or HTTP to HTTP, like signing, channel binding, or EPA. However, those are not applied or configured nearly as common as something like SMB signing. So, again, this makes the WebClient a great service to target when performing NTLM relays. However, something to keep in mind is WebClient is not running by default on Windows workstation versions and is not installed by default on Windows server.
Locally, on a workstation (and barring a few requirements), there is an almost guaranteed privilege escalation when relaying. These requirements would include
- NTLM protocol in use (obviously)
- WebClient running (we’ll cover this later)
- Absent LDAP protections (signing, channel binding)
- Alternatively no EPA on HTTP if targeting ADCS
- Or packet privacy if relaying ICertPassage Remote Protocol
- Ability to perform something like Shadow Credentials (write access to msDS-KeyCredentialLink)
- Alternatively Resource Based Constrained Delegation (write access to msds-AllowedToActOnBehalfOfOtherIdentity and control of an account with a SPN).
It does not always exist, but there is a pretty high chance at least one of (if not all) these opportunities will exist in a given environment. This is a high-level overview of some things that can be done with WebClient and a very short primer for NTLM relay. More information on NTLM relay for those interested can be found here: where Elad does a great job in covering everything in great depth.
The WebClient Service
Years ago, Lee Chagolla-Christensen (@tifkin_) discovered that you can check for the \pipe\DAV RPC SERVICE named pipe on a target to determine if WebClient is running remotely. With this information, we can remotely triage hosts for the presence of that named pipe to determine if NTLM relay with WebClient is possible. However, our capability is limited because we can only relay the target host when WebClient is already running, which makes this an opportunistic attack rather than a targeted attack. In terms of targeting a specific objective, sometimes that opportunistic relay doesn’t really get what you need. SMB can be relayed, but hardened configurations are more common and as a low privileged user, it may not be possible to control port 445.
Previously, I stated there was an almost guaranteed privilege escalation on a Windows host. One of the things I did not mention was what if the WebClient service is not running on the local host? Luckily, if the WebClient service is not running and you are not in a high integrity context, you can start the service. Years ago, James Forshaw discovered a programmatic way to start the WebClient service through an ETW Event.
What he found is if you can create and register an event through EtwEventRegister (or EventRegister) and EtwEventWrite (or EventWrite) with the event GUID of 22b6d684-fa63-4578-87c9-effcbe6643c7, it will trigger an event that starts the WebClient service. This is done through a service trigger event on Windows that starts the service when an event is created (triggered). This allows a non-privileged user to start the service or any other service with an event trigger.
A relay locally with WebClient is possible as a non-administrative user due to this ETW event, and privilege escalation is possible with any of the other previously mentioned requirements present. Since this ETW discovery, more techniques have been found that allow low-privileged users to start WebClient.
- Explorer.exe – If you are using Windows graphically (not through a C2), you can open explorer.exe and set the file path to a UNC/network path and browse to a dav share. Doing this will start WebClient.
- Net.exe – In a command prompt (or C2), you can use the net.exe command to map a dav server to a drive. Something like net.exe use s: http://dav-server will map the s drive to the dav server location and start the service.
- searchConnector.ms – An XML file that creates a search connector (used to connect users to web resources). If a user browses to a file path (or network share) with a searchConnector.ms file, it will start the WebClient service. This is the closest known attack to remotely starting WebClient. However, there are no guarantees that the target host you want to browse to the location will. While this is useful in situations, it doesn’t help us to get targeted relaying.
This highlights four different ways that we can use to start WebClient as a non-administrative user. I wanted to find out what these other options did to actually start the service and see if I could identify any unknown (and interesting) methods of starting the service. The first thing I looked into was starting WebClient with net.exe.
Running the net.exe command and looking through what happens with API Monitor we see some interesting things take place and more importantly some interesting DLLs get loaded. When the command is executed we see DLLs like MPR.dll, p9np.dll, drprov.dll, ntlanman.dll, davclnt.dll, davhlpr.dll, etc. A good starting point to dig deeper is MPR.dll. MPR is the Multiple Provider Router that is used for loading network providers. When MPR is called there are network provider DLLs that are dynamically loaded. This takes place through two separate init functions – MprLevel1Init and MprLevel2Init respectively. Level1Init will look in the registry at HKLM\SYSTEM\CurrentControlSet\Control\NetworkProvider\HwOrder:ProviderOrder for the network providers.
Looking at the ProviderOrder key, you see the previously seen names referenced there. These values will be used to find the actual dynamic-link libraries (DLLs) themselves. MprLevel1Init will look at HKLM\SYSTEM\CurrentControlSet\Services\<Provider Name>\NetworkProvider:ProviderPath and load that DLL. Naturally, we’re most curious about the WebClient provider and look at the registry path HKLM\SYSTEM\CurrentControlSet\Services\WebClient\NetworkProvider and see the davclnt.dll referenced there. The same applies to all the other network providers, Plan9 (p9np.dll), Remote Desktop (drprov.dll), Lanman (ntlanman.dll), and WebClient (davclnt.dll).
The second init function MprLevel2Init actually loads each DLL through LoadLibraryEx then gets all specific exported function addresses from the DLLs (we will talk more about this later) from the function GetProviderCapabilities. Each network provider has exported functions prepended with NP (presumably for network provider).
MPR will take the providers found in the registry and in the order specified will attempt to interact with a provided resource. If the provider does not work, it fails and moves on to the next and does the same thing. These exported functions are called to interact with the specified resource and are the same across each provider (just perform different actions to use the resource).
So, we’ve discovered a small piece of critical information: the network provider used during net.exe starting the WebClient service is davclnt.dll. Looking for DLLs in System32 that import davclnt based on their import table would potentially give some interesting information about what binaries leverage this provider. This information could uncover more avenues to pursue.
A search of this information shows that no System32 binaries import davclnt (statically, that is). We know that MPR imports it dynamically, so it makes sense that we wouldn’t see it when triaging every binary’s import table. What came to mind was a quick and dirty way of attempting to find any binary that may dynamically import it, which was to check the strings of the registry keys that referenced WebClient’s davclnt. This being the only registry location I saw it referenced, I felt it was a safe bet that the name davclnt and the registry location would find any further results for dynamic imports.
Checking that only shows what we pretty much already knew: MPR imports davclnt.dll. Based on this information, it does not look like any other DLLs load davclnt.dll. So, narrowing down our search, we target davclnt to where net.exe eventually goes when starting the service. Looking through davclnt.dll, we get a clear picture of how the WebClient starts.
Disassembling the code, we discover the function TriggerStartWebClientService that, when called, will call EtwEventRegister and EtwEventWrite. This was the same method James Forshaw discovered to programmatically start the service as a low privileged user.
Digging a bit further, we discover that all the previously mentioned ways of starting the service all boil down to davclnt.dll, which calls EtwEventRegister and EtwEventWrite. So, net.exe, explorer.exe, and searchConnector.ms all just do the same thing. So there are essentially two ways of starting the service: through service control manager (high integrity) or through ETW events (medium integrity). We can validate this by looking at the callstack of explorer.exe when browsing to a WebDav share.
As a side note: there is actually another DLL that is capable of starting the WebClient service. The davhlpr.dll DLL has a similarly named function (i.e., TriggerStartWebclientServiceIfNotRunning) that calls SendWebClientTriggerEvent which, again, calls the same ETW functions. This DLL, though, is used for the actual service itself rather than in other places like network providers. This function seems like it’s there to restart the service if it stops for an unexpected reason.
Investigating this further, it seems like the keys to our goal of starting WebClient remotely relies on MPR.dll and where it is used. This effectively means that the keys to the kingdom in this instance involves looking into how Windows uses MPR.
Let’s go back to the previous triage of looking into what binaries import MPR and seeing if there is anything that stands out. Our criteria is now to look for binaries that import MPR and have an RPC interface available to us to interact with (hopefully as a low privileged user). Again, this will only show us binaries that statically import MPR and not anything dynamic, but after a little bit of digging, I don’t believe anything dynamically imports MPR. Doing this, we uncover something interesting.
Encrypted File System
This triage shows us that efslsaext.dll imports MPR and has an RPC interface (i.e., C681D488-D850-11D0-8C52-00C04FD90F7E) available.
This shows us that the EFS LSA extension is potentially in play here. If you aren’t familiar with EFS, it gained its popularity with the PetitPotam coercion attack. PetitPotam was the second (to my knowledge) discovered coercion attack for NTLM authentication after MS-RPRN (a.k.a., SpoolSample). PetitPotam is still widely used, albeit with some updates since its original conception. But originally some of the RPC interfaces allowed for anonymous authentication, meaning that if a host was reachable NTLM authentication coercion was abusable without valid user credentials. Due to the prevalence of PetitPotam, Microsoft changed EFS to prevent anonymous access but low privileged users could still interact with the interface and call particular methods. Most recently, packet privacy was added to some functions but that did not really affect the coercion. In newer versions, some of the RPC methods have been deprecated and will immediately return “access denied” right after binding.
But, since efslsaext.dll looks interesting to us, lets take a deeper look at what it can do in terms of our objective. Since our target is davclnt!TriggerStartWebClientService, we need to trace back to see if there are any functions that can reach it. So, looking back at davclnt, we want to find what functions have a path to TriggerStartWebClientService. Doing that, we discover that NPAddConnection3 and NPGetResourceInformation call TriggerStartWebClientService. Going back to MPR, we want to find if either NPAddConnection3 or NPGetResourceInformation are called.
Doing a bit of function call analysis, we can see that if MPR!WNetGetResourceInformationW is called, we have a path to davclnt!NPGetResourceInformation that calls davclnt!TriggerStartWebClientService. Since we know that efslsaext.dll imports MPR, the next step would be to look at what functions import from MPR.
Recalling from the previous picture, we know that MPR!WNetGetResourceInformaionW will call davclnt!TriggerStartWebClientService, which means we potentially have a path from efslsaext.dll. This means we can potentially enable WebClient as a low privileged user on a remote host through an RPC call, since the function calls line up.
Going back to analysis of efslsaext.dll, our next step would be to see which functions can reach MPR!WNetGetResourceInformationW. Digging into this, we find that the following functions can:
- EfsRpcDuplicateEncryptionInfoFile_Downlevel
- EfsRpcEncryptFileSrv_Downlevel
- EfsRpcDecryptFileSrv_Downlevel
- EfsRpcAddUsersToFileEx_Downlevel
- EfsRpcRemoveUsersFromFile_Downlevel
- EfsRpcAddUsersToFile_Downlevel
- EfsRpcQueryRecoveryAgents_Downlevel
- EfsRpcQueryUsersOnFile_Downlevel
- EfsRpcFileKeyInfo_Downlevel
This means all of these RPC functions CAN reach the function that enables the WebClient service. Now, it is not as simple as just calling the RPC function and waiting for WebClient to start. There are some functions we need to make it through in order to reach our destination:
But we see with this screenshot above what functions are actually called in order for us to each MPR!WNetGetResourceInformationW and ultimately davclnt!TriggerStartWebClientService. This was all discovered manually, but I did write something that is capable of finding as well (excuse the mangled names).
Since we have multiple functions, we have to go through to get to our target function. This means we have some requirements to fulfill. If you have ever used PetitPotam in the past, you’ll know that arguments passed are file paths. In our use case here, the file paths we pass for the RPC calls will need to be reachable by the target host due to some checks EFS performs. One of the first functions called is NetShareGetInfo, which queries the passed network share and looks for a SHARE_INFO_2 struct to return. One of the values from this struct is Path, which the target share server returns.
Since we control the SMB server, we can make it return anything we want, which can be helpful. If we make it return something like C:\Windows\System32 in the response SHARE_INFO_2 struct, the host will think it’s a local file (which matters on subsequent steps).
If we pass an argument to the RPC call (like \\share\efslsaext.dll) and our SMB server returns C:\Windows\System32, subsequent functions CreateFileW and NtQueryFileInformation will actually check C:\Windows\System32\efslsaext.dll.
Once that executes, the path returned from NetShareGetInfo and the filename passed as the argument for the RPC are appended and that value is checked against its existence with CreateFileW. The handle returned from CreateFileW is passed to NtQueryVolumeFileInformation. These steps only exist in newer versions of the efs lsa extension and are not in older versions. However, once we get to the function efslsaext!EfsGetFullName, we have more checks that need to be fulfilled. GetDriveTypeW is called and must return type 4 for DRIVE-REMOTE, which will happen due to it being a share and GetFileAttributesW is called to check file information.
What does this mean? Our requirements are to have an SMB server and a WebDav server up and running with the files we are passing so the target host can reach them. The argument for something like EfsRpcDecryptFileSrv would be a UNC path like \\share\path\file.txt, where the share is a controlled host with file.txt on the share. Now the tricky part: we must only accept those initial SMB connections and then fail further SMB connections. In order for us to make it to the code path, we have to pass the checks like querying the files. However, if you recall from earlier, I mentioned the network providers are iterated in order against resources until one works. The provider order being Plan9, RDP, SMB, and WebClient, we have to fail all of them except WebClient after the initial checks are done. Plan9 and RDP will fail no matter what because the share resource will not work with those network providers, but SMB will work if we don’t explicitly fail. This is why we need an SMB server initially to accept those queries and then return an access denied or resource unavailable and then accept WebDav connections. Since we know the amount of SMB connections to expect, we can force our SMB server to accept that amount of connections and then stop accepting anything and have our WebDav server start accepting the incoming connections.
Here is our flow of operations: standup SMB server, accept NetShareGetInfo and GetDriveTypeW (and then force fail after), and have WebDav listen to accept the connection. You might be thinking, “Couldn’t we pass a Dav server path as the UNC path like \\davserver@8080\file.txt and get around the SMB requirement?” Unfortunately, no. That wouldn’t work because the calls like NetShareGetInfo need the WebClient service already running to know how to interact with them. It does not have any logic to start the service, so could see traffic hit a Dav server if you have it stood up. However, since it’s WebDav it would not continue because the call would fail. We run into a chicken and egg problem because we would need the service we are wanting to start to be already running. So the SMB requirement is still necessary.
Demo
Demo 1 (admin user)
When we run our script, we check the WebClient service on the remote target and see the service is running. We accomplished our objective of starting the WebClient service through an RPC call. Before we get too excited, we need to check from different privilege levels to make sure it works as expected. Since we can bind to this interface as a low privileged user and hit the TriggerStartWebClientService function, I have high hopes.
Demo 2 (standard user)
We check the service again and…WebClient is not started. It seems like there is some issue between the administrative user and the standard user. We need to open up windbg and validate that all of the function calls we expected to happen actually did. Stepping through the code, we see that both users do make it to and run davclnt!TriggerStartWebClientService. However, the non-admin user throws five exceptions and eventually returns.
We see that it attempts to trigger the service and checks each time if the service is actually running. If not, it tries again a few times before continuing. So this code seems to have the expectation that, if this function was called, the service should be running. Our next course of action is to trace the pieces between that function call and the starting of the service. This means we need to look at what happens when EtwEventWrite is called.
Digging Deeper
This process initially started by going down the path from ntdll!EtwEventWrite and into all of the kernel ETW function calls to see where the breakdown occurred.
Generally the usermode ETW function gets its entry into kernel functions from nt!NtTraceControl and flows through a number of kernel ETW functions. This did not really seem to be the path to follow because I wasn’t after real ETW events; just the triggering of the service. This path ultimately did not uncover any errors or have anything that stood out as root cause analysis for the non-admin user not starting the service. Since we know that we ultimately get to services.exe to start the service, the best course of action was to debug that process and see what happened from EtwEventWrite and services.exe. When we break at services.exe, we uncover some interesting things.
EventAggregation is used before getting to service!ScExtpTriggerArrived and ultimately services!ScExtpLaunchService. EventAggregation is the user mode DLL used for interacting with Windows Notification Facility (WNF) messages. Covering WNF in depth is outside the scope of this blog; however, if you’ve never heard of it, it’s an undocumented pub/sub notification mechanism Windows uses. Alex Ionescu and Gabrielle Viala did an awesome BlackHat talk in 2018 about it that I would recommend if you want to know more.
So once the ETW event is created, our ETW consumer picks up the event and creates a WNF message that services.exe is subscribed to. This application is subscribed to the WNF state name and, once it receives the message, starts the service. Continuing to trace this, we discover the ETW event consumer for this event is Desktop Activity Broker (i.e., DAB.dll). If we look for processes that load DAB.dll, we’ll discover an svchost process with the arguments of svchost.exe -k DomLaunch -p. Dab will have a callback function that is ready to accept the ETW event and call DAB!DabpActOnETWTrigger which will check if any action should be taken. DAB!DapbTakeAction creates the WNF message and NTDLL!NtUpdateWnfStateData eventually calls NT!ExpWnfWriteStateData. As mentioned before, services.exe is subscribed to this WNF state name so when the state data is written EventAggregation!WnfEventCallback is listening for the message. As shown in the screenshot above, there are some checks EventAggregation handles; however, if the message looks good, services!ScExtpTriggerArrived calls services!ScExtpLaunchService to start the service.
Still tracing back and looking at DAB.dll, we find out that the ETW consumer is Unified Background Process Manager (UBPM). One of the things this ETW consumer is used for is starting services. If we look at the trace providers in Performance Monitor (PerfMon), we can see multiple service start up providers listed.
This means we have the full picture of what happens when starting the WebClient service as a low-privileged user. From start to finish, we have a program like net.exe (or one of the other mentioned previously) attempt to interact with a resource. MPR loads network providers and each are tested against the passed resource. If WebClient is the resource, davclnt is called and checks if the WebClient service is running; if not, it calls TriggerStartWebClient service to create an ETW event. The ETW event is picked up by the consumer UBPM and does some checks to validate the event and creates a WNF message. Services.exe is subscribed to that message by its state name and, once it is created, EventAggregation validates the message and passes it to the function to start the service.
Service Start Root Cause
Now that we have the entire picture, we can identify why the service did not start as a non-administrative user. The root cause of this comes down to the permissions of the UBPM consumer.
The security descriptor of this consumer states that only UWPs, SYSTEM, Administrators and LOCAL can relay messages to it. This would explain why, locally, we can start the service as a non-administrative user because our token has the S-1-2-0 (LOCAL) SID applied to it.
When making an RPC call, one of the steps is to call RpcImpersonateClient, which will give our token a network SID. Since the ETW consumer is not hit, no WNF message is created and the service does not start. To validate this was the root cause, I updated the SDDL for this object to allow a low-privileged user to relay an event to it. Doing so started the service and validated the conclusion. The security descriptor can be updated with the follow code:
$user = New-Object System.Security.Principal.NTAccount("domain\user")
$userSid = $user.Translate([System.Security.Principal.SecurityIdentifier])
$sessionSDs = Get-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\WMI\Security
$sddl = ([wmiclass]"Win32_SecurityDescriptorHelper").BinarySDToSDDL($sessionSDs.'c09355a3-96af-4e8f-8d32-a2658dc2d5be').SDDL
$newSddl = $sddl + "(A;;0x200;;;$($userSid.Value))"
$sddl_updated = ([wmiclass]"Win32_SecurityDescriptorHelper").SDDLToBinarySD($newSddl)
Set-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\WMI\Security -Name c09355a3-96af-4e8f-8d32-a2658dc2d5be -Value $sddl_updated.BinarySD -Type Binary
Demo 3 (standard user with an updated security descriptor)
Windows 11
Windows 10 becomes end of life (EOL) later this year (October 2025) so, naturally, we need to check if there are any opportunities in Windows 11. The EFS LSA extension is considered legacy and deprecated in Windows 11, meaning it is no longer loaded into the lsass.exe process. The EFS usage now comes from its own service, aptly named EFS. Technically, this service has existed in Windows 10 as well but is not as widely used due to the LSA extension. Now that it is the go-to service, I decided to dig into it a bit.
The EFS service splits all of the EFS usage between multiple DLLs: Efssvc.dll, Efscore.dll, Efsext.dll, Feclient.dll, and enterprise data protection (EDP) DLLs. While efslsaext.dll had all of its functionality within its DLL (much like the WebClient service), the EFS service is not running by default on Windows 10/11. In order for us to interact with the RPC interfaces, we’d need it to be running. Fortunately for us, there are custom event triggers on the RPC bind for the service which causes the service to start. The RPC methods you want to interact with determine the RPC interface you would bind to to start the service. If you have attempted to use PetitPotam against a Windows 11 host and the EFS service is not running, it would fail due to the efslsaext.dll no longer loading and this (EFS) service not running. These would not expose any RPC interfaces available to you.
As I mentioned, the interface we want to bind to would be determined by the methods we want to interact with.
So if we wanted to leverage EfsRpcOpenFileRaw, we would need to bind to DF1941C5-FE89-4E79-BF10-463657ACF44D first to start the service. Alternatively, if you wanted to leverage the EfsKRpcEstablishRpcConnection method, you would need to bind to the 04EEB297-CBF4-466B-8A2A-BFD6A2F10BBA interface first.
Looking through the code of the different EFS DLLs, we see a lot of interesting things. One of the things I noticed with EFS service is that there is clear separation between server and client code in terms of EFS usability. This means code from the actual service DLL (i.e., efssvc.dll) does not appear to directly reach code that the EFS client side uses. This is something that seems to be blurred in the LSA extension and why it was possible to reach code that started the WebClient service. Of the EFS DLLs, only Feclient.dll can reach WebClient start code.
There are routes from Efscore.dll and Efsext.dll to Feclient.dll to start WebClient, but none that I have seen to date come from Efssvc.dll RPC methods. There do not seem to be paths from any EFS RPC methods that trigger the start of WebClient on Windows 11. But, the point is likely moot due to the UBPM consumer’s security descriptor not being any different on Windows 11 than Windows 10. Even if there was a path from RPC, we likely still would not be able to start the service. There are some interesting functions that do exist in the EFS DLLs that may warrant extra digging, like efscore!EfspEnablePrivilegesInSystemContext, but I have not looked too deeply into what it does or if that is ever called from an RPC function. Currently, there does not seem to be a valid path to our destination in the EFS service’s RPC functions.
Windows Notification Facility
During the process, we figured out that WNF is used when starting the service. Could we potentially extract away the ETW event step and start the service through a direct WNF call? The first thing we need to find out is the WNF state name. Some state names have human readable names (i.e., WNF_EFS_SERVICE_START) that you can kind of derive what the purpose is or what it does, but not all of them do. Some only have 64 bit numbers as their state names. There are different types of state names lifetimes that determine how they are used and if it likely will have a human readable name. These are WellKnownStateName, PermanentStateName, PersistentStateName, and TemporaryStateName. To identify the state name for the WebClient startup, I set a breakpoint during the Desktop Activity Broker’s process of creating the WNF message at NTDLL!RtlTestAndPublishWnfStateData.
With this state name, we can get some information about it. As stated in the BlackHat talk (or here), we can see the WNF state names are XOR’ed with a key of 0x41C64E6DA3BC0074 that, when XOR’ed, tell us about the name. By doing that with our discovered state name, we can see the Lifetime and Scope of the state name.
The state name is a temporary state name, which means that it is created and registered when the services.exe process starts, so it makes sense it wouldn’t necessarily have a human readable name. The unique value is something that is just iterated every time the process starts and is not guessable. Because of that, it makes it a little difficult to determine what the state name will be that is used for starting the WebClient service. It was a bit difficult to find more information about this state name and I could not find a way to really make the determination of what the state name is without debugging the process. Regardless, once we have the state name, we can attempt to create the WNF message and start the service.
Doing so, we see that we get an ACCESS_DENIED message. Since this is a temporary state name, we don’t have the luxury of easily finding the security descriptor in the registry where you’d see either Permanent or Persistent state names. Apparently, using NtQueryWnfStateData will give the security descriptor for temporary names, but I didn’t have much luck getting it. I didn’t look too deeply into it, since attempting to start the service from a WNF message as an administrator didn’t work. It didn’t feel like it really warranted further investigation into the security descriptor.
Just as a validation step, I ran it in a SYSTEM context and it created the message and started the service. While we are able to start the service through a WNF message directly, this is not really something that is useful operationally due to discovering the state name does not seem easily doable and it requires SYSTEM privileges to do so. So in the end, the WNF route was mildly interesting but not really useful.
Tooling
The tooling that was shown to make the RPC calls and standup the SMB and WebDav servers has been released and can be found here https://github.com/0xthirteen/rpc2wc. Additionally, that repo also contains the code to start the WNF service through a WNF message. Due to what we discovered, from the RPC call, the service will not start remotely unless you are an admin. In my mind, there really isn’t a use case for this code since you need to be an administrator for it to work and if you’re already an admin, you can just use Service Control Manager (SCM) to start the service. Same thing goes for the WNF message to service trigger. Identification for the WNF state name is difficult and you need to be SYSTEM to write to the state name. Regardless, the code is out there for anyone interested in it.
Conclusion
We walked through the process of what happens when triggering WebClient as a medium integrity user through an ETW event and, along the way, discovered some interesting things. Going back to our original question: Can you enable the WebClient service remotely as a low-privileged user? Almost, but no.
Through an RPC call as a low-privileged user, we can reach a function that will start the service; however, the root cause for not being successful was due to the security descriptor of the ETW consumer UBPM being locked down to only UWPs, administrators, system or local users. Does this mean it’s impossible? In my opinion no, but extremely unlikely.
In order for this to be possible, you’d need a way to interact with a local resource that has a local SID in its token as a low-privileged user. Alternatively, finding some RPC or DCOM method that gets a local token somehow (this seems unlikely, but I don’t know if that’s possible or not. Stranger things have happened). One final thought that came to mind would be tracking down UWPs that would expose any remote services, but that’s an entire can of worms and I have not spent a ton of time looking into that. However, that route does seem like an uphill battle. There are other DLLs that have RPC interfaces exposed that import MPR.dll, but I have not looked into them very closely but doubt there is really much there. Here they are, for those interested:
- edgehtml.dll – 9ccb59aa-1358-4169-aebb-ed83357d6304
- lsasrv.dll – 41baa680-50ce-4967-a8fa-0596343a7ccf d25576e4-00d2-43f7-98f9-b4c0724158f9 c0d930f0-b787-4124-99bc-21f0ecb642ce 3919286a-b10c-11d0-9ba8-00c04fd92ef5 12345778-1234-abcd-ef00-0123456789ab afc07e2e-311c-4435-808c-c483ffeec7c9 ace1c026-8b3f-4711-8918-f345d17f5bff
- mpnotify.exe – 3ca78105-a3a3-4a68-b458-1a606bab8fd6
- profsvc.dll -326731e3-c1c0-4a69-ae20-7d9044a4ea5c
- rdpclip.exe – de79fc6c-dc6f-43c7-a48e-63bbc8d4009d 10bd2718-13bd-4b84-8e7d-8b5c83770a86
- rpcss.dll – 412f241e-c12a-11ce-abff-0020af6e7a17 00000136-0000-0000-c000-000000000046 c6f3ee72-ce7e-11d1-b71e-00c04fc3111a b9e79e60-3d52-11ce-aaa1-00006901293f 99fcfec4-5260-101b-bbcb-00aa0021347a 4d9f4ab8-7d1c-11cf-861e-0020af6e7c57 000001a0-0000-0000-c000-000000000046 e60c73e6-88f9-11cf-9af1-0020af6e72f4 4943e10d-2d9c-4f31-a31b-212c9f1dca8b cb40a179-20e1-43f0-97fb-3c5c6ff37ec3 9b8699ae-0e44-47b1-8e7f-86a461d7ecdc
- winlogon.exe – 76f226c3-ec14-4325-8a99-6a46348418af 12e65dd8-887f-41ef-91bf-8d816c42c2e7 76f226c3-ec14-4325-8a99-6a46348418ae
Also, special thanks to THE Jonny Johnson for his ETW knowledge and helping find and validate the security descriptor for the UBPM ETW consumer.