Skip to content

Commit 97b2e85

Browse files
Merge pull request github#11713 from joefarebrother/sensitive-result-receiver
Java: Add query for leaking sensitive data through a ResultReceiver
2 parents 96ee0f6 + 834fc51 commit 97b2e85

File tree

8 files changed

+157
-0
lines changed

8 files changed

+157
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/** Definitions for the sensitive result receiver query. */
2+
3+
import java
4+
import semmle.code.java.dataflow.TaintTracking2
5+
import semmle.code.java.dataflow.FlowSources
6+
import semmle.code.java.security.SensitiveActions
7+
8+
private class ResultReceiverSendCall extends MethodAccess {
9+
ResultReceiverSendCall() {
10+
this.getMethod()
11+
.getASourceOverriddenMethod*()
12+
.hasQualifiedName("android.os", "ResultReceiver", "send")
13+
}
14+
15+
Expr getReceiver() { result = this.getQualifier() }
16+
17+
Expr getSentData() { result = this.getArgument(1) }
18+
}
19+
20+
private class UntrustedResultReceiverConf extends TaintTracking2::Configuration {
21+
UntrustedResultReceiverConf() { this = "UntrustedResultReceiverConf" }
22+
23+
override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
24+
25+
override predicate isSink(DataFlow::Node node) {
26+
node.asExpr() = any(ResultReceiverSendCall c).getReceiver()
27+
}
28+
}
29+
30+
private predicate untrustedResultReceiverSend(DataFlow::Node src, ResultReceiverSendCall call) {
31+
any(UntrustedResultReceiverConf c).hasFlow(src, DataFlow::exprNode(call.getReceiver()))
32+
}
33+
34+
private class SensitiveResultReceiverConf extends TaintTracking::Configuration {
35+
SensitiveResultReceiverConf() { this = "SensitiveResultReceiverConf" }
36+
37+
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr }
38+
39+
override predicate isSink(DataFlow::Node node) {
40+
exists(ResultReceiverSendCall call |
41+
untrustedResultReceiverSend(_, call) and
42+
node.asExpr() = call.getSentData()
43+
)
44+
}
45+
46+
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
47+
super.allowImplicitRead(node, c)
48+
or
49+
this.isSink(node)
50+
}
51+
}
52+
53+
/** Holds if there is a path from sensitive data at `src` to a result receiver at `sink`, and the receiver was obtained from an untrusted source `recSrc`. */
54+
predicate sensitiveResultReceiver(
55+
DataFlow::PathNode src, DataFlow::PathNode sink, DataFlow::Node recSrc
56+
) {
57+
exists(ResultReceiverSendCall call, SensitiveResultReceiverConf conf |
58+
conf.hasFlowPath(src, sink) and
59+
sink.getNode().asExpr() = call.getSentData() and
60+
untrustedResultReceiverSend(recSrc, call)
61+
)
62+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// BAD: Sensitive data is sent to an untrusted result receiver
2+
void bad(String password) {
3+
Intent intent = getIntent();
4+
ResultReceiver rec = intent.getParcelableExtra("Receiver");
5+
Bundle b = new Bundle();
6+
b.putCharSequence("pass", password);
7+
rec.send(0, b);
8+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
4+
<overview>
5+
<p>If a <code>ResultReceiver</code> is obtained from an untrusted source, such as an <code>Intent</code> received by an exported component,
6+
do not send it sensitive data. Otherwise, the information may be leaked to a malicious application.</p>
7+
</overview>
8+
9+
<recommendation>
10+
<p>
11+
Do not send sensitive data to an untrusted <code>ResultReceiver</code>.
12+
</p>
13+
</recommendation>
14+
15+
<example>
16+
<p>In the following (bad) example, sensitive data is sent to an untrusted <code>ResultReceiver</code>. </p>
17+
<sample src="SensitiveResultReceiver.java" />
18+
</example>
19+
20+
<references>
21+
</references>
22+
</qhelp>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @name Leaking sensitive information through a ResultReceiver
3+
* @description Sending sensitive data to a 'ResultReceiver' obtained from an untrusted source
4+
* can allow malicious actors access to your information.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @security-severity 8.2
8+
* @precision medium
9+
* @id java/android/sensitive-result-receiver
10+
* @tags security
11+
* external/cwe/cwe-927
12+
*/
13+
14+
import java
15+
import semmle.code.java.security.SensitiveResultReceiverQuery
16+
import DataFlow::PathGraph
17+
18+
from DataFlow::PathNode src, DataFlow::PathNode sink, DataFlow::Node recSrc
19+
where sensitiveResultReceiver(src, sink, recSrc)
20+
select sink, src, sink, "This $@ is sent to a ResultReceiver obtained from $@.", src,
21+
"sensitive information", recSrc, "this untrusted source"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: newQuery
3+
---
4+
* Added a new query, `java/android/sensitive-result-receiver`, to find instances of sensitive data being leaked to an untrusted `ResultReceiver`.

java/ql/test/query-tests/security/CWE-927/SensitiveResultReceiver.expected

Whitespace-only changes.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import android.os.Bundle;
2+
import android.os.ResultReceiver;
3+
import android.content.Intent;
4+
5+
class SensitiveResultReceiver {
6+
<T> T source() { return null; }
7+
8+
void test1(String password) {
9+
Intent intent = source();
10+
ResultReceiver rec = intent.getParcelableExtra("hi");
11+
Bundle b = new Bundle();
12+
b.putCharSequence("pass", password);
13+
rec.send(0, b); // $hasSensitiveResultReceiver
14+
}
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import java
2+
import TestUtilities.InlineExpectationsTest
3+
import semmle.code.java.security.SensitiveResultReceiverQuery
4+
5+
class TestSource extends RemoteFlowSource {
6+
TestSource() { this.asExpr().(MethodAccess).getMethod().hasName("source") }
7+
8+
override string getSourceType() { result = "test" }
9+
}
10+
11+
class ResultReceiverTest extends InlineExpectationsTest {
12+
ResultReceiverTest() { this = "ResultReceiverTest" }
13+
14+
override string getARelevantTag() { result = "hasSensitiveResultReceiver" }
15+
16+
override predicate hasActualResult(Location loc, string element, string tag, string value) {
17+
exists(DataFlow::PathNode sink |
18+
sensitiveResultReceiver(_, sink, _) and
19+
element = sink.toString() and
20+
loc = sink.getNode().getLocation() and
21+
tag = "hasSensitiveResultReceiver" and
22+
value = ""
23+
)
24+
}
25+
}

0 commit comments

Comments
 (0)