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
Tools
Compare all tools- mac_aptCLI · open source
- VelociraptorPlatform · open source
- plutilCLI · built into macOS
- dsclCLI · built into macOS
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
| Artifact | Path | Notes |
|---|---|---|
| Users | /private/var/db/dslocal/nodes/Default/users/*.plist | One plist per account, including _-prefixed service accounts and root |
| Groups | /private/var/db/dslocal/nodes/Default/groups/*.plist | admin.plist lists administrators |
| Login window settings | /Library/Preferences/com.apple.loginwindow.plist | lastUserName, autoLoginUser, Hide500Users, HiddenUsersList |
| Auto-login secret | /etc/kcpassword | Existence means auto-login is configured (content is an obfuscated password: do not decode) |
| Recovery account info | /System/Volumes/Preboot/**/AdminUserRecoveryInfo.plist, CryptoUserInfo.plist | Collected 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 (
admingroup), the most common target of privilege changes. - Hidden accounts: dscl attribute
IsHiddenset to 1, UID below 500 withHide500Users, or names inHiddenUsersList. - 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:
| Key | Meaning |
|---|---|
name | Short name(s) |
realname | Full name |
uid / gid | Numeric IDs; interactive users normally start at 501 |
generateduid | Account UUID, used in group membership and elsewhere |
home / shell | Home directory and shell (/usr/bin/false for service accounts) |
authentication_authority | Entries such as ;ShadowHash;HASHLIST:<...>, ;SecureToken;, ;Kerberosv5;... |
ShadowHashData | Password hash material. Out of scope: note its presence only |
accountPolicyData | Embedded plist with creationTime, passwordLastSetTime, failedLoginCount, failedLoginTimestamp |
LinkedIdentity | Apple Account link, when present |
hint | Password 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) andrecovery_account_info.yaml(Preboot). - Velociraptor
MacOS.System.Usersparses user plists andaccountPolicyData. - Handle the collected plists as sensitive: they contain password hash material.
Parsing
- mac_apt
USERSplugin: 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.Usersfor 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
IsHiddenset is a classic persistence trick (MITRE ATT&CK T1564.002). - Compare
creationTimeacross accounts: an admin created during the incident window stands out. - A burst of
failedLoginCountwith a recentfailedLoginTimestampsuggests password guessing; correlate with authentication entries in the unified logs. - Auto-login (
autoLoginUserplus/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_ADDand others) from macOS 14, so EDR telemetry may record account changes that the plists no longer show.