What's going on?

Earlier this week I was messing with an old detection analytic designed to fire an alert when a suspicious applet was launched. In the case of macOS post-exploitation looking for adhoc signed binaries named "applet" can provide a decent tip-off. The platform I was using reports the code signing properties of executables in just a binary state: "it is" or "it is not". However, there were some inconsistent detections across test variations. Accurately representing the code signing properties has historically been difficult for security vendors to get "right" in many cases. Whether that be consistency of reporting or the lack of detail (in this case).

As it turns the sensor I was using reported adhoc signed binaries as truly "signed". While there's no inherent harm in doing this... it does deny defenders additional resolution and increase their time spent when hunting through event timelines. Instead when presented only with binary reporting options vendors should err on the side of caution and report adhoc binaries as unsigned. The remainder of this blog will focus on details of the tests and wrap up with a glimpse into the innerworkings of osacompile.

MAC-UNSIGNED-APPLET-EXEC

The job of this analytic is to detect the execution of unsigned / adhoc signed applets. This detector's composition is based on process and code signing artifacts. Here we're looking for the execution of an application bundle: .app with the Mach-O name of "applet". Applet is the default name of the thin Mach-O wrapper created when saving OSA code as an app bundle either through ”Script Editor.app” or /usr/bin/osascript for example. Additionally, this has artifact has also been commonly seen in the XCSSET malware family.

Execution of the malicious applet

Execution of the malicious applet

XProtect malware detection event

XProtect malware detection event

events:
  - NEW_PROCESS
  - EXISTING_PROCESS
op: and
rules:
  - case sensitive: false
    op: contains
    path: event/FILE_PATH
    value: app/Contents/MacOS/applet
  - op: is
    path: event/FILE_IS_SIGNED
    value: 0

The primary artifact to point out here that caused confusion is event/FILE_IS_SIGNED which reports: 0 if the file is completely unsigned and 1 if it’s anything adhoc or above.

Test results

Variation CS flags Detects?
/usr/bin/osacompile 0x2 (adhoc) 🔥NO
"Script Editor.app"(don't sign) Not signed at all. ✅ YES
"Script Editor.app"(locally sign) 0x2 (adhoc) 🔥 NO
OSAKit API N/A (depends on implementation) N/A

Using /usr/bin/codesign -d -v <FILE_PATH> we can grab the signature of each file. Notice how the adhoc signed executables cause the sensor the report event/FILE_IS_SIGNED. Unfortunately in the case of macOS many would not consider this intuitive.

What do you think? Should adhoc be considered "signed" when you can only report a true / false representation?

I would honestly expect this not to be the case. When a solution only has two states: signed or unsigned and there are many variations in-between we should err on the side of caution --especially in a security context. For a Mach-O to be truly "signed" (at least against the Catalina model) -- in my opinion we should see a X.509 certificate chain indicating the trust of a CA (see the following Firefox example — notice the certificate chain).

Additionally, there are a few major classes of code signatures on macOS (from an analysis perspective): Unsigned, Adhoc, Developer ID. App Store, and Platform. Each of these is helpful for identification of where the binary came from.

❯ codesign -d -vvv /Applications/Firefox.app
Executable=/Applications/Firefox.app/Contents/MacOS/firefox
Identifier=org.mozilla.firefox
Format=app bundle with Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=863 flags=0x10000(runtime) hashes=18+5 location=embedded
...
CDHash=37271ba939e6813bc2a82cdb85e2c42c2c80ac6d
Authority=Developer ID Application: Mozilla Corporation (43AQ936H96)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Jun 19, 2023 at 3:01:26 AM
Notarization Ticket=stapled
TeamIdentifier=43AQ936H96

Compiling an applet