Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

This plug-in to monitor Exchange ActiveSync Sync events. It collects data saved in alienvault-siem database from my own tmg-web plugin. It is analog of the my user-logon-monitor plugin.

The plugin uses my own Python script activesync-monitor.py that creates two work files /var/cache/logon-monitor/as-access-history.list and /var/cache/logon-monitor/as-access-[today]-.list and also write in /var/log/activesync-access-monitor.log 5 types of evants:

1 - Hello! If a user is logged the first time today and is already registered in the past 5 days

2 - Wellcome back! If a user is logged the first time today and has last recorded more than 5 but less than 20 days ago

3 - Wow... Last time I saw you NN days ago! If a user last recorded more then 20 days ago

4 - New user-device pair! If a user-device pair never been regestered before

5 - User device change address! If source ip address changed from last access.

100 - Error! if a script has stoped by error when.

The corresponded plugin read this log and put messages in OSSIM.

Files in /var/cache/logon-monitor are also usefull. These files have csv format (user@domain;device_type;device_id;ip_address;time) for using in Excel. History file contain records abaut last user logon, today file - about first user logon today.

Russian text in user@domain supported especialy for Russian Windows users :) Controlled events are Exchange ActiveSync Sync command request on TMG.

# Alienvault plugin
# Author: Eugene Sokolov at esguardian@outlook.com
# Plugin activesync-monitor id:9007 version: 0.0.1
# Last modification: 2015-07-05 19:50
#
[DEFAULT]
plugin_id=9007
[config]
type=detector
enable=yes
source=log
location=/var/log/activesync-access-monitor.log
# create log file if it does not exists,
# otherwise stop processing this plugin
create_file=false
process=activesync-monitor.py
start=yes ; launch plugin process when agent starts
stop=no ; shutdown plugin process when agent stops
restart=yes ; restart plugin process after each interval
restart_interval=300
startup=/usr/local/bin/activesync-monitor.py
shutdown=
[as-access-plain]
event_type=event
regexp="=(?P<sid>.*?);\s(?P<desc>.*?)!\s(?P<user>.*?)\swith\s(?P<dev_type>.*?)\s(?P<dev_id>.*?)\sat\s(?P<time>.*?);\ssrc\sip:\s(?P<src_ip>.*?)\s"
plugin_sid={$sid}
username={$user}
date={normalize_date($time)}
src_ip={$src_ip}
userdata1={$dev_type}
userdata2={$dev_id}
Userdata3={$desc}
#! /usr/bin/python
# -*- coding: cp1251 -*-
# author Eugene Sokolov esguardian@outlook.com
# version 0.2.0
# last update 2015-08-06 at 10:18 MST
#
# create two work files /var/cache/logon-monitor/as-access-history.list and /var/cache/logon-monitor/as-access-<today_date>.list
# and also write in /var/log/activesync-access-monitor.log 5 types of evants:
# 1 - Hello! If a user is logged the first time today and is already registered in the past 5 days
# 2 - Wellcome back! If a user is logged the first time today and has last recorded more than 5 but less than 20 days ago
# 3 - Wow... Last time I saw you NN days ago! If a user last recorded more then 20 days ago
# 4 - New user-device pair! If a user-device pair never been regestered before
# 5 - User device change address! If source ip address changed from last access.
# 100 - Error! if a script has stoped by error when.
# The corresponded plugin read this log and put messages in OSSIM.
# Files in /var/cache/logon-monitor are also usefull. These files have csv format (user@domain;device_type;device-id;ip_address;time)
# for using in Excel. History file contain records abaut last user logon, today file - about first user logon today.
#
#
# Russian text in user@domain supported especialy for Russian Windows users :)
# Controlled events are Exchange ActiveSync Sync command request on TMG.
#
# version history:
# 0.2.0 changed section for obtaining correct username and domain
# because TMG write username to log in various formats and so there is various formats in SIEM database
# 0.1.0 first working
#
import sys
import MySQLdb
import codecs
import subprocess
import os
# import syslog
from datetime import *
def str_decode(str):
try:
s=codecs.decode(str, 'cp1251')
except:
try:
s=codecs.decode(str, 'utf8')
except:
s=str
return s
def check_that_first_later(first,second):
ft=datetime.strptime(first,'%Y-%m-%d %H:%M:%S')
st=datetime.strptime(second,'%Y-%m-%d %H:%M:%S')
if ft > st:
return True
else:
return False
# Datababe connection config. Use your own data
dbuser='root'
dbpass='fgDisHzsup'
dbhost='127.0.0.1'
dbschema='alienvault_siem'
asset_dbschema='alienvault'
# --- End of Database config
# ---- Init
mytz="'+03:00'"
mycharset='cp1251'
mylogpath='/var/log/activesync-access-monitor.log'
mydebuglogpath='/var/log/mydebug.log'
# my domain names synonims (netbios and dns)
# first element of tuple is "canonical" name for use in
# logon and logon_history.list,
# then two variants of domain names which may be in event record in database
my_doms_list = [('inbank.msk','inbankmsk','inbank.msk')]
today=date.today()
log_cache_fullpath='/var/cache/logon-monitor/as-access-' + today.strftime('%Y-%m-%d') + '.list'
log_history_fullpath='/var/cache/logon-monitor/as-access-history.list'
# read today logon hash
logon_dict={}
if os.path.isfile(log_cache_fullpath):
with codecs.open(log_cache_fullpath, 'r', encoding=mycharset) as f:
for line in f:
(user,dev_type,dev_id,astr,ltime) = line.strip().split(';')
udev = user + ';' + dev_type + ';' + dev_id
logon_dict[udev] = (astr,ltime)
f.close()
else:
if not os.path.exists('/var/cache/logon-monitor'):
os.makedirs('/var/cache/logon-monitor')
open(log_cache_fullpath,'a').close()
# read logon history hash
logon_history_dict={}
if os.path.isfile(log_history_fullpath):
with codecs.open(log_history_fullpath, 'r', encoding=mycharset) as f:
for line in f:
(user,dev_type,dev_id,astr,ltime) = line.strip().split(';')
udev = user + ';' + dev_type + ';' + dev_id
logon_history_dict[udev] = (astr,ltime)
f.close()
else:
open(log_history_fullpath,'a').close()
#
#
# set time interval for mySQL Select
period = 8
end_time=datetime.utcnow().strftime('%Y:%m:%d %H:%M:%S')
start_time=(datetime.utcnow() - timedelta(minutes=period)).strftime('%Y:%m:%d %H:%M:%S')
conn = MySQLdb.connect(host=dbhost, user=dbuser, passwd=dbpass, db=dbschema, charset='utf8')
cursor = conn.cursor()
when = "timestamp between '" + start_time + "' and '" + end_time + "'"
# ---- End of Init
# now start
# mydebuglog = open(mydebuglogpath, 'a')
mylog = codecs.open(mylogpath, 'a', encoding=mycharset)
#
# collect usernames in format username@domain (username@host) from siem database:
# tmg-web Exchange ActiveSync events and create hash tables on key 'username;device_type;device_id'
#
what = "username, userdata9, convert_tz(timestamp,'+00:00'," + mytz +") as time, inet_ntoa(conv(HEX(ip_src), 16, 10)) from acid_event join extra_data on id=extra_data.event_id"
where = "plugin_id=9004 and plugin_sid=2"
select="select " + what + " where " + where + " and " + when + " order by time"
cursor.execute(select)
row=cursor.fetchone()
try:
while row:
row_0=str_decode(row[0]).strip().lower()
syslog_message = ' at ' + str(row[2]).strip() + '; src ip: ' + str(row[3]).strip() + '\n'
#
# reading username and domain from database row
# we don't know format in wich username has stored
# "dom\user", "user@dom" or just "user"
#
if '\\' in row_0:
(udom,rest) = row_0.split('\\')
if '@' in rest:
uname = rest.split('@')[0]
else:
uname = rest
elif '@' in row_0:
(uname,udom) = row_0.split('@')
else:
uname = row_0
udom = 'undefined'
#
# reading ActiveSync command params stored in URI
#
paramlist=[]
paramlist = str_decode(row[1]).split('&')
param_user=""
for param in paramlist:
if 'DeviceId=' in param:
dev_id = param.split('=')[-1]
if 'DeviceType=' in param:
dev_type = param.split('=')[-1]
if 'User=' in param:
param_user = param.split('=')[-1]
#
# if previosly obtained from database username is "anonymous"
# try to obtain it from ActiveSync command params stored in URI
#
if uname == 'anonymous':
if '%40' in param_user:
(uname,udom) = param_user.split('%40')
elif '%5C' in param_user:
(udom,uname) = param_user.split('%5C')
else:
uname = param_user
#
# normalize username as user@domain
#
for i in my_doms_list:
if i[1] == udom or i[2] == udom:
udom = i[0]
break
uname = uname + '@' + udom
#
# creating userdevice key for dictionary
#
udev = uname + ';' + dev_type + ';' + dev_id
#
# check today logons and history for this userdevice and generate event
#
new_ip = str(row[3]).strip()
evtime = str(row[2]).strip()
if udev in logon_dict:
if check_that_first_later(evtime,logon_history_dict[udev][1]) and new_ip != logon_history_dict[udev][0]:
mylog.write(str_decode('=5; User device change address! ' + uname + ' with ' + dev_type + ' ' + dev_id + syslog_message))
logon_history_dict[udev] = (new_ip, evtime)
else:
logon_dict[udev] = (new_ip, evtime)
#
# uppend today logons if new
#
with codecs.open(log_cache_fullpath, 'a', encoding=mycharset) as out:
out.write(str_decode(udev + ';' + logon_dict[udev][0] + ';' + logon_dict[udev][1] + '\n'))
out.close()
#
if udev in logon_history_dict:
dt = datetime.strptime(logon_history_dict[udev][1],'%Y-%m-%d %H:%M:%S')
dd = (row[2] - dt).days
if dd <5:
mylog.write(str_decode('=1; Hello! ' + uname + ' with ' + dev_type + ' ' + dev_id + syslog_message))
elif dd < 20:
mylog.write(str_decode('=2; Wellcome back! ' + uname + ' with ' + dev_type + ' ' + dev_id + syslog_message))
else:
mylog.write(str_decode('=3; Wow ... Last time I saw you ' + str(dd) + ' days ago! ' + uname + ' with ' + dev_type + ' ' + dev_id + syslog_message))
logon_history_dict[udev] = (new_ip, evtime)
else:
mylog.write(str_decode('=4; New user-device pair! ' + uname + ' with ' + dev_type + ' ' + dev_id + syslog_message))
logon_history_dict[udev] = (new_ip, evtime)
#
# next row
#
row=cursor.fetchone()
#
# write history file
#
with codecs.open(log_history_fullpath, 'w', encoding=mycharset) as out:
for key in logon_history_dict.keys():
out.write(str_decode(key + ';' + logon_history_dict[key][0] + ';' + logon_history_dict[key][1] + '\n'))
out.close()
except Exception:
mylog.write('=100;Error! no_user at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S') + '; src ip 0.0.0.0; dst ip 0.0.0.0 '+ str(sys.exc_info()[0]) + '\n')
# raise
mylog.close()
# mydebuglog.close()
conn.close()
-- activesync-monitor
-- plugin_id: 9007
DELETE FROM plugin WHERE id = "9007";
DELETE FROM plugin_sid where plugin_id = "9007";
INSERT IGNORE INTO plugin (id, type, name, description) VALUES (9007, 1, 'activesync-monitor', 'Exchange ActiveSync access monitor');
INSERT IGNORE INTO plugin_sid (plugin_id, sid, category_id, class_id, name, priority, reliability) VALUES (9007, 1, NULL, NULL, 'Hello!',1, 3);
INSERT IGNORE INTO plugin_sid (plugin_id, sid, category_id, class_id, name, priority, reliability) VALUES (9007, 2, NULL, NULL, 'Wellcome back!',1, 3);
INSERT IGNORE INTO plugin_sid (plugin_id, sid, category_id, class_id, name, priority, reliability) VALUES (9007, 3, NULL, NULL, 'Old friend come back!',1, 3);
INSERT IGNORE INTO plugin_sid (plugin_id, sid, category_id, class_id, name, priority, reliability) VALUES (9007, 4, NULL, NULL, 'New user-device pair!',1, 3);
INSERT IGNORE INTO plugin_sid (plugin_id, sid, category_id, class_id, name, priority, reliability) VALUES (9007, 5, NULL, NULL, 'User device change address!',1, 3);
INSERT IGNORE INTO plugin_sid (plugin_id, sid, category_id, class_id, name, priority, reliability) VALUES (9007, 100, NULL, NULL, 'Error!',1, 3);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.