Menu Close

macOS Forensically Sound* Workstation Lockout with CrowdStrike Falcon and Jamf Pro

Designed as a possible last step before a MDM “Lock Computer” command, FSWL.bash *may aid in keeping a Mac computer online for investigation, while discouraging end-user tampering

Background

When a macOS computer is lost, stolen or involved in a security breach, the Mobile Device Management (MDM) Lock Computer command can be used as an “atomic” option to quickly bring some peace of mind to what are typically stressful situations, while the MDM Wipe Computer command can be used as the “nuclear” option.

For occasions where first forensically securing a macOS computer are preferred, the following approach may aid in keeping a device online for investigation, while discouraging end-user tampering.

Configuration

Complete the following steps to potentially forensically secure a macOS computer with CrowdStrike Falcon and Jamf Pro, which leverages the macOS 15 Sequoia (and earlier) built-in option to lock or unlock a user’s screen, which is based on a previous post: Actionable messages with Jamf Helper.

(*This approach may aid in keeping a device online for investigation, while discouraging end-user tampering and should not be used in production without thorough Red Team testing. Caveat emptor.)

A. Customize the FSWL.bash script for your environment
  1. Review and adjust the Global Variables as required for your environment
    • scriptLog (i.e., the location of your client-side logs)
############################################################################################
#
# Global Variables
#
############################################################################################

export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/

# Script Version
scriptVersion="0.0.2"

# Client-side Log
scriptLog="/var/log/org.churchofjesuschrist.log"

# jamfHelper Location
JH="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
  1. Set your preferred script defaults (which will be trumped in a policy’s script parameters)
    • Parameter 4: heading
    • Parameter 5: icon (i.e., the absolute path to client-side icon)
    • Parameter 6: description (i.e., the message to be displayed to the end-user; See: Bonus: Line-breaking Voodoo)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Jamf Pro Script Parameters
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

# Sets the heading of the window to the specified string
heading="${4:-"Heading [Parameter 4]"}"                                                  

# Absolute path to client-side icon
icon="${5:-"/System/Library/CoreServices/Diagnostics Reporter.app/Contents/Resources/AppIcon.icns"}"    

# Sets the main contents of the window to the specified string
description="${6:-"Description [Parameter 6]"}" 
  1. Set your preferred Organization Variables
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Organization Variables
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

# Script Human-readabale Name
humanReadableScriptName="Forensically Sound Workstation Lockout"

# Organization's Script Name
organizationScriptName="FSWL"
FSWL.bash
#!/bin/bash
# shellcheck disable=SC2034,SC2317

####################################################################################################
#
# Jamf Pro Forensically Sound Workstation Lockout
#
#   Purpose: Leverages the built-in macOS LockScreen binary to prevent end-user interaction
#
####################################################################################################
#
# HISTORY
#
# Version 0.0.1, 12-Sep-2024, Dan K. Snelson (@dan-snelson)
#   - Original, proof-of-concept version, based on: https://snelson.us/2023/01/jamf-helper/
#
# Version 0.0.2, 13-Sep-2024, Dan K. Snelson (@dan-snelson)
#   - Standardized script
#
####################################################################################################
#
# Global Variables
#
####################################################################################################

export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/

# Script Version
scriptVersion="0.0.2"

# Client-side Log
scriptLog="/var/log/org.churchofjesuschrist.log"

# jamfHelper Location
JH="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Jamf Pro Script Parameters
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

# Sets the heading of the window to the specified string
heading="${4:-"Heading [Parameter 4]"}"                                                  

# Absolute path to client-side icon
icon="${5:-"/System/Library/CoreServices/Diagnostics Reporter.app/Contents/Resources/AppIcon.icns"}"    

# Sets the main contents of the window to the specified string
description="${6:-"Message [Parameter 6]"}"   



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Organization Variables
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

# Script Human-readabale Name
humanReadableScriptName="Forensically Sound Workstation Lockout"

# Organization's Script Name
organizationScriptName="FSWL"



####################################################################################################
#
# Functions
#
####################################################################################################

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Client-side Logging
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

function updateScriptLog() {
    echo "${organizationScriptName} ($scriptVersion): $( date +%Y-%m-%d\ %H:%M:%S ) - ${1}" | tee -a "${scriptLog}"
}

function preFlight() {
    updateScriptLog "[PRE-FLIGHT]      ${1}"
}

function logComment() {
    updateScriptLog "                  ${1}"
}

function notice() {
    updateScriptLog "[NOTICE]          ${1}"
}

function info() {
    updateScriptLog "[INFO]            ${1}"
}

function errorOut(){
    updateScriptLog "[ERROR]           ${1}"
}

function error() {
    updateScriptLog "[ERROR]           ${1}"
    (( errorCount++ )) || true
}

function warning() {
    updateScriptLog "[WARNING]         ${1}"
    (( errorCount++ )) || true
}

function fatal() {
    updateScriptLog "[FATAL ERROR]     ${1}"
    exit 1
}

function quitOut(){
    updateScriptLog "[QUIT]            ${1}"
}



####################################################################################################
#
# Pre-flight Checks
#
####################################################################################################

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Client-side Logging
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if [[ ! -f "${scriptLog}" ]]; then
    touch "${scriptLog}"
    if [[ -f "${scriptLog}" ]]; then
        preFlight "Created specified scriptLog"
    else
        fatal "Unable to create specified scriptLog; exiting.\n\n(Is this script running as 'root' ?)"
    fi
else
    preFlight "Specified scriptLog exists; writing log entries to it"
fi




# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Logging Preamble
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

preFlight "\n\n###\n# $humanReadableScriptName (${scriptVersion})\n###\n"
preFlight "Initiating …"



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Confirm script is running as root
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if [[ $(id -u) -ne 0 ]]; then
    fatal "This script must be run as root; exiting."
fi



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Ensure computer does not go to sleep during SYM (thanks, @grahampugh!)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

caffeinatedPID="$$"
preFlight "Caffeinating this script (PID: $caffeinatedPID)"
caffeinate -dimsu -w $caffeinatedPID &



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Complete
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

preFlight "Complete!"



####################################################################################################
#
# Program
#
####################################################################################################

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Display Message
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

notice "The following message will be displayed to the end-user:"
logComment "${heading} ${description}"

notice "Lock screen with specified message"
osascript -e "set Volume 10"
afplay /System/Library/Sounds/Blow.aiff
/System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/MacOS/LockScreen &
displayMessage=$( "$JH" -windowType "fs" -icon "${icon}" -heading "${heading}" -description "${description}" ) &



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Exit
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

quitOut "Exit"

exit 0
B. Add the FSWL.bash script to your Jamf Pro server
  1. Add the FSWL.bash script to your Jamf Pro server
  2. Specify the following for Options > Parameter Labels
    • Parameter 4: Heading
    • Parameter 5: Icon (absolute path)
    • Parameter 6: Message
  3. Click Save
C. Create a Jamf Pro Policy to execute FSWL.bash
  1. Create a new Jamf Pro Policy, using the following as a guide for Options > General:
    • Set Display Name to Forensically Sound Workstation Lockout (0.0.2)
    • Enable Trigger > Custom and enter a secure name
    • Set Execution Frequency to Ongoing
  1. Select the Scripts payload and add the FSWL.bash script, specifying the following Parameter Values
    • Heading: Contact Company SecOps
    • Icon (absolute path): /System/Library/CoreServices/Diagnostics Reporter.app/Contents/Resources/AppIcon.icns
    • Message: Your Mac may be involved in a security incident.

Please contact Church SecOps immediately:
+1 (801) 555-1212 and mention Code 8675-309.

      See: Bonus: Line-breaking Voodoo
  1. Scope the policy to All Computers
  2. Click Save
D. Add Scripts to CrowdStrike Falcon Real Time Response

Add the following two scripts to Falcon Real Time Response:

CrowdStrike Falcon Forensically Sound Workstation Lockout.zsh

Adjust the customEventNameGoesHere to match the value specified in Step C.

#!/bin/zsh

# macOS Forensically Sound Workstation Lockout (0.0.2)
# (Estimated Duration: 0h:0m:01s)

echo -e "\n\n\n###\n# macOS Forensically Sound Workstation Lockout (0.0.2)\n# (Estimated Duration: 0h:0m:01s)\n###"

# Initialize SECONDS
SECONDS="0"

# Jamf binary-related Variables
jamfVersion=$( /usr/local/bin/jamf -version | cut -d "=" -f2 )
jamfPid=$( pgrep -a "jamf" | head -n 1 )
if [[ -n "${jamfPid}" ]]; then
    echo "• jamf ${jamfVersion} binary already running …"
    ps -p "${jamfPid}"
    echo "• Killing  ${jamfPid} …"
    kill "${jamfPid}"
fi

echo "• Executing Jamf Pro Policy Trigger …"
/usr/local/bin/jamf policy -trigger customEventNameGoesHere -verbose

echo -e "\nExecution Duration: $(printf '%dh:%dm:%ds\n' $((SECONDS/3600)) $((SECONDS%3600/60)) $((SECONDS%60)))"
CrowdStrike Falcon Forensically Sound Workstation Release.zsh
#!/bin/zsh

# macOS Forensically Sound Workstation Release (0.0.1)
# (Estimated Duration: 0h:0m:17s)

echo -e "\n\n\n###\n# macOS Forensically Sound Workstation Release (0.0.1)\n# (Estimated Duration: 0h:0m:17s)\n###"

# Initialize SECONDS
SECONDS="0"

####################################################################################################
#
# Functions
#
####################################################################################################

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Kill a specified process
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

function killProcess() {
    process="$1"
    if process_pid=$( pgrep -a "${process}" 2>/dev/null ) ; then
        echo "Attempting to terminate the '$process' process …"
        echo "(Termination message indicates success.)"
        kill "$process_pid" 2> /dev/null
        if pgrep -a "$process" >/dev/null ; then
            echo "ERROR: '$process' could not be terminated."
        fi
    else
        echo "The '$process' process isn't running."
    fi
}

# Release the Forensically Sound Workstation Lockout
killall -v LockScreen
killall -v jamfHelper

echo -e "\nExecution Duration: $(printf '%dh:%dm:%ds\n' $((SECONDS/3600)) $((SECONDS%3600/60)) $((SECONDS%60)))"
E. Testing

Jamf Pro

Once the script, policy and message are set in Jamf Pro, the “lockout magic” is enabled / disabled from Falcon Real Time Response.

CrowdStrike Falcon Real Time Response

Falcon Real Time Response commands are used to:

  1. Lock the Mac: CrowdStrike Falcon Forensically Sound Workstation Lockout.zsh securely calls the Jamf Pro policy to execute the FSWL.bash script to prevent the computer from accepting keyboard or mouse input
  2. Investigate the incident
  3. Release the lock: CrowdStrike Falcon Forensically Sound Workstation Release.zsh terminates the processes locking the Mac

Posted in CrowdStrike Falcon, Jamf Pro, macOS, Scripts, SecOps, Tips & Tricks

Related Posts