Fluency SIEM APIs
Fluency's own SIEM API functions are also exposed and accessed via FPL.
Fluency_EntityinfoCheck
- Fluency_EntityinfoCheck(entity, key)
- checks if key exists in the entity table
- return true if key exists in the table and false otherwise
let hit = Fluency_EntityinfoCheck("HOME_NET", "20.0.0.1")
if (hit) {
printf("home net")
} else {
printf("internet")
}
Fluency_EntityinfoLookup
- Fluency_EntityinfoLookup(entityinfo_tablename, keyCol, key, valueCol)
- check value from one column based on key column value
- return an object {exist, value}
- all arguments are type "string"
Example Code:
// eid = 4719
let eventID = sprintf("%v",eid)
let {exist, value} = fluencyEntityinfoLookup("AD_EventID", "Event ID", eventID, "Description")
if (exist) {
printf("value '%s'", value)
}
Example EntityInfo Table: AD_EventID
Event ID | Description |
---|---|
1100 | The event logging service has shut down |
... | ... |
4719 | System audit policy was changed |
... | ... |
Example Output:
printf: value 'System audit policy was changed'
Platform_EntityProvider_Lookup
- Platform_EntityProvider_Lookup(plugin, customer, key)
- gets the entity infos for the plugin, customer and key
- returns an array of entity info entries
- each entry uses the default entity info format, {id, obj, entity}
let agentInfos = Platform_EntityProvider_Lookup("SentinelOne", "*", agentID)
if len(agentInfos) > 0 {
let agentInfo = agentInfos[0]
newObj.agent = agentInfo.obj
newObj.uuid = agentID
envelop.obj["entity"] = agentInfo.entity
} else {
// printf("agentID lookup missing: %s", agentID)
}
Platform_EntityProvider_Refresh
- Platform_EntityProvider_Refresh(plugin, customer, entries)
- the default entity info entry: {id, obj, entity}
- id is the entity key: EDR agent uuid, device name or username. Must be unique for each integration
- obj is the entity object from the vendor
- entity is the normalized fields for UEBA correlation: {agentID, username, asset, ADAsset, ADUser, privateIP, publicIP}
- typical use case is to run Platform_EntityProvider_Refresh as a hourly cronjob. Then run Platform_EntityProvider_Lookup in FPL parser or rule.
- the default entity info entry: {id, obj, entity}
function main(doc) {
Platform_PluginLambda("SentinelOne", "*", (customer) => {
let agents = Plugin_SentinelOne_LoadAgent()
let agentInfos = agents.Map( (_, obj) => {
// printf("uuid %s", obj.uuid)
let entity = {
agentID: obj.uuid,
username: obj.externalId,
asset: obj.computerName,
ADAsset: obj.activeDirectory?.computerDistinguishedName,
ADUser: obj.activeDirectory?.lastUserDistinguishedName
}
if obj.machineType == "server" {
entity.privateIP = obj.lastIpToMgmt
}
return {
id: obj.uuid,
obj: obj,
entity: entity
}
})
Platform_EntityProvider_Refresh("SentinelOne", customer, agentInfos)
return {}
})
return {}
}
Fluency_LavadbFpl
- Fluency_LavadbFpl(searchText)
- Search LavaDB in FPL (v1)
- typically the searchText uses the template function
- return the search result in the form of a table
- this function will throw an exception if there is an error in the fplv1 search
- aggregate on a column that does not exist will cause an exception
let office_aad_by_ops_fpl = `
search {from="{{.from}}", to="{{.to}}"} sContent("@sender","office365") and sContent("@source","Audit.AzureActiveDirectory")
let { {{ .groupBy }} } = f("@fields")
aggregate events = count() by {{ .groupBy }}
sort 15 events
`
let office_aad_by_ops = fluencyLavadbFpl(
template(office_aad_by_ops_fpl, {from:"-24h", to:"@h", groupBy: "Operation"})
)
return {office_aad_by_ops}
NOTE: Returns exception:
exception: ServiceError: fluencyLavadbFpl: function search error
Fluency_LavadbQuery
- Fluency_LavadbQuery(query, options, ()=>{})
- Search LavaDB with Lucene query
- options: {from, to, dataType}
- from, to could be relative time "@h", "@m", "@d" or absolute time "2021-01-01T00:00:00Z", OR unix epoch time in millisecond
- dataType: "event" or "metaflow"
let hits = Fluency_LavadbQuery("", {from:"-4h@m", to:"@m"}, (obj) => {
return {sender:obj["@sender"], source:obj["@source"]}
})
return {hits}
Fluency_BehaviorSearch
- Fluency_BehaviorSearch(query, from, to, ()⇒{})
- Search Fluency Behavior Event database
NOTE: This function is limited to returning
8192
entries per call. Thefrom
andto
window should be made smaller to stay under this limit.
NOTE: 'from' and 'to' are inclusive for this API
function main() {
let query = `behaviorRule:"O365_AzureAD_Add_Member_To_Group"`
let matchingEvents = Fluency_BehaviorSearch(query, "-2d@d", "@d", (obj) => {
let ActorIDs = obj.attributes.Find((_, x) => x.aliase == "ActorIDs").value
let GroupDisplayName = obj.attributes.Find((_, x) => x.aliase == "GroupDisplayName").value
let Operation = obj.attributes.Find((_, x) => x.aliase == "Operation").value
let Username = obj.attributes.Find((_, x) => x.aliase == "Username").value
let ts = new Time(obj.timestamp)
return {Username, Operation, ActorIDs, GroupDisplayName, ActorIDs, Date: ts.Format("2006-01-02T15:04:05Z07:00")}
})
return {matchingEvents}
}
function main(){
let behaviorEvents = Fluency_BehaviorSearch("riskScore: [100 TO *]", "-10d@m", "@m", (obj) => {
let {behaviorRule, behavior, riskScore, key } = obj
return {behaviorRule, behavior, riskScore, key}
})
return {behaviorEvents}
}
Fluency_SummarySearch
- Fluency_SummarySearch(query, from, to, ()⇒{})
- Search Fluency Behavior Summary database
function main() {
let behaviorSummary = Fluency_SummarySearch("riskScore: [3000 TO *] AND NOT (status:new)", "-10d@m", "@m", (obj) => {
let {id, behaviorRules, riskScore, key, dayIndex, status } = obj
return {id, obj, behaviorRules, riskScore, key, dayIndex, status}
})
return {behaviorSummary}
}
Fluency_ResourceLoad
- Fluency_ResourceLoad(vendor, resource, customer, (obj <,customer>)⇒ {})
- loads the resource from a vendor with customer where each vendor has their own type of resources
- throws a ServiceError exception if no results are found
- vendor: Office365: resource: user, group, device, application, installedApp
- vendor Sentinelone: resource: agent, threat, application
- vendor AD: resource: user, asset
- vendor fehx (FireEyeHx): resource: device
- vendor Qualys: resource: host
- using "*" on customer will get results from all customers
function main() {
let users = Fluency_ResourceLoad("office365", "user", "*", (obj, customer) => {
let fields = obj["@office365User"]
let {userType, userPrincipalName, roles, accountEnabled, createdDateTime} = fields
return {customer, userType, userPrincipalName, roles, accountEnabled, createdDateTime}
})
return {users}
}
Fluency_DeviceSearch
- Fluency_DeviceSearch(query, from, to, ()=>{})
- Search Fluency Import Device database for devices created within the time frame
let newDevices = Fluency_DeviceSearch("", "-7d@m", "@m", (obj) => {
let {name, group, device:{name:devName, category}, ips, createdOn} = obj
return {name, group, devName, category, ips, createdOn}
})
Fluency_Device_Lookup
- Fluency_Device_Lookup(ipAddress)
- Lookup device information by ipAddress from Fluency Device database
Fluency_Device_LookupName
- Fluency_Device_LookupName(deviceName)
- Lookup device information by name from Fluency Device database
Fluency_Device_Add
- Fluency_Device_Add(device)
- Add device information to Fluency Device database
Fluency_Device_Update
- Fluency_Device_Update(ipAddress, newName)
- assigns ipAddress to a new name
function main({obj, size}) {
let sender = obj["@sender"]
let deviceEntry = Fluency_Device_Lookup(sender)
if (deviceEntry) {
printf("%s", deviceEntry)
} else {
printf("device not found")
deviceEntry = {
name:"$name",
description:"Added by FPL processor",
ips: [sender],
group:"$group",
device: {
name:"$subCategory",
category:"$category"
}
}
Fluency_Device_Add(deviceEntry)
}
// call platform metric api...
return "pass"
}
Fluency_FusionEvent
- Fluency_FusionEvent(partition, source)
- Send Fusion Event to Fluency Fusion Service (legacy)
let t = new Time()
let partition={
partition: "default",
dataType: "event",
time_ms: t.UnixMilli()
}
let source={
logon: {
ip:"10.132.47.10",
domain:"TERPLAB.COM",
username:"foobar"
},
dtype:"windows-logon"
}
Fluency_FusionEvent(partition, source)