Category Archives: AppleScript
Earlier this year, Digita Security’s Patrick Wardle took apart a cross-platform backdoor trojan he nicknamed ”ColdRoot’. Wardle was retro-hunting possible malware by searching for apps on VirusTotal that access Apple’s TCC privacy database.
For those unfamiliar, TCC.db is the database that backs the System Preferences > Security & Privacy | Accessibility preferences pane and which controls, among other things, whether applications are allowed access to the Mac’s Accessibility features. Of interest from a security angle is that one of the permissions an app with access to Accessibility can gain is the ability to simulate user clicks, such as clicking “OK” and similar buttons in authorisation dialogs.
One particular comment in Wardle’s article caught my eye:
there is no legitimate or benign reason why non-Apple code should ever reference [the TCC.db] file!
While in general that is probably true, there is at least one good reason I can think of why a legitimate app might reference that file: reading the TCC.db used to be the easiest way to programmatically retrieve the list of apps that are allowed Accessibility privileges, and one reason why a piece of software might well want to do that is if it’s a piece of security software like DetectX Swift.
If your aim is to inform unsuspecting users of any changes or oddities in the list (such as adware, malware or just sneaky apps that want to backdoor you for their own ends), then reading TCC.db directly is the best way to get that information.
Just call me ‘root’
Since Apple put TCC.db under SIP protection subsequent to my reports on Dropbox’s user-unfriendly behaviour, apps are no longer able to write to the database via SQL injection. An app with elevated privileges can, however, still read the database. A sufficiently privileged app (or user) can output the current list with:
sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db 'select * from access'
On my machine, this indicates that there are twelve applications in the System Preferences’ Privacy pane, all of which are enabled save for two, namely LaunchBar and Safari:
We can see from the output that LaunchBar and Safari both have the ‘allowed’ integer set to ‘0’ (the middle of the three values in “0|0|1”) , whereas all the other apps have it set to ‘1’ (“0|1|1”).
It’s not clear to me why reading the database should require privileges. Certainly, Apple have provided APIs by which developers can test to see if their own apps are included in the database or not. The AXIsProcessTrusted() global function, for example, will return whether a calling process is a trusted accessibility client (see AXUIElement.h for related functions):
However, there remains a case (as I will demonstrate shortly) where developers may well need to know whether apps other than their own are, or are not, in the list. Moreover, there doesn’t seem to be any obvious vulnerability in allowing read access to that data, just so long as the write protection remains, as it does, in place.
The use case for being able to read the TCC.db database is clearly demonstrated by apps like DetectX Swift: security apps that conform to the principle of least privilege, always a good maxim to follow whenever practical. Asking users to grant elevated privileges just to check which apps are in Accessibility is akin to opening the bank vault in order to do an employee head count. Surely, it would be more secure to be able to determine who, if anyone, is in the vault without having to actually take the risk of unlocking the door.
Did they put a CCTV in there?
Without direct access to TCC.db, we might wonder whether there are any other less obvious ways by which we can determine which apps are able to access Accessibility features. There are three possibilities for keeping an eye on bad actors trying to exploit Accessibility without acquiring elevated privileges ourselves, each of which has some drawbacks.
1. Authorisation dialogs
The first is that we can read the property list that records all invocations of the Accessibility authorisation dialog, without admin rights:
defaults read ~/Library/Preferences/com.apple.universalaccessAuthWarning.plist
That gives us a list of all the apps that macOS has ever thrown the ‘some.app would like permission to control your computer’ dialog alert for, along with an indication of the user’s response (1= they opened sys prefs, 0= they hit Deny).
This list could prove useful for identifying adware installers that try to trick users into allowing them into Accessibility (looking at you, PDFPronto and friends), but it’s main drawback is that the list is historical and doesn’t indicate the current denizens of Accessibility. It doesn’t tell us whether the apps in the list are currently approved or not, only that each app listed once presented the user with the option of opening System Preferences and what the user chose to do about it at that time.
2. Distributed Notifications
The second option is that developers can register their apps to receive notifications when any other application gets added or removed from the list with code similar to this:
DistributedNotificationCenter.default().addObserver(self, selector: #selector(self.accessibilityChanged), name: NSNotification.Name.init("com.apple.accessibility.api"), object: nil)
This ability was in fact added to DetectX Swift in version 1.04.
However, Apple hasn’t made this API particularly useful. Although we don’t need elevated privileges to call it, the NSNotification returned doesn’t contain a userInfo dictionary – the part in Apple’s notification class that provides specific information about a notification event. All that the notification provides is news that some change occurred, but whether that was an addition or a deletion, and which app it refers to, is not revealed:
Even so, notification of the change is at least something we can present to the user. Alas, this is only useful if the app that’s receiving the notification is actually running at the time the change occurs. For an on-demand search tool like DetectX Swift, which is only active when the user launches it, the notification is quite likely to be missed.
It would be nice, at least, if Apple would provide a more useful notification or an API for developers wishing to keep their users safe and informed. The lack of a userInfo dictionary in the notification was apparently reported as a bug to Apple several years back, but I suppose it could always use a dupe.
3. AppleScript – everyone’s favourite ‘Swiss Army Knife’
There is, as it turns out, a third way to reliably get exactly all the info we need about who has access to Accessibility. We can use AppleScript to return a complete list of apps in Accessibility that are enabled. Note that the output of this unprivileged script, shown here in the results pane of Script Debugger, returns a more readable version of the same list of apps we obtained from the privileged sqlite3 query of TCC.db, minus Safari and LaunchBar, which as we previously noted were not enabled:
Fantastic! There’s just one problem. While this AppleScript does not require blanket elevated privileges to run – in short, it doesn’t require an administrator password – it does need to be run by an app that is itself already in the list of Accessibility apps. If you have a script runner like Apple’s Script Editor, or third-party tools like Script Debugger or FastScripts, already approved in Accessibility, then you can run it without authorisation. It’s also worth noting that the script relies on launching and quitting System Preferences, which it attempts to do as quietly as possible.
As for DetectX Swift, I may consider adding something like this to a future version as an option for users who are happy to add DetectX to the list of apps in Accessibility.
Have your own tips about accessing TCC.db? Let us know in the Comments!
Featured pic: Can’t STOP Me by smilejustbcuz
I’ve been using Slack quite a bit recently, but I’m still not that confident with its text formatting options. Sure, they’re simple enough, but when I’m on a workspace with a 10-minute editing timeout and I’ve a heavily formatted message to send, there’s plenty of chance I might not get the formatting just the way I want in time.
That got me to thinking there must be an editor that supports Slack’s style of markdown, but I was surprised to see from Slack’s help that in fact, they don’t support regular markdown at all:
Hmm, that’s a bit disappointing. What to do?
This only works if you have access to BBEdit’s advanced features (either you’re still on the trial or you bought a license) as you’ll need the Markup menu and its ‘Preview in BBEdit’ option (Control-Command-P) for this to work.
To use the script, save it (or an alias to it) in BBEdit’s Scripts folder as ‘Slack Preview.scpt’ and assign it a shortcut key in BBEdit’s Preferences:
You’ll find the script available in the menu bar, but it’s going to be more convenient to use the keyboard shortcut. You may also need to muscle-memory the shortcut for opening the Preview window (Control-Command-P) if it isn’t open already when you run the script.
The script will prompt you if that happens:
Finally, here’s a little 1-minute video showing the script in action. You’ll note from the screenshot at the top of this post that I’ve improved the styling a bit since the video was made to more closely emulate the default Slack style, but those of you with better CSS skills than I are welcome to fiddle with that to suit your taste.
So what are you waiting for? Oh, you missed the link to the script? Here you go, then.
I often need to process lists of ‘dirty text’ which can contain lots of whitespace, new lines and so on. Fortunately, BBEdit allows you to run AppleScripts directly from its Script menu, which means we can define lots of useful little text processing scripts and have them ready to hand. I’ve got several that I’ll share over the next few posts, but probably the most oft-used of them is this one for removing whitespace.
Here’s what the script looks like. You can download the source from my pastebin here.
And here’s a little giffy showing it at work:
Share and enjoy! 🙂
Spoofing or phishing – presenting a user with fake authentication requests – is a common email tactic, but it’s not the only vector where you need to be on your guard. Every version of macOS is vulnerable to a very simple phishing attack right on your desktop that doesn’t require admin privileges to run, would not be detected by GateKeeper or XProtect, and which could easily be placed on your mac by any of the nefarious malware / adware installer scripts that come with some less reputable software downloads.
This attack isn’t new, but it’s not often talked about. The easiest way to see how it works is in this quick 4-minute demo:
As you can see, it’s easy to grab the icon of any Application and put it in the script; it doesn’t even have to be the icon of an app that’s running. The simple demo I gave above could easily launch iTunes first to increase the coherence of the attack, or it could use a completely different icon, including the icon of security programs you may have running on your mac.
How can you check?
If you were presented with a password request like this and wanted to check whether it’s legitimate or not, an easy way would be to use my free utility DetectX Swift’s Profiler. Click the Profiler function, and search for ‘osascript’ within the Running Processes section. Note how DetectX Swift shows you the text of the script being run, confirming that this dialog is up to no good:
At our house, giving visitors the wifi password is always an exercise in frustration. Can’t remember? Oh, then I’ve got to either go trawling through Keychain Access or log in to the router, neither of which are particularly appealing.
Here’s an easier way. It’ll require an admin password (and you’ll need to supply it for as many passwords as you’re looking for – no ‘with administrator privileges’ will help here I’m afraid), but otherwise requires nothing more than a quick double-click of this saved script, once you’ve added your own SSID names in place of the dummy ones.
Just to be clear, you do NOT need to add the passwords to the script. So long as the mac that you run this script on already knows the Wifi password, the script will retrieve it.
One of the things I find intrusive are the constant Swift Compiler warnings while I’m actually in the middle of writing a block of code (e.g, ‘…value was never used consider replacing…’). Well, yeah, it’s not been used *yet* …grrr!
However, turning off compiler warnings isn’t something I want to do either. It’s too easy to go into the build settings, turn them off, do a bit of coding, take a break, do a bit more coding…oh, three thousand lines later and I suddenly realize why Xcode hasn’t been correcting my mistakes all afternoon!
This script allows you to quickly and easily toggle the warnings from a hotkey, and just gives you a gentle reminder as to what you’ve done. Of course that won’t stop you forgetting, but assigning a hotkey for this script makes it painless to just turn warnings off and back on again as soon as you’ve got past whatever bit of code the compiler was complaining about.
Xcode unfortunately doesn’t have its own scripts menu, so in order to assign the script a hotkey, you’ll need to either make it into a Service with Automator or use a script runner like Red Sweater’s FastScripts.
display notification "Suppress Warnings was set to " & aVal with title "Swift Compiler - Warnings Policies"
tell application id "com.apple.dt.Xcode"
tell its front document
tell its front project
tell its front target
tell its build configuration "Debug"
set b to build setting "SWIFT_SUPPRESS_WARNINGS"
if b's value is "NO" then
set b's value to "YES"
set b's value to "NO"
my sendNotification(b's value)
do shell script "SD=~/.spindump.txt; rm $SD; spindump 1 1 -file $SD; grep 'Fan speed' $SD; rm $SD" with administrator privileges
Copy and paste the above into your (Apple)Script Editor and run it. You’ll need an Adminstrator password.
However, that doesn’t really tell you what you want to know: is my mac too hot or not? Should I do something about it?
Fortunately, we can get a better idea of the mac’s thermal state (and we don’t need admin privs to do it!), with this script:
The script not only reports the mac’s thermal state, but prints out Apple’s recommended advice, if any. Uncomment the last line of the script to get the result in a display dialog box; otherwise, you can just read the result in the results pane of your script editor.
Featured Image: Flicker
The idea behind BackupCam is to keep a continuous, rolling video of the last few minutes of activity on your mac, in just the same way as dash cams in cars work.
There’s a couple of scenarios where this might be useful. If you’re working on a project where ‘undo’ doesn’t always work reliably or when you most need it to – Xcode, for example, can often let you get your project in a mess without offering you a clear path as to how you got there or how to get back, short of discarding all changes in a particular file – with BackupCam you’ll be able to see exactly how you got to where you are.
Similarly, BackupCam can also help you to review changes that you may not have noticed at the time – perhaps if you were distracted by something else happening, either on screen or off. This can help both as a security and a troubleshooting tool
BackupCam can record up to the previous 30 minutes activity, so may help you recover something that is missed even by Time Machine or other traditional file backup mechanism.
More details are over on the BackupCam webpage, but I’ll just note here that BackupCam can also be controlled by AppleScript, with all the flexibility that that offers. Here’s a sample script that checks whether the last recording was longer ago than the time interval set in BackupCam. If it is, it kicks off a new recording session:
BackupCam is still in the early stages of development (we’re calling v1 a beta), so please feel free to report any bugs or enhancments you’d like to see. At the moment, it requires 10.11.6 or higher and only records the main display. I plan to add support for multiple displays in a future update.
Is it me, or is AppleScript experiencing something of an Indian Summer? It seems everywhere I go, people are talking more about macOS automation, AppleScript and even Apple’s curious hybrid syntax AppleScriptObjC (ASObjC).
Of course, some people have suffered miserably at the hands of AppleScript in the past, and even though the thought of scripting with access to Cocoa APIs through Objective-C is tempting, they fear the AppleScript side of it.
If that’s you, bear in mind that AppleScriptObjC isn’t really “AppleScript + Objective-C” at all. It is actually just a dialect of Objective-C that will be accepted in the (Apple)Script Editor and can be run by an instance of the AppleScript component. In plainer English, you can use Objective-C in an AppleScript without any AppleScript whatsoever!
The point of doing so would be that one could package Objective-C code in a .scpt file (or scptd bundle or AppleScript .app), and also mix whatever scripting language you prefer with calls to Cocoa’s APIs.*
The problem that using ASObjC presents anyone familiar with Objective-C is how to translate ‘pure’ Objective-C into the dialect that Script Editor (and other applescript runners like FastScripts, Keyboard Maestro, Automator, etc) can understand. If you use LateNight Software’s Script Debugger for scripting, you’ll already know that the work is done for you by the app’s built-in code completion. If you’re battling on in Apple’s default Script Editor, you’ll need to do the translation manually.
By way of example, then, here’s some original Objective-C, and below it, a translation that would work in Script Editor:
NSString *aString = @"hello";
NSString *bString = @" world";
aString = [aString stringByAppendingString:bString];
NSUserNotification *notif = [[NSUserNotification alloc] init];
notif.informativeText = aString;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notif];
set aString to NSString's stringWithString:"hello"
set bString to NSString's stringWithString:" world"
set aString to aString's stringByAppendingString:bString
set notif to NSUserNotification's alloc's init
set notif's informativeText to aString
NSUserNotificationCenter's defaultUserNotificationCenter()'s deliverNotification:notif
As you can see, there’s a direct 1-to-1 correspondence, with the 6 statements in Objective-C paralleled by the 6 statements in AppleScriptObjC.
The main peculiarity is the use of possessive word forms and that variable attribution is done by using
"set X to Y" rather than
"X = Y". Type declaration is done via the idiom
'set <var> to <NSObject>'s <class init method>', which returns an instance of the object just as it would normally. You call instance methods by putting the instance in front of the method just as you would in regular Objective-C (e.g, see line 3 of the examples).
As you can see in the screenshot below showing Xcode and Script Editor, they work in the same way. You’ll notice in Script Editor there is a
'use' statement (equivalent to Objective-C’s ‘import’), and there’s also a whole load of property statements. These latter are peculiar to the ASObjC translation, and don’t have a counterpart in pure Objective-C. All you need to know about these is for each kind of Objective-C object you want to use (NSString, NSArray, whatever*), you’ll want a property statement for it at the beginning of the script. The statement always has the same form:
property <NSObject> : a reference to current application's < NSObject>
I think the best way to think of ASObjC was recently summed up by Sal Saghoian, when he said that ASObjC is “…the ultimate duct tape. You can do anything you want with ASObjC. You own the computer.”
*not all Cocoa frameworks nor all Objective-C objects can be bridged to, but pretty much all the most useful ones are available.
I recently discovered a neat little extra automation tool on top of the familiar ones of AppleScript, Automator, and script runners like FastScripts and Keyboard Maestro. Meet Hammerspoon, which differs significantly in not using Apple Events to do many of its automation tasks. Instead, Hammerspoon bridges directly to Apple APIs using the lua scripting language, and that allows you to do some interesting things.
Here’s a good example. One of the ‘danger zones’ on your mac – by which I mean one of the favourite places for adware, malware and other assorted badwares to infect – is your LaunchAgents folders. Apps like my DetectX and FastTasks 2 keep an eye on these areas by design, warning you in the Changes and History logs when files have been added or removed from them – but Hammerspoon can add an extra little ‘canary’ warning for you too. With only a few lines of code in Hammerspoon’s config file, you can set up an alert that will fire whenever the LaunchAgents folder is modified.
It has been possible to rig up something similar for a long time with Apple’s built-in Folder Actions, but there’s a couple of reasons why I prefer Hammerspoon for this task. One, despite Apple’s attempt to improve Folder Actions’ reliability, I’m still not convinced. I get inconsistent reports when asking System Events to check whether Folder Actions is enabled even on 10.11 and 10.12. Second, Folder Actions is limited in what it will respond to. By default, only if an item is added. With a bit of effort, you can rig it up to watch for items deleted, too, but that’s pretty much it. With Hammerspoon, it’ll alert you whenever the folder or its contents are modified in any way whatsoever. The final reason for preferring Hammerspoon for this particular task is ease of use. It really is as simple as pasting this code into the config file:
hs.alert.show("Launch Agents folder was modified")
hs.alert.show("Canary folder was modified")
local aWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/Library/LaunchAgents/", myFolderWatch):start()
local bWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/_Acanary/", canaryFolderWatch):start()
And what’s the config file you ask? Nothing complicated! Just launch Hammerspoon, click its icon and choose ‘open config’.
That will launch your default text editor, and all you do is paste your code into there, save it (no need to tell it where, Hammerspoon already knows) and then go back to the Hammerspoon icon and click ‘Reload config’. That’s it. Less than a minute’s work!
There’s a lot, lot more you can do with Hammerspoon, so if you’re interested head off and check out the Getting Started guide. One of the nice things is that you can even call AppleScripts with it, so you really do have a world of automation options to choose from!