OS X intrusion monitoring using push notifications

Warning: You need to be very comfortable using the Terminal to follow along. I take no responsibility for what happens to your system if you try to follow these instructions without understanding what they do.


Watching the documentary film The KGB, the Computer, and Me inspired me to try my hand at setting up an intrusion detection system, similar to the pager alert system Cliff Stoll used to alert himself when the intruding hacker logged onto the computers he administered at Berkeley in the 1980s.

My home server exposes both VNC and SSH to the public Internet, and it has always worried me that someone could hack into it and perform any number of unsavory deeds without me ever finding out. I did some quick studying and decided I should be able to detect VNC and SSH log ins and send myself a push notification without too much trouble.

 

Detecting log in events

Connection events in OS X 10.10 Yosemite are recorded in the system.log file. As soon as a new line is added to this log file the intruder notification system should look at it to see if it indicates that a successful log in event has occurred.

We can run tail on the log file in the terminal to see the system log messages in real time.

> tail -F /var/log/system.log

By analyzing log events while logging in using VNC and SSH I determined that suitable phrases to look for are Authentication: SUCCEEDED for VNC connections and Accepted keyboard-interactive/pam for SSH.

To automate this work we create a shell script in our home folder named notifylogin.sh.


while read line; do
    if [[ $line == *"Authentication: SUCCEEDED"* ]]
    then
        echo "VNC login detected"
    fi

    if [[ $line == *"Accepted keyboard-interactive/pam"* ]]
    then
        echo "SSH login detected"
    fi
done

Before we can try our script out we need to make the script file executable.

> chmod +x notifylogin.sh 

We can now verify our intrusion detection by piping the output from the system log file into our script and logging in using VNC and SSH.

> tail -F /var/log/system.log | ./notifylogin.sh 
Seems to work.

Seems to work.

 

Sending push notifications

We'll use Boxcar.io to send push notifications to our phones. The Boxcar 2 app needs to be installed on the phone meant to receive our notifications, but you don't need to create a Boxcar account or sign in unless you feel like it. You do need to give it permission to show you notifications, obviously.

We will need to copy the access token out of the app's settings pane so Boxcar knows where to send our notifications.

Copy user token.gif

Once we have our access token we can use curl from the terminal to send ourselves a push notification. Substitute your personal access token for all occurrences of [ACCESS TOKEN] in the commands and scripts below.

> curl -k -d "user_credentials=[ACCESS TOKEN]" \
       -d "notification[title]=Login detected" \
       -d "notification[long_message]=Someone logged in" \
       https://new.boxcar.io/api/notifications

For more information about the parameters we can send to Boxcar, see How to send a notification to Boxcar users.

We now add a function for sending push notifications to notifylogin.sh, and add calls to this function when either type of log in event has been detected.


function notifyLogin {
    curl -k -d "user_credentials=[ACCESS TOKEN]" \
         -d "notification[title]=Login detected" \
         -d "notification[long_message]=Someone logged in using $1" \
         https://new.boxcar.io/api/notifications
}

while read line; do
    if [[ $line == *"Authentication: SUCCEEDED"* ]]
    then
        echo "VNC login detected"
        notifyLogin VNC
    fi

    if [[ $line == *"Accepted keyboard-interactive/pam"* ]]
    then
        echo "SSH login detected"
        notifyLogin SSH
    fi
done

 

Start monitoring on system startup

We're almost done, but an intrusion detection system that doesn't automatically start on boot isn't worth much. To finish up we add a launch daemon to our machine.

First we create a script file watchlog.sh that performs the tail command and pipes the results to notifylogin.sh. We need to make sure we give the full path to notifylogin.sh since this will be executed by the system, so replace [USERNAME] to provide the correct path.


tail -F /var/log/system.log | /Users/[USERNAME]/notifylogin.sh

As usual we need to mark this script as executable for it to work.

> chmod +x watchlog.sh

We then create a .plist file in the /Library/LaunchDaemons/ folder to launch this script on system boot. We name the file com.theevilboss.notifylogins.plist, and make sure to replace [USERNAME] to provide the correct path to watchlog.sh.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Label</key>
        <string>com.theevilboss.notifylogins</string>
        <key>ProgramArguments</key>
        <array>
            <string>/Users/[USERNAME]/watchlog.sh</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
    </dict>
</plist>

 

Verification and troubleshooting

After restarting the machine we should be able to confirm that the notification system works by simply logging in using VNC or SSH and waiting for a notification banner to appear on our phone.

If no notifications come through there are a few likely issues:

  1. Banner notifications should be enabled for the Boxcar app.
  2. We must use our personal Boxcar access token in the notifylogin.sh script file.
  3. The watchlog.sh script file must contain the correct path to notifylogin.sh.
  4. The com.theevilboss.notifylogins.plist file must contain the correct path to watchlog.sh.
  5. The com.theevilboss.notifylogins.plist file should be placed in the folder /Library/LaunchDaemons/.

Another issue is that the file com.theevilboss.notifylogins.plist could be somehow malformed, in which case it wouldn't load. We can see if it's loaded using launchctl in the terminal.

> sudo launchctl list com.theevilboss.notifylogins

DISCLAIMER: This solution is what I came up with after a little research, but it may not be the best way to do it. If there's an obviously superior method, or if something I did is a bad idea for any reason I very much want to hear about it.