#!/bin/bash
#
# Script to grab data out of the Snort MySQL database and identify users.


# NOTE!  Before you can use this script, you must change the defines
# in the following lines to match those at your company.
#
# A few constants needed.  User with R/W privileges to snort database.
MYUSER="snort"
MYPASS="secret"
SNORTDB="snort"
# Now define the public IP address ranges used by your company.
# If you have more than one discontiguous range, you'll need to edit
# the SQL generation code lower down in this script.  It's not hard to do.
IPLOW="1.2.3.0"
IPHIGH="1.2.3.254"
# If you would like usernames found with the -n switch to attempt to
# translate to real names in your corporate LDAP directory, then uncomment
# the following lines.
##LDAPSEARCH="/usr/bin/ldapsearch"
##LDAPHOST="localhost"
##LDAPBIND="cn=Snort Lookup, o=Your Company"
##LDAPPASS="secret"
### How to look up users in your LDAP directory.
### The following looks up (for username "jsmith")
###   "mail=jsmith () yourcompany com"
##LATTRIBUTE="mail"
##LPREFIX=""
##LSUFFIX="@yourcompany.com"

function usage() {
  cat <<EOF >&2
Usage:	$0 [ -s "scriptlike" ] [ -l | -i ip ] [ -n ] [ -d ] [ -t hours ]

Rummages through the 'snort' MySQL database looking for signatures that:
	-s "x"		Have a signature like '%x%'
	-l		Have a source IP on our network.
	-i "ip"		Have the given source IP; exclusive of -l, above.
At least one of the above must be specified.

Options:
	-b		Debug SQL - Prints executed SQL to stderr
	-c		Sort by count instead of by IP
	-d		Use destination IP; the default is source IP.
	-g		Sort by signature name instead of by IP
	-n		Perform a NetBIOS name lookup on returned IPs.
	-t hours	Only consider signatures received within <hours>.
EOF
}

if TEMP=`getopt -o bcdgi:lns:t: -n "$0" -- "$@"`; [ $? -ne 0 ]; then
  usage; exit 1
fi

eval set -- "$TEMP"

LIKE=""; LOCALS=""; IP=""; SRCDST="ip_src"; DONBT=""; HOURS=""; DBG=""
BYCOUNT=""; BYSIG=""
while true ; do
  if [ "$1" = "-d" ];	then SRCDST="ip_dst";	shift
  elif [ "$1" = "-b" ]; then DBG=1;		shift
  elif [ "$1" = "-c" ]; then BYCOUNT=1;		shift
  elif [ "$1" = "-g" ]; then BYSIG=1;		shift
  elif [ "$1" = "-i" ]; then IP="$2";		shift 2
  elif [ "$1" = "-l" ]; then LOCALS=1;		shift
  elif [ "$1" = "-n" ]; then DONBT=1;		shift
  elif [ "$1" = "-s" ]; then LIKE="$2";		shift 2
  elif [ "$1" = "-t" ]; then HOURS="$2";	shift 2
  elif [ "$1" = "--" ]; then			shift; break
  else echo "Internal getopt error?" >&2;	exit 2
  fi
done
if [ $# -ne 0 ]; then
  usage; exit 1
elif [ -n "$IP" -a -n "$LOCALS" ]; then
  echo -e "\n\nCannot specify both -i and -l.\n" >&2
  usage; exit 1
elif [ -n "$HOURS" ] && ! echo "$HOURS" | grep -q '^[1-9][0-9]*$'; then
  echo -e "\n\nArgument to -t must be a positive integer.\n" >&2
  usage; exit 1
elif [ -z "$IP" -a -z "$LOCALS" -a -z "$LIKE" ]; then
  echo -e "\n\nMust specify at least one of either -i, -l or -s\n" >&2
  usage; exit 1
fi

function makesql () {
  local wa="WHERE" srt="rawip, signame"
  if [ -n "$BYSIG" ]; then
    srt="signame, rawip"
  fi
  if [ -n "$BYCOUNT" ]
    then srt="cnt, $srt"
  fi
  cat <<EOF
SELECT DISTINCT iphdr.$SRCDST as rawip,
		INET_NTOA(iphdr.$SRCDST) as prettyip,
		signature.sig_name as signame,
		COUNT(*) AS cnt
       FROM signature, event, iphdr
EOF
  if [ -n "$LIKE" ]; then
    if ! echo "$LIKE" | grep -q '%'; then
      LIKE="%${LIKE}%"
    fi
    echo "       WHERE signature.sig_name LIKE '$LIKE'"; wa="AND"
  fi
  echo "       $wa event.signature = signature.sig_id"
  if [ -n "$HOURS" ]; then
    echo "       AND NOW() <= event.timestamp + INTERVAL '$HOURS' HOUR"
  fi
  if [ -n "$IP" ]; then
    echo "       AND iphdr.$SRCDST = INET_ATON('$IP')"
  elif [ -n "$LOCALS" ]; then
    cat <<EOF
       AND ( (iphdr.$SRCDST BETWEEN INET_ATON('$IPLOW')
				AND INET_ATON('$IPHIGH')) OR
	     (iphdr.$SRCDST BETWEEN INET_ATON('10.0.0.0')
				AND INET_ATON('10.255.255.255')) OR
	     (iphdr.$SRCDST BETWEEN INET_ATON('192.168.0.0')
				AND INET_ATON('192.168.255.255')) )
EOF
  fi
  cat <<EOF
       AND iphdr.sid = event.sid
       AND iphdr.cid = event.cid
       GROUP BY rawip, signame ORDER BY $srt;
EOF
}

function vb () {		# Strip vertical-bar character
  echo "$*" | tr -d '|'
}

function nbti () {	# nbti ip.address
  local nbname wgroup uname x name code group c r
  x="`nmblookup -A "$1" 2>/dev/null`"
  if [ -z "$x" -a -z "$NMBD" ]; then
    x="`nmblookup -r -A "$1" 2>/dev/null`"
  fi
  echo "$x" | grep '<[0-9a-f]\{2\}> -.*[BM] <ACTIVE>' |\
  ( nbname=""; wgroup=""; uname=""
    while read -r x; do
      name="`echo $x | cut '-d<' -f1`"; name="`echo $name`"
      code="`echo $x | cut '-d<' -f2 | cut '-d>' -f1`"
      group="`echo $x | grep '<[0-9a-f]*> - <GROUP>'`"
      if [ "$code" = "00" ]; then
	if [ -n "$group" ]; then
	  if [ -z "$wgroup" ]
	    then wgroup="$name"
	  fi
	elif [ -z "$nbname" ]
	  then nbname="$name"
	fi
      elif [ "$code" = "03" -a "$name" != "$nbname" -a \
	     \( -z "$uname" -o "$uname" = "$nbname" \) ] && \
	   ! echo "$name" | grep -q '\$$'
	then uname="$name"
      fi
    done
    if echo "$uname" | grep -q '^[^ ]\+$' && [ -n "$LDAPSEARCH" ]; then
      rname="`$LDAPSEARCH -h "$LDAPHOST" -D "$LDAPBIND" -w "$LDAPPASS" \
			  "${LATTRIBUTE}=${LPREFIX}${uname}${LSUFFIX}" \
			  cn    2>/dev/null |\
	      grep '^cn=' | head -1 | cut -d= -f2`"
    else					# Real Name (no usename)
      rname="$uname"; uname=""
    fi
    echo "`vb "$nbname"`|`vb "$wgroup"`|`vb "$uname"`|`vb "$rname"`"
  )
}


#########################################################################
#									#
#		 Run the query and output the results...		#
#									#
#########################################################################

if [ -n "$DBG" ]; then
  echo -e "\nSQL Query:\n" >&2; makesql >&2; echo >&2
fi

makesql | mysql --user="$MYUSER" --password="$MYPASS" -s -B "$SNORTDB" |\
while IFS=$'\t\n' read r p n c; do
  if [ "$r" = "$oip" ]; then
    printf "                %6s: \"%s\"\n" "x$c" "$n"
    continue
  fi
  oip=$r
  if [ -n "$DONBT" ]; then
    echo; echo
  fi
  printf "%15s %6s: \"%s\"\n" "$p" "x$c" "$n"
  if [ -n "$DONBT" ]; then
    dns="`host "$p" 2>/dev/null | grep 'domain name pointer' |\
	  head -1 | tr 'A-Z' 'a-z' | sed -e 's/^.*pointer //' -e 's/\.*$//'`"
    nbt="`nbti "$p"`"
    if [ -n "$dns" ]; then
      printf "%15s: %s\n" "DNS" $dns
    fi
    nbname="`echo "$nbt" | cut '-d|' -f1`"
    wgroup="`echo "$nbt" | cut '-d|' -f2`"
    uname="`echo "$nbt" | cut '-d|' -f3`"
    rname="`echo "$nbt" | cut '-d|' -f4`"
    if [ -n "$nbname" ]; then
      printf "%15s: %s\n" "NBName" "$nbname"
    fi
    if [ -n "$wgroup" ]; then
      printf "%15s: %s\n" "WGroup" "$wgroup"
    fi
    if [ -n "$uname" ]; then
      printf "%15s: %s\n" "UName" "$uname"
    fi
    if [ -n "$rname" ]; then
      printf "%15s: %s\n" "RName" "$rname"
    fi
  fi
done
