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())
--
Linux-audit mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/linux-audit