Skip to main content

So, from time to time we get computers stolen, and more often than one might think, they don't get erased but rather just gets a new admin account (using the .applesetupdone trick).
Our users aren't too happy about us collecting geo-location on a daily basis so we crafted a nifty little script that gives us the adress of the stolen computer. The script gets scoped to a static group containing all computers that are stolen (or lost) and set to run ongoing on all triggers (to get as much data as possible). As soon as the computer reports back in to the JSS, these scripts starts collecting data.



Along with the adress-script we have another script that prints the safari surf history of the current logged in user to stdin so it gets into the policy log to be viewed and finally one script that prints out the external IP and DNS name of the computer.



The adress scripts works by listing all the wifi networks seen by the computer and crafts a URL that requests data from google. The result is the approximate adress of the computers present location and the coordinates and finally the accuracy of the positioning.
We have recovered several computers using these scripts as all we have to do is provide law enforcement with location, IP/ISP and some "proof" of who we think is using the computer (you can often tell by the surf log since people tend to log into sites like Facebook and email which nicely prints out the name in the page title and therefore gets logged using the surf history part).
The record shows that the script is amazingly correct, or rather, Google is extremely good at provide location just using wifi networks. Most of the time, Google reports an accuracy of "150" which I guess would refer to 150 meters but from experience, that could easily be 15 meters, it's that accurate!



These scripts work for us, your mileage may vary, computers may catch fire, the scripts may contains bugs and so on...



Nevertheless, here are the scripts:



To get external IP:



#!/bin/sh
IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
DNS=$(host $IP | awk '{print $5}' | sed "s/.$//g")
echo "$IP - $DNS"


To log the surf history to the JSS (nb does not work in 10.10 as Safari now saves the history in a sqlite 3-file):



#!/bin/sh

unset LANG

#echo "START"

PREFIX="/tmp/safari-log"

USER=$(w | grep console | awk '{print $1}')
eval cp ~$USER/Library/Safari/History.plist $PREFIX-history-bin.plist
plutil -convert xml1 -o $PREFIX-history-xml.plist $PREFIX-history-bin.plist
split -p "WebHistoryDomains.v2" $PREFIX-history-xml.plist $PREFIX-
tail -n +5 $PREFIX-aa | egrep -o "(<string>.*</string>)|(<dict>)" | sed -E "s/</?string>//g" | sed "s/<dict>//g" | grep -v "^http://www.google.*,d.Yms$" > $PREFIX-history.txt

OLD_IFS=$IFS
IFS=""
exec 5<$PREFIX-history.txt
while read -u 5 LINE
do
echo $LINE | egrep -s "^[0-9.]+$" > /dev/null
if [ $? -ne 0 ] ; then
echo $LINE
else
TIME=$(expr $(echo $LINE | egrep -o "^[0-9]*") + 978307200)
date -r $TIME
fi
done
IFS=$OLD_IFS

rm $PREFIX-*

#echo "END"


And finally the one that prints out the address (location):



#!/bin/sh
INTERFACE=$(networksetup -listallhardwareports | grep -A1 Wi-Fi | tail -1 | awk '{print $2}')
STATUS=$(networksetup -getairportpower $INTERFACE | awk '{print $4}')
if [ $STATUS = "Off" ] ; then
sleep 5
networksetup -setairportpower $INTERFACE on
fi

/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport -s | tail -n +2 | awk '{print substr($0, 34, 17)"$"substr($0, 52, 4)"$"substr($0, 1, 32)}' | sort -t $ -k2,2rn | head -12 > /tmp/gl_ssids.txt

if [ $STATUS = "Off" ] ; then
networksetup -setairportpower $INTERFACE off
fi

OLD_IFS=$IFS
IFS="$"
URL="https://maps.googleapis.com/maps/api/browserlocation/json?browser=firefox&sensor=false"
exec 5</tmp/gl_ssids.txt
while read -u 5 MAC SS SSID
do
SSID=`echo $SSID | sed "s/^ *//g" | sed "s/ *$//g" | sed "s/ /%20/g"`
MAC=`echo $MAC | sed "s/^ *//g" | sed "s/ *$//g"`
SS=`echo $SS | sed "s/^ *//g" | sed "s/ *$//g"`
URL+="&wifi=mac:$MAC&ssid:$SSID&ss:$SS"
done
IFS=$OLD_IFS

#echo $URL
curl -s -A "Mozilla" "$URL" > /tmp/gl_coordinates.txt
LAT=`cat /tmp/gl_coordinates.txt | grep "lat" | awk '{print $3}' | tr -d ","`
LONG=`cat /tmp/gl_coordinates.txt | grep "lng" | awk '{print $3}' | tr -d ","`
ACC=`cat /tmp/gl_coordinates.txt | grep "accuracy" | awk '{print $3}' | tr -d ","`
#echo "LAT: $LAT"
#echo "LONG: $LONG"
#echo "ACC: $ACC"

curl -s -A "Mozilla" "http://maps.googleapis.com/maps/api/geocode/json?latlng=$LAT,$LONG&sensor=false" > /tmp/gl_address.txt
ADDRESS=`cat /tmp/gl_address.txt | grep "formatted_address" | head -1 | awk '{$1=$2=""; print $0}' | sed "s/,$//g" | tr -d " | sed "s/^ *//g"`

if [ $EA -ne 0 ] ; then
echo "<result>$ADDRESS (lat=$LAT, long=$LONG, acc=$ACC)</result>"
else
echo "$ADDRESS (lat=$LAT, long=$LONG, acc=$ACC)"
fi

rm /tmp/gl_ssids.txt /tmp/gl_coordinates.txt /tmp/gl_address.txt

I have setup the stolen static group, assigned the stolen machine to the stolen static group, added the scripts and created the policy to push the scripts to the machine....My question is where will the data appear? If still fairly new to JamfPro.



Many thanks,
Allen


If you are using the Safari history script - it will show in the log for the Policy.


Thank you!


EDIT



never mind. figured it out.


Just implemented @bollman's location script in my JAMF instance - amazing how accurate it seems to be!



One thing to be aware of is that this script relies on wifi being enabled and actively turns it on (and then back off) if it finds it in the off state. I'm betting most users won't dig seeing their wifi turning on by itself (and then back off, which is even MORE suspicious-looking), so consider fiddling with the if statements so that, if the script finds the wifi to be on then go ahead and do "it's thing" but, if it's off, simply return a message like "WiFi was disabled during last check-in; location not acquired."



Also, reading @JayDuff's comment about hitting an OVER_QUERY_LIMIT message from Google (which makes sense, considering how much this will hammer their servers, depending on the size of your system inventory), I'm thinking you could do something like store the name of the active WiFi network as a text file in /tmp when a lookup is performed, then modify the script to first compare that to the current wifi network the NEXT time the script is accessed and only proceed with a geo-lookup if it's changed. That should minimize the amount of lookups.


@ChrisJScott-work What you'd look at to see if it changed is the BSSID. You might have a WiFi network available over a large area so the SSID might not change, but the BSSID will as it's unique to each base station.


Great suggestion, @StoneMagnet! You kind of read my mind... one thing that would be an issue w/ my solution is that, for example, my company has offices all over the world and they all have same wifi name - any user traveling from one office to the next w/o connecting to another network would not get their location updated. Not a matter of life or death, but a hiccup.



Anyhow, your suggestion would resolve that - thanks!



One other question: the accuracy - what is that a measurement of? Feet? Meters? Smoots (https://en.wikipedia.org/wiki/Smoot)?


This script doesn't seem to work anymore.



I keep getting the following errors when running the script



line 40: [: -ne: unary operator expected
(lat=, long=, acc=)

I was just about to come and post the same thing as @ooshnoo I noticed the other day, I get the same error also.


@bollman Any thoughts on why it no worky???


@JayDuff On the imagesnap/screencapture and logging I have used a this with ARD before:
/tmp/imagesnap - | base64 | pbcopy



The other half is
pbpaste | base64 -D -o ~/Desktop/shot.jpg;open ~/Desktop/shot.jpg



One might simply pipe the base64 encode into the logs and decode as needed.


Not working as 404 error from google. Looks like the API is gone


deleted


I think it's still there, they just altered it so you have to insert your API key in the URL request each time.


@guidotti nope. entered my API key and it still fails


I haven't gotten around to fix this, but I really need to. It's been a while, but I think I somehow "reverse-engineered" how an android phone finds out it's location with no GPS. I'd have to go back and see if I saved the info on how I got the info, heh.
I'll get back to this thread.


@bollman
Any luck on fixing this?


Considering we're now trying to track down a lost system, sure am wishing this script was working!!!! Agh!!!


@ChrisJScott-work You could download and install prey if it's still checking in. https://www.preyproject.com/
There is an api key that you can use to link it to your account when it's installed. We just recently tracked down a stolen laptop with it.



#!/bin/sh

#download prey
curl -o /private/tmp/prey-mac-1.7.3-x64.pkg "https://downloads.preyproject.com/prey-client-releases/node-client/1.7.3/prey-mac-1.7.3-x64.pkg"

#Run Prey Installer
API_KEY=yourkey sudo -E installer -pkg /private/tmp/prey-mac-1.7.3-x64.pkg -target /

#Remove Temp File
rm /private/tmp/prey-mac-1.7.3-x64.pkg

exit 0

Nice and thanks for sharing.



We just firmware lock the machines, and put up a desktop wallpaper that says "We're not sure how you got this machine, but it was stolen" and then do a bunch of things:
- Disable terminal.app, iTunes, updates, printers
- Set accounts to log out after 3 minutes and set a screen saver to 1 minute
- Require a new password of ridiculous requirements be set every day
- Continuously remove any admin accounts
- Limit surfing to just Bing, Google, and Yahoo
- Make everything in the dock as big as possible
- Make everything on the desktop as big as possible
- Limit all external media to read-only
- Restrict all preferences, sharing services, functionality (camera, iCloud, etc),
- Hide most of the button under the Apple icon
- Enable Parental Controls and limit the computers to 90 minutes a day



@Nick_Gooch - Be aware that scripts are stored on the local machine prior to execution, and it is possible to scrape API keys for installers and JAMF API calls from any scripts deployed to a client. I'd be careful of putting such items on a stolen machine.


@Seven What is the danger of them getting the api key to prey? There is nothing in the prey account aside from the stolen computer?


@Nick_Gooch - Well, it looks like prey can be a paid for service, so you'd be exposing your API creds to being stolen, which could lead to you paying for someone else to use the product.



Overall, I was just cautioning that users can see the raw content of scripts, so just a reminder to people in the thread to be careful about putting sensitive creds for licenses, accounts, or APIs in the scripts.


Thanks, I see what you are saying. I'm not to concerned in this case but could see how that could be a problem. Would putting it in as a script variable be any better?



We used the free version with the one stolen laptop we had so far. After prey was installed the police recovered it in less then an hour.


just looking at this but can see it still needs fixing


Install Prey, it is really worth it !
A former employee left with his work laptop and I needed a way to make sure he had the computer.
This script was not working anymore so I created a free account with prey.
The API number is at the bottom of the settings page.



Create a script in Jamf with the following content :



#!/bin/sh

#download prey
curl -o /private/tmp/prey-mac-1.7.3-x64.pkg "https://downloads.preyproject.com/prey-client-releases/node-client/1.7.3/prey-mac-1.7.3-x64.pkg"

#Run Prey Installer
API_KEY=CHANGETHIS sudo -E installer -pkg /private/tmp/prey-mac-1.7.3-x64.pkg -target /

#Remove Temp File
rm /private/tmp/prey-mac-1.7.3-x64.pkg

exit 0


Scope it to the Mac you are looking for.
You can "monitor" 3 computers at once with the free version of prey.
as soon as prey is installed, you will receive a mail alert, put the computer in lost mode in prey and wait for the location and screenshots form the isight camera + desktop, to populate reports.



Enjoy !


Reply