Skip to main content

Risk Engine

The Risk Engine empowers security teams to create policies that automatically identify when applications exceed your organization's risk tolerance. You can configure these policies to flag applications for various reasons, such as known malware detection or organization restrictions on virtualization software.

The system uses a flexible plugin architechture, allowing multiple plugins to participate in the process of deciding whether a given application is above or below the line of risk.

When the Risk Engine is enabled, every time Santa uploads an event to Workshop the binary / application inside the events are passed to it. The Risk Engine then generates an authorization request for each of its plugins in parallel, setting a deadline. If all plugins return an ALLOW decision within the deadline then the event is considered safe. If any plugins returns DENY or DENY_MALWARE then the event is considered dangerous. If any plugins return errors or respond after the deadline they are treated as if the plugin returned a DENY decision.

Configuration

UI

The Risk Engine can be configured in the Settings page.

API Methods

The Risk Engine can be configured using the UpdateRiskEngineSettings method.

Internal vs. Remote Plugins

Risk Engine plugins come in two flavors - Internal, which are included as part of Workshop and Remote, which are extensions that can be written by customers or North Pole Security.

Secrets URL

Some options in Risk Engine plugins can be configured to use AWS and GCP secret stores.

AWS

  1. Give the Workshop service account read access to the secret The Workshop service account is displayed at the top of the Settings page

  2. Pass the ARN to the secret prefixed with aws:// e.g. aws://arn:aws:secretsmanager:us-east-1:940000000003:secret:Secret-YYLN9X

GCP

  1. Give the Workshop service account read access to the secret The Workshop service account is displayed at the top of the Settings page

  2. Specify the path to the secret as gcp://projects/<projectID>/secrets/<secretID>/versions/latest

Included Plugins

Workshop's internal plugins include:

VirusTotal

The VirusTotal plugin will check the SHA-256 of the binary against VirusTotal using the file report API.

The VirusTotal plugin will cache results per user defined parameters. This ensures that results are timely and saves expensive API calls.

Options

  • API Key - Your VirusTotal Key; this is either the raw string or a secrets URL
  • Cache Time - How long in seconds the cache entries should be kept alive for in seconds
  • Cache Entries - How many entries to cache
  • Excluded Engines - A list of engines to exclude results from

Reversing Labs

The Reversing Labs plugin will check the SHA-256 hash of a binary against ReversingLabs Spectra file reputation API. If the API deems it malicious it returns a DENY_MALWARE response.

The ReversingLabs plugin will cache results per user defined parameters. This ensures that results are timely and saves expensive API calls.

Options

  • Username - your reversing labs username
  • Password - your reversing labs username or a secret URL
  • Cache Entries - How many entries to cache
  • Cache Time - How long in seconds the cache entries should be kept alive for
  • Cache Entries - How many entries should be cached (up to 50,000)

Blockable Rules

The Blockable Rules plugin allows you to write rules using the Common Expression Language to match properties of a blockable. A blockable is a collection of attributes from the binary that Santa or Workshop policy can be matched on. All matchable attributes will be populated into the blockable object.

If the CEL expression returns true then this plugin will return a DENY decision, otherwise it will return an ALLOW. You may also use CEL macros for working with nested structures such as entitlements.

This is an extremely powerful feature that allows you to flag entire classes of software. Furthermore it allows you to tailor an extremely granular policy.

For example to have the risk engine return a DENY decision for any virtualization software you can use the following rule:

has(blockable.entitlements) &&
blockable.entitlements.exists(e, e.key == "com.apple.security.hypervisor") ||
blockable.entitlements.exists(e, e.key == "com.apple.security.virtualization")

You can use all of the attributes of a blockable in rules.

This includes:

  • sha256 - The SHA-256 of the binary
  • cdhash - The CDHash of the binary
  • signing_id - The signing ID, prefixed with either the team ID or platform
  • team_id - The 10-digit alphanumeric Team ID of the binary that uniquely identifies the publisher
  • signed_by - The certificate chain
  • entitlements - The array of entitlements provided

Remote Risk Engine Plugins

In addition to the included plugins the Risk Engine can be extended via

Writing Your Own Remote Risk Engine Plugins

To write your own remote risk engine plugin you need to simply create a server that takes an HTTP POST with JSON consisting of the PluginAuthzRequest and that returns an RemoteRiskEnginePluginServiceAuthorizeRequest serialized to JSON.

The interaction is essentially as follows:

note

Plugin authors are responsible for TLS and authorization.

Handling Requests

The first step is to make a web service that can receive and unmarshal a PluginAuthzRequest.

// A RemoteRiskEnginePluginServiceAuthorizeRequest is a request made by Workshop to
// a plugin to authorize a binary / blockable.
message RemoteRiskEnginePluginServiceAuthorizeRequest {
string tx_id = 1; // The transaction ID of the request.
BinaryBlockable blockable = 2; // The binary to authorize with all blockable attributes.
google.protobuf.Timestamp timestamp = 3; // The timestamp of the request.
google.protobuf.Timestamp deadline = 4; // The deadline for the plugin to return a decision before it is automatically considered a denial.
}

After unmarshaling the RemoteRiskEnginePluginServiceAuthorizeRequest you can find all of the details about the binary in the blockable field. This contains a subset of the attributes Santa has recorded at the time of execution, including signing information.

Each request has a transaction ID (tx_id) field and all responses are expected to have the same value in their transaction ID field.

Each RemoteRiskEnginePluginServiceAuthorizeRequest also contains a deadline that the plugin must respond with a PluginAuthzResponse before to be considered. Failure to respond within the deadline will be treated as a if the plugin had responded with a deny decision.

Once the data from the request has been processed a RemoteRiskEnginePluginServiceAuthorizeResponse must be send back to Workshop with a decision and and explanation for the decision.

The structure of the RemoteRiskEnginePluginServiceAuthorizeResponse is as follows:

// This message is used by a remote risk engine plugin to represent the decision
// for a blockable. All errors and timeouts are treated as denials.
message RemoteRiskEnginePluginServiceAuthorizeResponse {
string tx_id = 1; // The transaction ID of the request this response is for.
Decision decision = 2; // The decision for the blockable.
Explanation explanation = 3; // An explanation for the decision.
string error = 4; // An error message containing any errors the plugin encountered.
string plugin_uuid = 5; // The UUID of the plugin that made the decision.
google.protobuf.Timestamp good_until = 6; // The time the decision is considered valid until for caching.
}

Decisions can be one of the following:

DecisionMeaning
UNKNOWNThis is a programming error and should not be used
DENYThe plugin has determined the binary should be blocked by policy.
DENY_MALWAREThe plugin has determined the binary is malware and should be blocked
ALLOWThe plugin believes this binary is safe.
TIMEOUTThe plugin or something it depends on has timed out
ERRORThe plugin has encountered an error

All decisions except for allow are considered a denial.

See the Decision proto for more details

Additionally plugin authors are expected to provide an explanation for the decision and optionally a URL for getting more information. Workshop presents this information to to users and also helps with debugging.

See the Explanation proto for more details

Exceptions

Risk Engine plugin decisions can be overridden using Exceptions. These are created through the UI or API and grant users in a targeted tag an exception to specific Risk Engine plugin decisions. All exceptions include an expiration date after which they no longer apply.

For example, if the Risk Engine's Blockable Rules plugin has a rule banning VPN software e.g.

has(blockable.entitlements) &&
blockable.entitlements.exists(e, e.key == "com.apple.developer.networking.networkextension" && e.value.contains("packet-tunnel-provider-systemextension"))

But you wanted to let members of the tag vpn-access approve their own VPN software then you could accomplish this by granting the exception to tag for the specific Blockable Rule.

Expiry

Exceptions all have an expiration date built into them after which point they will not longer be considered.

This can be updated via both the UI and API.

Configuration

UI

Exceptions can be configured via the UI under the Exceptions tab on the Risk Engine Settings portion of the main Settings page.

API

Exceptions can also be mangaged via the CreateException,UpdateException, ListExceptions, and DeleteException methods in API