Cortex XDR PoC: Monitoring Malicious Chrome Extensions

Showing results for 
Show  only  | Search instead for 
Did you mean: 
Please sign in to see details of an important advisory in our Customer Advisories area.

Cortex XDR PoC: Monitoring Malicious Chrome Extensions

L3 Networker


PoC Lab: Monitoring Malicious Chrome Extensions

By:  @mfakhouri 

Executive Summary



With the convenience Chrome extensions provide, such as ad blocking, enhanced web viewing, and improving user experiences, it is no surprise that malicious actors seek to leverage Web Store services to deliver malicious content. In recent times, strikingly passive tactics have been implemented within these extensions to thwart detection from the EDR and along with the end user. One recent example even included a malicious extension that discreetly entered in an affiliate code whenever the user made a purchase online. Cortex XDR Pro is studied to implement a workflow to detect the installation of any Chrome extension as soon as it is installed on the endpoint. This begins from the creation of an XQL query, which is later pivoted toward a Python script leveraged in the Action Center to gather more information on all extensions pertaining to their respective users/endpoints.


What is a Google Chrome Extension?


Chrome extensions are add-ons installed on Chromium-based browsers to improve user functionality when browsing the web. Some popular variants include ad blockers, volume enhancers, theme adjusters, etc. With its relevant convenience demonstrated, users will often browse through the available Chrome Web Store for new tools to improve upon their current experience on the web.


Extensions utilize software code to perform these controls mentioned in the form of content scripts. Depending on the extension installed, this will load if the user navigates to the corresponding page in accordance with the information retrieved from extension pages. Because extension pages are able to utilize full access to the Browser extension APIs (as outlined in the following model), they are typically leveraged to communicate with content scripts to launch attacks when users browse the web.




Recent malicious actions with Chrome Extensions 


Malicious Chrome extensions have been an ongoing threat to the Web Store historically. Though security has improved (such as reputable tags attributed to extensions on the Web Store), recent cybersecurity news has documented its relevance. In one security report, five malicious extensions were studied between March 2022 and September 2022 after they were discovered to be malicious. These extensions in question totaled an installation count of over 1.4 million. One of these extensions even had a “featured” tag. All five performed subtle but similar behaviors, redirecting user purchases to utilize an affiliate link associated with the threat actor. 


Cortex XDR will be examined to approach the issue with subtlety in malicious extensions as demonstrated in recent trends.


XQL Creation + Correlation Rule and Widget Pivot


Utilizing XQL (Cortex XDR’s query language), detections can be made to determine if critical file changes were made in directories pertaining to Chrome extensions with Cortex XDR Correlation Rules. 


Tracking particular Chrome extensions in itself can be tricky given the nature of their build pattern in the filesystem. There is no exact indicator of which extension is which. A unique extension ID is attributed to every Chrome extension when it is added to the Web Store. When a Chrome extension is installed on the endpoint, its assigned extension ID is set as the directory name where all of its configurations are created. This directory can be located at /Users/”Current User”/AppData/Local/Google/Chrome/User Data/Default/Extensions/”Extension ID”.





As a result of these intricacies, XQL can reveal a surprisingly large amount of information given the limited knowledge an end user could know about Chrome Extension functionality on the file system.


In the process of building this query, we know that we're dealing with a Windows agent along with the causality actor process image name chrome.exe (since it is installed from Chrome). Further, we are also looking for file data. To keep clutter to a minimum, we can also search for only our test host at the moment.  Let's see what can be deciphered from this simple XQL query after installing any Chrome extension on our endpoint.


dataset = xdr_data

| filter event_type = FILE

| filter agent_hostname = “mf_Windows10”

| filter agent_os_type = AGENT_OS_WINDOWS

| filter causality_actor_proccess_image_name = “chrome.exe”





Well… there’s not a whole lot here aside from multiple file manipulations made from the same PID. What can be more useful, however, is exploring the causality group owner of one of these instances for more context to what is going on (right-click, select Causality View).






Here, we can see a variety of event types initiated by the same chrome.exe process. This includes a particular tab for file types as well. A variety of relevant XQL query information can be kept in mind for the future and our goal (see action_type, file_name, file_path). When browsing through the relevant file names seen in the list, one can be identified as “chromextensionid_” + a four digit number + “.crx”.





A “.crx” is a file extension attributed to installed Chrome extensions. This file is installed in the Chrome Web Store file path, which is later deleted and utilized to create the aforementioned folder with the extension ID. Here, we can reverse engineer our way to now detect added extensions from Chrome. This can be done by navigating to the right-click menu -> viewing the event in XQL.


(note: this is different from the directory mentioned earlier. This directory captures the temporary file creation that occurs in \Web Store Downloads\ Directory whereas finalized Chrome extension directories are stored at  /Users/”Current User”/AppData/Local/Google/Chrome/User Data/Default/Extensions/”Extension ID” )


This will initially be the individual event. But, many fields can be deciphered from this one event. This includes the hostname, agent OS type, file type, action file name/path, etc. More information on field names for the xdr_file type can be found in the “schema” tab along with the Cortex XDR schema reference.


With this information, the following XQL query was created to detect reveal additional insights on newly installed Chrome Extensions:


config case_sensitive = true | 

preset = xdr_file

| filter agent_os_type = AGENT_OS_WINDOWS 

| filter event_sub_type = FILE_CREATE_NEW

| filter causality_actor_process_image_name = "chrome.exe"

| filter action_file_path contains "\Webstore Downloads\"

| filter action_file_name not contains "Zone.Identifier"

| alter action_file_name = split(action_file_name, "_")

| arrayexpand action_file_name 

| filter action_file_name not contains "crx"

| fields _time as Time, agent_hostname as Hostname, agent_ip_addresses as IPv4, actor_effective_username as User, action_file_name as extensionID


The first several filters begin to siphon down cases to exactly match our intended desire to manage the initially created Chrome Extensions within the \Web Store Downloads\ directory. The “contains” operator is particularly useful in this case because the file path changes depending on the user who installed the extension.


There is also some additional filtering to clean up some duplicate file entries initially installed in this directory (specifically the zone.identifier files) along with altering the output of the extension ID box to only include the Extension ID itself as well. We will examine how this could potentially be useful when leveraging agent scripts later, but it also serves as a way to clean up our output when running the XQL query. Here is the output after installing two separate Chrome extensions.





The fields I utilized were what I found valuable from the list to show in a simple XQL query, but many more fields can be leveraged as mentioned in the schema tab. The extension ID is now revealed to us along with the responsible user and host.


With this helpful query, we are now able to provide an alert when a new Chrome extension is found to have been installed from the Web Store utilizing Correlation Rules (Detection Rules > Correlations > +Add Correlation).





After pasting in our XQL Query, a new Correlation Rule Alert will be triggered depending on the configured schedule. I established the severity to be low because this is likely a normal occurrence on an endpoint depending on your web browsing policy. But, it most certainly is an important consideration to check in the case one is installed.






Depending on the policy for Chrome Extension installations, this could be a poor implementation due to the number of alerts it would generate. For a more convenient way to digest some of this information in a viewable format, I found that widgets also serve as a sufficient tool. Perhaps this could slot into a “Web Browsing” dashboard listing either the entire query or counting Chrome extensions installed per each host, all within a given timeframe. The available time frames are one day, week, and month and can be configured at the top right after widget creation (Dashboards and Reports -> Customize -> Widget Library -> Create a Custom XQL Widget).





Action Center: Agent Script Library


The ability for Cortex XDR to retrieve these extension IDs is incredibly useful. Using scripting in itself to acquire the extension IDs of folders on the endpoints can and will be tricky. This is especially the case when many users exist on the endpoint scanned. Further, this prevents obfuscation of the Chrome extension (either accidentally or from the extension itself) since our XQL query captures the temporary file created when initially building the extension.


The Action Center is examined to capture greater information regarding the Chrome extensions found because of its ability to execute scripts on multiple endpoints at a time. This will utilize input gathered from our XQL query created in the prior section. The script created for this workflow retrieves information from a public repository of documented malicious Chrome extensions along with information from the Chrome Web Store. This information will be primarily based off of the extension ID and host information that serve as the scripts input.


There are some Python library limitations when utilizing the Agent Script Library as well, but I was able to gather all of the information needed whilst maintaining within these boundaries with the following libraries: requests, regex (re), socket, and os.





Here, the script gathers the raw information available from the following GitHub repository created to promote collaboration around malicious Chrome extension ID findings. This repository has been untouched for some time, but the creator has reportedly come back and plans on future updates/contributions. Typically, IDs are added alongside sources to negate the chances of false positives. Perhaps we can come together as a community to further enrich the list of available IDs. 


The comparison serves as an extra check in the case that a recent ID came out to be malicious publicly, (but should not be utilized as a firm evaluation as to the legitimacy of particular extensions). This is not uncommon behavior, as Chrome extensions can remain in the Web Store for a certain amount of time even after IDs were found to be evil. We’ll explore some interesting functionality implemented to gain insight if values were not able to be found, or if an obstruction is in place on the endpoint side.


Next, we format the output to match how the script intends to read the information given. It is slightly complex given the nature of the information that is inserted but saves a lot of potential time wasted copying and pasting information over in the script.




This portion will take the input captured from the XQL query (being the host, user, and extension id) and split it into three separate lists. This allows us to only have to iterate through a single list whilst maintaining correlation integrity with matching indices for each list being related in the data entry. Format input splits the information into a list using regex. Since the info is split up into three (and will always be divisible by three given the nature of the input), the modulus operator can be used to calculate which data entry should go where after iterating through the format input variable and distributing it into the created lists.





Next, we get to the iteration of the entries given. The loop will first check if the host selected is in the list to begin with. This is because the user has the option of selecting any particular host/filter of hosts when executing a script from the Agent Script Library and may select a host not entered into the input.


Once the host does match, it will begin retrieving the information from the Web Store. All extension IDs on the Web Store utilize a similar naming scheme, with the ID listed at the end (as seen in the extractid variable). There is a subsection of this labeled “this doesn’t matter” because it is later adjusted to the name of the extension when the ID is interpreted at the end when the domain is resolved.


Then, the title of the extension is extracted for the output. After that, the directory of the extension is examined according to the user to check for obfuscation. Os.path.exists comes in handy here since all we need to do is build the directory string from the given input.





Next, the script will attempt to grab additional page information (description, url) and will fail if it is unable to do so. This will typically occur when an extension is removed from the Web Store, where the page will then lead to a 404 error. This is also an important lead to consider when handling Chrome Extensions since an extension could still be installed on a web browser despite its removal from the Web Store.


If it is removed (as seen in the except statement path) it will check for the existence of the directory in the default location. Since there is no information to be retrieved if this is the case afterwards, the continue statement restarts the loop for the next host entry in the information.


The script next checks for the information in the malicious listing from the raw information given from GitHub. This is easily interchangeable with whatever trusted source one desires by altering the initial GitHub that the information is retrieved from. Finally, the structure of the string output is built with the str build variable, and the following output is given on a test run of the host:




The output is broken up into two major sections. The first is the top part listing the statistics (ex. x extensions associated with y desktop) and the second is the extensive information retrieved. One example is an extension removed from the Web Store, found in the malicious listing (as marked by the [X]), and noting that the path for the extension is obstructed since it does not exist. The second example in the bottom part includes an extension that does exist, where the path also exists, and the script is able to retrieve information online from the WebStore.


Now that the formatting and output is correct, we can now create a new script to execute from the Action Center. A new Agent Library Script can be created from Incident Response > Response > Action Center > Agent Script Library > + New Script. After uploading the created Python script, we can enter the metadata. Further, we can clarify our input to run on an entry point rather than running automatically. This is because this script depends on data retrieved from the XQL query mentioned, being in the format of hostname, user, and extension ID.




Next, we can save the script and run it on our endpoint. We’ll need the information from our XQL query to run the information properly. Going to our created Widget and locking the fields in the order (hostname, user, extensionid) allows for a convenient way to retrieve the information. This can also be done by opening up the query in XQL, exporting it as a csv, and copying the values required as well.



After pasting the information and running, we are finally able to retrieve the information similar to what was seen on the test script:





On the endpoint, the result window has crucial endpoint data collected regarding potentially malicious endpoints contained or ones obstructed/missing from their original paths. If you hover over the return value, it displays the more informative output regarding that particular endpoint. This allows for a less intimidating return value for each endpoint listed, which can be examined further if necessary when viewing the preview of the return value. This also creates an ease of visibility when filtering. For example, use Return Value contains “[X]” search to look for extensions flagged as malicious.


The thorough report in its entirety can be collected via copying and pasting the cell values in its entirety as well. Selecting the cell or highlighting it and copying/pasting will capture all the information stored in the Return Value field.


As always, actions taken can be revisited in the “All Actions” tab in the Action Center. This includes the endpoint script execution that was just performed and its results through the right-click menu:






The Chrome Web Store has historically been leveraged by threat actors to take advantage of unsuspecting end users. Without policies in place mitigating what can and cannot be installed by these users, malicious chrome extensions have a proven track record of existing on potentially millions of users' devices at a time. Cortex XDR can seamlessly monitor these extensions as soon as they are installed on the endpoint by monitoring temporary files within the extension's initial creation using XQL queries. This can also be detected utilizing Correlation rules to find immediate additions or can be a convenient custom widget to add to any “Web Browsing Security” Dashboard. With the collection of these Extension IDs upon its installation, the Agent Script Library was also found as a compatible tool to retrieve additional information upon multiple endpoints at a time.


Technical Lab Details:




L5 Sessionator

Thank you for your contribution! 

  • 1 replies
Like what you see?

Show your appreciation!

Click Like if a post is helpful to you or if you just want to show your support.

Click Accept as Solution to acknowledge that the answer to your question has been provided.

The button appears next to the replies on topics you’ve started. The member who gave the solution and all future visitors to this topic will appreciate it!

These simple actions take just seconds of your time, but go a long way in showing appreciation for community members and the LIVEcommunity as a whole!

The LIVEcommunity thanks you for your participation!