# Copyright (C) 2004 Nicolas Delon # All Rights Reserved # # This file is part of the Prelude program. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. import sys import socket import time import string from prelude import * import FileSummary from Misc import time_to_string def enumeration_to_string(enumeration): if len(enumeration) == 0: return "" if len(enumeration) == 1: return enumeration[0] ret = string.join(enumeration[:-1], ", ") ret += " and %s" % enumeration[-1] return ret class Alert: def __init__(self): self.res = idmef_message_new() t = time.time() self["alert.create_time"] = t self["alert.detect_time"] = t self._additional_data_count = 0 def __del__(self): idmef_message_destroy(self.res) def __setitem__(self, key, value): path = idmef_path_new_fast(key) type = idmef_path_get_value_type(path, idmef_path_get_depth(path) - 1) if type == IDMEF_VALUE_TYPE_STRING: value = idmef_value_new_string(value) elif type == IDMEF_VALUE_TYPE_DATA: data = idmef_data_new_char_string_dup_fast(value, len(value)) value = idmef_value_new_data(data) elif type == IDMEF_VALUE_TYPE_UINT32: value = idmef_value_new_uint32(long(value)) elif type == IDMEF_VALUE_TYPE_UINT64: value = idmef_value_new_uint64(long(value)) elif type == IDMEF_VALUE_TYPE_ENUM: value = idmef_value_new_enum_from_numeric(idmef_path_get_class(path, idmef_path_get_depth(path) - 1), value) elif type == IDMEF_VALUE_TYPE_TIME: time = idmef_time_new() idmef_time_set_from_time(time, long(value)) value = idmef_value_new_time(time) else: raise Exception("idmef_value_type %d not supported" % type) idmef_path_set(path, self.res, value) idmef_path_destroy(path) idmef_value_destroy(value) def setClassification(self, classification): self["alert.classification.text"] = classification def setImpact(self, description, severity=IDMEF_IMPACT_SEVERITY_HIGH, completion=IDMEF_IMPACT_COMPLETION_SUCCEEDED): self["alert.assessment.impact.severity"] = severity self["alert.assessment.impact.completion"] = completion self["alert.assessment.impact.type"] = IDMEF_IMPACT_TYPE_FILE self["alert.assessment.impact.description"] = description def setFile(self, file, origin, index=0): self["alert.target(0).file(%d).category" % index] = origin self["alert.target(0).file(%d).name" % index] = file.getBaseName() self["alert.target(0).file(%d).path" % index] = file.getPath() if file.ctime: self["alert.target(0).file(%d).create_time" % index] = file.ctime if file.mtime: self["alert.target(0).file(%d).modify_time" % index] = file.mtime if file.atime: self["alert.target(0).file(%d).access_time" % index] = file.atime if file.size: self["alert.target(0).file(%d).data_size" % index] = int(file.size) if file.inode: self["alert.target(0).file(%d).inode.number" % index] = int(file.inode) def setAdditionalData(self, meaning, data): for key, value in ("meaning", meaning), ("data", data), ("type", IDMEF_ADDITIONAL_DATA_TYPE_STRING): self["alert.additional_data(%d).%s" % (self._additional_data_count, key)] = value self._additional_data_count += 1 class Report: def __init__(self): prelude_init(len(sys.argv), sys.argv) self._client = prelude_client_new("prelude-fic") self._analyzer = prelude_client_get_analyzer(self._client) idmef_analyzer_set_model(self._analyzer, "pyfic") idmef_analyzer_set_version(self._analyzer, "0.1") idmef_analyzer_set_class(self._analyzer, "HIDS") prelude_client_start(self._client) def __del__(self): prelude_client_destroy(self._client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS) def _sendAlert(self, alert): idmef_alert_set_analyzer(idmef_message_get_alert(alert.res), idmef_analyzer_ref(self._analyzer), -1) prelude_client_send_idmef(self._client, alert.res) def fileAdded(self, file): alert = Alert() alert.setFile(file, origin=IDMEF_FILE_CATEGORY_CURRENT) alert.setClassification("file added") alert.setImpact("file %s added" % file.filename) self._sendAlert(alert) def fileRemoved(self, file): alert = Alert() alert.setFile(file, origin=IDMEF_FILE_CATEGORY_ORIGINAL) alert.setClassification("file removed") alert.setImpact("file %s removed" % file.filename) self._sendAlert(alert) def fileModified(self, changed_properties, original_file, current_file): alert = Alert() alert.setFile(original_file, IDMEF_FILE_CATEGORY_ORIGINAL, 0) alert.setFile(current_file, IDMEF_FILE_CATEGORY_CURRENT, 1) if current_file.isLocal(): hostname = socket.gethostname() hostaddr = socket.gethostbyname(hostname) alert["alert.target(0).node.name"] = hostname alert["alert.target(0).node.address(0).address"] = hostaddr else: alert["alert.target(0).node.name"] = current_file.getHost() alert["alert.target(0).service.name"] = current_file.getProtocol().upper() alert["alert.target(0).service.port"] = current_file.getPort() alert["alert.target(0).service.protocol"] = "TCP" property_handlers = ( (FileSummary.INODE, "inode", lambda f: f.inode, lambda f: f.inode), (FileSummary.MODE, "mode", lambda f: oct(f.mode), lambda f: oct(f.mode)), (FileSummary.LINK, "link target", lambda f: f.link_target_filename, lambda f: f.link_target_filename), (FileSummary.LINKS, "links", lambda f: f.links, lambda f: f.links), (FileSummary.UID, "uid", lambda f: f.uid, lambda f: f.uid), (FileSummary.GID, "gid", lambda f: f.gid, lambda f: f.gid), (FileSummary.SIZE, "size", lambda f: f.size, lambda f: "%d bytes" % f.size), (FileSummary.CTIME, "last change time", lambda f: time_to_string(f.ctime), lambda f: time_to_string(f.ctime)), (FileSummary.ATIME, "last access time", lambda f: time_to_string(f.atime), lambda f: time_to_string(f.atime)), (FileSummary.MTIME, "last modification time", lambda f: time_to_string(f.mtime), lambda f: time_to_string(f.mtime)), (FileSummary.MD5, "md5 digest", lambda f: f.md5_digest, lambda f: f.md5_digest), (FileSummary.SHA, "sha digest", lambda f: f.sha_digest, lambda f: f.sha_digest) ) my_changed_properties = [ ] for property, name, original_handler, current_handler in property_handlers: if changed_properties & property: my_changed_properties.append((name, original_handler(original_file), current_handler(current_file))) for property, original_value, current_value in my_changed_properties: alert.setAdditionalData(property, "changed from %s to %s" % (original_value, current_value)) impact_description = "%s of file %s %s been changed" % \ (enumeration_to_string(map(lambda x: x[0], my_changed_properties)), current_file.filename, ("has", "have")[len(my_changed_properties) > 1]) if changed_properties & (FileSummary.MD5|FileSummary.SHA): alert.setClassification("file content altered") alert.setImpact(impact_description) else: alert.setClassification("file attributs altered") alert.setImpact(impact_description, severity=IDMEF_IMPACT_SEVERITY_MEDIUM) self._sendAlert(alert)