Skip to main content

Rules

A rule describes flows that we want to catch (e.g, user input flowing into command execution). A rule is made of a set of source kinds, a set of sink kinds, a name, a code, and a description.

Here is an example of a rule in JSON:

{
"name": "User input flows into code execution (RCE)",
"code": 1,
"description": "Values from user-controlled source may eventually flow into code execution",
"sources": [
"UserCamera",
"UserInput",
],
"sinks": [
"CodeAsyncJob",
"CodeExecution",
]
}

For guidance on modeling sources and sinks, see the next section, Models and Model Generators.

Rules used by Mariana Trench can be specified with the --rules-paths argument. The default set of rules that run can be found in configuration/rules.json.

Transform Rules

Some flows are only interesting if they also pass through a specific method. These methods can be modeled as a propagation with transforms. Then, to catch these flows, we specify the ordered list of transforms here in the rule.

Here is an example of a transform rule in JSON:

{
"name": "URI Query Parameters flow into Internal Intent data",
"code": 2,
"oncall": "prodsec_mobile",
"description": "Values from a query parameter source may eventually flow into Internal Intent data",
"sources": [
"UriQueryParameter"
],
"transforms": [
"IntentData"
],
"sinks": [
"LaunchingFamilyComponent"
]
}

The flow will only be created if UriQueryParameter flows through IntentData and then into LaunchingFamilyComponent. It will not be created when UriQueryParameter flows into LaunchingFamilyComponent without passing through the IntentData transform.

See Models and Model Generators for how to model transforms.

Multi-source multi-sink rules are used to track the flow of taint from multiple sources to multiple sinks. This can, for example, be useful if you want to track both the source types "SensitiveData" and "WorldReadableFileLocation" to an IO operation as displayed in the code below.

File externalDir = context.getExternalFilesDir() // source WorldReadableFileLocation
String sensitiveData = getUserToken() // source SensitiveData

File outputFile = new File(externalDir, "file.txt");
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
fos.write(sensitiveData.getBytes()); // sink Argument(0) and Argument(1)
}

Such a rule can be defined as follows:

  1. Define the sources as usual (see documentation above).
  2. Define sinks on FileOutputStream::write as follows:
{
"model_generators": [
{
"find": "methods",
"where": [ /* name = write */ ... ],
"model": {
"sink": [
{
"kind": "PartialExternalFileWrite",
"partial_label": "outputStream",
"port": "Argument(0)"
},
{
"kind": "PartialExternalFileWrite",
"partial_label": "outputBytes",
"port": "Argument(1)"
}
]
}
}
}

There are two sinks. There should always be as many sinks as there are sources, and the sinks must share the same kind. This looks almost like a regular sink definition, with an additional partial_label field. The partial_label field is used when defining the multi-source/sink rule below.

  1. Define rules as follows:
  {
"name": "Experimental: Some name",
"code": 9001,
"description": "More description here.",
"multi_sources": {
"outputBytes": [
"SensitiveData"
],
"outputStream": [
"WorldReadableFileLocation"
]
},
"partial_sinks": [
"PartialExternalFileWrite"
]
}

Pay attention to how the labels and partial sink kinds match what is defined in the sinks above.

NOTE: Multi-source/sink rules currently support exactly 2 sources/sinks only.