Author: Peter Solagna, Cloud Security Consultant
Google Cloud Security Operations lets you monitor logs for matches against indicators of compromise (IOCs).
While you can enable 1st party threat intelligence feeds, many organizations also ingest proprietary IOC data to enhance detections.
To ingest custom IOCs, parse the data as an entity context graph and populate the ioc.* fields. SecOps includes default parsers for sources like MISP.
Matching event logs appear automatically in the IOC MATCHES dashboard under Alerts & IOC.
These dashboard matches rely on specific internal data model fields:
-
ioc.active_timerange.end
-
ioc.active_timerange.start
-
ioc.categorization
-
ioc.confidence_score
-
ioc.description
-
ioc.domain_and_ports.*
-
ioc.feed_name
-
ioc.ip_and_ports.*
Note: The ioc.* fields are populated during ingestion normalization and are internal-only (not searchable via UDM search, nor usable in your own detections).
Automatic IOC MATCHES dashboard matches do not generate alerts; to trigger alerts there is need to explicitly create detection rules.
Sometimes threat intelligence teams want to have more control on the IOCs that are matched, adding and removing them dynamically. In this article we will explore options to achieve this within the SecOops existing frameworks.
Secops entity graph lifecycle
Beyond IOCs matches, SecOops uses entity graph for events enrichment and to enable detection rules joining events with entities.
Although UDM structure for entities contains information about the entity validity interval ( event.idm.entity.metadata.interval.start_time event.idm.entity.metadata.interval.end_time ) best practices suggest not to explicitly filter entities by the validity interval in your queries as Secops will automatically consider the entities that are valid during the time of the event being correlated with them.
Standard entities default to a validity duration of few days, but IOC entities do not expire as long as they are retained in SecOps. An entity is identified as an IOC if the metadata.threat
(SecurityResult) field is populated.
Good to know: Searching for entity graph entries via UDM Search will display multiple 24-hour validity records for a single ingested entity, whereas RAW Log Search displays single ingested records with full validity intervals. For example:
-
RAW log search: it will return one entry for each raw log ingested, and the UDM rendering will show the interval with the full validity, normally:
-
1970-01-01 -> 9999-12-31
-
-
UDM Search: it will return multiple entries of the same entity with a validity interval of approximately 24h, for example:
-
2026-08-19T00:00:00Z -> 2026-08-20T00:00:00Z
-
2026-08-20T00:00:00Z -> 2026-08-21T00:00:00Z
-
Important: Once an IOC is ingested, it cannot be reliably deactivated, for the purpose of the IOC MATCHES dashboard.
An IOC entry's validity is defined by the metadata.interval fields. If an IOC's attributes are updated by ingesting a new entry, the updated attributes are only effective during the new entry's specified interval. For instance, imagine an IOC is first ingested with an end time 1 year away. If it is later updated with a new entry setting the end time to 6 months away, the attributes will change for that 6-months period. However, once that 6-months period ends, the attributes will automatically revert to the original values from the first entry (the one set to expire in 1 yearyears). In summary: ingesting a new entity with a shorter validity interval is not a viable way to invalidate an IOC.
Workarounds for invalidate your IOCs
If you need more freedom in managing your IOCs in Google Cloud SecOops, the options proposed here work within the following constraints:
-
IOC data will be used in custom detection rules created in the SecOops instances.
-
IOC Matches tables are low priority as they do not generate alerts.
Workaround 1: Add explicitly a validity flag to your IOC entity graph
Pros:
-
Provides fine-grained control over the validity of your IOC entities.
-
Leverages the fact that you already use detection rules to trigger alerts for IOC matches.
Cons:
-
Requires modifications to parsers and detection logic.
-
Requires implementing the
threat_statusflag in the source IOC database.
This option assumes you do not set a validity time when ingesting the entity graph, meaning the validity window is unlimited by default.
Your data source (e.g., MISP) will attach a validity flag to the IOC entries sent to SecOps. The parser must then parse this specific attribute and map it to a UDM field; the most appropriate field is: event.idm.entity.metadata.threat.threat_status flag in the source IOC database.
This field can accept the following values:
ACTIVE
CLEARED
FALSE_POSITIVE
THREAT_STATUS_UNSPECIFIED
Since the interval is not set, if the same IOC is ingested multiple times, SecOps will update the threat_status to the most recently ingested value. When your threat intelligence team needs to stop using specific indicators in detections, they must re-send the entry to SecOps with the threat_status flag updated.
Using IOC data in SecOps: You will not rely on the IOC MATCHES dashboard for the IOC detection and response process. Instead, you must implement dedicated detection rules to generate alerts and feed detections into the incident response process. These detection rules must validate the threat_status , for example:
…
$ioc.graph.metadata.product_name = "MISP"
$ioc.graph.metadata.threat.threat_status = "ACTIVE"
$ioc.graph.entity.ip = $e.principal.ip
…
Changing the product_name with the name specified by your IOC stream.
Parser changes. As an example, the following code can be used to extend the MISP parser to populate the threat_status field in the output entity graph:
if [threat_status] == "ACTIVE" { # Make sure that threat_status is populated with the value in the RAW log.
mutate {
replace => {
"threat_sr.threat_status" => "%{threat_status}"
}
}
} else if [threat_status] == "CLEARED" {
mutate {
replace => {
"threat_sr.threat_status" => "%{threat_status}"
}
}
} else if [threat_status] == "FALSE_POSITIVE" {
mutate {
replace => {
"threat_sr.threat_status" => "%{threat_status}"
}
}
} else if [threat_status] == "THREAT_STATUS_UNSPECIFIED" {
mutate {
replace => {
"threat_sr.threat_status" => "%{threat_status}"
}
}
} else {
# Default is ACTIVE
mutate {
replace => {
"threat_sr.threat_status" => "ACTIVE"
}
}
}
Workaround 2: ingest only short lived IOC entities
Pros:
-
It keeps the IOC MATCHES dashboard aligned with your expectations
Cons:
-
It does not allow for fine tuning IOC life cycle, with a potential risk of high false positives
-
It potentially requires an high volume of IOC streamed into SecOops, which may impact your IOC database performance
An alternative approach is to ingest all your entities with a short validity window, and re-ingest them at regular intervals. For example you could configure your IOC Feeds to be parsed with a metadata.interval of 30 days from the moment of ingestion, and re-ingest them every 3 weeks, again with a 4 week interval, as long as they are still relevant for your organization. Without re-ingestion your indicators will expire at the end of the 4 weeks period.
With this approach, your IOCs MATCHES dashboard will also be aligned with your expectations, but you will not be able to remove indicators with short turnaround.
Conclusion
Implementing the workflows described in this article will enable your organization to dynamically curate your IOC data,disabling the indicators when they are not relevant, which ensures that you alert only on high-confidence, currently active threats.
