Placement of User into Incorrect Group

Description

Placement of User into Incorrect Group is an access control vulnerability where a product or administrator assigns a user to a group with more privileges than intended. When users are placed in groups that have greater access rights than they should have, they can bypass security policies, access unauthorized resources, and perform actions beyond their intended authorization level. This can occur through automated systems that incorrectly assign group memberships, configuration errors, or flaws in user management logic.

Risk

This vulnerability directly leads to privilege escalation. Users assigned to overly privileged groups can access sensitive data, modify critical system configurations, perform administrative functions, or escalate their privileges further. The risk is particularly severe when users are assigned to administrative or root-level groups, as this grants nearly unlimited system access. In enterprise environments, incorrect group placement can violate compliance requirements, expose confidential data, and create audit trail gaps. The issue may persist undetected for extended periods since the user appears to have legitimate access.

Solution

Implement strict group assignment validation that verifies the appropriateness of group memberships before applying them. Use the principle of least privilege—assign users to the minimum groups necessary for their role. Implement approval workflows for privileged group assignments. Regularly audit group memberships to detect and correct inappropriate assignments. Use role-based access control (RBAC) with clearly defined role-to-group mappings. Ensure proper cleanup of group memberships when users change roles or leave the organization. Validate configuration options that affect group memberships. Never assign users to privileged groups (wheel, admin, root) without explicit verification.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

Gain Privileges or Assume Identity - Users gain privileges beyond their intended authorization level through incorrect group membership.
ConfidentialityScope: Confidentiality

Read Application Data - Incorrect group placement may grant access to sensitive data the user should not see.
IntegrityScope: Integrity

Modify Application Data - Users in privileged groups may modify data or configurations beyond their authority.

Example Code

Vulnerable Code

# Vulnerable: Automatic group assignment without validation
class VulnerableUserManager:

    def create_user(self, username, email, department):
        user = User(username=username, email=email)
        user.save()

        # Vulnerable: Automatic group assignment based on department
        # No validation of whether this is appropriate
        if 'IT' in department:
            # Vulnerable: All IT staff get admin group
            user.groups.add(Group.objects.get(name='administrators'))

        if 'Finance' in department:
            user.groups.add(Group.objects.get(name='financial_data'))

        return user

    def update_user_department(self, user, new_department):
        # Vulnerable: Groups not cleaned up on department change
        # User accumulates privileges from all previous departments
        user.department = new_department

        if 'IT' in new_department:
            user.groups.add(Group.objects.get(name='administrators'))

        user.save()
// Vulnerable: Group assignment from untrusted source
public class VulnerableUserService {

    public void processUserRegistration(HttpServletRequest request) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // Vulnerable: Group from request parameter
        String requestedGroup = request.getParameter("group");

        User user = new User(username, hashPassword(password));

        // Vulnerable: No validation of group assignment
        if (requestedGroup != null) {
            Group group = groupRepository.findByName(requestedGroup);
            if (group != null) {
                user.addGroup(group);  // User can request any group!
            }
        }

        userRepository.save(user);
    }
}

// Attacker sets group=administrators to become admin
// Vulnerable: Default group assignment is too privileged
<?php
class VulnerableAccountCreator {

    const DEFAULT_GROUP = 'power_users';  // Vulnerable: Overly privileged default

    public function createAccount($email, $password) {
        $user = new User();
        $user->email = $email;
        $user->password = password_hash($password, PASSWORD_BCRYPT);

        // Vulnerable: All new users get power_users group
        $user->group = self::DEFAULT_GROUP;

        $user->save();
        return $user;
    }
}
?>
// Vulnerable: Daemon doesn't clear groups before privilege drop
#include <unistd.h>
#include <grp.h>

void vulnerable_drop_privileges(uid_t target_uid, gid_t target_gid) {
    // Vulnerable: Only sets primary GID, supplementary groups remain
    if (setgid(target_gid) != 0) {
        perror("setgid");
        exit(1);
    }

    if (setuid(target_uid) != 0) {
        perror("setuid");
        exit(1);
    }

    // User still has supplementary groups from before!
    // May still be member of 'wheel', 'admin', etc.
}
// Vulnerable: LDAP sync assigns overly broad groups
async function vulnerableSyncFromLDAP(ldapUser) {
    const user = await User.findOrCreate({ email: ldapUser.email });

    // Vulnerable: Direct mapping from LDAP groups without filtering
    for (const ldapGroup of ldapUser.memberOf) {
        // Vulnerable: No validation of group appropriateness
        const localGroup = mapLDAPGroupToLocal(ldapGroup);
        if (localGroup) {
            await user.addGroup(localGroup);
        }
    }

    // LDAP misconfiguration could grant admin access to all users
    await user.save();
}

Fixed Code

# Fixed: Validated group assignment with approval workflow
class FixedUserManager:

    PRIVILEGED_GROUPS = {'administrators', 'root', 'superusers'}
    DEPARTMENT_GROUPS = {
        'Engineering': ['developers', 'git_users'],
        'Finance': ['financial_readonly'],
        'IT': ['helpdesk', 'it_support'],  # Not administrators
    }

    def create_user(self, username, email, department, requester):
        user = User(username=username, email=email)
        user.save()

        # Fixed: Only assign appropriate groups based on department
        allowed_groups = self.DEPARTMENT_GROUPS.get(department, [])

        for group_name in allowed_groups:
            group = Group.objects.get(name=group_name)
            user.groups.add(group)

        return user

    def request_group_membership(self, user, group_name, requester, justification):
        """Request membership in a group - privileged groups require approval"""

        if group_name in self.PRIVILEGED_GROUPS:
            # Fixed: Privileged group requires approval
            request = GroupMembershipRequest(
                user=user,
                group_name=group_name,
                requester=requester,
                justification=justification,
                status='pending'
            )
            request.save()
            self.notify_approvers(request)
            return {'status': 'pending_approval'}
        else:
            # Non-privileged groups can be self-service
            self.add_to_group(user, group_name)
            return {'status': 'completed'}

    def update_user_department(self, user, new_department, requester):
        # Fixed: Clean up old group memberships
        old_department = user.department
        old_groups = set(self.DEPARTMENT_GROUPS.get(old_department, []))
        new_groups = set(self.DEPARTMENT_GROUPS.get(new_department, []))

        # Remove groups from old department
        for group_name in old_groups - new_groups:
            group = Group.objects.get(name=group_name)
            user.groups.remove(group)

        # Add groups for new department
        for group_name in new_groups - old_groups:
            group = Group.objects.get(name=group_name)
            user.groups.add(group)

        user.department = new_department
        user.save()

        # Audit log
        AuditLog.log('department_change', user, requester,
                     f'{old_department} -> {new_department}')
// Fixed: Strict group assignment with validation
public class FixedUserService {

    private static final Set<String> SELF_ASSIGNABLE_GROUPS =
        Set.of("users", "newsletter_subscribers");

    private static final Set<String> PRIVILEGED_GROUPS =
        Set.of("administrators", "superusers", "security");

    public void processUserRegistration(HttpServletRequest request) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        User user = new User(username, hashPassword(password));

        // Fixed: Only assign default unprivileged group
        user.addGroup(groupRepository.findByName("users"));

        // Fixed: Ignore any group requests from user
        // Group assignments beyond default require admin action

        userRepository.save(user);
        auditLog.log("user_created", user.getId(), "default_group_assigned");
    }

    public void assignGroupToUser(User user, String groupName, Admin admin) {
        // Fixed: Validate group assignment
        if (PRIVILEGED_GROUPS.contains(groupName)) {
            if (!admin.hasPermission("assign_privileged_groups")) {
                throw new AccessDeniedException(
                    "Cannot assign privileged group: " + groupName);
            }
        }

        Group group = groupRepository.findByName(groupName);
        if (group == null) {
            throw new IllegalArgumentException("Unknown group: " + groupName);
        }

        user.addGroup(group);
        userRepository.save(user);

        // Fixed: Audit all group assignments
        auditLog.log("group_assigned", user.getId(), groupName, admin.getId());
    }
}
// Fixed: Least privilege default groups
<?php
class FixedAccountCreator {

    const DEFAULT_GROUP = 'basic_users';  // Fixed: Minimal privilege default

    private $groupValidator;

    public function createAccount($email, $password) {
        $user = new User();
        $user->email = $email;
        $user->password = password_hash($password, PASSWORD_BCRYPT);

        // Fixed: Assign minimal default group
        $user->group = self::DEFAULT_GROUP;

        $user->save();

        // Audit log
        $this->auditLog('account_created', $user->id, self::DEFAULT_GROUP);

        return $user;
    }

    public function assignGroup($userId, $groupName, $adminId) {
        // Fixed: Validate group assignment is appropriate
        if (!$this->groupValidator->canAssign($adminId, $groupName)) {
            throw new UnauthorizedException(
                "Not authorized to assign group: $groupName"
            );
        }

        $user = User::find($userId);
        $user->addGroup($groupName);
        $user->save();

        $this->auditLog('group_assigned', $userId, $groupName, $adminId);
    }
}
?>
// Fixed: Properly clear supplementary groups before privilege drop
#include <unistd.h>
#include <grp.h>

int fixed_drop_privileges(uid_t target_uid, gid_t target_gid) {
    // Fixed: Clear supplementary groups first
    if (setgroups(0, NULL) != 0) {
        perror("setgroups");
        return -1;
    }

    // Now set the primary GID
    if (setgid(target_gid) != 0) {
        perror("setgid");
        return -1;
    }

    // Finally drop to target UID
    if (setuid(target_uid) != 0) {
        perror("setuid");
        return -1;
    }

    // Verify we can't regain privileges
    if (setuid(0) == 0) {
        fprintf(stderr, "Error: Still able to regain root\n");
        return -1;
    }

    return 0;
}
// Fixed: LDAP sync with group filtering and validation
const ALLOWED_GROUPS = new Set([
    'users', 'developers', 'designers', 'support'
]);

const PRIVILEGED_GROUPS = new Set([
    'administrators', 'superusers', 'security_team'
]);

async function fixedSyncFromLDAP(ldapUser) {
    const user = await User.findOrCreate({ email: ldapUser.email });

    // Fixed: Track existing groups for cleanup
    const existingGroups = new Set(await user.getGroupNames());
    const newGroups = new Set();

    for (const ldapGroup of ldapUser.memberOf) {
        const localGroup = mapLDAPGroupToLocal(ldapGroup);

        if (!localGroup) continue;

        // Fixed: Only allow non-privileged groups from LDAP
        if (PRIVILEGED_GROUPS.has(localGroup)) {
            console.warn(`Skipping privileged group ${localGroup} for ${ldapUser.email}`);
            continue;
        }

        if (!ALLOWED_GROUPS.has(localGroup)) {
            console.warn(`Unknown group ${localGroup} for ${ldapUser.email}`);
            continue;
        }

        newGroups.add(localGroup);
    }

    // Fixed: Update groups atomically
    await user.setGroups(Array.from(newGroups));

    // Audit changes
    const added = [...newGroups].filter(g => !existingGroups.has(g));
    const removed = [...existingGroups].filter(g => !newGroups.has(g));

    if (added.length || removed.length) {
        await AuditLog.create({
            action: 'ldap_sync_groups',
            user: user.id,
            added,
            removed
        });
    }

    await user.save();
}

CVE Examples

  • CVE-1999-1193: Operating system automatically assigned new users to the privileged "wheel" group.
  • CVE-2010-3716: Web application allowed requests to create arbitrary group memberships.
  • CVE-2008-5397: Configuration options inadvertently caused unintended group memberships.
  • CVE-2007-6644: CMS allowed users to promote themselves to administrator role.
  • CVE-2007-3260: Product assigned members to root group inappropriately.
  • CVE-2002-0080: Daemon failed to clear supplementary groups before dropping privileges.

  • CWE-286: Incorrect User Management (parent)
  • CWE-269: Improper Privilege Management (related)
  • CWE-732: Incorrect Permission Assignment for Critical Resource (related)
  • CWE-1212: Authorization Errors (category)

References

  1. MITRE Corporation. "CWE-842: Placement of User into Incorrect Group." https://cwe.mitre.org/data/definitions/842.html
  2. OWASP. "Access Control Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Access_Control_Cheat_Sheet.html
  3. NIST. "Guide to Attribute Based Access Control (ABAC)."