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
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Gain Privileges or Assume Identity - Users gain privileges beyond their intended authorization level through incorrect group membership. |
| Confidentiality | Scope: Confidentiality Read Application Data - Incorrect group placement may grant access to sensitive data the user should not see. |
| Integrity | Scope: 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.
Related CWEs
- 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
- MITRE Corporation. "CWE-842: Placement of User into Incorrect Group." https://cwe.mitre.org/data/definitions/842.html
- OWASP. "Access Control Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Access_Control_Cheat_Sheet.html
- NIST. "Guide to Attribute Based Access Control (ABAC)."