Skip to content

User activityPersistence

dslocal User Accounts: macOS Local Users and Groups

dslocal plists define every local macOS account and group, with creation, password-change and failed-login times that expose rogue or hidden accounts.

Location
/private/var/db/dslocal/nodes/Default/users/<name>.plist
Proves
Which local accounts exist, when they were created, who is admin, and recent password and failed-login activity
Timestamps
accountPolicyData: Unix epoch seconds (UTC)
Access
root; Full Disk Access recommended for the collector
Retention
Until the account is deleted; home may survive in /Users/Deleted Users
Collection
UAC, mac_apt, Velociraptor, ditto

What it is

macOS stores local users and groups in the local Directory Services node ("dslocal"), managed by opendirectoryd. Each account is a property list named after the short name; each group is a plist in a sibling folder. Values are stored as arrays, even single values such as uid.

This is the macOS answer to "which accounts exist and when did they appear". It is the place to spot an account created by an attacker, a user silently added to admin, or an account hidden from the login window.

Where it lives

ArtifactPathNotes
Users/private/var/db/dslocal/nodes/Default/users/*.plistOne plist per account, including _-prefixed service accounts and root
Groups/private/var/db/dslocal/nodes/Default/groups/*.plistadmin.plist lists administrators
Login window settings/Library/Preferences/com.apple.loginwindow.plistlastUserName, autoLoginUser, Hide500Users, HiddenUsersList
Auto-login secret/etc/kcpasswordExistence means auto-login is configured (content is an obfuscated password: do not decode)
Recovery account info/System/Volumes/Preboot/**/AdminUserRecoveryInfo.plist, CryptoUserInfo.plistCollected by UAC
Deleted user homes/Users/Deleted Users/When "Save the home folder in a disk image" was chosen

The dslocal folder is readable only by root. Collect it with a Full Disk Access collector.

What it proves

  • The complete list of local accounts with UID, GID, home, shell, real name and GeneratedUID.
  • When an account was created and when its password was last set.
  • Failed login count and the time of the last failure (password guessing, locked-out users).
  • Administrator membership (admin group), the most common target of privilege changes.
  • Hidden accounts: dscl attribute IsHidden set to 1, UID below 500 with Hide500Users, or names in HiddenUsersList.
  • Last logged-in user and configured auto-login user.
  • Whether the account holds a SecureToken or is linked to an Apple Account (LinkedIdentity).

It does not give a login history. Use the unified logs, sudo logs and last/utmpx records for sessions.

Key fields

User plist:

KeyMeaning
nameShort name(s)
realnameFull name
uid / gidNumeric IDs; interactive users normally start at 501
generateduidAccount UUID, used in group membership and elsewhere
home / shellHome directory and shell (/usr/bin/false for service accounts)
authentication_authorityEntries such as ;ShadowHash;HASHLIST:<...>, ;SecureToken;, ;Kerberosv5;...
ShadowHashDataPassword hash material. Out of scope: note its presence only
accountPolicyDataEmbedded plist with creationTime, passwordLastSetTime, failedLoginCount, failedLoginTimestamp
LinkedIdentityApple Account link, when present
hintPassword hint

Older macOS versions stored login timestamps in a passwordpolicyoptions structure instead (parsed by Plaso's macuser plugin).

Group plist (admin.plist): users (short names, dscl GroupMembership) and groupmembers (GeneratedUIDs, dscl GroupMembers).

Timestamps

creationTime and passwordLastSetTime are real numbers and failedLoginTimestamp an integer, all Unix epoch seconds in UTC.

sudo plutil -extract accountPolicyData.0 raw -o - \
  /private/var/db/dslocal/nodes/Default/users/alice.plist | base64 -D > ap.plist
plutil -p ap.plist
date -u -r 1727600000        # convert a value

On a live system: dscl . -read /Users/alice accountPolicyData. The APFS birth time of the user plist and of /Users/<name> should roughly agree with creationTime; a mismatch deserves explanation (migration, restore, tampering).

Retention

Records stay until the account is deleted. Deleting an account removes the plist; its home folder may remain in /Users, be archived to /Users/Deleted Users/, or be erased, depending on the option chosen. Older copies of dslocal can survive in local APFS snapshots and Time Machine backups, which is how you recover a deleted rogue account.

Collection

sudo ditto /private/var/db/dslocal/nodes/Default ./case/dslocal
sudo cp -p /Library/Preferences/com.apple.loginwindow.plist ./case/
ls -la /etc/kcpassword 2>/dev/null > ./case/kcpassword_present.txt
# Live views
dscl . -list /Users UniqueID > ./case/users_uid.txt
dscl . -read /Groups/admin GroupMembership > ./case/admins.txt
  • UAC files/system/user_accounts.yaml (user plists, Accounts*.sqlite), library_preferences.yaml (loginwindow) and recovery_account_info.yaml (Preboot).
  • Velociraptor MacOS.System.Users parses user plists and accountPolicyData.
  • Handle the collected plists as sensitive: they contain password hash material.

Parsing

  • mac_apt USERS plugin: name, UID, UUID, GID, creation and password-set dates, hints, home and Darwin paths. python mac_apt.py -o out MOUNTED /Volumes/evidence USERS.
  • Velociraptor MacOS.System.Users for fleet-wide review.
  • plutil / dscl for spot checks, as above.

Quick admin audit on a copy:

plutil -extract users json -o - ./case/dslocal/groups/admin.plist
for f in ./case/dslocal/users/*.plist; do
  printf '%s uid=%s\n' "$(basename "$f" .plist)" \
    "$(plutil -extract uid.0 raw -o - "$f" 2>/dev/null)"
done | sort -t= -k2 -n

Investigator tips

  • A new account with UID below 500, a name mimicking a service account, or IsHidden set is a classic persistence trick (MITRE ATT&CK T1564.002).
  • Compare creationTime across accounts: an admin created during the incident window stands out.
  • A burst of failedLoginCount with a recent failedLoginTimestamp suggests password guessing; correlate with authentication entries in the unified logs.
  • Auto-login (autoLoginUser plus /etc/kcpassword) weakens physical security and explains logins without authentication.
  • Check new admins against sudo logs, shell history and TCC grants made by that account.
  • Endpoint Security emits Open Directory events (ES_EVENT_TYPE_NOTIFY_OD_CREATE_USER, ..._OD_GROUP_ADD and others) from macOS 14, so EDR telemetry may record account changes that the plists no longer show.

See also