[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[PATCH] auditd - TTY support for audisp-prelude



Hi all,

Attached is a patch for auditd to add support for TTY audits ( pam_tty_audit session module ) to audisp-prelude.

Alerts are reported with:
alert.classification.text = "Keylogger"
alert.assessment.impact.severity = LOW

and actual keystrokes carried on alert.additional_data.

Attached you will find also a basic python commandline script to query keylogger data from prelude database.

Hope it helps.

Matteo Sessa 
IT Systems Administrator 
D.B.M. srl 
Via Enrico Noe, 23
20133 Milano (MI), Italy
Landline: (+39) 02-266005-21
Mobile: (+39) 334-6220662

diff -rup audit-1.7.18/audisp/plugins/prelude/audisp-prelude.c audit-1.7.18-ba/audisp/plugins/prelude/audisp-prelude.c
--- audit-1.7.18/audisp/plugins/prelude/audisp-prelude.c	2010-09-19 21:55:07.000000000 +0200
+++ audit-1.7.18-ba/audisp/plugins/prelude/audisp-prelude.c	2011-04-28 10:45:20.000000000 +0200
@@ -44,7 +44,7 @@
 typedef enum { AS_LOGIN, AS_MAX_LOGIN_FAIL, AS_MAX_LOGIN_SESS, AS_ABEND,
 	AS_PROM, AS_MAC_STAT, AS_LOGIN_LOCATION, AS_LOGIN_TIME, AS_MAC,
 	AS_AUTH, AS_WATCHED_LOGIN, AS_WATCHED_FILE, AS_WATCHED_EXEC, AS_MK_EXE,
-	AS_MMAP0, AS_WATCHED_SYSCALL, AS_TOTAL } as_description_t;
+	AS_MMAP0, AS_WATCHED_SYSCALL, AS_TTY, AS_TOTAL } as_description_t;
 const char *assessment_description[AS_TOTAL] = {
  "A user has attempted to login",
  "The maximum allowed login failures for this account has been reached. This could be an attempt to gain access to the account by someone other than the real account holder.",
@@ -61,7 +61,8 @@ const char *assessment_description[AS_TO
  "A user has attempted to execute a program that is being watched.",
  "A user has attempted to create an executable program",
  "A program has attempted mmap a fixed memory page at an address sometimes used as part of a kernel exploit",
- "A user has run a command that issued a watched syscall"
+ "A user has run a command that issued a watched syscall",
+ "A user has entered keystrokes on a terminal"
 };
 typedef enum { M_NORMAL, M_TEST } output_t;
 typedef enum { W_NO, W_FILE, W_EXEC, W_MK_EXE } watched_t;
@@ -1981,6 +1982,83 @@ static void handle_watched_syscalls(aupa
 	}
 }
 
+static int tty_alert(auparse_state_t *au, idmef_message_t *idmef,
+		idmef_alert_t *alert)
+{
+	int ret;
+
+	idmef_source_t *source;
+	idmef_user_t *suser;
+	idmef_user_id_t *user_id;
+	idmef_impact_type_t impact_type;
+    idmef_assessment_t *assessment;
+	idmef_impact_t *impact;
+	idmef_impact_severity_t severity;
+	prelude_string_t *str;
+	idmef_impact_completion_t completion = IDMEF_IMPACT_COMPLETION_ERROR;
+
+	/* Fill in information about the event's source */
+	ret = idmef_alert_new_source(alert, &source, -1);
+	PRELUDE_FAIL_CHECK;
+
+	ret = idmef_source_new_user(source, &suser);
+	PRELUDE_FAIL_CHECK;
+	idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION);
+	ret = idmef_user_new_user_id(suser, &user_id, 0);	
+	PRELUDE_FAIL_CHECK;
+	idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER);
+	ret = get_loginuid_info(au, user_id);
+	PRELUDE_FAIL_CHECK;
+
+	ret = get_tty_info(au, user_id);
+	PRELUDE_FAIL_CHECK;
+
+	ret = get_comm_info(au, source, NULL);
+	PRELUDE_FAIL_CHECK;
+
+	ret = add_execve_data(au, alert);
+	PRELUDE_FAIL_CHECK;
+
+	ret = add_serial_number_data(au, alert);
+	PRELUDE_FAIL_CHECK;
+
+	/* Describe event */
+	ret = set_classification(alert, "Keylogger");
+	PRELUDE_FAIL_CHECK;
+
+	/* Assess impact */
+	if (get_loginuid(au) == 0)
+		impact_type = IDMEF_IMPACT_TYPE_ADMIN;
+	else
+		impact_type = IDMEF_IMPACT_TYPE_USER;
+	// Adjust the rating on AVC's based on if they succeeded or not
+	completion = IDMEF_IMPACT_COMPLETION_SUCCEEDED;
+	severity = IDMEF_IMPACT_SEVERITY_LOW;
+	
+	ret = idmef_alert_new_assessment(alert, &assessment);
+	PRELUDE_FAIL_CHECK;
+	ret = idmef_assessment_new_impact(assessment, &impact);
+	PRELUDE_FAIL_CHECK;
+	idmef_impact_set_severity(impact, severity);
+	PRELUDE_FAIL_CHECK;
+	idmef_impact_set_type(impact, impact_type);
+	PRELUDE_FAIL_CHECK;
+	ret = idmef_impact_new_description(impact, &str);
+	PRELUDE_FAIL_CHECK;
+	prelude_string_set_ref(str, assessment_description[AS_WATCHED_EXEC]);
+	PRELUDE_FAIL_CHECK;
+
+	send_idmef(client, idmef);
+	idmef_message_destroy(idmef);
+
+        return 0;
+
+ err:
+	syslog(LOG_ERR, "watched_exec_alert: IDMEF error: %s.\n", 
+		prelude_strerror(ret));
+        idmef_message_destroy(idmef);
+        return -1;
+}
 static void handle_event(auparse_state_t *au,
 		auparse_cb_event_t cb_event_type, void *user_data)
 {
@@ -2129,6 +2207,15 @@ static void handle_event(auparse_state_t
 				// The previous call moves the current record
 				auparse_goto_record_num(au, num);
 				break;
+			case AUDIT_TTY:
+				if (config.tty == E_NO)
+					break;
+				if (config.tty_act != A_IDMEF)
+					break;
+				if (new_alert_common(au, &idmef, &alert) >= 0){
+					tty_alert(au, idmef, alert);
+				}
+				break;
 			default:
 				break;
 		}
diff -rup audit-1.7.18/audisp/plugins/prelude/audisp-prelude.conf audit-1.7.18-ba/audisp/plugins/prelude/audisp-prelude.conf
--- audit-1.7.18/audisp/plugins/prelude/audisp-prelude.conf	2010-09-19 21:55:07.000000000 +0200
+++ audit-1.7.18-ba/audisp/plugins/prelude/audisp-prelude.conf	2011-06-17 12:19:13.000000000 +0200
@@ -56,3 +56,6 @@ watched_exec_action = idmef
 detect_watched_mk_exe = yes
 watched_mk_exe_action = idmef
 
+detect_tty = yes
+tty_action = idmef
+
Only in audit-1.7.18-ba/audisp/plugins/prelude: .audisp-prelude.c.swo
Only in audit-1.7.18-ba/audisp/plugins/prelude: libpreludedb-1.0.0
diff -rup audit-1.7.18/audisp/plugins/prelude/prelude-config.c audit-1.7.18-ba/audisp/plugins/prelude/prelude-config.c
--- audit-1.7.18/audisp/plugins/prelude/prelude-config.c	2010-09-19 21:55:07.000000000 +0200
+++ audit-1.7.18-ba/audisp/plugins/prelude/prelude-config.c	2011-04-28 10:45:27.000000000 +0200
@@ -122,6 +122,10 @@ static int watched_mk_exe_parser(struct 
 		prelude_conf_t *config);
 static int watched_mk_exe_act_parser(struct nv_pair *nv, int line, 
 		prelude_conf_t *config);
+static int tty_parser(struct nv_pair *nv, int line, 
+		prelude_conf_t *config);
+static int tty_act_parser(struct nv_pair *nv, int line, 
+		prelude_conf_t *config);
 static int sanity_check(prelude_conf_t *config, const char *file);
 
 static const struct kw_pair keywords[] = 
@@ -158,6 +162,8 @@ static const struct kw_pair keywords[] =
   {"watched_exec_action",        watched_exec_act_parser,	0 },
   {"detect_watched_mk_exe",      watched_mk_exe_parser,		0 },
   {"watched_mk_exe_action",      watched_mk_exe_act_parser,	0 },
+  {"detect_tty",                 tty_parser,		0 },
+  {"tty_action",                 tty_act_parser,	0 },
   { NULL,             NULL }
 };
 
@@ -215,6 +221,8 @@ void clear_config(prelude_conf_t *config
 	config->watched_exec_act = A_IDMEF;
 	config->watched_mk_exe = E_YES;
 	config->watched_mk_exe_act = A_IDMEF;
+	config->tty = E_YES;
+	config->tty_act = A_IDMEF;
 	ilist_create(&config->watched_accounts);
 }
 
@@ -790,6 +798,23 @@ static int watched_mk_exe_act_parser(str
         return 1;
 }
 
+static int tty_parser(struct nv_pair *nv, int line,
+	prelude_conf_t *config)
+{
+	if (lookup_enabler(nv->value, &config->tty) == 0)
+		return 0;
+        syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line);
+        return 1;
+}
+
+static int tty_act_parser(struct nv_pair *nv, int line,
+	prelude_conf_t *config)
+{
+	if (lookup_action(nv->value, &config->tty_act) == 0)
+		return 0;
+        syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line);
+        return 1;
+}
 /*
  * This function is where we do the integrated check of the audispd config
  * options. At this point, all fields have been read. Returns 0 if no
diff -rup audit-1.7.18/audisp/plugins/prelude/prelude-config.h audit-1.7.18-ba/audisp/plugins/prelude/prelude-config.h
--- audit-1.7.18/audisp/plugins/prelude/prelude-config.h	2010-09-19 21:55:07.000000000 +0200
+++ audit-1.7.18-ba/audisp/plugins/prelude/prelude-config.h	2011-04-27 12:34:21.000000000 +0200
@@ -64,6 +64,8 @@ typedef struct prelude_conf
 	action_t watched_exec_act;
 	enable_t watched_mk_exe;
 	action_t watched_mk_exe_act;
+	enable_t tty;
+	action_t tty_act;
 } prelude_conf_t;
 
 void clear_config(prelude_conf_t *config);
#!/usr/bin/env python
import sys
import argparse
import time
from cgi import escape
from prettytable import PrettyTable
from prewikka import IDMEFDatabase
from prewikka import Config
from preludedb import *
from prelude import *

class StoreDate(argparse.Action):
	def __init__(self,option_strings,dest, nargs=None, const=None, default=None, type=None, choices=None, required=False, help=None, metavar=None):
		argparse.Action.__init__(self, option_strings=option_strings, dest=dest, nargs=nargs, const=const, default=default, type=type,
					choices=choices, required=required, help=help, metavar=metavar )
	def __call__(self, parser, namespace, values, option_string=None):
		time_begin = time.strptime(values, "%Y-%m-%d %H:%M:%S")
		setattr(namespace, self.dest, time_begin)

class PreludeKeylogger:
	parsed_args = None
	def __init__(self,args):
		self.parseArgs(args)

	def parseArgs(self, argv):
		parser = argparse.ArgumentParser()
		parser.add_argument('-u', '--user', action='append', dest='user', default=[], help='Specific user to query, repeat for multiple users' )
		parser.add_argument('-f', '--from-time', dest='time_begin', action=StoreDate, default=None, help='Specify start time for querying')
		parser.add_argument('-t', '--to-time', dest='time_end', action=StoreDate, default=None, help='Specify end time for querying')
		parser.add_argument('-n', '--node', dest='node', action='append', default=[], help='Specific node to query, repeat for multiple nodes')
		parser.add_argument('--html', dest='output_html', action='store_true', default=False, help='Trigger HTML output')
		try:
			self.parsed_args = parser.parse_args()
		except ValueError:
			sys.stderr.write('Invalid datetime format, format is "YYYY-MM-DD hh:mm:ss"\n')
			sys.exit(-2)

	def buildCriteria(self):
		criteria="alert.classification.text = \"Keylogger\""
		if ( self.parsed_args.time_begin is not None ):
			strtime = time.strftime("%Y-%m-%d %H:%M:%S", self.parsed_args.time_begin )
			criteria = criteria + ' && alert.detect_time >= "' + strtime + '"'
		if ( self.parsed_args.time_end is not None ):
			strtime = time.strftime("%Y-%m-%d %H:%M:%S", self.parsed_args.time_end )
			criteria = criteria + ' && alert.detect_time <= "' + strtime + '"'

		users_criteria = []
		for user in self.parsed_args.user:
			users_criteria.append('alert.source(-1).user.user_id(-1).name == "' + user + '"')
		if (len(users_criteria) != 0):
			criteria = criteria + ' && ( ' + ' || '.join(users_criteria) + ' )'

		nodes_criteria = []
		for node in self.parsed_args.node:
			nodes_criteria.append('alert.analyzer(-1).node.name == "' + node + '"')
		if (len(nodes_criteria) != 0):
			criteria = criteria + ' && ( ' + ' || '.join(nodes_criteria) + ' )'
		return criteria
			
	def main(self):
		criteria = self.buildCriteria()
		config = Config.Config(None)
		preludedb_init()
		idmef_db = IDMEFDatabase.IDMEFDatabase(config.idmef_database)
		my_selection = preludedb_path_selection_new()
		res = idmef_db.getValues([
			"alert.detect_time",
			"alert.analyzer(-1).node.name",
			"alert.source(-1).user.user_id(-1).name",
			"alert.source(-1).user.user_id(-1).number",
			"alert.source(-1).process.name",
			"alert.source(-1).process.pid",
			"alert.additional_data(0).data"
			],criteria)
		x = PrettyTable(['Time','Node','User(pid)','Process(pid)','Keystrokes'])
		x.set_field_align("Keystrokes", "l")
		x.set_padding_width(1)
		for record in res:
			detect_time=record[0]
			node_name=record[1]
			user_id=record[2]
			user_pid=record[3]
			process_name=record[4]
			process_pid=record[5]
			keystrokes=record[6]
			x.add_row([detect_time, node_name, user_id+'('+str(user_pid)+')', process_name+'('+str(process_pid)+')', keystrokes])
			#print "%s\t%s\t%s(%s)\t%s(%s)\t%s" % ( detect_time, node_name, user_id, user_pid, process_name, process_pid, keystrokes)
		if ( self.parsed_args.output_html ):
			print '<html><head><title>Prelude keylogger report</title></head><body>'
			print '<strong>Criteria: </strong><p style="font-family: fixedsys, monospace">' + escape(criteria) + '</p><br><br>'
			x.print_html()
			print '</body></html>'
		else:
			print "Criteria: " + criteria
			x.printt()

if __name__ == '__main__':
	sys.exit(PreludeKeylogger(sys.argv).main())




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]