Crowdstrike Smart Group

New Contributor III


We are trying to create a smart group that targets machines that don't have the Falcon Sensor on them.

Weird question. In the criteria for this we want to select an "application title" but are not sure which file that would be. There is no ".app" file for this. Just wondering if anyone has created a smart group with this criteria and knows what to target.




There is a Crowdstrike EA already available from Jamf that you could use in the Resources>Extention Attributes. It was working as of March of this year when I last needed it.

New Contributor II

Alternatively, you can check if your devices are running the Crowdstrike (Falcon?) service.

New Contributor III

Yeah, someone else had mentioned the services option (or i think the criteria is "running processes") but said that the service isn't always guaranteed to be running. I have the attribute from CS resources and it's uploaded ("Displays version of CrowdStrike Falcon Sensor if installed") and it appears now as a criteria for the SG just not sure what goes in the value field.

New Contributor II

Run a recon/inventory update on any machine. The script should return a value that you can check in a computer's inventory, then use it as the criteria value for your Smart Group.

New Contributor II

This is what I use as an Extension Attribute

if [ -e /Library/LaunchDaemons/com.crowdstrike.falcond.plist ]; then
    echo "<result>Installed</result>"
    echo "<result>Not Installed</result>"
exit 0

Valued Contributor

This is the EA that I use to pull the CS version. It is named CS Falcon Version. I then have a Smart Group called CS Falcon Installed keyed on that EA and looking for the word "version".


## Run the sysctl command to check for Falcon Host, and print column 2 from the result
FHCheck=$(sysctl cs.version 2>&1 | awk '{print $2}')

## If Falcon Host is not installed, column 2 from the sysctl command is "unknown". Check to see if that was our result
if [ "$FHCheck" == "unknown" ]; then
   result="Not Installed"
   ## If the output was not "unknown", set the result variable to the command's output
   result="Version $FHCheck Installed"

## This line is what actually gets picked up in the JSS for the Extension Attribute
echo "<result>$result</result>"

Honored Contributor

I have a daily compliance script that runs and checks security agents and writes statuses to a flat file. EAs pick it up and read the values in for reporting. However, I can tell you that this can lead to false positives. I actually spoke about this specific issue in the Mac Admin's Pod Cast when they graciously had me on as a guest. You can listen here

This can lead to many false positives, as technically you can have a healthy agent on the client endpoint, but it may not be communicating with the CS tenant. Typically these are edge cases, but they do happen. Now add in tamper protection and you can find yourself in a pickle.

my daily compliance script:



This script will be the data collector and compliance checker
it will run on a scheduled basis and report or put devices in scope for remediation
through automation


# import modules
import sys
from SystemConfiguration import SCDynamicStoreCopyConsoleUser
import plistlib
import subprocess
from CoreFoundation import CFPreferencesCopyAppValue
import platform
import os
import time

# global vars

# grabbing user info in case we need it
USER, UID, GID = SCDynamicStoreCopyConsoleUser(None, None, None)
# location of the plist file we will use to record system state
    "/Library/Application Support/JAMF/snowflake/com.snowflake.systemstate.plist"
# detect OS version, in case we need it later
OS_VERS = platform.mac_ver()[0]

# start functions

def validate_folder_path():
    """detect if our folder path exists, create if it does not, set permissions, force non zero exit on error"""
    folder_path = "/Library/Application Support/JAMF/snowflake"
    if not os.path.exists(folder_path):
            os.mkdir(folder_path, 0o755)
        except OSError as e:
            print("Could not create folder with error %s" % e)

def validate_fv2():
    """validate that FV2 is enabled and FDE is present"""
    cmd = ["/usr/bin/fdesetup", "status"]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = proc.communicate()
    result = out.strip()
    # testing the return status of the command, create dict of success or failure
    if proc.returncode != 0:
        fde_dict = {"fv2status": "error"}
        print("Error message: %s" % err)
        return fde_dict
    if "On" in result:
        fde_dict = {"fv2status": "true"}
        return fde_dict
    elif "Off" in result:
        fde_dict = {"fv2status": "false"}
        return fde_dict

def validate_screenlock():
    """validate that the screen will lock if idle for 10 minutes"""
    # grab the preference set on device for the screen saver timeout
    domain = "/Library/Managed Preferences/"
    idle_time = CFPreferencesCopyAppValue("idleTime", domain)
    # test for 10 minutes of idle time for compliance
    # return a pass/fail dict value
    if idle_time == 600:
        idle_dict = {"screencompliant": "true"}
        return idle_dict
        idle_dict = {"screencompliant": "false"}
        return idle_dict

def health_check_cs():
    """detect if CS is healthy"""
    # if this command has zero output, CS is not healthy
    cmd = ["sysctl", "cs"]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = proc.communicate()
    if proc.returncode != 0:
        # crowd strike is not installed, or is not in a healthy state
        print("error %s" % err)
        cs_dict = {"cs_sys": "false"}
        return cs_dict
    if proc.returncode == 0:
        cs_dict = {"cs_sys": "true"}
        return cs_dict

def cs_daemon_check():
    """checks if the cs daemon is running"""
    cmd = ["/bin/launchctl", "list"]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = proc.communicate()
    if "com.crowdstrike.falcond" not in out:
        cs_daemon_dict = {"cs_daemon": "false"}
        return cs_daemon_dict
        cs_daemon_dict = {"cs_daemon": "true"}
        return cs_daemon_dict

def cs_comms():
    """this function will test if the client can talk to the CS """
    cmd = ["/Library/CS/falconctl", "stats"]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = proc.communicate()
    if proc.returncode != 0:
        return {"cs_con": "error"}
    outlines = out.splitlines()
    for line in outlines:
        if "State:" in line:
            result = line.split(":")[1]
            if "connected" not in result:
                cs_dict = {"cs_con": "false"}
                return cs_dict
            elif "connected" in result:
                cs_dict = {"cs_con": "true"}
                return cs_dict

def health_check_tenable():
    """check if tenable agent is healthy, may need to check if the SaaS is also healthy
    and not in maintenance or down, which would return a false positive"""
    cmd = ["/Library/NessusAgent/run/sbin/nessuscli", "agent", "status"]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = proc.communicate()
    if proc.returncode == 0:
        tenable_dict = {"tenablestatus": "true"}
        return tenable_dict
        tenable_dict = {"tenablestatus": "false"}
        print("Error: %s" % err)
        return tenable_dict

def set_computername():
    """function to set the computer's hostname is set to the serial number"""
    cmd = ["system_profiler", "SPHardwareDataType", "-xml"]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = proc.communicate()
    data = plistlib.readPlistFromString(out)
    serial = data[0]["_items"][0]["serial_number"]
    options = ["HostName", "ComputerName", "LocalHostName"]
    for option in options:["scutil", "--set", option, serial])

# gotta have a main

def main():
    """gotta have some main"""
    # check if our custom folder exists
    # test if the hostname is compliant
    # generate epoch date stamp to record last run
    date = int(time.time())
    # create the master dict
    main_dict = {}
    if main_dict.get("cs_daemon") == "true":
    elif main_dict.get("cs_daemon") == "false":
        main_dict.update({"cs_comms": "false"})
    main_dict.update({"os_vers": OS_VERS})
    # build the data to write to the flat file
    data = dict(timestamp=date, healthstate=main_dict)
    plistlib.writePlist(data, PLIST_FILE)

# run the main, or the jewels
if __name__ == "__main__":

What we ended up doing is shipping all the CS data and jamf data to our data platform and then compare how many active Macs are checking into Jamf Pro, but not into our Crowdstrike tenant. My Jamf data was off, it was only reporting very few failures, but when comparing the Jamf data to the CS data I could see that we had more failed agents. In the end, the only really good way to understand the health of your third party agents is to compare data from your management tool (like Jamf Pro) to the data of your third party agents.

New Contributor III

^ Tom starts talking about it around the 27min mark if anyone else is lurking like me.

New Contributor

hey @tomt how did you get CS in the criteria section?

Valued Contributor

@Anton That's the title of my EA.