There are a few methods that can be used to legitimately install extensions on macOS. Google forces developers to distribute extensions through the web store. Recently, Google changed their policy so that extensions cannot be installed from third party sites. Adversaries may still host extensions on the web store anyway, but it was a necessary policy change. Alternatively, extensions can be installed on macOS with the use of mobile configuration profiles. Configuration profiles are used on macOS and iOS to manage various settings, including power management, DNS servers, login items, WiFi settings, wallpaper, and even applications like Google Chrome. End-users can install profiles by double-clicking on the file or on the command line with the profiles command. Mobile configuration profiles are XML-formatted and follow a relatively simple format. To create a mobile configuration profile, a payload uuid, application ID, and update url (this will be covered later) are required. For more information on configuration profiles, please refer to this article and this reference. Here is an example that can be used as a template. The ExtensionInstallSources key specifies the URL in which extensions can be installed from. Wildcards can be used in the protocol, host, and URI fields. The ExtensionInstallForceList value refers to a list of extensions that will be installed without the users’ consent and cannot be uninstalled. The PayloadRemovalDisallowed key prevents non-admin users from uninstalling the profile. For other keys that can be used to manage extensions and other Google Chrome settings in general please refer to this. Configuration profiles can be utilized to manage a myriad of settings for macOS and warrant further investigation for additional offensive use cases.
An interesting note about configuration profiles; they can can be delivered via email and subsequently, the end-user will not see any prompts from Gatekeeper, MacOS’s code signing enforcement and verification tool. However, the user will be prompted to confirm the installation of a profile.
If the profile is unsigned, the end-user will be presented with a second prompt (figure 2) before being prompted for admin credentials.
However, when installing a signed profile, the OS will only prompt once for the install and then admin credentials. After the installation, the profile contents can be seen in the *Profiles *preference pane. If the profile is unsigned, that will be noted in red.
Now that the extension policy has been set for Chrome, when the application is opened, it will make a series of web requests to the update URL specified in the profile. The update URL should point to an update manifest file that specifies the application ID and the URL for the extension file (.crx). Refer to the auto update documentation for an example manifest file. Next, Chrome will download the extension and save it to ~/Library/Application Support/Google/Chrome/Default/Extensions/APPID. At this point, the extension is loaded into the browser and executed. Note that during this entire process, the configuration profile is the only component that requires user interaction. Similarly, on Windows, an extension can be silently installed by modifying registry keys noted here. However, if the installation source is a third party site, Chrome will only allow inline installs. This type of install will requires users to browse to your site and then redirect users to the Chrome web store to complete the installation.
In order to easily manage bug fixes and security patches, extensions can be automatically updated. When hosting extensions in the Chrome Web Store, Google handles updating your extension. Just upload a new copy of your extension and after a few hours, the browser will update the extension via the web store. For extensions hosted outside the web store, developers have more control. Chrome uses the update url in the manifest.json file to periodically check for updates. During this process, Chrome will read the update manifest file and compare the version in the manifest with the current version of the extension. If the manifest version is higher, the browser will download the newest version of the extension and install it. For an example update manifest, please go here. The update manifest is an XML formatted file that contains the APPID and a URL that points to the .crx file. Auto-update is a particularly useful feature for adversaries. In Figures 4 and 5, a malicious extension uses one domain for standard C2 comms and uses another domain to host the update manifest and extension file. Imagine the scenario where incident response has identified the C2 domain as malicious and blocks all traffic to that domain (1). However, traffic to the update URL is still permitted (2 & 3). An adversary can update the manifest version, change the C2 domain (4), the update URL, and even modify some of the extensions core code. After some time, Google Chrome will make a request to the update URL and load the new version of the extension with new C2 domains.
Additionally, if an attacker loses control of the extension, or perhaps it even crashed, they can remotely trigger execution with an update. As long as the extension remains installed, Chrome will continue to try and check for updates. If only the manifest version is updated, Chrome will re-install the extension and trigger it’s execution. Next, we’ll cover how to use a PoC Chrome extension and manage the C2 server with Apfell.
Malicious Extensions: IRL
Apfell is a post-exploitation framework centered around customization and modularity. The framework targets the macOS platform by default, but a user can create new C2 profiles that target any platform. Apfell is an ideal framework to use for a malicious Chrome extension. Lets walk through configuring a custom C2 profile and generating a payload.
1) For initial setup instructions, please see the apfell documentation here. Once the apfell server is up, register a new user and make yourself an admin. Next, you’ll need to clone the chrome-extension and chrome_extension-server projects onto your apfell server.
2) Navigate to manage operations -> payload management. The payloads for apfell-jxa and linfell are both defined on this page. For each payload, there are several commands defined. Any of these commands can be modified within the console and then updated in the agent (specifically for apfell-jxa and linfell). At the bottom left corner of the payloads page is an import button. This allows a user to import a custom payload, along with each command, from a json file. Please import this file to save some time creating the payload. If the import was successful, you should see *chrome-extension *as a new payload type with a few commands to start.
3) Now open a terminal session on your apfell server and navigate to the chrome_extension-server project. Run the install.sh script to install golang and compile the server binary. Verify that the server binary compiled successfully and is present in the $HOME/go/src/chrome-ext-server directory.
4) Navigate to Manage Operations -> C2 Profiles. Click on Register C2 profile on the bottom left of the page. Here you’ll provide the name of the profile, description, and supported payloads. Upload the C2 server binary ($HOME/go/src/chrome-ext-sever/server) and the C2 client code (./chrome-ext/apfell/c2profiles/chrome-extension.js) for the extension.
5) Once the profile is submitted, the profiles page should update and show the new profile.
6) Back in your terminal session on the apfell server, edit the c2config.json file and choose the options you desire.
7) Copy the c2config.json to the apfell/app/c2profiles/default/chrome-extension/ directory. Rename the server binary to chrome-extension_server. This is necessary in order to start the c2 server from the apfell UI. Now start the C2 server in apfell.
8) Navigate to Create Components -> Create Base Payload. Select chrome-extension for the C2 profile and payload type. Fill out the required parameters (Hostname, Port, Endpoint, SSL, and Interval). Provide the desired filename and then click submit. If all goes well, a success message will be displayed at the top of the page.
9) To download the payload, go to Manage Operations -> Payload Management. Now that you’ve setup the extension payload and C2 profile, you can export them to use in other operations.
10) Copy and paste all of the code from the payload into chrome extension project file ./chrome_ext/apfell/extension-skeleton/src/bg/main.js. Edit the manifest.json file in the extension-skeleton directory and replace all of the *_REPLACE values. The update_url does not need to be a legitimate URL if the auto update isn’t used.
11) Open Google Chrome, click on More -> More Tools -> Extensions and toggle developer mode. Click on pack extension and select the extension-skeleton directory within the chrome_ext project. Click on pack extension once again and Chrome will output the .crx file with the private key. Note that you’ll need to keep the private key in order to update the extension.
12) The last piece of information you’ll need is the application ID. Unfortunately, the only way to obtain this is to install the extension and note the ID shown on the extensions page. Drag the extension file (.crx) onto the extensions page to install it.
13) Now you have the information needed to create your mobile configuration file and host the update manifest and crx file. Add the application ID and url that points to the crx file to the update manifest file. Then add the application id and update_url to the example mobile configuration file here. Additionally, you’ll need to add in two unique UUIDs.
14) Now the setup is complete! If everything is properly configured, installing the mobile configuration profile should trigger a silent install of the extension and add a new callback on the apfell active callbacks page. Refer to the Payload Delivery section for details on installing the profile.
In the target infection section, we briefly covered a delivery mechanism for chrome extensions that allows for silent and hidden installs via mobile configuration profiles. From a defensive perspective, detections for this delivery mechanism should be focused on the *profiles *command and its arguments. This would be most suitable in a situation where the attacker already has access to the victim host. Specifically, the command for installing a profile looks like this:
The corresponding osquery rule would look like this: “SELECT * FROM process_events WHERE cmdline=’%profiles install%’;”. This may not be the best answer in an enterprise environment but its a solid starting point. Also note that the osquery schema now includes a chrome extensions table. Additionally, when a profile is installed through the UI, the MCXCompositor process writes a binary plist to the /Library/Managed Preferences/username/ directory. The plist file is a copy of the mobile configuration profile. The filename is determined by the PayloadType key in the configuration profile.
There may be other data sources that enable more robust detections for the use of mobile configuration profiles but this should serve as a good start.
Google Chrome extensions should definitely be considered for initial access and persistence. I would encourage other red teamers and security researchers to investigate the Chrome APIs for additional functionality.