x-pack/filebeat/module/o365/audit/config/pipeline.js (930 lines of code) (raw):

// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. var processor = require("processor"); var console = require("console"); // PipelineBuilder to aid debugging of pipelines during development. function PipelineBuilder(pipelineName, debug) { this.pipeline = new processor.Chain(); this.add = function (processor) { this.pipeline = this.pipeline.Add(processor); }; this.Add = function (name, processor) { this.add(processor); if (debug) { this.add(makeLogEvent("after " + pipelineName + "/" + name)); } }; this.Build = function () { if (debug) { this.add(makeLogEvent(pipelineName + "processing done")); } return this.pipeline.Build(); }; if (debug) { this.add(makeLogEvent(pipelineName + ": begin processing event")); } } function appendFields(options) { return function(evt) { options.fields.forEach(function (key) { var value = evt.Get(key); if (value != null) evt.AppendTo(options.to, value); }); } } // logEvent(msg) // // Processor that logs the current value of evt to console.debug. function makeLogEvent(msg) { return function (evt) { console.debug(msg + " :" + JSON.stringify(evt, null, 4)); }; } // makeConditional({condition:expr, result1:processor|expr, [...]}) // // Processor that selects which processor to run depending on the result of // evaluating a _condition_. Result can be boolean (if-else equivalent) or any // other value (switch equivalent). Unspecified values are a no-op. function makeConditional(options) { return function (evt) { var branch = options[options.condition(evt)] || function(evt){}; return (typeof branch === "function" ? branch : branch.Run)(evt); }; } // makeMapper({from:field, to:field, default:value mappings:{orig: new, [...]}}) // // Processor that sets the `to` field by mapping of `from` field's value. function makeMapper(options) { return function (evt) { var key = evt.Get(options.from); if (key == null && options.skip_missing) return; if (options.lowercase && typeof key == "string") { key = key.toLowerCase(); } var value = options.default; if (key in options.mappings) { value = options.mappings[key]; } else if (typeof value === "function") { value = value(key); } if (value != null) { evt.Put(options.to, value); } }; } // Makes sure a name can be used as a field in the output document. function validFieldName(s) { return s.replace(/[\ \.]/g, '_') } /* Turns a `common.NameValuePair` array into an object. Multiple-value fields are stored as arrays. input (a NameValuePair array): from_field: [ {Name: name1, Value: value1}, {Name: name2, Value: value2}, {Name: name2, Value: value2b}, [...] {Name: nameN, Value: valueN} ] output (an object): to_field: { name1: value1, name2: [value2, value2b], [...] nameN: valueN } */ function makeObjFromNameValuePairArray(options) { return function(evt) { var src = evt.Get(options.from); var dict = {}; if (src == null) return; if (!(src instanceof Array)) { evt.Put(options.to, {"_raw": src} ); return; } for (var i=0; i < src.length; i++) { var name, value; if (src[i] == null || (name=src[i].Name) == null || (value=src[i].Value) == null) continue; name = validFieldName(name); if (name in dict) { if (dict[name] instanceof Array) { dict[name].push(value); } else { dict[name] = [value]; } } else { dict[name] = value; } } evt.Put(options.to, dict); } } /* Converts a Common.ModifiedProperty array into an object. input: from_field: [ {Name: name1, OldValue: old1, NewValue: new1}, {Name: name2, OldValue: old2, NewValue: new2}, {Name: name2, OldValue: old2b, NewValue: new2b}, [...] {Name: nameN, OldValue: oldN, NewValue: newN}, ], output: to_field: { name1: { OldValue: old1, NewValue: new1 }, name2: { OldValue: [old2, old2b], NewValue: [new2, new2b] }, [...] nameN: { OldValue: oldN, NewValue: newN } } */ function makeDictFromModifiedPropertyArray(options) { return function(evt) { var src = evt.Get(options.from); var dict = {}; if (src == null || !(src instanceof Array)) return; for (var i=0; i < src.length; i++) { var name, newValue, oldValue; if (src[i] == null || (name=src[i].Name) == null || (newValue=src[i].NewValue) == null || (oldValue=src[i].OldValue) == null) continue; name = validFieldName(name); if (name in dict) { if (dict[name].NewValue instanceof Array) { dict[name].NewValue.push(newValue); dict[name].OldValue.push(oldValue); } else { dict[name].NewValue = [newValue]; dict[name].OldValue = [oldValue]; } } else { dict[name] = { NewValue: newValue, OldValue: oldValue, }; } } evt.Put(options.to, dict); } } function exchangeAdminSchema(debug) { var builder = new PipelineBuilder("o365.audit.ExchangeAdmin", debug); builder.Add("saveFields", new processor.Convert({ fields: [ {from: 'o365audit.OrganizationName', to: 'organization.name'}, {from: 'o365audit.OriginatingServer', to: 'server.address'}, ], ignore_missing: true, fail_on_error: false })); return builder.Build(); } function typeMapEnrich(conversions) { return function (evt) { var action = evt.Get("event.action"); if (action != null && conversions.hasOwnProperty(action)) { var conv = conversions[action]; if (conv.action !== undefined) evt.Put("event.action", conv.action); if (conv.category !== undefined) evt.Put("event.category", conv.category); if (conv.type !== undefined) evt.Put("event.type", conv.type); var n = conv.copy !== undefined? conv.copy.length : 0; for (var i=0; i<n; i++) { var value = evt.Get(conv.copy[i].from); if (value != null) evt.Put(conv.copy[i].to, value); } } } } function azureADSchema(debug) { var azureADConversion = { 'Add user.': { action: "added-user-account", category: 'iam', type: ['user', 'creation'], copy: [ { from: 'o365audit.ObjectId', to: 'user.target.id', } ], }, 'Update user.': { action: "modified-user-account", category: 'iam', type: ['user', 'change'], copy: [ { from: 'o365audit.ObjectId', to: 'user.target.id', } ], }, 'Delete user.': { action: "deleted-user-account", category: 'iam', type: ['user', 'deletion'], copy: [ { from: 'o365audit.ObjectId', to: 'user.target.id', } ], }, }; var builder = new PipelineBuilder("o365.audit.AzureActiveDirectory", debug); builder.Add("setIAMFields", typeMapEnrich(azureADConversion)); return builder.Build(); } function teamsSchema(debug) { var teamsConversion = { 'TeamCreated': { action: "added-group-account-to", category: 'iam', type: ['group', 'creation'], copy: [ { from: 'o365audit.TeamName', to: 'group.name', } ], }, 'MemberAdded': { action: "added-users-to-group", category: 'iam', type: ['group', 'change'], }, 'Delete user.': { action: "deleted-user-account", category: 'iam', type: ['user', 'deletion'], copy: [ { from: 'o365audit.ObjectId', to: 'user.target.id', } ], }, }; var builder = new PipelineBuilder("o365.audit.MicrosoftTeams", debug); builder.Add("setIAMFields", typeMapEnrich(teamsConversion)); builder.Add("groupMembersToRelatedUser", function (evt) { var m = evt.Get("o365audit.Members"); if (m == null || m.forEach == null) return; m.forEach(function (obj) { if (obj != null && obj.hasOwnProperty('UPN')) evt.AppendTo('related.user', obj.UPN); }) }) return builder.Build(); } function azureADLogonSchema(debug) { var builder = new PipelineBuilder("o365.audit.AzureActiveDirectoryLogon", debug); builder.Add("setEventAuthFields", function(evt){ evt.Put("event.category", "authentication"); var outcome = evt.Get("event.outcome"); // As event.type is an array, this sets both the traditional // "authentication_success"/"authentication_failure" // and the ECS standard "start". var types = ["start"]; if (outcome != null && outcome !== "unknown") { types.push("authentication_" + outcome); } evt.Put("event.type", types); }); return builder.Build(); } function sharePointFileOperationSchema(debug) { var builder = new PipelineBuilder("o365.audit.SharePointFileOperation", debug); builder.Add("saveFields", new processor.Convert({ fields: [ {from: 'o365audit.ObjectId', to: 'url.original'}, {from: 'o365audit.SourceRelativeUrl', to: 'file.directory'}, {from: 'o365audit.SourceFileName', to: 'file.name'}, {from: 'o365audit.SourceFileExtension', to: 'file.extension'}, ], ignore_missing: true, fail_on_error: false })); var actionToCategoryType = { ComplianceSettingChanged: ['configuration', 'change'], FileAccessed: ['file', 'access'], FileDeleted: ['file', 'deletion'], FileDownloaded: ['file', 'access'], FileModified: ['file', 'change'], FileMoved: ['file', 'change'], FileRenamed: ['file', 'change'], FileRestored: ['file', 'change'], FileUploaded: ['file', 'creation'], FolderCopied: ['file', 'creation'], FolderCreated: ['file', 'creation'], FolderDeleted: ['file', 'deletion'], FolderModified: ['file', 'change'], FolderMoved: ['file', 'change'], FolderRenamed: ['file', 'change'], FolderRestored: ['file', 'change'], }; builder.Add("setEventFields", function(evt) { var action = evt.Get("o365audit.Operation"); if (action == null) return; var fields = actionToCategoryType[action]; if (fields == null) return; evt.Put("event.category", fields[0]); evt.Put("event.type", fields[1]); }); return builder.Build(); } function exchangeMailboxSchema(debug) { var builder = new PipelineBuilder("o365.audit.SharePointFileOperation", debug); builder.Add("saveFields", new processor.Convert({ fields: [ {from: 'o365audit.MailboxOwnerUPN', to: 'user.email'}, {from: 'o365audit.LogonUserSid', to: 'user.id', type: 'string'}, {from: 'o365audit.LogonUserDisplayName', to: 'user.full_name'}, {from: 'o365audit.OrganizationName', to: 'organization.name'}, {from: 'o365audit.OriginatingServer', to: 'server.address'}, {from: 'o365audit.ClientIPAddress', to: 'client.address'}, {from: 'o365audit.ClientProcessName', to: 'process.name'}, ], ignore_missing: true, fail_on_error: false })); return builder.Build(); } function dataLossPreventionSchema(debug) { var builder = new PipelineBuilder("o365.audit.DLP", debug); builder.Add("setEventFields", new processor.AddFields({ target: 'event', fields: { kind: 'alert', category: 'file', type: 'access', }, })); builder.Add("saveFields", new processor.Convert({ fields: [ // SharePoint metadata {from: 'o365audit.SharePointMetaData.From', to: 'user.id'}, {from: 'o365audit.SharePointMetaData.FileName', to: 'file.name'}, {from: 'o365audit.SharePointMetaData.FilePathUrl', to: 'url.original'}, {from: 'o365audit.SharePointMetaData.UniqueId', to: 'file.inode'}, {from: 'o365audit.SharePointMetaData.UniqueID', to: 'file.inode'}, {from: 'o365audit.SharePointMetaData.FileOwner', to: 'file.owner'}, // Exchange metadata {from: 'o365audit.ExchangeMetaData.From', to: 'source.user.email'}, {from: 'o365audit.ExchangeMetaData.Subject', to: 'message'}, // Policy details {from: 'o365audit.PolicyId', to: 'rule.id'}, {from: 'o365audit.PolicyName', to: 'rule.name'}, ], ignore_missing: true, fail_on_error: false })); builder.Add("setMTime", new processor.Timestamp({ field: "o365audit.SharePointMetaData.LastModifiedTime", target_field: "file.mtime", layouts: [ "2006-01-02T15:04:05", "2006-01-02T15:04:05Z", ], ignore_missing: true, ignore_failure: true, })); builder.Add("appendDestinationEmails", function(evt) { var list = []; var fields = [ 'o365audit.ExchangeMetaData.To', 'o365audit.ExchangeMetaData.CC', 'o365audit.ExchangeMetaData.BCC', ]; for (var i=0; i<fields.length; i++) { var value = evt.Get(fields[i]); if (value == null) continue; if (value instanceof Array) { list = list.concat(value); } else { list.push(value); } } if (list.length == 1) { evt.Put("destination.user.email", list[0]); } else if (list.length > 1) { evt.Put("destination.user.email", list); } }); // ExceptionInfo is documented as string but has been observed to be an object. builder.Add("fixExceptionInfo", function(evt) { var key = "o365audit.ExceptionInfo"; var eInfo = evt.Get(key); if (eInfo == null) return; if (typeof eInfo === "string") { if (eInfo === "") { evt.Delete(key); } else { evt.Put(key, { Reason: eInfo, }); } } }); builder.Add("extractRules", function(evt) { var policies = evt.Get("o365audit.PolicyDetails"); if (policies == null) return; // rule.id will be an array of all rules' IDs. var ruleIds = []; // rule.name will be an array of all rules' names. var ruleNames = []; // event.severity will be the higher severity seen. var maxSeverity = -1; // event.outcome will determine if access to sensitive data was allowed. // Either because the rules were configured to only alert or because // the alert was overridden by the user. var allowed = true; for (var i = 0; i < policies.length; i++) { var rules = policies[i].Rules; if (rules == null) continue; for (var j = 0; j < rules.length; j++) { var rule = rules[j]; var id = rule.RuleId; var name = rule.RuleName; var sev = severityToCode(rule.Severity); if (id != null && name != null) { ruleIds.push(id); ruleNames.push(name); } if (sev > maxSeverity) maxSeverity = sev; if (allowed) { if (rule.Actions != null && rule.Actions.indexOf("BlockAccess") > -1) { allowed = false; } } } } if (ruleIds.length === 1) { evt.Put("rule.id", ruleIds[0]); evt.Put("rule.name", ruleNames[0]); } else if (ruleIds.length > 0) { evt.Put("rule.id", ruleIds); evt.Put("rule.name", ruleNames); } if (maxSeverity > -1) { evt.Put("event.severity", maxSeverity); } evt.Put("event.outcome", (allowed || isBlockOverride(evt))? "success" : "failure"); }); return builder.Build(); } // Numeric mapping for o365 mgmt API severities. function severityToCode(str) { if (str == null) return -1; switch (str.toLowerCase()) { case 'informational': return 1; // undocumented severity. case 'low': return 2; case 'medium': return 3; case 'high': return 4; default: return -1; } } // Was a DLP alert overridden with an exception? function isBlockOverride(evt) { switch (evt.Get("o365audit.Operation").toLowerCase()) { // Undo means the block was undone via change of policy or override. case "dlpruleundo": return true; // Info means it was detected as a false positive but no action taken. case "dlpinfo": return false; } // It's not clear to me the format of ExceptionInfo. It could be an object // or a string containing a JSON object. Assume that if present, an exception // is made. var exInfo = evt.Get('o365audit.ExceptionInfo'); return exInfo != null && exInfo !== ""; } function yammerSchema(debug) { var builder = new PipelineBuilder("o365.audit.Yammer", debug); builder.Add("saveFields", new processor.Convert({ fields: [ {from: 'o365audit.ActorUserId', to: 'user.email'}, {from: 'o365audit.ActorYammerUserId', to: 'user.id', type: 'string'}, {from: 'o365audit.FileId', to:'file.inode'}, {from: 'o365audit.FileName', to: 'file.name'}, {from: 'o365audit.GroupName', to: 'group.name'}, {from: 'o365audit.TargetUserId', to: 'destination.user.email'}, {from: 'o365audit.TargetYammerUserId', to: 'destination.user.id'}, ], ignore_missing: true, fail_on_error: false })); var yammerConversion = { // Network or verified admin changes the Yammer network's configuration. // This includes setting the interval for exporting data and enabling chat. NetworkConfigurationUpdated: { category: "configuration", type: "change", }, // Verified admin updates the Yammer network's security configuration. // This includes setting password expiration policies and restrictions // on IP addresses. NetworkSecurityConfigurationUpdated: { category: ["iam", "configuration"], type: ["admin", "change"], }, // Verified admin updates the setting for the network data retention // policy to either Hard Delete or Soft Delete. Only verified admins // can perform this operation. SoftDeleteSettingsUpdated: { category: "configuration", type: "change", }, // Network or verified admin changes the information that appears on // member profiles for network users network. ProcessProfileFields: { category: "configuration", type: "change" }, // Verified admin turns Private Content Mode on or off. This mode // lets an admin view the posts in private groups and view private // messages between individual users (or groups of users). Only verified // admins only can perform this operation. SupervisorAdminToggled: { category: "configuration", type: "change" }, // User uploads a file. FileCreated: { category: "file", type: "creation" }, // User creates a group. GroupCreation: { category: "iam", type: ["group", "creation"], }, // A group is deleted from Yammer. GroupDeletion: { category: "iam", type: ["group", "deletion"] }, // User downloads a file. FileDownloaded: { category: "file", type: "access" }, // User shares a file with another user. FileShared: { category: "file", type: "access" }, // Network or verified admin suspends (deactivates) a user from Yammer. NetworkUserSuspended: { category: "iam", type: "user" }, // User account is suspended (deactivated). UserSuspension: { category: "iam", type: "user" }, // User changes the description of a file. FileUpdateDescription: { category: "file", type: "access" }, // User changes the name of a file. FileUpdateName: { category: "file", type: "creation", }, // User views a file. FileVisited: { category: "file", type: "access", }, }; builder.Add("setEventFields", typeMapEnrich(yammerConversion)); return builder.Build(); } function securityComplianceAlertsSchema(debug) { var builder = new PipelineBuilder("o365.audit.SecurityComplianceAlerts", debug); builder.Add("saveFields", new processor.Convert({ fields: [ {from: 'o365audit.Comments', to: 'message'}, {from: 'o365audit.Name', to: 'rule.name'}, {from: 'o365audit.PolicyId', to: 'rule.id'}, {from: 'o365audit.Category', to: 'rule.category'}, {from: 'o365audit.EntityType', to: 'rule.ruleset'}, // This contains the entity that triggered the alert. // Name of a malware or email address. // Need to find a better ECS field for it. {from: 'o365audit.AlertEntityId', to: 'rule.description'}, {from: 'o365audit.AlertLinks', to: 'rule.reference'}, ], ignore_missing: true, fail_on_error: false })); builder.Add("setEventFields", new processor.AddFields({ target: 'event', fields: { kind: 'alert', category: 'web', type: 'info', }, })); // event.severity is numeric. builder.Add("mapSeverity", function(evt) { var sev = severityToCode(evt.Get("o365audit.Severity")); if (sev >= 0) { evt.Put("event.severity", sev); } }); builder.Add("mapCategory", makeMapper({ from: 'o365audit.Category', to: 'event.category', default: 'authentication', lowercase: true, mappings: { 'accessgovernance': 'authentication', 'datagovernance': 'file', 'datalossprevention': 'file', 'threatmanagement': 'malware', }, })); builder.Add("saveEntity", makeConditional({ condition: function(evt) { return evt.Get("o365audit.EntityType"); }, 'User': new processor.Convert({ fields: [ {from: "o365audit.AlertEntityId", to: "user.id", type: 'string'}, ], ignore_missing: true, fail_on_error: false }), 'Recipients': new processor.Convert({ fields: [ {from: "o365audit.AlertEntityId", to: "user.email"}, ], ignore_missing: true, fail_on_error: false }), 'Sender': new processor.Convert({ fields: [ {from: "o365audit.AlertEntityId", to: "user.email"}, ], ignore_missing: true, fail_on_error: false }), 'MalwareFamily': new processor.Convert({ fields: [ {from: "o365audit.AlertEntityId", to: "threat.technique.id"}, ], ignore_missing: true, fail_on_error: false }), })); return builder.Build(); } function splitEmailUserID(prefix) { var idField = prefix + ".id", nameField = prefix + ".name", domainField = prefix + ".domain", emailField = prefix + ".email"; return function(evt) { var email = evt.Get(idField); if (email == null) return; var pos = email.indexOf('@'); if (pos === -1) return; evt.Put(emailField, email); evt.Put(nameField, email.substr(0, pos)); evt.Put(domainField, email.substr(pos+1)); } } function AuditProcessor(tenant_names, debug) { var builder = new PipelineBuilder("o365.audit", debug); var unsetIPValues = {"null": true, "<null>": true, "": true}; builder.Add("cleanupNulls", function(event) { [ "o365audit.ClientIP", "o365audit.ClientIPAddress", "o365audit.ActorIpAddress", "o365audit.OriginatingServer" ].forEach(function(field) { if (event.Get(field) in unsetIPValues) event.Delete(field); }); }); builder.Add("convertCommonAuditRecordFields", new processor.Convert({ fields: [ {from: "o365audit.Id", to: "event.id"}, {from: "o365audit.ClientIP", to: "client.address"}, {from: "o365audit.ClientIPAddress", to: "client.address"}, {from: "o365audit.ActorIpAddress", to: "client.address"}, {from: "o365audit.UserId", to: "user.id", type: "string"}, {from: "o365audit.Workload", to: "event.provider"}, {from: "o365audit.Operation", to: "event.action"}, {from: "o365audit.OrganizationId", to: "organization.id"}, // Extra common fields: {from: "o365audit.UserAgent", to: "user_agent.original"}, ], ignore_missing: true, fail_on_error: false })); builder.Add("mapEventType", makeMapper({ from: 'o365audit.RecordType', to: 'event.code', // Keep original RecordType for unknown mappings. default: function(recordType) { return recordType; }, mappings: { 1: 'ExchangeAdmin', // Events from the Exchange admin audit log. 2: 'ExchangeItem', // Events from an Exchange mailbox audit log for actions that are performed on a single item, such as creating or receiving an email message. 3: 'ExchangeItemGroup', // Events from an Exchange mailbox audit log for actions that can be performed on multiple items, such as moving or deleted one or more email messages. 4: 'SharePoint', // SharePoint events. 6: 'SharePointFileOperation', // SharePoint file operation events. 8: 'AzureActiveDirectory', // Azure Active Directory events. 7: 'OneDrive', // OneDrive for Business events. 9: 'AzureActiveDirectoryAccountLogon', // Azure Active Directory OrgId logon events (deprecating). 10: 'DataCenterSecurityCmdlet', // Data Center security cmdlet events. 11: 'ComplianceDLPSharePoint', // Data loss protection (DLP) events in SharePoint and OneDrive for Business. 12: 'Sway', // Events from the Sway service and clients. 13: 'ComplianceDLPExchange', // Data loss protection (DLP) events in Exchange, when configured via Unified DLP Policy. DLP events based on Exchange Transport Rules are not supported. 14: 'SharePointSharingOperation', // SharePoint sharing events. 15: 'AzureActiveDirectoryStsLogon', // Secure Token Service (STS) logon events in Azure Active Directory. 16: 'SkypeForBusinessPSTNUsage', // Public Switched Telephone Network (PSTN) events from Skype for Business. 17: 'SkypeForBusinessUsersBlocked', // Blocked user events from Skype for Business. 18: 'SecurityComplianceCenterEOPCmdlet', // Admin actions from the Security & Compliance Center. 19: 'ExchangeAggregatedOperation', // Aggregated Exchange mailbox auditing events. 20: 'PowerBIAudit', // Power BI events. 21: 'CRM', // Microsoft CRM events. 22: 'Yammer', // Yammer events. 23: 'SkypeForBusinessCmdlets', // Skype for Business events. 24: 'Discovery', // Events for eDiscovery activities performed by running content searches and managing eDiscovery cases in the Security & Compliance Center. 25: 'MicrosoftTeams', // Events from Microsoft Teams. 28: 'ThreatIntelligence', // Phishing and malware events from Exchange Online Protection and Office 365 Advanced Threat Protection. 29: 'MailSubmission', // Submission events from Exchange Online Protection and Microsoft Defender for Office 365. 30: 'MicrosoftFlow', // Microsoft Power Automate (formerly called Microsoft Flow) events. 31: 'AeD', // Advanced eDiscovery events. 32: 'MicrosoftStream', // Microsoft Stream events. 33: 'ComplianceDLPSharePointClassification', // Events related to DLP classification in SharePoint. 34: 'ThreatFinder', // Campaign-related events from Microsoft Defender for Office 365. 35: 'Project', // Microsoft Project events. 36: 'SharePointListOperation', // SharePoint List events. 37: 'SharePointCommentOperation', // SharePoint comment events. 38: 'DataGovernance', // Events related to retention policies and retention labels in the Security & Compliance Center 39: 'Kaizala', // Kaizala events. 40: 'SecurityComplianceAlerts', // Security and compliance alert signals. 41: 'ThreatIntelligenceUrl', // Safe links time-of-block and block override events from Office 365 Advanced Threat Protection. 42: 'SecurityComplianceInsights', // Events related to insights and reports in the Office 365 security and compliance center. 43: 'MIPLabel', // Events related to the detection in the Transport pipeline of email messages that have been tagged (manually or automatically) with sensitivity labels. 44: 'WorkplaceAnalytics', // Workplace Analytics events. 45: 'PowerAppsApp', // Power Apps events. 46: 'PowerAppsPlan', // Subscription plan events for Power Apps. 47: 'ThreatIntelligenceAtpContent', // Phishing and malware events for files in SharePoint, OneDrive for Business, and Microsoft Teams from Office 365 Advanced Threat Protection. 48: 'LabelContentExplorer', // Events related to data classification content explorer. 49: 'TeamsHealthcare', // Events related to the Patients application in Microsoft Teams for Healthcare. 50: 'ExchangeItemAggregated', // Events related to the MailItemsAccessed mailbox auditing action. 51: 'HygieneEvent', // Events related to outbound spam protection. 52: 'DataInsightsRestApiAudit', // Data Insights REST API events. 53: 'InformationBarrierPolicyApplication', // Events related to the application of information barrier policies. 54: 'SharePointListItemOperation', // SharePoint list item events. 55: 'SharePointContentTypeOperation', // SharePoint list content type events. 56: 'SharePointFieldOperation', // SharePoint list field events. 57: 'MicrosoftTeamsAdmin', // Teams admin events. 58: 'HRSignal', // Events related to HR data signals that support the Insider risk management solution. 59: 'MicrosoftTeamsDevice', // Teams device events. 60: 'MicrosoftTeamsAnalytics', // Teams analytics events. 61: 'InformationWorkerProtection', // Events related to compromised user alerts. 62: 'Campaign', // Email campaign events from Microsoft Defender for Office 365. 63: 'DLPEndpoint', // Endpoint DLP events. 64: 'AirInvestigation', // Automated incident response (AIR) events. 65: 'Quarantine', // Quarantine events. 66: 'MicrosoftForms', // Microsoft Forms events. 67: 'ApplicationAudit', // Application audit events. 68: 'ComplianceSupervisionExchange', // Events tracked by the Communication compliance offensive language model. 69: 'CustomerKeyServiceEncryption', // Events related to the customer key encryption service. 70: 'OfficeNative', // Events related to sensitivity labels applied to Office documents. 71: 'MipAutoLabelSharePointItem', // Auto-labeling events in SharePoint. 72: 'MipAutoLabelSharePointPolicyLocation', // Auto-labeling policy events in SharePoint. 73: 'MicrosoftTeamsShifts', // Teams Shifts events. 75: 'MipAutoLabelExchangeItem', // Auto-labeling events in Exchange. 76: 'CortanaBriefing', // Briefing email events. 78: 'WDATPAlerts', // Events related to alerts generated by Windows Defender for Endpoint. 82: 'SensitivityLabelPolicyMatch', // Events generated when the file labeled with a sensitivity label is opened or renamed. 83: 'SensitivityLabelAction', // Event generated when sensitivity labels are applied, updated, or removed from a file. 84: 'SensitivityLabeledFileAction', // Events generated when a file labeled with a sensitivity label is opened or renamed. 85: 'AttackSim', // Attack simulator events. 86: 'AirManualInvestigation', // Events related to manual investigations in Automated investigation and response (AIR). 87: 'SecurityComplianceRBAC', // Security and compliance RBAC events. 88: 'UserTraining', // Attack simulator training events in Microsoft Defender for Office 365. 89: 'AirAdminActionInvestigation', // Events related to admin actions in Automated investigation and response (AIR). 90: 'MSTIC', // Threat intelligence events in Microsoft Defender for Office 365. 91: 'PhysicalBadgingSignal', // Events related to physical badging signals that support the Insider risk management solution. 93: 'AipDiscover', // Azure Information Protection (AIP) scanner events. 94: 'AipSensitivityLabelAction', // AIP sensitivity label events. 95: 'AipProtectionAction', // AIP protection events. 96: 'AipFileDeleted', // AIP file deletion events. 97: 'AipHeartBeat', // AIP heartbeat events. 98: 'MCASAlerts', // Events corresponding to alerts triggered by Microsoft Cloud App Security. 99: 'OnPremisesFileShareScannerDlp', // Events related to scanning for sensitive data on file shares. 100: 'OnPremisesSharePointScannerDlp', // Events related to scanning for sensitive data in SharePoint. 101: 'ExchangeSearch', // Events related to using Outlook on the web (OWA) to search for mailbox items. 102: 'SharePointSearch', // Events related to searching an organization's SharePoint home site. 103: 'PrivacyInsights', // Privacy insight events. 105: 'MyAnalyticsSettings', // MyAnalytics events. 106: 'SecurityComplianceUserChange', // Events related to modifying or deleting a user. 107: 'ComplianceDLPExchangeClassification', // Exchange DLP classification events. 109: 'MipExactDataMatch', // Exact Data Match (EDM) classification events. 113: 'MS365DCustomDetection', // Events related to custom detection actions in Microsoft 365 Defender. 147: 'CoreReportingSettings', // Reports settings events. 148: 'ComplianceConnector', // Events related to importing non-Microsoft data using data connectors in the Microsoft Purview compliance portal. 174: 'DataShareOperation', // Events related to sharing of data ingested via SystemSync. 181: 'EduDataLakeDownloadOperation', // Events related to the export of SystemSync ingested data from the lake. }, })); builder.Add("setEventFields", new processor.AddFields({ target: 'event', fields: { kind: 'event', type: 'info', // Not so sure about web as a default category: category: 'web', }, })); builder.Add("mapEventOutcome", makeMapper({ from: 'o365audit.ResultStatus', to: 'event.outcome', lowercase: true, default: 'success', mappings: { 'success': 'success', // This one is necessary to map Success 'succeeded': 'success', 'partiallysucceeded': 'success', 'true': 'success', 'failed': 'failure', 'false': 'failure', }, })); builder.Add("makeParametersDict", makeObjFromNameValuePairArray({ from: 'o365audit.Parameters', to: 'o365audit.Parameters', })); builder.Add("makeExtendedPropertiesDict", makeObjFromNameValuePairArray({ from: 'o365audit.ExtendedProperties', to: 'o365audit.ExtendedProperties', })); builder.Add("makeModifiedPropertyDict", makeDictFromModifiedPropertyArray({ from: 'o365audit.ModifiedProperties', to: 'o365audit.ModifiedProperties', })); // Turn AlertLinks into an array of keyword instead of array of objects. builder.Add("alertLinks", function (evt) { var list = evt.Get("o365audit.AlertLinks"); if (list == null || !(list instanceof Array)) return; var links = []; for (var i=0; i<list.length; i++) { var link = list[i].AlertLinkHref; if (link != null && typeof link === "string" && link.length > 0) { links.push(link); } } switch (links.length) { case 0: evt.Delete('o365audit.AlertLinks'); break; case 1: evt.Put("o365audit.AlertLinks", links[0]); break; default: evt.Put("o365audit.AlertLinks", links); } }); // Populate event specific fields. var dlp = dataLossPreventionSchema(debug); builder.Add("productSpecific", makeConditional({ condition: function(event) { return event.Get("event.code"); }, 'ExchangeAdmin': exchangeAdminSchema(debug).Run, 'ExchangeItem': exchangeMailboxSchema(debug).Run, 'AzureActiveDirectory': azureADSchema(debug).Run, 'AzureActiveDirectoryStsLogon': azureADLogonSchema(debug).Run, 'SharePointFileOperation': sharePointFileOperationSchema(debug).Run, 'SecurityComplianceAlerts': securityComplianceAlertsSchema(debug).Run, 'ComplianceDLPSharePoint': dlp.Run, 'ComplianceDLPExchange': dlp.Run, 'Yammer': yammerSchema(debug).Run, 'MicrosoftTeams': teamsSchema(debug).Run, })); builder.Add("extractClientIPPortBrackets", new processor.Dissect({ tokenizer: '[%{_ip}]:%{_port}', field: 'client.address', target_prefix: 'client', 'when.and': [ {'not.has_fields': ['client._ip', 'client._port']}, {'contains.client.address': ']:'}, ], })); builder.Add("extractClientIPv4Port", new processor.Dissect({ tokenizer: '%{_ip}:%{_port}', field: 'client.address', target_prefix: 'client', 'when.and': [ {'not.has_fields': ['client._ip', 'client._port']}, {'contains.client.address': '.'}, {'contains.client.address': ':'}, // Best effort to avoid parsing IPv6-mapped IPv4 as ip:port. // Won't succeed if IPv6 address is not shortened. {'not.contains.client.address': '::'}, ], })); // Copy the client/server.address to .ip fields if they are valid IPs. builder.Add("convertIPs", new processor.Convert({ fields: [ {from: "client.address", to: "client.ip", type: "ip"}, {from: "server.address", to: "server.ip", type: "ip"}, {from: "client._ip", to: "client.ip", type: "ip"}, {from: "client._port", to: "client.port", type: "long"}, ], ignore_missing: true, fail_on_error: false })); builder.Add("removeTempIP", function (evt) { evt.Delete("client._ip"); evt.Delete("client._port"); }); builder.Add("setSrcDstFields", new processor.Convert({ fields: [ {from: "client.ip", to: "source.ip"}, {from: "client.port", to: "source.port"}, {from: "server.ip", to: "destination.ip"}, ], ignore_missing: true, fail_on_error: false })); [ 'user', 'user.target', 'source.user', 'destination.user', ].forEach(function (prefix) { builder.Add('setFromID' + prefix, splitEmailUserID(prefix)); }) builder.Add("setNetworkType", function(event) { var ip = event.Get("client.ip"); if (ip == null) return; event.Put("network.type", ip.indexOf(".") !== -1? "ipv4" : "ipv6"); }); builder.Add("setRelatedIP", appendFields({ fields: [ "client.ip", "server.ip", ], to: 'related.ip' })); builder.Add("setRelatedUser", appendFields({ fields: [ "user.name", "user.target.name", "file.owner", ], to: 'related.user' })); // Set user-agent from an alternative location. builder.Add("altUserAgent", function(evt) { var ext = evt.Get("o365audit.ExtendedProperties.UserAgent"); if (ext != null) evt.Put("user_agent.original", ext); }); // Set host.name to the O365 tenant. This is necessary to aggregate events // in SIEM app based on the tenant instead of the host where Filebeat is // running. builder.Add("setHostName", function(evt) { var value; if ((value=evt.Get("organization.id"))!=null) { value = value.toLowerCase(); evt.Put("host.id", value); // Use tenant name provided in the configuration. if (value in tenant_names && value !== "") { evt.Put("organization.name", value); evt.Put("host.name", tenant_names[value]); return; } } if ((value=evt.Get("organization.name"))!=null || (value=evt.Get("user.domain")) != null ) { evt.Put("host.name", value); } }); builder.Add("saveRaw", new processor.Convert({ fields: [ {from: "o365audit", to: "o365.audit"}, ], mode: "rename" })); var chain = builder.Build(); return { process: chain.Run }; } var audit; // Register params from configuration. function register(params) { var tenant_names = {}; if (params.tenants != null) { for (var i = 0; i < params.tenants.length; i++) { tenant_names[params.tenants[i].id] = params.tenants[i].name.toLowerCase(); } } audit = new AuditProcessor(tenant_names, params.debug); } function process(evt) { return audit.process(evt); }