{"license": "Elastic License v2", "risk_score_mapping": [], "actions": [], "exceptions_list": [], "references": ["https://secutec-external-api-docs.ams3.cdn.digitaloceanspaces.com/index.html"], "rule_id": "securesight-unsolved-high-severity", "name": "SecureSight: high or critical finding still unsolved", "description": "A finding at severity 3 (high) or 4 (critical) that nobody has dispositioned. Fires when the finding CHANGES, not continuously: @timestamp carries the resource's updated_at, so a finding that sits untouched does not re-enter the rule window. That is the delta contract working as intended \u2014 see the note.", "type": "query", "language": "kuery", "query": "event.dataset: \"securesight.threats\" and event.severity >= 3 and ss.status: \"unsolved\"", "index": ["securesight-threats"], "severity": "high", "risk_score": 73, "from": "now-30m", "interval": "15m", "max_signals": 200, "enabled": true, "tags": ["SecureSight", "Vulnerability"], "author": ["Secutec"], "false_positives": ["A finding the customer has already decided to accept but has not yet set to accepted through PATCH /v1/threats/{id}. Set the status rather than tuning the rule."], "note": "## Triage\n\n1. Open the finding in MySecutec by `ss.id`.\n2. Decide: fix, accept, or false positive.\n3. Write the decision back with `PATCH /v1/threats/{threat_id}`. The next poll delivers the new status and this rule stops matching it.\n\n## Why it fires on change, not on state\n\nThe External API is a delta stream ordered by when the platform finished processing a change. Filebeat maps that to `@timestamp`, so every document in the rule's window is something that moved. The standing count of unsolved high-severity findings belongs on the dashboard, not in a rule.", "severity_mapping": [{"field": "event.severity", "operator": "equals", "value": "3", "severity": "high"}, {"field": "event.severity", "operator": "equals", "value": "4", "severity": "critical"}], "threat": [{"framework": "MITRE ATT&CK", "tactic": {"id": "TA0042", "name": "Resource Development", "reference": "https://attack.mitre.org/tactics/TA0042/"}, "technique": [{"id": "T1588", "name": "Obtain Capabilities", "reference": "https://attack.mitre.org/techniques/T1588/", "subtechnique": [{"id": "T1588.006", "name": "Vulnerabilities", "reference": "https://attack.mitre.org/techniques/T1588/006/"}]}]}], "timestamp_override": "event.ingested", "timestamp_override_fallback_disabled": false}
{"license": "Elastic License v2", "risk_score_mapping": [], "actions": [], "exceptions_list": [], "references": ["https://secutec-external-api-docs.ams3.cdn.digitaloceanspaces.com/index.html"], "rule_id": "securesight-critical-aging", "name": "SecureSight: critical finding open longer than 30 days", "description": "A severity-4 finding still unsolved a month after it was first seen. Uses the threat.age_days runtime field rather than @timestamp: @timestamp is updated_at, which a rescan re-stamps, so an ageing finding looks new by that clock.", "type": "query", "language": "kuery", "query": "event.dataset: \"securesight.threats\" and event.severity: 4 and ss.status: \"unsolved\" and threat.age_days > 30", "index": ["securesight-threats"], "severity": "critical", "risk_score": 90, "from": "now-24h", "interval": "1h", "max_signals": 100, "enabled": true, "tags": ["SecureSight", "SLA"], "author": ["Secutec"], "false_positives": ["A finding accepted as a known risk. Set it to accepted through the API; it then leaves this rule permanently."], "note": "## What this is really asking\n\nAn ageing critical is an SLA question, not a new discovery. The rule surfaces one the next time it is re-observed \u2014 every rescan re-stamps `updated_at` and brings it back into the window, so in practice each ageing critical resurfaces on the module's own scan cadence rather than on a timer of ours.\n\n**It is not an always-on backlog view.** For that, use the *Ageing criticals* panel on the SecureSight overview dashboard, which queries state rather than change.", "timestamp_override": "event.ingested", "timestamp_override_fallback_disabled": false}
{"license": "Elastic License v2", "risk_score_mapping": [], "actions": [], "exceptions_list": [], "references": ["https://secutec-external-api-docs.ams3.cdn.digitaloceanspaces.com/index.html"], "rule_id": "securesight-leaked-credentials", "name": "SecureSight: leaked credentials for a person", "description": "An unsolved LCM finding against a person asset \u2014 a credential belonging to a named individual has turned up in a breach corpus. ss.module_data carries the breach name, the credential category and whether a password was present.", "type": "query", "language": "kuery", "query": "event.dataset: \"securesight.threats\" and ss.module: \"lcm\" and ss.status: \"unsolved\" and securesight.asset.type: \"person\"", "index": ["securesight-threats"], "severity": "high", "risk_score": 75, "from": "now-30m", "interval": "15m", "max_signals": 100, "enabled": true, "tags": ["SecureSight", "Credential Access", "LCM"], "author": ["Secutec"], "false_positives": ["`ss.module_data.password_category: \"no_password\"` means only informational data leaked and no credential is at risk. Add `and not ss.module_data.password_category: \"no_password\"` if that noise is unwelcome."], "note": "## Triage\n\n1. `ss.asset.name` and `user.email` identify the account.\n2. `ss.module_data.breach_name` and `.compromised_at` say where and when.\n3. Force a password reset, then check sign-in logs for use between `compromised_at` and the reset.\n4. Set the finding to solved through the API.\n\nModule enrichment is module-specific by design: `ss.module_data` for an `lcm` finding has a different shape from an `asm` one. Key on `ss.module` before reading it.", "threat": [{"framework": "MITRE ATT&CK", "tactic": {"id": "TA0006", "name": "Credential Access", "reference": "https://attack.mitre.org/tactics/TA0006/"}, "technique": [{"id": "T1589", "name": "Gather Victim Identity Information", "reference": "https://attack.mitre.org/techniques/T1589/", "subtechnique": [{"id": "T1589.001", "name": "Credentials", "reference": "https://attack.mitre.org/techniques/T1589/001/"}]}]}], "timestamp_override": "event.ingested", "timestamp_override_fallback_disabled": false}
{"license": "Elastic License v2", "risk_score_mapping": [], "actions": [], "exceptions_list": [], "references": ["https://secutec-external-api-docs.ams3.cdn.digitaloceanspaces.com/index.html"], "rule_id": "securesight-new-public-asset", "name": "SecureSight: newly discovered internet-facing asset", "description": "An asset that is reachable from the internet and was first seen in the last seven days. New external surface that nobody announced is the shadow-IT signal worth waking up for.", "type": "query", "language": "kuery", "query": "event.dataset: \"securesight.assets\" and ss.public_facing: true and ss.deleted: false", "index": ["securesight-assets"], "severity": "medium", "risk_score": 47, "from": "now-24h", "interval": "1h", "max_signals": 1000, "enabled": true, "tags": ["SecureSight", "Inventory", "ASM"], "author": ["Secutec"], "false_positives": ["A planned deployment. The fix is a change record to check against, not a quieter rule."], "note": "## Triage\n\n`ss.identifying_properties` carries what the discovering module matched on \u2014 an ip/port pair for a service, a hostname for a subdomain. `ss.modules` says which module found it.\n\nThen `GET /v1/assets/{id}/related` for the neighbours and `GET /v1/assets/{id}/threats` for what is already known about it.", "timestamp_override": "event.ingested", "timestamp_override_fallback_disabled": false, "filters": [{"meta": {"type": "custom", "key": "ss.first_seen_at", "alias": "first seen in the last 7 days", "negate": false, "disabled": false}, "query": {"range": {"ss.first_seen_at": {"gte": "now-7d"}}}}]}
{"license": "Elastic License v2", "risk_score_mapping": [], "actions": [], "exceptions_list": [], "references": ["https://secutec-external-api-docs.ams3.cdn.digitaloceanspaces.com/index.html"], "rule_id": "securesight-asset-removed", "name": "SecureSight: asset removed from the inventory (tombstone)", "description": "Secutec no longer tracks this asset. It arrived on the delta stream once with deleted=true and deleted_at set; drop it, and every threat attached to it, from any mirrored copy. Threats are not tombstoned separately \u2014 they cascade from the asset (DEV-3298).", "type": "query", "language": "kuery", "query": "event.dataset: \"securesight.assets\" and ss.deleted: true", "index": ["securesight-assets"], "severity": "low", "risk_score": 21, "from": "now-24h", "interval": "1h", "max_signals": 1000, "enabled": true, "tags": ["SecureSight", "Inventory", "Tombstone"], "author": ["Secutec"], "false_positives": ["Routine decommissioning. Most removals are intended \u2014 this rule is informational and exists so the UNintended one is visible."], "note": "## Why an alert for a deletion\n\nBefore DEV-3298 a removal was invisible: the soft delete moved the row into the consumer's next window and out of the stream's filter in the same UPDATE, so a poller kept the asset as live data forever with no way to notice. The tombstone is the repair, and this rule is the proof it arrives.\n\n## Triage\n\n1. Delete the asset and its threats from the mirror.\n2. If the removal was not expected, restore it in MySecutec \u2014 it is redelivered with `deleted: false` and the mirror heals on the next poll.\n\nAn asset created and removed between two polls arrives as a tombstone for an id the mirror never saw. Ignore it; it is harmless.", "timestamp_override": "event.ingested", "timestamp_override_fallback_disabled": false}
