Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

Launching apps in Sonoma 14.6.1: Conclusions

By: hoakley
3 September 2024 at 14:30

Over a series of three articles last week, I pored over thousands of log entries to examine how macOS Sonoma 14.6.1 checks applications it’s launching, under normal full security settings, with reduced security, and for known malware. This article draws together my conclusions from those tests run in virtual machines on an Apple silicon Mac.

Layered security

Like other security functions in macOS, app launch security is built in layers, including checks of

  • code-signing certificates (multiple times);
  • CDHashes, including their consistency, and against Apple’s database for notarized apps, and their revocation;
  • quarantine extended attributes, which normally trigger a user consent dialog, and may result in app translocation;
  • previous launch, in the LaunchServices database;
  • matches with Yara rules in XProtect’s data;
  • user consent to a first launch prompt dialog;
  • launch and other constraints.

Additional data may also be collected and stored in the provenance database that first appeared in Ventura.

Not all checks are performed on every launch of an app. At a minimum, for a notarized app that has been run only recently, these might consist of only local checks against CDHashes and with the app’s existing entry in the LaunchServices database. Checks are also modified by reducing security settings:

  • Disabling Gatekeeper checks doesn’t stop those checks from taking place, but apparently ignores some results, notably those obtained by XProtect. It doesn’t affect checks of CDHashes against Apple’s database.
  • Disabling SIP has more pervasive effects in largely disabling the com.apple.syspolicy sub-system, affecting several layers, although checks of CDHashes against Apple’s database are unaffected.

com.apple.syspolicy

In full security conditions, one sub-system dominates log entries concerning app launch security, com.apple.syspolicy. This is clearest in Gatekeeper and XProtect checks. Although the log entries that follow may appear bewildering, they are the best illustration of this point.

When launching a notarized app that hasn’t previously been run on that Mac and has a quarantine xattr, Gatekeeper and XProtect scans are reported in the following sequence of entries:
com.apple.syspolicy.exec GK process assessment: <private> <-- (<private>, <private>)
com.apple.syspolicy.exec Gatekeeper assessment rooted at: <private>
com.apple.syspolicy.exec Skipping TCC check due to process: 692, 0, 692
com.apple.syspolicy.exec queueing up scan for code: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: (null)), (id: (null)), (bundle_id: (null))
com.apple.syspolicy.exec starting work for scan for code: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: (null)), (id: (null)), (bundle_id: (null))
com.apple.syspolicy.exec allowUI is YES, creating codeEval object: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: (null)), (id: (null)), (bundle_id: (null))
com.apple.syspolicy.exec Adding default exception for team: <private>
com.apple.syspolicy.exec Registered app bundle for protection: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: (null)), (bundle_id: (null))
com.apple.syspolicy.exec GK performScan: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: (null)), (bundle_id: (null))
com.apple.xprotect XProtectScan beginAnalysisWithResultsHandler continueOnError is set to 0
com.apple.xprotect XPAssessment performAnalysisOnFileImpl continueOnError set to 0
com.apple.xprotect Xprotect is performing a direct malware and dylib scan: <private>

Those checks later complete in entries such as:
com.apple.syspolicy.exec GK Xprotect results: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: (null)), (bundle_id: (null)), XPScan: 0,-7676743164328624005,2024-08-26 08:19:01 +0000,(null)
com.apple.syspolicy.exec GK scan complete: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: (null)), (bundle_id: (null)), 4, 4, 0
com.apple.syspolicy.exec scan finished, waking up any waiters: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: co.eclecticlight.SystHist), (bundle_id: co.eclecticlight.SystHist)
com.apple.syspolicy.exec App gets first launch prompt because responsibility: <private>, <private>
com.apple.syspolicy.exec GK evaluateScanResult: 0, PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: co.eclecticlight.SystHist), (bundle_id: co.eclecticlight.SystHist), 1, 0, 1, 0, 4, 4, 0
com.apple.syspolicy.exec GK eval - was allowed: 1, show prompt: 1
com.apple.syspolicy.exec Skipping TCC check due to process: 692, 0, 692
com.apple.syspolicy Found console users: <private>
com.apple.syspolicy.exec Prompt shown (5, 0), waiting for response: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: co.eclecticlight.SystHist), (bundle_id: co.eclecticlight.SystHist)

When SIP has been disabled, there are precious few entries from com.apple.syspolicy or com.apple.syspolicy.exec. Instead, XProtect appears to be left to its own devices, and doesn’t fare well:
com.apple.xprotect XPAssessment performAnalysisOnFileImpl continueOnError set to 0
com.apple.xprotect XprotectService Calling SecAssessmentCreate with URL <private>, context <private>
XprotectService SecTrustEvaluateIfNecessary
com.apple.xprotect XprotectService Bundle is not apple signed
com.apple.xprotect XprotectService Bundle size result: 18388222 (YES)
com.apple.xprotect XprotectService Always scan: YES
com.apple.xprotect XprotectService Starting malware scan for: <private>
kernel XprotectService [697] crossed memory high watermark (15 MB); EXC_RESOURCE
kernel Full corpse enqueued for XprotectService
com.apple.xnu memorystatus kernel kernel EXC_RESOURCE -> XprotectService[697] exceeded mem limit: ActiveSoft 15 MB (non-fatal)
ReportCrash event condition bump 0 -> 1
ReportCrash post-exception thread qos drop 21 -> 17
ReportCrash PID 697 exceeded the memory high watermark; Invoking ReportMemoryException with corpse.

There are no other entries referring to Gatekeeper or those checks. The effects of disabling SIP appear extensive and pervasive throughout several of the layers of app launch security.

CDHashes are central

With the adoption of notarization, apps run in macOS should now fall into one of five categories:

  • signed by Apple, either its own apps or those delivered through its App Store;
  • notarized by Apple, with its CDHashes added to Apple’s database;
  • signed (either with a Developer certificate, or ad hoc) locally, and not distributed over the internet, with its own unique CDHashes;
  • unwanted or malicious, with revoked CDHashes,
  • unrecognised, and potentially malicious.

These emphasise the importance of the online ‘notarization’ checks of CDHashes performed in all circumstances where macOS doesn’t have previous records of saved CDHashes for that code. Their primary purpose isn’t to validate notarization, but to identify code as known good, known bad, or unknown. When Apple’s security engineers identify new malware, its CDHashes can quickly be added to the database as being revoked, so ensuring that all subsequent checks of the same CDHash will be classified as revoked, for malicious code. This is a rapid response that should have no false positives, in which benign code is mistakenly identified as being malicious.

Typically, the checking sequence is reported in the log with:
com.apple.syspolicy looking up ticket: <private>, 2, 1
com.apple.syspolicy cloudkit record fetch: <private>, <private>
com.apple.syspolicy cloudkit request cache info: <private>, max-age=300
com.apple.syspolicy CKTicketStore network reachability: 1, Mon Aug 26 09:15:45 2024
com.apple.syspolicy Inserting ticket: <private>
com.apple.syspolicy completing lookup: <private>, 0

[and so on with further lookups]
and those are among the only entries from com.apple.syspolicy seen when SIP is disabled.

When full security is enabled, those are completed with
com.apple.syspolicy.exec GK evaluateScanResult: 0, PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: co.eclecticlight.SystHist), (bundle_id: co.eclecticlight.SystHist), 1, 0, 1, 0, 4, 4, 0
But when SIP is disabled, those don’t appear, and seem to be substituted by application of Security rule 11 instead.

The downside of CDHash checks is that their false negative rate can be alarmingly high. Change a single bit in the code being hashed, and the hash will amplify that change, and is completely different. Hence the importance of notarization to establish which CDHashes definitely aren’t from malicious code.

One threat to this system occurs when a user mistakenly blocks their Mac from connecting to Apple’s database using CloudKit, for example using a misconfigured software firewall. Without a suitable vulnerability, malicious software shouldn’t be able to use this approach to block a payload from being checked.

I don’t know whether any third-party security products use a similar checking mechanism with their own local or remote CDHash databases, but this appears to be a great advantage to the protection built into macOS.

Performance

Two of the checks performed with full security enabled are dependent on the size of the app being checked. Fully validating an app’s CDHashes against those in its signature or notarization ticket should benefit from hardware acceleration, particularly on Apple silicon, and can be tackled hierarchically. It appears unlikely to result in significant delays to launching an app.

XProtect scans are more likely to be responsible for observable delays in app launch times, though. With the recent growth in the number of Yara rules, and their length, scans performed after an app’s first launch are the most probable cause of large and complex app bundles requiring several seconds before the app can be run.

Summary

I have updated the flow chart I first proposed as a result of observations made of app launches in Sonoma 14.4.1:

launchsonomaapp2

This is also available as a tear-out PDF here: launchsonomaapp2

I welcome any evidence that will refine and improve that, please.

Previous articles

Launching apps in Sonoma 14.6.1: Full security
Launching apps in Sonoma 14.6.1: Reduced security
Launching apps in Sonoma 14.6.1: Known malware
How does Sonoma check an app before launch? (Sonoma 14.4.1)

Launching apps in Sonoma 14.6.1: Known malware

By: hoakley
29 August 2024 at 14:30

Previous articles in this series described how macOS 14.6.1 security systems check the launch of apps when full security is in force on an Apple silicon Mac, and how those are changed by disabling SIP and Gatekeeper checks. Those have shown how checks are layered in accordance with the Security architecture of macOS, how different layers are invoked according to the status of an app (whether it’s quarantined, notarized, or has been run previously), and how extensive are the effects of disabling SIP. But no account of app security can be complete without examining how it protects against real malware, the aim of this article.

Methods

In these tests, I have again run four variants of the same 14.6.1 VM:

  • Full Security, with SIP and Gatekeeper/XProtect enabled;
  • Full Security, with Gatekeeper/XProtect disabled;
  • Permissive Security, with SIP disabled;
  • Permissive Security, with both SIP and Gatekeeper/XProtect disabled.

Samples of malicious software were obtained from the Objective-See Foundation’s collection. Three were chosen:

  • Atomic Stealer (AMOS, or Soma)
  • Genieo (InstallMac)
  • XCSSET

These were downloaded directly to each of the four VMs, when they were running in isolation in ViableS. Each was then unZipped and the contents moved to the Documents folder to try to ensure that their code wouldn’t be subjected to app translocation. Full log extracts were obtained from the Full Security VM for the first 5 seconds after launching Atomic Stealer and XCSSET; as the Genieo sample only installed its payload and didn’t launch its code, no log record was obtained for that. Log records weren’t obtained for the other three VMs, although the results of running the malicious payloads were observed for comparison against those of the Full Security VM.

Atomic Stealer

zamosshot

This was presented in a disk image that hadn’t been signed by a Developer certificate, and encouraged the user to try to bypass full Gatekeeper checks by opening the malicious payload CardGame.app using the Open command in the Finder’s contextual menu, a common strategy adopted by malware developers. This ruse was spotted early as a security exception with the code -67062, indicating that the disk image was unsigned, and that resulted in the app being translocated in its disk
SecTranslocateCreateSecureDirectoryForURL: created /private/var/folders/s0/[…]/CardGame.app
This appears to be a less usual cause of translocation, although strictly within its rules.

AMFI quickly found a code signature issue, as reported by the kernel
AMFI: '/private/var/folders/s0/[…]/CardGame.app/Contents/MacOS/My Go Application.app' has no CMS blob?
AMFI: '/private/var/folders/s0/[…]/CardGame.app/Contents/MacOS/My Go Application.app': Unrecoverable CT signature issue, bailing out.
AMFI: code signature validation failed.

Gatekeeper and XProtect scans followed, and the CDHashes were checked with Apple’s database over CloudKit. This discovered that one of the hashes had been revoked
Notarization daemon found revoked hash: {length = 20, bytes = 0xe430ea6d59a70ac00c1b8552092f4de0bbb80232}
resulting in another security exception, this time of -66992, confirming that this code has been revoked. That check was then repeated with the same result.

Shortly after that, the XProtect scan was completed, finding a match for Atomic Stealer A
GK Xprotect results: PST: (vuid: 11F66D42-5827-3465-A741-F434860C2862), (objid: 20), (team: (null)), (id: (null)), (bundle_id: (null)), XPScan: 11,-7676743164328624005,2024-08-27 07:31:10 +0000,MACOS.SOMA.A
and the decision was made to present the malware warning prompt
present prompt: uid=501, conn=yes, type=Malware, op.ident=2F90B5EF-D483-43C7-BBD1-77E8EABF4D62, info.ident=8D56578B-833F-4629-86F0-4E0A8EDD7D49, info={<private>}
indicating that it’s game over for the CardGame app and its disk image.

This sample of Atomic Stealer was thus detected by two different and independent methods: its CDHash ‘notarization’ check revealing its revocation, and the XProtect scan matching it to the known signature of MACOS.SOMA.A. As the first of those is unaffected by disabling SIP or Gatekeeper, it’s not surprising that the sample was detected and blocked in each of the four VMs.

Genieo

zgenieshot

This was presented in an Installer package as an Intel binary. This claimed to install “Apple software” and triggered a request to download and install Rosetta 2 if that wasn’t already available. The installer appeared to complete without eliciting any warnings, and it’s presumed that the malware would either have been detected later when there was an attempt to launch it, or in an XProtect Remediator scan.

All four VMs behaved identically, and there was no sign of recognition that the software installed might be malicious. This raises questions about the security inherent to Installer packages and whether there might be exploits available using Intel binaries in Rosetta 2, given that it resigns translated executable code.

XCSSET

zxcsshot

This was presented in a bogus app named Xcode. Attempting to run that resulted in its detection, and the invitation to remove it, in this case without it being positively identified.

As this was presented as an app containing unsigned code, that came under suspicion early during its assessment, and it was translocated even though it had been moved from its original location
SecTranslocateCreateSecureDirectoryForURL: created /private/var/folders/s0/[…]/Xcode.app
That appears to have occurred beyond previous rules for translocation.

Gatekeeper and XProtect scans followed, and it was confirmed that the code was unsigned
Error Domain=NSOSStatusErrorDomain Code=-67062
Unsigned code in: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 81906), (team: (null)), (id: (null)), (bundle_id: (null))

CDHash checks using CloudKit didn’t find a match, and were simply reported as
ticket not available: <private>
Gatekeeper’s scan reported that the app didn’t contain a bundle, but XProtect found no match with current Yara rules. The decision was made to present the malware warning prompt
present prompt: uid=501, conn=yes, type=Malware, op.ident=A66F9ED6-EDE7-48E9-B1F8-74CB77C43C9E, info.ident=39D1FBB5-2620-483B-AD3C-6FC5118A406F, info={<private>}
and the attempt to launch the app was blocked.

As these traits would still be detected with SIP and Gatekeeper disabled, all four VMs blocked the code and displayed the same alert to the user.

Limitations

In reality, it’s common for attacks to consist of the initial download of a small dropper, which in turn downloads the main payload. One of the disadvantages of testing malware samples is that this presentation of the payload can’t be taken into account. Payloads are often downloaded using methods that escape quarantine. Another significant difference is that samples often lack code signatures that may be present in the originals, and may change frequently as Developer certificates are revoked and replaced.

Detection information

There appears to be almost no information on how macOS detects different groups of malicious software. Inevitably, Apple provides none at all, and few in-depth analyses of malware give any details about its presentation, in terms of any signatures used, and whether they or CDHashes have since been revoked by Apple. This is a difficult area, given that many of those who analyse and report on malware work for vendors of security products. There appears to be a valuable role for independent assessment of whether and how detection takes place in macOS, major factors in any risk assessment.

I’d like to express my gratitude to the Objective-See Foundation for collecting and making available its extensive library of malware samples, without which none of these tests would have been possible.

Launching apps in Sonoma 14.6.1: Reduced security

By: hoakley
28 August 2024 at 14:30

In the first of these articles, I examined security aspects of the process of launching various app configurations in macOS Sonoma 14.6.1, on an Apple silicon Mac with full boot security and other security settings. This article moves on to discover how those change when boot security and security settings are reduced. Full details of how this was done are given in the previous article.

To remind you, the apps used were:

  • SystHist – notarized, quarantined, moved from its landing folder to avoid app translocation;
  • SilentKnight – notarized, not quarantined, previously run;
  • Sparsity – notarized, not quarantined, not previously run;
  • DelightEd3 – not notarized, signed with a Developer certificate, not quarantined, not previously run;
  • DelightEd3resigned – not notarized, ad hoc signed, not quarantined, not previously run.

None of the apps run in an app sandbox, and those notarized use a hardened runtime.

This article covers these three variants of the same 14.6.1 VM:

  • Full Security, with Gatekeeper/XProtect disabled;
  • Permissive Security, with SIP disabled;
  • Permissive Security, with both SIP and Gatekeeper/XProtect disabled.

In each VM, settings were confirmed using SilentKnight, which in turn calls standard system tools to determine current security settings, such as those when both SIP and Gatekeeper were disabled.

sksipoff

Gatekeeper disabled

Surprisingly, with Gatekeeper assessments disabled, com.apple.syspolicy.exec still reported that Gatekeeper assessments were made
GK process assessment: <private> <-- (<private>, <private>)
Gatekeeper assessment rooted at: <private>

and later
queueing up scan for code: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 69229), (team: (null)), (id: (null)), (bundle_id: (null))
GK performScan: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 69229), (team: QWY4LRW926), (id: (null)), (bundle_id: (null))

Following that, XProtect scanned
XPAssessment performAnalysisOnFileImpl continueOnError set to 0
Xprotect is performing a direct malware and dylib scan: <private>

using its standard Yara rules.

CloudKit ticket lookup also proceeded as normal. After a while, though, XProtect announced
Xprotect is skipping executable assessment: <private>

This concluded with
GK scan complete: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 69229), (team: QWY4LRW926), (id: (null)), (bundle_id: (null)), 4, 4, 0
and
GK evaluateScanResult: 0, PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 69229), (team: QWY4LRW926), (id: co.eclecticlight.SystHist), (bundle_id: co.eclecticlight.SystHist), 1, 0, 1, 0, 4, 4, 0
GK eval - was allowed: 1, show prompt: 1

The normal prompt for user consent was displayed, and handled as expected. Following that, launch proceeded normally.

Similar entries appeared in the checks made on all apps that had undergone Gatekeeper and XProtect assessment when full security was in force. There is nothing in the log entries to indicate that disabling Gatekeeper had any effect on the checks that were made, although as none of these apps failed assessment, it’s possible that any failures would have been ignored.

SIP disabled

When SIP was disabled, the structure of pre-launch assessments changed, and appeared disordered in comparison to those performed under full security and with only Gatekeeper disabled. Most notable, perhaps, was the almost complete absence of log entries from the com.apple.syspolicy subsystem, which in full security is so prominent, although its service syspolicyd did appear in entries.

Although quarantine was recognised, no entry reported the start or conclusion of any GK (Gatekeeper) assessment, nor subsequent XProtect scans. Instead, the XProtect service wrote
Bundle is not apple signed
Bundle size result: 18388222 (YES)
Always scan: YES

Normal ticket checks were made via CloudKit, but shortly after those were completed, XProtect tried to use its standard Yara rules, and ran out of memory doing so, with the kernel reporting
process XprotectService [697] crossed memory high watermark (15 MB); EXC_RESOURCE
XProtectService therefore ran into trouble before it had even started to scan the app. While some entries suggested prompting the user for their consent, that doesn’t appear to have happened. Eventually the app launched in spite of the disorder that had preceded.

When launching a notarized app that wasn’t quarantined, neither Gatekeeper nor XProtect appear to have had any involvement in the approval of the launch.

SIP and Gatekeeper disabled

Results were essentially identical to those obtained with SIP alone disabled, even down to XProtectService exceeding its memory high watermark, and the almost complete absence of log entries from the com.apple.syspolicy subsystem.

SIP and Gatekeeper settings

Prior to examining these log records, I thought I had a clear idea as to what these two controls do. In fact, neither of them does what you’d expect.

Disabling Gatekeeper or XProtect checks doesn’t stop them from occurring, although it might result in macOS ignoring any errors they might find. That would be consistent with the statement in the spctl man page: “Operations that would be denied by system policy will be allowed to proceed; assessment APIs always report success.”

On the other hand, disabling SIP almost completely stops the whole com.apple.syspolicy subsystem, which ordinarily plays a major role in pre-launch checking of apps. This effectively kills both Gatekeeper and XProtect, leaving those checks in disarray. When the XProtectService tries to lend a hand, its attempt to ingest the current Yara rules runs it out of memory, and it appears unable to render any useful assistance to the pre-launch checks.

This may explain why disabling SIP has the effect of shortening the time to launch an app, most noticeably with larger and more complex apps. In return for launching in a shorter time, the app probably isn’t checked against XProtect’s Yara definitions, so could still contain malicious code that would pass undetected.

In the next article I’ll show what does happen when this system encounters live malware.

Launching apps in Sonoma 14.6.1: Full security

By: hoakley
27 August 2024 at 14:30

This is the first of a series of three articles that look in detail at the launch process of apps in macOS Sonoma 14.6.1, with the emphasis on security checks. This follows my earlier look in 14.4.1, and covers a wider range of situations, including the effects of disabling SIP and Gatekeeper, and how known malicious software is handled.

Methods

All tests were performed in a series of Sonoma 14.6.1 virtual machines (VMs) running on a Mac Studio M1 Max host, also running 14.6.1. VMs are preferred as they enable a consistent environment and easy control of boot security and security settings, together with relatively low rates of log entries. Log extracts were obtained using Ulbow and analysed in their entirety for the first 5-7 seconds after launching apps in the Finder.

Apps used were:

  • SystHist – notarized, quarantined, moved from its landing folder to avoid app translocation;
  • SilentKnight – notarized, not quarantined, previously run;
  • Sparsity – notarized, not quarantined, not previously run;
  • DelightEd3 – not notarized, signed with a Developer certificate, not quarantined, not previously run;
  • DelightEd3resigned – not notarized, ad hoc signed, not quarantined, not previously run.

None of the apps run in an app sandbox, and those notarized use a hardened runtime.

Four variants of the same 14.6.1 VM were run:

  • Full Security, with SIP and Gatekeeper/XProtect enabled;
  • Full Security, with Gatekeeper/XProtect disabled;
  • Permissive Security, with SIP disabled;
  • Permissive Security, with both SIP and Gatekeeper/XProtect disabled.

All had bridged network access to the network and internet, and shared folders with the host, when running these non-malicious apps.

This article describes what happens in the log in the first of those conditions, full security with both SIP and Gatekeeper/XProtect enabled.

Quarantined notarized app

This underwent the fullest checks of these tests. Once LaunchServices announces that it’s opening the app, the following sequence of events is recorded.

CDHashes from the app are copied, here only those for the Arm architecture. As the app is unknown, it’s next registered with LaunchServices. Gatekeeper assessment is then started just 0.07 seconds after announcement of the launch, in the log entry
GK process assessment: <private> <-- (<private>, <private>)
com.apple.syspolicy.exec then starts work on scanning for code, followed by the first mention by LaunchServices that the app is quarantined.

The Gatekeeper scan is announced in
GK performScan: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: (null)), (bundle_id: (null))
followed by the XProtect scan in
Xprotect is performing a direct malware and dylib scan: <private>
and assignment of the risk category according to its quarantine
QUARANTINE: Setting risk category to LSRiskCategoryUnsafeExecutable
XProtect states the Yara rules it’s using
Using XProtect rules location: /Library/Apple/System/Library/CoreServices/XProtect.bundle/Contents/Resources/XProtect.yara

com.apple.syspolicy next processes the app’s notarization ticket
looking up ticket: <private>, 2, 1
by trying to fetch its record using CloudKit. That’s followed by entries indicating the network access required to connect with iCloud and check the ticket. Success is reported by com.apple.syspolicy in
CKTicketStore network reachability: 1, Mon Aug 26 09:15:45 2024
looking up ticket: <private>, 2, 0

and further lookups.

A little later, Gatekeeper announces the XProtect results
GK Xprotect results: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: (null)), (bundle_id: (null)), XPScan: 0,-7676743164328624005,2024-08-26 08:19:01 +0000,(null)
and its scan is complete
GK scan complete: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: (null)), (bundle_id: (null)), 4, 4, 0

Because this is the first launch of a quarantined app, com.apple.syspolicy.exec decides it gets a first launch or “code-evaluation” prompt “because responsibility”. If the user gives approval, the app is allowed to proceed. Its quarantine flag is updated, and the bundle record registered as trusted. The final step is then to create and save its provenance data
Created provenance data for target: TA(e8217440d9326f59, 2), PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: co.eclecticlight.SystHist), (bundle_id: co.eclecticlight.SystHist)
Handling provenance root: TA(e8217440d9326f59, 2)
Wrote provenance data on target: TA(e8217440d9326f59, 2), PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 62947), (team: QWY4LRW926), (id: co.eclecticlight.SystHist), (bundle_id: co.eclecticlight.SystHist)
Putting executable into provenance with metadata: TA(e8217440d9326f59, 2)
Putting process into provenance tracking with metadata: 692, TA(e8217440d9326f59, 2)
Tracking process with attributes: 692, TA(e8217440d9326f59, 2)

Without quarantine

A notarized app that hasn’t been run previously on that system and isn’t quarantined undergoes a similar sequence, but without the first launch or “code-evaluation” prompt. Its bundle record is registered as trusted, rather than being classified as an Unsafe Executable, but it still gets a full XProtect scan and ticket lookup using CloudKit.

Subsequent launches

The briefest launch process is that for an app that has only recently been run. That appears to skip Gatekeeper and XProtect assessments, and there’s no ticket lookup either. Pre-launch processes can then take less than 0.1 second.

Launching a known app following a cold boot can be as quick, although in this case there is a brief Gatekeeper assessment reported in the log. The key entry here comes from com.apple.syspolicy.exec:
Code already evaluated, using results.
Those are checked by Gatekeeper before launch proceeds, with the kernel reporting
evaluation result: 2, exec, allowed, cache, 1724654056, 4, c0a2e35c20a69dfd, /Applications/SilentKnight.app

Signed with developer certificate

An unquarantined app that isn’t notarized but is correctly signed using a Developer certificate is similar to its notarized equivalent, except that looking up the ticket using CloudKit is of course unsuccessful. Repeated attempts are made to find it, though, before going on to check “the legacy list” and check “legacy policy”. This results in the decision
Match downgraded from DevID to None based on legacy policy for: PST: (vuid: 7C5C43BF-A338-4228-B61E-5038F1D93EDB), (objid: 60118), (team: QWY4LRW926), (id: (null)), (bundle_id: (null))
but the kernel decides to allow launch to proceed
evaluation result: 6, exec, allowed, cache, 1724660700, 0, 9576bac3e248c07b, /Applications/DelightEd3.app

Ad hoc signature

This is detected early during pre-launch checks by AMFI (Apple Mobile File Integrity), despite the bundle record being registered as trusted. The kernel reports
AMFI: '/Applications/DelightEd3resigned.app/Contents/MacOS/DelightEd' is adhoc signed.
AMFI then records
No certificate chain found
Failure getting cert chain
Basic requirement validation failed, error: Error Domain=NSOSStatusErrorDomain Code=-67050 UserInfo={SecCSArchitecture=<private>}

and an error code of -423, given as “The file is adhoc signed or signed by an unknown certificate chain”.

Despite that, Gatekeeper assessment continues, with an XProtect scan. Attempts to look up the app’s ticket inevitably fail despite many attempts, and an error code of -67018 “Code did not match any currently allowed policy” is awarded. Launch then proceeds.

In the next article I’ll show how those are affected by disabling SIP and Gatekeeper assessments.

Last Week on My Mac: Layered security and herd immunity

By: hoakley
25 August 2024 at 15:00

Reviewing security products intended to detect and remove malicious software is far harder than it used to be. There was a time when all you had to do was set your virtual machine to Reduced Security, disable SIP, and maybe Gatekeeper too, then strip any quarantine extended attributes from samples of recent malware. With nothing to trigger checks by macOS security, the malware was fully exposed to the security software under test, and you could assess how successful the product was at detection.

Yet in Sonoma it seems that you also need to disable AMFI (Apple Mobile File Integrity), or macOS will intervene to detect the malware before the product under test, which then won’t get a look-in.

LaunchSonomaApp1

Back in Sonoma 14.4.1 I summarised those layers of checks in this diagram.

Although it’s encouraging that malware detection is now so pervasive, this makes it almost impossible to assess the other side of an anti-malware product, how well it can remove the malicious software it discovers. The same applies to Apple’s own XProtect Remediator. Stunting and tricking macOS to the point where malware can deploy fully isn’t anywhere near as easy as it was.

xprmensis01

You know you have failed again when macOS pops up an alert like this, telling you that it has intercepted your attempt to bypass its protection.

From a user’s point of view, this can only be good. macOS security protection is designed and applied in multiple layers so that, even if something manages to trick its way past one check, the next layer is there to stop it in its tracks.

The strangest thing about XProtect Remediator isn’t the fact that, left to its own devices, it doesn’t inform the user when it detects malware, but that it will happily remove the malware it detects and let you carry on using your Mac as if nothing had happened. If you’re as old school as me, you might wonder why you shouldn’t have to wipe your Mac completely, restore it in DFU mode if an Apple silicon model, and rebuild it from scratch. Surely the slightest suspicion of anything malicious demands such a scorched earth approach?

That too depends on what security protection your Mac has active. If that includes SIP and the SSV, then there’s no known malware that can alter anything on the SSV, and what it can do on your Data and other volumes is also limited. The days of viruses wreaking havoc throughout your entire Mac thankfully seem long since past. Viruses, of course, were designed to replicate themselves throughout a Mac’s storage, but today’s trojans and stealers are after two things, and those are your secrets and money.

But that depends on the behaviour of potential victims of malware. Like any business, those who try to profit from theft of our secrets and other malicious behaviour have to consider the size of their market of victims. For many years, this was one of the Mac’s defences against malware: why would anyone want to develop for such a small proportion of potential victims, when PCs readily provided far more? More recently, some have unfortunately recognised our potential, and now we’re suffering the consequences.

This is where standard macOS security protection comes into play. At present, the great majority of Macs running Big Sur or later have SIP enabled and their SSV fully protected. The victim market for malware requiring write access to the System volume is tiny. But what if it became more usual for Macs to be unprotected, and all that malware had to do was gain root privileges to be able to write to the System volume?

This is a phenomenon akin to vaccination and resulting herd immunity in pandemics like Covid. States and nations with low protection by vaccination and little herd immunity suffered the highest rates of infection and consequent death rates. But so few realised that exercising their personal choice to remain unvaccinated had that effect, despite the fact that humans throughout the world had eliminated the deadly disease of smallpox by building herd immunity in the late twentieth century.

Security protection in macOS works in layers. Each layer you disable opens wider the window of opportunity for attackers. The more Macs that have any given layer of protection disabled, the lower the herd immunity to attack, and the more likely that malware will try to take advantage of that.

Why launch constraints can crash apps

By: hoakley
23 August 2024 at 14:30

Some apps may crash when launched because there’s something wrong in the app. In Ventura and later, that might occur because macOS is refusing to run them because of security rules, specifically launch constraints. These were extended in Sonoma to allow any app to limit the code it runs to what should be there, in launch environment and library constraints. This article explains what these are, and how you can recognise when constraints are applied.

Code without constraints

Launching an app without constraints isn’t as unconstrained as that might suggest. It’s still given an environment to run in, with settings such as the user’s Home folder and some standard paths including a temporary folder buried in /var/folders. If you’re interested to see what those can include, Mints has a button to show you its own launch environment.

On top of those, the app is limited by standard permissions as to what it can access without obtaining elevated privileges, and everything is subject to the privacy restrictions imposed by TCC according to the app’s Privacy & Security Settings.

But the app can be run from pretty well anywhere, and can run code from libraries, frameworks and other places as it wishes.

Launch constraints

The first set of launch constraints became obvious if you tried to copy and run from a different location one of the apps bundled in Ventura. This has had its purposes in the past, for example to run Network Utility after Apple first gutted then removed it. Try that with one of Ventura’s bundled apps, and the copy can’t be run from any location apart from the SSV it’s installed in, as it crashes immediately. Look in its crash report and you’ll see something like
Exception Type: EXC_CRASH (SIGKILL (Code Signature Invalid))
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: CODESIGNING 4 Launch Constraint Violation

That’s given in a bit more detail in the main log, for Terminal as
AMFI: Launch Constraint Violation (enforcing), error info: c[1]p[1]m[1]e[2], (Constraint not matched) launching proc[vc: 1 pid: 2440]: /Users/hoakley/Documents/00crypt/Terminal.app/Contents/MacOS/Terminal, launch type 0, failure proc [vc: 1 pid: 2440]: /Users/hoakley/Documents/00crypt/Terminal.app/Contents/MacOS/Terminal
ASP: Security policy would not allow process: 2440, /Users/hoakley/Documents/00crypt/Terminal.app/Contents/MacOS/Terminal
xpcproxy exited due to OS_REASON_CODESIGNING | Launch Constraint Violation, error info: c[1]p[1]m[1]e[2], (Constraint not matched) launch type 0, failure proc [vc: 1]: /Users/hoakley/Documents/00crypt/Terminal.app/Contents/MacOS/Terminal

The same happens if you try running a forbidden command, such as /usr/libexec/periodic-wrapper.

Open the app using Apparency and view its Launch Information, and you’ll see the launch constraints that caused this.

apparency2

For the Chess app, those read
(on-authorized-authapfs-volume or on-system-volume ) and launch-type = 3 /* CS_LAUNCH_TYPE APPLICATION / and validation-category = 1 /* CS_VALIDATION_CATEGORY_PLATFORM */
which should give you a good idea that app can only be run from its standard location in the SSV or System volume. To make this even harder, Sonoma’s Finder tries to stop you from even copying bundled apps to other locations, and you now have to be ingenious to try launch constraints out.

Launch constraints were first described by Csaba Fitzl, and he has since compiled a listing of all those known. Those shown for Chess.app are Category 14, and common to other bundled apps. Their effect is to prevent all copies of that app from being launched from elsewhere.

Trust caches

Instead of macOS looking up each binary’s launch constraints from the binary itself, all those constraints are assembled into Trust Caches, where they’re listed by the code directory’s hash (cdhash). To look up the launch constraints for the Terminal app, the system first calculates the cdhash for its code directory, then looks in the Trust Cache for the launch constraints given for that cdhash.

The System volume contains a static Trust Cache that covers all the executable binaries that come as part of the system. That’s locked into read-only storage during the early kernel boot phase of startup. Additional Trust Caches are authenticated to ensure they haven’t been tampered with, and loaded when required. Apple cites the example of the Trust Cache required by the code within macOS software updates (known as the update brain) that runs the process, allowing it to run with platform privileges, as it requires to perform the update. Apple gives further details on Trust Caches in its Platform Security Guide.

Disabling launch constraints

What if you need to ignore those launch constraints imposed by macOS? Because system executables are laid out in the static Trust Cache, there’s no way to modify that, and no way to override it. All you can do is disable System Integrity Protection (SIP), which is required for launch constraints to operate.

Environment constraints

Launch constraints and the Trust Cache system are complete and fully enforced as of Ventura 13.3, and have been extended for use by third-parties in Sonoma. Developers can build dictionaries containing facts and applying operations to them to improve the security of their apps. Constraint dictionaries are either saved in property lists for launchd, or in those used for signing code. These too are associated with cdhashes, use some categories common to other trust caches, and work similarly to protect third-party code such as helper apps.

While they might appear overkill, they can be used to address known security problems, of which the most prominent must be maintaining trust with privileged helper apps and XPC services, which have often proved weak points in app security. Apple provides two detailed articles, one explaining how to define these constraints, the other how to apply them. I suspect that we’ll be seeing more of these in the future.

Controlling System Integrity Protection using csrutil: a reference

By: hoakley
21 August 2024 at 14:30

System Integrity Protection (SIP) is more than one of the frontline security protections in macOS, it’s a whole family of them, including controls over kernel extensions, protection for NVRAM, and more. This article explains how you can manage different features in SIP using the csrutil command tool, primarily on Apple silicon Macs running Sonoma or Sequoia.

SIP fully enabled

Unless you have very good reason to do otherwise, your Mac should have SIP fully enabled at all times. Utilities like SilentKnight check whether this is so, and if SIP isn’t enabled, you need to fix that as soon as possible. Start your Mac up in Recovery mode, pass through to Options, and open Terminal from the menu there. Enter the following command:
csrutil enable
and then authenticate when prompted. When you have quit Terminal, open Startup Security Utility from the same menu. You should there be able to set your Mac’s security back to full, unless you need to run it at reduced security to load a third-party kernel extension. Once that is set, restart your Mac and it should have returned to normal with SIP fully active, as SilentKnight should confirm.

SIP’s features

Its original and core feature is to protect much of the system from being changed, even by those with root privileges, referred to here as Filesystem Protections, and introduced in El Capitan, in 2015. This is now extensive, and ranges from whole directory trees in the System volume down to individual extended attributes such as com.apple.macl on otherwise unprotected files.

SIP is also responsible for enforcing requirements for the signing and notarization of non-system kernel extensions, and protection of the integrity of the kernel, respectively termed Kext Signing and Kernel Integrity Protections. It imposes restrictions on the use of kernel debuggers in Debugging Restrictions and the DTrace tool in DTrace Restrictions.

Restrictions over changes that can be made to the NVRAM and boot arguments it can apply to the kernel are controlled by SIP’s NVRAM Protections and Boot-arg Restrictions, while the Authenticated Root Requirement should be self-explanatory. Other features of SIP, including the enforcement of the hardened runtime required of notarized apps, are most probably part of its Apple Internal features.

SIP partially disabled

Although there may be occasions when you need to disable SIP completely, those should be extremely rare. In most cases, you should aim to disable only the features that require it, then return SIP to fully enabled as soon afterwards as possible. One good way to remind yourself that you have temporarily disabled SIP is to write that on a sticky note and put that on your Mac’s display until you’ve enabled SIP again. Don’t leave it to memory, as you’re likely to procrastinate and forget.

To disable SIP completely, start up in Recovery, set Startup Security Utility to Reduced Security, and open Terminal. There enter the command
csrutil disable
confirm that’s what you really want to do, and authenticate. When you restart your Mac, SIP should then be disabled, as you can confirm in Terminal using
csrutil status

Finer control is separated out into its individual features:

  • Apple Internal, disabled in XNU by CSR_ALLOW_APPLE_INTERNAL, and only disabled when SIP is fully disabled
  • Kext Signing, disabled by CSR_ALLOW_UNAPPROVED_KEXTS, abbreviated in csrutil to kext
  • Filesystem Protections, disabled by CSR_ALLOW_UNRESTRICTED_FS, abbreviated to fs
  • Debugging Restrictions, disabled by CSR_ALLOW_KERNEL_DEBUGGER and CSR_ALLOW_TASK_FOR_PID, abbreviated to debug
  • DTrace Restrictions, disabled by CSR_ALLOW_UNRESTRICTED_DTRACE, abbreviated to dtrace
  • NVRAM Protections, disabled by CSR_ALLOW_UNRESTRICTED_NVRAM, abbreviated to nvram
  • BaseSystem Verification, abbreviated to basesystem
  • Boot-arg Restrictions, disabled with nvram
  • Kernel Integrity Protections, disabled with kext
  • Authenticated Root Requirement, disabled by CSR_ALLOW_UNAUTHENTICATED_ROOT, managed separately using csrutil authenticated-root disable and enable.

To enable SIP with one of these features disabled, use a command of the form
csrutil enable --without [feature]
where [feature] is the abbreviated name of the feature to be disabled, such as kext. Chain those options to disable multiple features, such as
csrutil enable --without kext --without nvram
which enables SIP with the exception of Kext Signing and Kernel Integrity Protections, and NVRAM Protections and Boot-arg Restrictions.

Other configuration flags in XNU that don’t appear to be directly supported by csrutil include: CSR_ALLOW_TASK_FOR_PID, CSR_ALLOW_DEVICE_CONFIGURATION, CSR_ALLOW_ANY_RECOVERY_OS and CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE. Those should be disabled when SIP is fully disabled, though.

Authenticated Root Requirement is managed separately from other SIP controls. To enable it, use the command
csrutil authenticated-root enable
To disable it,
csrutil authenticated-root disable
and to check its status
csrutil authenticated-root status
Note that fully enabling SIP also enables Authenticated Root Requirement.

The following summary table should help you use these complicated options for control of SIP features using csrutil.

csrutilopts

To disable a specific feature, use the --without option for that abbreviated feature name, or the authenticated-root disable option. Note that running csrutil enable also enables the Authenticated Root Requirement. If you need to disable that, use the command to do so after setting other SIP features using csrutil enable --without.

These commands should be good to use on Apple silicon (and Intel) Macs running Sonoma, and don’t appear to have changed in Sequoia.

Although you can check SIP status in regular user mode, making changes to it should be performed in Recovery mode, and in Apple silicon Macs any disabling of its features is likely to require Reduced Security to be set in Startup Security Utility. Status reports for most combinations of disabled features will warn that the combination being used may not be supported in the future; the only common exceptions to that are when SIP is fully enabled or fully disabled.

Thanks to Ryan for adding CSR_ALLOW_TASK_FOR_PID to the control list given above.

❌
❌