The Local A-DEM I Mean, Admin Paradox

Mark PlatteMark Platte
15 min read

*Since vb.net does not exist as syntax here i used basic not optimal but best i can do.

When elevation just doesn't cut it.

You know the deal:
You get DEM elevation, launch something as admin... and still get blocked.

Because SYSTEM ≠ user, and elevation ≠ local admin.

Apps running under SYSTEM, installers, services they don’t care about your nice little “run elevated” badge from DEM.

But here’s the mind blowing thing and the paradox:

Quote

You can use that limited DEM elevation to give yourself real local admin rights.

Let that sink in.

The elevation might not let you install the thing, but it will let you add your domain account to the local administrators group after which, yes, you can install the thing. We still have some legacy which unfortunately cannot be solved in an other way.

So I built a small VB.NET utility in 2020 to do exactly that (and it still works in W11 in 2025):

  • Only runs on whitelisted domains

  • Grabs the real logged-in user

  • Adds that user to the local admin group

  • Cleans up after itself

  • And politely exits stage left

Because when elevation gives you lemons, you might as well use them to rewrite your access level.

For example :

1. Registering COM Components or DLLs

Why DEM elevation fails:
Registering with regsvr32 modifies system-wide settings under HKLM\Software\Classes and may copy files to C:\Windows\System32 both actions require full local administrator rights, not just an elevated user token.

Example:

regsvr32 somecontrol.dll

Result:
Fails silently or throws Access Denied unless the user is a member of the local Administrators group with a full token.

Real-world impact:
Some legacy or enterprise applications attempt on-the-fly COM registration during runtime, especially if:

  • They're repairing broken file associations,

  • They're verifying component registration on launch,

  • Or they self-register plugins dynamically (e.g., older .NET, VB6, or Delphi apps).

Symptoms:

  • App crashes at startup

  • Features silently disabled

  • "ActiveX component can't create object" errors

  • Missing right-click menu entries or Office add-ins

Why this breaks with DEM elevation:
Because even though the process is elevated, the HKLM hive and System32 folder reject write access from anything other than a full local admin token.

The Token Fuzz: Why DEM Elevation ≠ Full Admin

When you elevate a process (like with DEM), you're not becoming an admin you're just getting a filtered copy of the admin token. Windows politely lets you think you have power, but behind the scenes, you're still fenced in.

User Tokens: Standard vs. Elevated

Every Windows process runs with an access token it defines what you're allowed to do.

  • Standard Token → what you get by default, even if you're in the Administrators group

  • Elevated Token → what you get when you click "Run as Administrator" or respond to a UAC prompt

Even if you're technically a local admin, UAC ensures that you don't run with full privileges unless you explicitly elevate.

The DEM Limitation

DEM (and most user environment tools) "elevate" by launching the process in an elevated user context but:

  • It's not SYSTEM

  • It doesn’t have all Se privileges*

  • It gives you an elevated user token, not a full administrative one

Result?
You're still blocked from doing things that require true machine-level control.

SYSTEM Context ≠ Elevated User

Here’s the kicker and the fuzz:

  • Tools like regsvr32, service installers, or driver writers don’t care if you’re "elevated"

  • They check your token:
    “Are you SYSTEM?”
    “Do you have full local admin privileges?”

If the answer is no access denied.
Even though DEM says you’re elevated, Windows knows better.

The Paradox in Action

Let’s say you launch an installer from DEM:

  • It tries to write to HKLM\Software

  • It needs to copy files to C:\Program Files

  • It might register a COM component or start a service

You’re elevated but not enough.
Windows checks → sees a filtered token → denies the write.

But here’s the paradox:

Quote

That same fuzzy token can still be used to add yourself to the local Administrators group granting you full rights Run as Administrator.

LCLGRP.Invoke("Add", New Object() {DOMUSR.Path.ToString})

More about this line of code later but first some drawbacks :

If you use Run as Administrator after your user was added to the local Administrators group, you don’t need to log off and back on to gain elevated access in that new process. But here’s the nuance:

What Works Immediately

Once you're added to the local Administrators group:

  • Any process launched with Run as Administrator will get a full admin token.

  • You can now register COM components, install services, write to HKLM, etc.

  • You don’t need to log off to use regsvr32, installers, or system tools if you launch them elevated after being added.

What Still Requires a Logoff/Login (so no luck in non persistent use cases)

  • Your current Explorer shell (taskbar, file explorer, etc.) is still running under your old token.

  • Any already-running tools (Command Prompt, PowerShell, etc.) keep using the non-admin token.

  • You may not see permission changes reflected in Explorer or apps started before elevation unless you:

    • Restart them with Run as Admin, or

    • Log off and back on (or restart Explorer cleanly).

So DEM can’t help you install the app,
…but it can help you become the kind of admin who can.

Before doing the deep dive in a very short code snippet , yep it really isn't much let's look at the requirements:

Requirements

Before using the code below, make sure your environment meets the following:

Environment Prerequisites

  • Windows client or server with .NET Framework 4.x

  • Domain-joined machine

  • Domain user running the app (must be able to see the domain via WinNT://)

  • DEM or UEM tool capable of launching an executable with "elevated rights"

Code Dependencies

  • System.Management.dll (for WMI call to get current user)

  • System.DirectoryServices.dll (for modifying local groups via WinNT provider)

These are standard .NET libraries and don’t require additional NuGet packages.

Execution Rights

  • Run via a tool like DEM with elevation configured

  • The executing user must have permission to add domain users to local groups (in practice: this script adds the current domain user, so no impersonation or credentials are needed)

**To program it yourself you need the following:

1. Project Type**

  • Windows Forms App (.NET Framework)

  • Target framework: .NET Framework 4.7.2 or higher (recommended for modern Windows compatibility)

2. Required Imports

These namespaces must be imported at the top of your form:

Imports System.Management 
Imports System.DirectoryServices

3. Project References

Ensure your project references the following assemblies (they are included by default in WinForms projects targeting .NET Framework):

ReferencePurpose
System.Management.dllUsed for WMI queries to get the logged-in user
System.DirectoryServices.dllUsed to access and manipulate local/domain user and group objects

Quote

In Visual Studio:
Right-click ReferencesAdd ReferenceAssemblies → Check:

  • System.Management

  • System.DirectoryServices

4. UI Components (Windows Form)

Minimal UI required (since it closes itself). You can keep:

  • One default form: Form1

  • FormBorderStyle = None (optional, makes it fully silent)

  • ShowInTaskbar = False (optional, hides it)

  • StartPosition = CenterScreen (or manual)

You don’t need any controls on the form.

5. Application Settings

In Project Properties:

  • Set Output type to: Windows Application

  • Enable "Enable application framework"

  • Optionally check "Make single instance application"

The code explained followed by the full code :

Imports System.Management
Imports System.DirectoryServices

Purpose:

  • System.Management: Used to query WMI for the currently logged-on user.

  • System.DirectoryServices: Used to access and modify local and domain group membership.

Const AllowedDomain As String = "YOURDOMAIN"  ' NetBIOS domain name
Const WinNTDomainPath As String = "WinNT://YOURDOMAIN"

Purpose:

This defines your domain name in one place to avoid hardcoding it twice.

  • AllowedDomain: The domain this tool is allowed to run in (from %USERDOMAIN%).

  • WinNTDomainPath: Used by DirectoryEntry to find users in the domain.

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

Executes when the form loads (application starts)

In this case three things are done :

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        If Environment.UserDomainName.ToUpper() = AllowedDomain Then
            Me.Visible = False
            adduserasadmin()

            Dim strAppdata As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
            Try
                System.IO.File.Delete(strAppdata & "\Microsoft\Windows\Start Menu\Programs\Startup\Add addlocaladmin shortcut.lnk")
            Catch
                ' Ignore file delete errors
            End Try

            Me.Close()
        Else
            MsgBox("This program can only run within the whitelisted domain.", vbInformation, "I shall not run :)")
            Me.Close()
        End If
    End Sub
  1. check if the domain of the user is an allowed domain to execute more of the code

  2. run the adduserasadmin() routine

  3. Delete the shortcut set by DEM in startup to run after login : System.IO.File.Delete(strAppdata & "\Microsoft\Windows\Start Menu\Programs\Startup\Add addlocaladmin shortcut.lnk") the program assumes the startup folder of the logged on user : "shell:starup" location when run with win+r

adduserasadmin() The Core Action Block

This is the function that does the actual work: adding the current domain user to the local "Administrators" group on the machine.

Here’s what each part does:

Try

Wraps the logic in a Try/Catch block to avoid crashing the app on permission or lookup errors.

Dim UserName As String = get_user()

Calls the get_user() function to retrieve the username of the actual user logged into the machine (not the one running the process if elevated via DEM).

Example return: "j.smith"

Dim PCNAME As String = Environment.MachineName

Gets the local computer name, needed to construct the WinNT:// path for the local machine context.

Example: "PC-0237"

Dim localusrname As String = UserName

Assigns the username to a new variable.

Dim LCL As New DirectoryEntry("WinNT://" & PCNAME & ",computer")

Creates a DirectoryEntry object targeting the local computer. This object allows us to query and modify local users and groups.

Example:
"WinNT://PC-0237,computer"

Dim DOM As New DirectoryEntry(WinNTDomainPath)

Creates a DirectoryEntry for your domain (e.g., "WinNT://NKI"), so you can search for domain users.

WinNTDomainPath is a constant you define earlier in the code.

Dim DOMUSR As DirectoryEntry = DOM.Children.Find(localusrname, "user")

Finds the user object in the domain by name.
If "j.smith" exists in the domain and is a valid user, this returns the full domain user entry.

Fails silently if:

  • The user doesn’t exist in the domain.

  • The domain is unreachable.

Dim LCLGRP As DirectoryEntry = LCL.Children.Find("Administrators", "group")

Finds the local Administrators group on the computer.
This is where we want to add the domain user.

LCLGRP.Invoke("Add", New Object() {DOMUSR.Path.ToString})

Calls the Add method on the local Administrators group and passes the full AD path of the domain user.

This is equivalent to running:

net localgroup administrators "DOMAIN\j.smith" /add

But done programmatically using the legacy WinNT:// provider.

Catch ex As Exception
    ' Silently fail
End Try

Catches any exceptions without throwing an error.
This avoids breaking the form or showing a stack trace if:

  • The user is already in the group

  • The domain is unavailable

  • The app lacks permission to modify group memberships

get_user() Getting the Real Logged-On User

In some cases (especially with DEM or UEM tools), using Environment.UserName or WindowsIdentity.GetCurrent() won’t give you the actual desktop user it may return SYSTEM, LOCAL SERVICE, or the launching context.

This function solves that by asking WMI who is actually logged on at the console.

Code Explanation

Public Function get_user() As String

Declares a public function that returns the currently logged-on username (just the short name no domain prefix).

Dim username As String = "test"

Initializes the username variable with a dummy value. This gets overwritten in the loop, but avoids uninitialized errors.

Dim searcher As New ManagementObjectSearcher("SELECT UserName FROM Win32_ComputerSystem")

Creates a WMI query to retrieve the UserName property from the Win32_ComputerSystem class which tells you who's logged on to the console session.

This is the most reliable way to get the real desktop user.

Dim collection As ManagementObjectCollection = searcher.Get()

Executes the query and returns a collection of results. Usually only one object is returned, but it's still a collection.

For Each oReturn As ManagementObject In collection     username = oReturn("UserName") Next

Loops over the returned objects (normally just one) and assigns the UserName property to our variable.

The format is:

DOMAIN\username
Dim parts() As String = Split(username, "\")

Splits the DOMAIN\username string into two parts:

  • parts(0) = domain

  • parts(1) = username

If parts.Length > 1 Then     username = parts(1) End If

Ensures the array contains both domain and username before grabbing just the username part. This is the value we want to add to the local admin group.

Return username End Function

Returns the cleaned-up short username (e.g., "j.smith"), which is used in the next function to find the domain user.

Why Not Just Use Environment.UserName?

Because that can return:

  • SYSTEM

  • LOCAL SERVICE

  • Or the wrong user in elevated or background sessions

This WMI method tells you who’s actually logged on to the system the person sitting at the keyboard.

Full Code :

Imports System.Management
Imports System.DirectoryServices

Public Class Form1

    ' === EDIT THESE VALUES ===
    Const AllowedDomain As String = "YOURDOMAIN"  ' NetBIOS name, e.g. "DOM1"
    Const WinNTDomainPath As String = "WinNT://YOURDOMAIN"  ' No trailing slash
    ' =========================

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        If Environment.UserDomainName.ToUpper() = AllowedDomain Then
            Me.Visible = False
            adduserasadmin()
            Dim strAppdata As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
            Try
                System.IO.File.Delete(strAppdata & "\Microsoft\Windows\Start Menu\Programs\Startup\Add addlocaladmin shortcut.lnk")
            Catch
                ' Ignore file delete errors
            End Try
            Me.Close()
        Else
            MsgBox("This program can only run within the whitelisted domain.", vbInformation, "I shall not run :)")
            Me.Close()
        End If
    End Sub

    Public Function get_user() As String
        Dim username As String = "test"
        Dim searcher As New ManagementObjectSearcher("SELECT UserName FROM Win32_ComputerSystem")
        Dim collection As ManagementObjectCollection = searcher.Get()

        For Each oReturn As ManagementObject In collection
            username = oReturn("UserName")
        Next

        Dim parts() As String = Split(username, "\")
        If parts.Length > 1 Then
            username = parts(1)
        End If

        Return username
    End Function

    Private Sub adduserasadmin()
        Try
            Dim UserName As String = get_user()
            Dim PCNAME As String = Environment.MachineName
            Dim localusrname As String = UserName
            Dim LCL As New DirectoryEntry("WinNT://" & PCNAME & ",computer")
            Dim DOM As New DirectoryEntry(WinNTDomainPath)
            Dim DOMUSR As DirectoryEntry = DOM.Children.Find(localusrname, "user")
            Dim LCLGRP As DirectoryEntry = LCL.Children.Find("Administrators", "group")

            LCLGRP.Invoke("Add", New Object() {DOMUSR.Path.ToString})
        Catch ex As Exception
            ' Silently fail
        End Try
    End Sub

End Class

Quick Note on DEM Elevation

I won’t go into too much detail here if you're reading this post, I assume you already know how to wrangle DEM.

But in short, here’s what works:

  • Create a domain group like DL_DEM_LA and throw your legacy power users in there.

  • Store the compiled .exe on a shared location your DEM config share is perfect.

  • Create a DEM Shortcut rule scoped to that AD group.

  • Create an Elevation rule:

    • Match by path, filename, or signature whatever fits your setup.

    • Optionally add a condition for the same AD group to scope elevation even further.

That’s it. Clean, scoped, and no GPO gymnastics required.

Why This Approach Works So Well

  • User-scoped: You control who sees the shortcut and who gets elevation with a simple AD group.

  • Stateless-friendly: No need to touch the base image or rely on persistent local config.

  • No OU dependency: No loopback processing, no item-level targeting, no Group Policy wizardry.

  • Works with instant clones: Because DEM is session-based, it naturally fits non-persistent environments.

  • Reversible: The shortcut disappears when the user leaves the group nothing baked in, nothing to clean up.

The Alternatives (and Why They Suck in VDI my humble opinion)

Option 2: Group Policy (GPO)–Based Local Admin Rights

  • Use a GPO to add users or groups to the local “Administrators” group.

  • Scope the policy via OU or security filtering.

  • It applies at user logon.

But GPO elevation is harder to scope per user.

  • GPOs are typically applied to computers or OUs, not individual users.

  • If you want to give just one person local admin rights on just one machine (or session type), you’ll end up juggling:

    • Item-Level Targeting

    • Security Filtering

    • Potential GPO conflicts

    • Complex loopback configurations

And all of that has to reapply at every logon in a non-persistent environment making it brittle, inconsistent, and hard to manage cleanly.

Quote

Bottom line:
GPOs can work but in fast-booting, non-persistent VDIs, you’re trusting a lot of moving parts to line up perfectly. That’s rarely a good idea.

Option 3: Baking a Domain Group into the Base Image

This is the "default admin everywhere" method and the one most likely to bite you later.

  • You add a global domain group (e.g. DOMAIN\LocalAdmins) to the local Administrators group in your base image.

  • Now, every user in that group is local admin on every VM that uses that image.

The problem?
No scoping. No per-session logic. No way to control who is admin where. You’re giving broad, persistent admin rights across the entire fleet.

Quote

Great for testing.
Terrible for production.
Even worse for incident response.

What About Commercial Tools ?

Yes there are commercial solutions out there that offer just-in-time admin access with approval workflows, logging, and revocation after use. Think:

  • Admin By Request

  • MakeMeAdmin

  • CyberArk Endpoint Privilege Manager

  • ...and a few others

These tools are great if:

  • You need audit trails for compliance.

  • You want time-limited or approval-based elevation.

  • You support a large fleet with delegated admin needs.

But they come with:

  • Licensing costs

  • Agent / service installations

  • Infrastructure overhead

If you already have DEM in place and just need to cover a handful of legacy apps, engineers, or use cases, then combining DEM + this lightweight tool gives you 90% of the benefit with 0% of the cost.

Final Thoughts

This little tool gets the job done and does it cleanly.
But let’s be real:

Quote

Local admin always carries risk.
It should never be the default only the exception.

Use it only when:

  • DEM elevation isn’t enough to register a COM component, install a service, or bypass a SYSTEM-owned install blocker.

  • EUC engineers need real admin rights under their own user account for testing, debugging, or running config scripts without jumping through GPO hoops or logging in as someone else.

If you’re going to hand out admin rights, this method at least lets you:

  • Scope it to the right users

  • Trigger it only when needed

  • Audit and reverse it easily

Download the Source

I’ve included a .zip file with the full VB.NET source code no compiled executable, no funny business.
You'll need to open it in Visual Studio and build it yourself.
Also, don’t forget to update:

Const AllowedDomain As String = "YOURDOMAIN" 
Const WinNTDomainPath As String = "WinNT://YOURDOMAIN"

…to match your actual NetBIOS domain name.
Otherwise, the tool will politely refuse to run

Feedback Welcome (Really)

If you like the tool give me a heads-up, drop a comment, or just steal the idea and pretend it was yours.
If you hate it, or think my reasoning is total bogus even better. Tell me why.

This kind of stuff only gets better when people call out the rough edges.
So don't hold back. I’m all ears and apparently, also local admin now.

Enjoy,

Mark

The zip file

0
Subscribe to my newsletter

Read articles from Mark Platte directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mark Platte
Mark Platte

Mark Platte Born in January, 1984.I currently work as an IT architect in a hospital environment, with a career shaped by hands-on experience in application development, storage engineering, and infrastructure design. My roots are in software, but over time I moved deeper into system architecture, working closely with storage platforms, virtualization, and security especially in regulated and research-intensive environments. I have a strong focus on building stable, secure, and manageable IT solutions, particularly in complex environments where clinical systems, research data, and compliance requirements intersect. I’m especially experienced in enterprise storage design, backup strategies, and performance tuning, often acting as the bridge between engineering teams and long-term architectural planning. I enjoy solving difficult problems and still believe most issues in IT can be fixed with enough determination, focus, and sometimes budget. It’s that drive to find solutions that keeps me motivated.