Skip to content

TCC Database Forensics: macOS Privacy Permissions

Analyze macOS TCC.db privacy permissions: access table columns, service names, auth_value meanings, MDM grants, SIP protection and unified log evidence.

Published on 8 min read

Transparency, Consent and Control, better known as TCC, is the macOS framework that decides whether an application may use the camera, the microphone, the screen, the user's documents, other applications' data or the accessibility APIs. Every decision is recorded in SQLite databases. For an investigator, those databases answer a very specific set of questions: which applications were granted sensitive capabilities, whether a suspicious binary asked for Full Disk Access or Screen Recording, when a permission last changed, and whether a grant came from a user click or from an MDM policy. Because malware on macOS often needs TCC permissions to do anything interesting, the TCC database is frequently where its intentions become visible.

Where the TCC databases live

DatabasePathTypical contentProtection
System TCC/Library/Application Support/com.apple.TCC/TCC.dbFull Disk Access, Accessibility, Screen Recording, Input Monitoring, Developer Tools, Endpoint Security clientsSIP-protected and TCC-protected
User TCC~/Library/Application Support/com.apple.TCC/TCC.dbCamera, Microphone, Contacts, Calendars, Photos, Automation (Apple Events), Desktop/Documents/Downloads folders, removable and network volumesTCC-protected (needs Full Disk Access to read)
MDM overrides/Library/Application Support/com.apple.TCC/MDMOverrides.plistPrivacy Preferences Policy Control (PPPC) grants pushed by MDMSIP-protected

The split between the two databases is by service, not by who granted it, and it is not perfectly stable across releases. Treat the "typical content" column as a guide and always query both databases. Each local user has their own user-level TCC.db, so collect it for every account under /Users.

On a live system, reading any of these files requires the reading process to hold Full Disk Access. System Integrity Protection additionally prevents modification of the system database, even by root. Collection tools such as Aftermath, mac_apt and UAC will silently miss the files if the terminal or agent they run from lacks Full Disk Access, so record the privileges used during collection (see macOS forensic acquisition). When working from a mounted image on an analysis host, open a copy of the database, never the original, and include the -wal and -shm files if present.

The access table

The table that matters is access. Its schema has evolved; on macOS Big Sur and later it contains, among others, the following columns:

ColumnMeaning
serviceThe TCC service identifier, for example kTCCServiceCamera
clientThe bundle identifier or the absolute path of the requesting program
client_type0 if client is a bundle identifier, 1 if it is an absolute path
auth_valueThe decision (see below)
auth_reasonWhy the decision was made (user consent, user set, MDM policy, and so on)
auth_versionVersion of the authorization record format
csreqCode signing requirement blob used to verify the client's identity
indirect_object_identifierThe target for services that have one, notably Automation (the app being controlled)
flagsAdditional flags
last_modifiedTime of the last change, as Unix epoch seconds

Recent releases have added further columns (for example process and boot identifiers, and reminder-related fields). Run .schema access on your copy before writing queries; do not assume a column exists. On Mojave and Catalina the table used an allowed column (0 or 1) and a prompt_count column instead of auth_value and auth_reason.

auth_value and auth_reason

auth_valueCommonly documented meaning
0Denied
1Unknown
2Allowed
3Limited (for services that support partial access, such as Photos)

auth_reason values are not formally documented by Apple. Community research commonly maps small integers to reasons such as "user consent" (the user answered a prompt), "user set" (the user toggled it in System Settings), "system set", "MDM policy" and "entitled". Use these interpretations as hypotheses and confirm them against the unified log or a test system of the same version before stating them in a report.

Service names worth knowing

ServiceWhat it grants
kTCCServiceSystemPolicyAllFilesFull Disk Access
kTCCServiceAccessibilityControl of the UI via accessibility APIs (keystroke injection, clicking)
kTCCServiceScreenCaptureScreen Recording
kTCCServiceListenEventInput Monitoring (observing keyboard and mouse events)
kTCCServicePostEventPosting synthetic input events
kTCCServiceMicrophone / kTCCServiceCameraMicrophone and camera
kTCCServiceAppleEventsAutomation: sending Apple Events to another app (target in indirect_object_identifier)
kTCCServiceSystemPolicyDesktopFolder, ...DocumentsFolder, ...DownloadsFolderAccess to those user folders
kTCCServiceSystemPolicyRemovableVolumes, ...NetworkVolumesAccess to removable and network volumes
kTCCServiceAddressBook, kTCCServiceCalendar, kTCCServicePhotosContacts, calendars, photos
kTCCServiceEndpointSecurityClientPermission for an Endpoint Security client

From a threat perspective, Accessibility, Input Monitoring and Screen Recording map to keylogging and screen surveillance; Full Disk Access maps to data theft; Automation grants to com.apple.Terminal, com.apple.finder or com.apple.systemevents are frequently abused to borrow another application's permissions.

Querying TCC.db

Work on a copy. A basic review of all decisions, newest first:

SELECT service,
       client,
       client_type,
       auth_value,
       auth_reason,
       indirect_object_identifier,
       datetime(last_modified, 'unixepoch') AS last_modified_utc
FROM access
ORDER BY last_modified DESC;

Focus on the high-risk services and on path-based clients, which are often command-line tools or unpackaged binaries rather than signed applications:

SELECT service, client, auth_value,
       datetime(last_modified, 'unixepoch') AS last_modified_utc
FROM access
WHERE service IN ('kTCCServiceSystemPolicyAllFiles',
                  'kTCCServiceAccessibility',
                  'kTCCServiceScreenCapture',
                  'kTCCServiceListenEvent',
                  'kTCCServicePostEvent')
   OR client_type = 1
ORDER BY last_modified DESC;

Run it from the command line:

cp "/Volumes/Evidence/Users/analyst/Library/Application Support/com.apple.TCC/TCC.db"* ./case/tcc_user/
sqlite3 -header -column ./case/tcc_user/TCC.db < high_risk.sql

Note that last_modified is Unix epoch seconds, unlike many other Apple databases that use Mac Absolute Time. Also note that last_modified reflects the last change to the row, not every use of the permission. TCC.db does not record each camera activation or each file read.

A denied row (auth_value = 0) is evidence too: it shows that a client requested the capability and the user refused, which can be the earliest trace of an intrusion attempt.

MDM and PPPC grants

In managed fleets, administrators pre-approve permissions with Privacy Preferences Policy Control (PPPC) configuration profiles. These grants are reflected in MDMOverrides.plist next to the system database rather than as ordinary user decisions. Read it with plutil -p. When a suspicious client holds Full Disk Access, determine whether it came from MDM (check the plist and the installed profiles with sudo profiles show -type configuration) or from the user. A malicious PPPC profile would itself be a significant finding and points toward the MDM or profile installation path. Note that PPPC can pre-grant many services but cannot silently grant camera, microphone or Screen Recording; for Screen Recording, MDM can only allow standard users to approve it themselves.

TCC Parser decodes TCC.db (with its -wal), MDMOverrides.plist and the csreq blobs together in the browser, which makes it easier to tell MDM grants from user decisions.

TCC in the unified log

tccd is the daemon that makes decisions, and it logs under the com.apple.TCC subsystem. This is where you find per-request evidence that the database does not keep:

log show --info --predicate 'subsystem == "com.apple.TCC"' \
  --start "2026-09-27 00:00:00" --end "2026-09-28 23:59:59" > tcc_log.txt

On recent releases, request handling messages include the service, the requesting and responsible processes and the resulting decision. The exact message format changes between versions, and some values may be shown as <private>. Retention is limited, so collect logs early (see Unified Logs). Correlating a TCC request with process execution, quarantine events and a new LaunchAgent (see launchd persistence) often produces a clean infection timeline.

tccutil and evidence destruction

tccutil reset <service> [bundle-id] removes decisions from the database, for example tccutil reset ScreenCapture com.example.agent. Users and IT staff run it legitimately to fix permission problems, and attackers or cleanup scripts may run it to remove traces. A reset deletes rows, so a missing entry is not proof that access was never granted. Look for gaps with the unified log, APFS snapshots, Time Machine backups and the database's own free pages or WAL file. Never run tccutil on a system under investigation before collection.

Pitfalls

  • Schema changes: column sets differ between Mojave, Big Sur and current releases. Check .schema before querying and adapt scripts per version.
  • Incomplete collection: without Full Disk Access, the database copy fails or comes back empty. A missing TCC.db in a triage package is usually a collection problem, not an absence of data.
  • Bundle ID versus path: an entry keyed on a bundle identifier applies to any program that satisfies the stored csreq. Decode and compare the requirement before assuming the binary on disk is the one that was approved.
  • Responsible process attribution: permissions are often attributed to the responsible app (for example Terminal) rather than the script or binary that actually used them. A grant to Terminal can cover everything launched from it.
  • auth_reason interpretation: the integer meanings are community-documented, not official. Hedge accordingly in reports.
  • Not a usage log: TCC.db records decisions and their last change, not each access. Use the unified log and KnowledgeC/Biome for usage (see KnowledgeC and Biome).

Frequently asked questions

Where are the TCC databases on macOS?

There is a system-wide database at /Library/Application Support/com.apple.TCC/TCC.db and a per-user database at ~/Library/Application Support/com.apple.TCC/TCC.db for each user. Permissions such as Full Disk Access, Accessibility and Screen Recording are usually stored in the system database, while camera, microphone, contacts and folder access are usually stored per user.

What does auth_value 2 mean in TCC.db?

On macOS Big Sur and later, auth_value 2 means the client was allowed access to the service. A value of 0 means denied, and 3 is generally interpreted as limited access. Older releases used an allowed column instead of auth_value.

Can I read TCC.db without Full Disk Access?

Generally no. Both databases are protected by TCC itself and the system database is also protected by System Integrity Protection, so the process reading them, for example Terminal or your collection tool, needs Full Disk Access on a live system. On a dead-box image mounted on an analysis machine this restriction does not apply.

Related guides