As a security researcher at XM Cyber, designing core product support for MacOS was one of my goals last year. Designing this support meant researching MacOS’s attack surface with a focus on creating the greatest impact for our current and future clients. As I began my research, I noticed that, as opposed to my initial assumptions, MacOS were not standalone devices that sometimes were managed by an MDM solution; rather, they were integrated and partially managed by the client’s Active Directory (on-prem or Azure). I started mapping out the attack surface that an Active Directory integration adds to MacOS, focusing on performing a lateral movement from the Mac- to the Windows-based parts of the Active Directory, and vice versa. While doing the research, I stumbled upon many security tools that provide most, if not all, of the required capabilities that I was looking for, such as “Bifrost” and “Orchard” by Cody Thomas (kudos!).
To become more familiar with the attack vectors, I began to develop an extension for BloodhoundAD (which is very useful) that allows the collection of data required for Bloodhound on Mac machines. I followed the same technical limitations that we use at XM Cyber to ensure a safe and reliable attack simulation without experiencing an undesirable negative effect.
The source code of MacHound can be found here.
MacOS Integration With Microsoft Active Directory
First, let’s introduce the topic of integrating MacOS with Microsoft’s Active Directory. You can configure a Mac to access basic user account information in a Microsoft Active Directory domain of a Windows 2000 (or later) server (see the note below on the versions). The Active Directory connector, which is listed in the Services pane of Directory Utility, generates all the attributes required for macOS authentication from the standard attributes in Active Directory user accounts. The connector also provides support for Active Directory authentication policies, including password changes, expirations, forced changes, and security options. Because the connector supports these features, making schema changes to the Active Directory domain is not required to acquire basic user account information.
The macOS uses DNS to query the topology of the Active Directory domain. For authentication, it uses Kerberos and Lightweight Directory Access Protocol (LDAP) for user and group resolution.
When macOS is fully integrated with Active Directory, users experience these scenarios:
● They’re subject to the organization’s domain password policies;
● They use the same credentials to authenticate and gain authorization to secured resources;
● An Active Directory Certificate Services server issues user and machine certificate identities; and
● They can automatically traverse a Distributed File System (DFS) namespace and mount the appropriate underlying Server Message Block (SMB) server.
Active Directory-Based Attack Vectors
Apple MacOS supports multiple remote access and management services, one of which is Active Directory. When Active Directory is enabled, Active Directory users and groups are listed in local groups, such as local administrators and privileged groups. Active Directory on MacOS also permits the use of remote access and control features, such as SSH, VNC, Apple Remote Desktop, and AppleEvent script execution.
By default, MacOS provides support for remote access by utilizing the following services:
– VNC, known as “Screen Sharing”
– SSH, called “Remote Login”
– Apple Remote Desktop (ARD), or “Remote Management”
– AppleEvent, known as “Remote Apple Event”
Remote services authentication and authorization are managed by the local directory service on a Mac using a dedicated group for each service, to which privileged users and groups are added.
The two primary ways to add Active Directory users to privileged groups on a Mac are:
● Add Active Directory users/groups directly to service-specific groups or the local administrators’ group. Another elegant option: Enable access to “All Users,” which allows all authorized Active Directory users to log in!
● Enable the “Administration by Active Directory Groups״ feature as part of the Active Directory integration.
This process covers the Active Directory to MacOS attack paths. The next step is to extract the local credentials of Active Directory users on MacOS machines to laterally move to the Active Directory.
The three types of MacOS users are:
– Local Users — Managed by the local OpenDirectory service, they aren’t connected in any way to the Active Directory.
– Network Users — Volatile Active Directory users who require a connection to the DC server to authenticate.
– Mobile Users — Active Directory users with a local backup for their credentials and files.
Generally, on MacOS, passwords can be extracted in one of three ways:
· Social engineering the user into entering their password via a pop-up window;
· Extracting either the Kerberos ticket or the user’s ShadowHashData and brute-forcing it repeatedly; or
· Using the mkpassdb command line tool in network users to extract password hashes.
MacHound is the proof-of-concept for the implementation of MacOS information gathering of Active Directory administrative permissions and local user sessions. Moreover, it allows for the insertion of the gathered information into the Bloodhound Active Directory tool database. MacHound is split into two main components: the local MacOS information gathering script and a database ingestor script. Both scripts are currently written in Python3.9.1, but future plans include migrating the MacOS collection to AppleEvent scripts.
MacHound’s output allows network administrators or auditors to obtain a clearer image of the security posture of Active Directory-joined MacOS machines. The provided picture includes detection of privileged users or groups on MacOS machines, and vice versa, those users with the potential to be compromised on MacOS machines. Since MacHound is the result of an internal research effort, we customized some of the functionality to use it with the XM Cyber product. As a result, the data collection is limited to read-write operations on files and calls for native library functions. The only exception is the execution of command-line utilities to extract the SMBSID of MacOS machines, which, unfortunately, was unavoidable.
The data collector script is the first part of MacHound, which retrieves three types of information:
- The local machine’s SMB SID in the Active Directory;
- Currently logged-in sessions of Active Directory users, either network or mobile users; and
- Administrative group members who are Active Directory entities, either in a group or as a user, and their SMB SID in the Active Directory.
The script’s output is a json-formatted file that includes all the gathered information as described above.
How Does the Data Collection Work? — Computer SMB SID
Since I couldn’t find a native way to extract this information, I had to resort to executing command line utilities and parsing the output. To do so, MacHound executes the scutil command that manages system configuration parameters, along with a request to show the com.apple.opendirectoryd.ActiveDirectory property. The property contains most of the information about the integration of the local host in the Active Directory, including the DNS name of the joined Active Directory, the name of the machine account in the AD, the machine’s role and its Active Directory objectGUID, which sadly, is not supported by Bloodhound. Using the information gathered, MacHound once again executes a command line utility, the dscl command, which is the Directory Service configuration tool. Using the NodeName and the TrustAccount properties from the scutil execution, MacHound concatenates the properties of the machine account from the Active Directory and extracts the machine’s SMB guid.
How Does the Data Collection Work? — Local OpenDirectory Scheme
To understand how MacHound extracts Active Directory users and group information, we first must understand how MacOS stores the local OpenDirectory scheme. On MacOS machines, both user authentication and authorization are managed with a local directory based on OpenDirectory. In other words, roughly speaking, each MacOS machine has an instance of directory service that manages the local users and groups. The local directory scheme is stored as plist files, under the /var/db/dslocal/nodes/Default folder, which contains sub-folders for every component of the directory, such as Users and Groups. For example, to read the properties of the local “admin” users, the path to the plist is: /var/db/dslocal/nodes/Default/users/admin.plist. The path of the local administrators’ group is /var/db/dslocal/nodes/Default/groups/admin.plist.
The user plist files contain properties, such as the user’s full name, profile picture, password hash and the generated uid in the local directory service. The group’s plist contains properties such as group name, group SID and the group members, both users and groups. Upon execution of the MacHound collector script, the script first rebuilds the entire local directory scheme in the memory. It does this by reading all the plist files of both users and groups and indexing them based on the entity name. Once the scheme is loaded, it’s possible to obtain members of groups, either they are users [SH1] or other groups, which allows you to get a clear understanding of all group members, either direct or nested.
How Does the Data Collection Work? — Local Administrative Group Members
After establishing the administrative local group, we want to focus on and understand how to parse the local directory service scheme, so we can identify the Active Directory users and groups used to remotely access the MacOS. The MacHound collector iterates the chosen administrative groups and checks for each member or nested group to determine whether the stored UUID is part of the local directory scheme. If the UUID is not found in the scheme, the collector tries to convert the UUID to the SMBSID of the entity using the membership API, specifically, by using the mbr_uuid_to_sid function. The mbr_uuid_to_sid function resolves the UUID to the SMBSID by querying the DC server via secure LDAP protocol or, when unable to reach the DC server, using the local cache. Utilizing the cache usually results in partial results, therefore it is recommended to execute the script only when the DC servers are accessible. If the SMBSID can be resolved, it’s stored in the output json file.
How Does the Data Collection Work? — Logged-in Sessions
Logged-in sessions are collected using the utmpx API, specifically, the getutxent function. This function returns the logged user’s name, which is tested against the directory scheme to determine if they are a local user or a mobile Active Directory user. Network users, which are completely volatile, are not stored in the local directory, and we cannot directly query the relevant UUID. Theoretically, it’s possible to directly query the Active Directory to obtain the relevant user; however, this was not accurate enough for our product simulation as some edge cases may cause false-positive sessions. Once the logged-in user’s UUID has been fetched from the local directory, MacHound queries the SMBSID again using the Membership API and stores the output in the json file.
MacHound Collector Final Output
After MacHound finishes collecting all the necessary data, it dumps it into a json file. The json file contains a number of important elements: the computer name, computer SMBSID in the Active Directory, and a list of administrative users and groups. It stores the latter in a dictionary in which the key is the edge type in the Bloodhound database (AdminTo, CanSSH, etc.), and the values are a pair of SMBSID and the type of the entity, user, or group.
The second part of MacHound is the database ingestor. The ingestor inputs a folder containing the collector’s output json files and files for each MacOS machine, and it ingests the information into the neo4j database used by Bloodhound. The ingestor flow is fairly simple. It reads the contents of the json files, validates that the entities’ SMBSID is in the bloodhound database, and creates the edges in the database. Finally, Bloodhound now has sessions and AdminTo edges to MacOS machines.
MacHound’s current proof-of-concept code does suffer from some major drawbacks. Using Python3 for the collector code was a comfortable choice for a proof-of-concept; however, I believe that for MacHound to become a useful production-ready utility, the collector should be converted to AppleScript.
Next, I want to address the ingestor. I want to drop the usage of a separate ingestor and let the collector output json files that Bloodhound can support out of the box without a middleman and direct access to the neo4j database.
Conclusion and Final Thoughts
I won’t judge anyone for choosing to integrate their MacOS machines into Active Directory; in some cases, this integration is required. However, anyone choosing this integration should be aware of the possible risks created by doing so, especially when considering the possibility for misconfigurations. Using the MacHound utility allows network administrators to gain some general insights about their MacOS deployment and Active Directory that were not available until this point. However, it covers only a small portion of the attack surface, while many other attack vectors and possible risks for enterprise environments exist that are not covered by MacHound.
Rony Munitz is Senior Security Researcher at XM Cyber