Querying Spotlight APIs With JXA

  1. I enjoy writing scripts/utilities for macOS and often use Swift and JXA. When already in a coding context, it just doesn’t feel right to me personally going from code to call an on-disk binary and then going back to code when I can just make the API calls directly from code. I have found JXA to be a bit of a challenge at times when it comes to learning how to make certain API calls since there is limited documentation available publicly. However, since JXA will remain on macOS (while other scripting runtimes such as python, perl, and ruby are being removed from base macOS installs), I enjoy putting the time in to learn JXA a bit more.
  2. As a red teamer, moving away from the command line to API calls is always preferred, especially as the state of macOS detections continues to improve over time.
......//runs the on-disk mdfind binary
var dir_check = currentApp.doShellScript('mdfind kMDItemKind=Folder');
//convert to an array
var dir_check2 = dir_check.split('\r');
//loop through all array values in search of TCC Protected Folders
for(let p=0; p<dir_check2.length; p++){
if (dir_check2[p] == "/Users/" + username + "/Desktop"){
results += "[+] Terminal already has folder access to /Users/" + username + "/Desktop\n";
}
if (dir_check2[p] == "/Users/" + username + "/Documents"){
results += "[+] Terminal already has folder access to /Users/" + username + "/Documents\n";
}
if (dir_check2[p] == "/Users/" + username + "/Downloads"){
results += "[+] Terminal already has folder access to /Users/" + username + "/Downloads\n";
}
}......
......let username = NSUserName()//set MDQuery string
let queryString = "kMDItemKind = Folder -onlyin /Users/\(username)"
let query = MDQueryCreate(kCFAllocatorDefault, queryString as CFString, nil, nil)
//run the query
MDQueryExecute(query, CFOptionFlags(kMDQuerySynchronous.rawValue))
//loop through query results
for i in 0..<MDQueryGetResultCount(query) {
if let rawPtr = MDQueryGetResultAtIndex(query, i) {let item = Unmanaged<MDItem>.fromOpaque(rawPtr).takeUnretainedValue()//grab kMDItemPath value for each entry
if let path = MDItemCopyAttribute(item, kMDItemPath) as? String {
//search for certain TCC Protected Directory Paths
if path == "/Users/\(username)/Desktop" {
print("[+] Terminal HAS ALREADY been granted TCC access to \(path)")}if path == "/Users/\(username)/Documents"{print("[+] Terminal HAS ALREADY been granted TCC access to \(path)")}if path == "/Users/\(username)/Downloads"{print("[+] Terminal HAS ALREADY been granted TCC access to \(path)")}}}}......
......var username = $.NSUserName().js;//set the query string for MDQuery
var queryString = "kMDItemKind = Folder -onlyin ~";
//create a new MDQuery instance
let query = $.MDQueryCreate($(), $(queryString), $(), $());
//execute the query
if ($.MDQueryExecute(query, 1)){
//loop through query results
for(var i = 0; i < $.MDQueryGetResultCount(query); i++){
var mdItem = $.MDQueryGetResultAtIndex(query, i);
//grab the kMDItemPath value for each search result
var mdAttrs1 = $.MDItemCopyAttribute($.CFMakeCollectable(mdItem), $.kMDItemPath)
var mdAttrs = ObjC.deepUnwrap(mdAttrs1);
//search for strings matching certain TCC protected dirs
if (mdAttrs == "/Users/" + username + "/Desktop"){
results += "[+] Terminal already has folder access to /Users/" + username + "/Desktop\n";
}
if (mdAttrs == "/Users/" + username + "/Documents"){
results += "[+] Terminal already has folder access to /Users/" + username + "/Documents\n";
}
if (mdAttrs == "/Users/" + username + "/Downloads"){
results += "[+] Terminal already has folder access to /Users/" + username + "/Downloads\n";
}
}
...
...
example TCC-Checker.js output

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Cedric Owens

Cedric Owens

Red teamer with blue team roots🤓👨🏽‍💻 Twitter: @cedowens