# AddMember

## Summary

|                            |                                      |
| -------------------------- | ------------------------------------ |
| **FSProtect ACL Alias**    | AddMember                            |
| **AD Alias**               | Write Members                        |
| **Affected Object Types**  | Group                                |
| **Exploitation Certainty** | Certain                              |
| **AD Attribute**           | Members                              |
| **AD Attribute GUID**      | bf9679c0-0de6-11d0-a285-00aa003049e2 |
| **AD Right**               | WriteProperty                        |

## Description

The `AddMember` permission allows an object (user, group, computer, etc.) to be added as a member of a group in Active Directory, which simplifies management of group memberships. This permission is implemented via the `Write Members` (`WriteProperty`) Access Control Entry (ACE).

If this permission is misconfigured, it can introduce a serious vulnerability. An attacker could exploit `AddMember` by using the `Add-ADGroupMember` command or equivalent LDAP/ADWS requests to add themselves or other accounts to a domain group, enabling unauthorized access, privilege escalation, and persistence.

## Identification

### PowerShell

#### Active Directory Module

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

**1.** Find-AddMember function

```powershell
function Find-AddMember {
    [CmdletBinding()]
    param ( [string]$Target = $null, [string]$SearchBase = $null, [string]$OutputPath = "AddMember.csv" )
    Import-Module ActiveDirectory -ErrorAction Stop 
    Write-Host "Searching for permissions on the 'member' attribute of groups..."
    # Access Control Type of Access Control Entry (Allow)
    $AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow
    # Active Directory Rights of Access Control Entry (WriteProperty)
    $ActiveDirectoryRights = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty
    # Guid of member attributeCN=Cert Publishers,CN=Users,DC=forestall,DC=labs
    $MemberAttributeGuid = "bf9679c0-0de6-11d0-a285-00aa003049e2"
    $foundAcls = @()
    $groupsToScan = @()
    try {
        if ($Target) {
            # If Target is provided, get that specific group
            Write-Host "Searching for permissions on specific group: '$Target'."
            $specificGroup = Get-ADGroup -Identity $Target -Properties nTSecurityDescriptor -ErrorAction Stop
            if ($specificGroup) {
                $groupsToScan += $specificGroup
            } else {
                Write-Output "Group '$Target' not found."
                return
            }
        } else {
            # Otherwise, get all groups in the domain
            Write-Host "Searching for all Active Directory groups."
            # Automatically use the default domain naming context if no SearchBase is provided
            $actualSearchBase = if ($SearchBase) { $SearchBase } else { (Get-ADRootDSE).DefaultNamingContext }
            $groupsToScan = Get-ADGroup -Filter '*' -SearchBase $actualSearchBase -ErrorAction Stop
        }
        if (-not $groupsToScan) {
            Write-Output "No Active Directory groups were found to scan."
            return
        }
        foreach ($group in $groupsToScan) {
            $GroupDistinguishedName = $group.DistinguishedName
            try {
                $acl = Get-Acl -Path "AD:$GroupDistinguishedName"
                foreach ($ace in $acl.Access) {
                    # Filtering Non-Inherited Allowed WriteProperty ACE on the specific GUID
                    if ($ace.AccessControlType -eq $AccessControlType -and
                        ($ace.ActiveDirectoryRights -band $ActiveDirectoryRights) -and
                        ($ace.ObjectType -eq $MemberAttributeGuid) -and
                        -not $ace.IsInherited -and
                        -not $isExcluded) {
                        $foundAcls += [PSCustomObject]@{
                            'Vulnerable Object' = $GroupDistinguishedName
                            'Internal Threat'   = $ace.IdentityReference.Value # Get the string representation
                        }
                    }
                }
            }catch { Write-Warning "Could not retrieve ACL for '$GroupDistinguishedName': $($_.Exception.Message)"}
        }
    }
    catch {
        Write-Error "Failed to retrieve Active Directory objects: $($_.Exception.Message)"
        return
    }
    if ($foundAcls.Count -gt 0) {
        Write-Host "Found $($foundAcls.Count) Active Directory object(s) with 'WriteProperty' permissions on the 'member' attribute$exclusionMessage."
        try {
            $foundAcls | Sort-Object -Unique 'Vulnerable Object', 'Internal Threat' | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
            Write-Output "Results exported successfully to '$OutputPath'"
        }
        catch { Write-Error "Failed to export results to CSV file '$OutputPath': $($_.Exception.Message)"  }
    } else { Write-Output "No Active Directory objects found with 'WriteProperty' permissions on the 'member' attribute$exclusionMessage."}}
```

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

```powershell
Find-AddMember
```

**3.** Scan a specific object

```powershell
Find-AddMember -Target "CN=Account Operators,CN=Builtin,DC=forestall,DC=labs"
```

**4.** Use `SearchBase` to limit the search scope

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

#### .NET Directory Services

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

**1.** Find-AddMemberSimple function

```powershell
function Find-AddMemberSimple {
    [CmdletBinding()] param(  [string]$Target)
    $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 local RootDSE..."
        try {
            $root      = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
            $baseDN    = $root.Properties["defaultNamingContext"].Value
            $ldapPath  = "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 }
        }
    }
    # 2) Inspect ACLs for each group
    $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]::WriteProperty) -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.ToString();
                    Principal = $who
                }
            }
        }
    }
    if ($found) {
        $found | Export-Csv -Path ".\AddMemberAcls.csv" -NoTypeInformation -Encoding UTF8
        Write-Host "Exported $($found.Count) entr$(if($found.Count -eq 1){'y'}else{'ies'}) to AddMemberAcls.csv"
    }
    else {Write-Host "No 'member' ACLs found."  }
}

```

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

```powershell
Find-AddMemberSimple
```

**3.** Scan a specific object

```powershell
Find-AddMemberSimple -Target "CN=Cert Publishers,CN=Users,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 `Write Members`.

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

![ADUC](/files/n8NrofhIqL6bqCeAJCJm)

## Exploitation

Vulnerabilities related to `Access Control Entries` can be exploited through `LDAP` and `ADWS` requests. The `Add-ADGroupMember` cmdlet in the `ActiveDirectory` module can be used to automate this exploitation process.

### Windows

```powershell
Add-ADGroupMember -Identity "<Vulnerable Group>" -Members "<object to be added>"
```

Example:

```powershell
Add-ADGroupMember -Identity "Backup Operators" -Members adam
```

![Add member ActiveDirectory Module](/files/ihPz3ClEto8ZgjKgb3dw)

### Linux

Add group member using `bloodyAD`

```bash
bloodyAD --host <dchost> -d <domain> -u <user> -p '<pass>' add groupMember '<targetGroup>' <usetoadd> 
```

Example:

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

![add member bloodyAD](/files/mBvEO6rZQUQzm6P8I30g)

## Mitigation

Access Control Entries that are identified as dangerous should be removed or corrected by following the steps below.

**1.** Open Active Directory Users and Computers and enable "Advanced Features".

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

**3.** Click the Advanced button to open the Advanced Security Settings dialog.

**4.** Edit or remove the ACE(s) that grant the dangerous permissions.

**5.** Click Apply, then OK to save your changes.

![ADUC](/files/n8NrofhIqL6bqCeAJCJm)

## Detection

Monitoring for abuse of the `AddMember` permission involves correlating group membership changes with ACL modifications on the member attribute. The following Windows Security Event IDs are particularly useful:

| Event ID | Category                        | Description                                           | Fields/Attributes             | References                                                                                                                     |
| -------- | ------------------------------- | ----------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| 4728     | Security Group Management       | A member was added to a security-enabled global group | Target Group, Member SID/Name | <https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=4728>                                     |
| 4732     | Security Group Management       | A member was added to a local security group.         | Target Group, Member SID/Name | <https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4732> |
| 5136     | Audit Directory Service Changes | A directory service object was modified.              | ntSecurityDescriptor          | <https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5136>                                     |
| 4662     | Audit Directory Service Access  | An operation was performed on an object.              | AccessList, AccessMask        | <https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662>                                     |

## References

* [DACL - The Hacker Recipes](https://www.thehacker.recipes/ad/movement/dacl/)
* [Abusing ACL Misconfigurations - RedFox Security](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/addmember.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.
