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
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:
- Define the sources as usual (see documentation above).
- 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.
- 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.
Exploitability Rules
For android, some source to sink flows are only considered valid if the root callable of the source to sink flow is also accessible from outside the app. This access is controlled by exported setting in the manifest file. Exploitability rules allow us to additionally constraint source to sink rules on a call-chain flow from the root callable of the source to sink flow up to a call-chain effect source which is identified using the android manifest. Eg.
class ExportedActivity extends Activity {
void onCreate() {
Util.rootCallable(this);
}
}
class Util {
void rootCallable(Activity activity) {
toSink(activity.getSource());
}
}
Here, if we want to report an issue only if the android manifest sets exported: true
for ExportedActivity
. You can specify the explotability rule as follows:
{
"name": "Source to sink flow is reachable from an exported class",
"code": 1,
"description": "Values from source may eventually flow into sink",
"sources": [
"ActivityUserInput"
],
"effect_sources": [
"Exported"
],
"sinks": [
"LaunchingComponent"
]
}
Here, source to sink flow is found in rootCallable()
but the issue will be reported iff ExportedActivity
has exported:true
in the android manifest.
ExportedActivity::onCreate(): with effect_source: Exported
|
rootCallable(): with inferred effect_sink: ActivityUserInput@LaunchingComponent
|
+-----------------+-----------------+
| |
getSource(): with toSink(): with
source kind: ActivityUserInput sink kind: LaunchingComponent