Skip to content

Commit dc3389f

Browse files
committed
Add remote-user-input-System-property-env-lookup.ql
1 parent def3e88 commit dc3389f

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* Detects flow from remote user input to a lookup of an environment variable or
3+
* a System property. Some environment variables or System properties might store
4+
* sensitive data, therefore letting an untrusted user access them should be avoided.
5+
*
6+
* @id todo
7+
* @kind path-problem
8+
*/
9+
10+
// TODO: The flow reported by this query is not very useful yet; might have to
11+
// define more barriers, or switch from taint to data flow? (but that might cause
12+
// too many false negatives)
13+
14+
import java
15+
import semmle.code.java.dataflow.DataFlow
16+
import semmle.code.java.dataflow.FlowSources
17+
18+
/**
19+
* If a method is called which allows retrieving the value for an entry
20+
* in some form, has the expression which is used as key as result.
21+
*/
22+
Expr getKeyArg(MethodAccess mapMethodCall) {
23+
exists(Method m |
24+
m = mapMethodCall.getMethod().getSourceDeclaration() and
25+
(
26+
// Also considers 'setter' methods since they return the value
27+
m.hasName([
28+
"compute", "computeIfAbsent", "computeIfPresent", "get", "getOrDefault", "merge", "put",
29+
"putIfAbsent", "replace",
30+
// `java.util.Properties` methods
31+
"getProperty", "setProperty"
32+
])
33+
or
34+
// `remove(key)`; ignore `remove(key, value)` which returns `boolean`
35+
m.hasName("remove") and m.getNumberOfParameters() = 1
36+
) and
37+
// Key arg is for all these methods at index 0
38+
result = mapMethodCall.getArgument(0)
39+
)
40+
}
41+
42+
/** Sink for environment variable name */
43+
class EnvSink extends DataFlow::Node {
44+
EnvSink() {
45+
exists(MethodAccess getEnvCall, Method getEnvMethod |
46+
getEnvCall.getMethod() = getEnvMethod and
47+
getEnvMethod.getDeclaringType() instanceof TypeSystem and
48+
getEnvMethod.hasName("getenv")
49+
|
50+
// Call to `System.getenv(arg)`
51+
this.asExpr() = getEnvCall.getArgument(0)
52+
or
53+
// Call to `System.getenv()` followed by map value lookup
54+
getEnvMethod.hasNoParameters() and
55+
exists(MethodAccess mapMethodCall |
56+
DataFlow::localExprFlow(getEnvCall, mapMethodCall.getQualifier()) and
57+
this.asExpr() = getKeyArg(mapMethodCall)
58+
)
59+
)
60+
}
61+
}
62+
63+
/** Sink for System property name */
64+
class SystemPropertySink extends DataFlow::Node {
65+
SystemPropertySink() {
66+
// Call to `System.getProperty(arg)`
67+
exists(MethodAccess getPropertyCall, Method getPropertyMethod |
68+
getPropertyCall.getMethod() = getPropertyMethod and
69+
getPropertyMethod.getDeclaringType() instanceof TypeSystem and
70+
getPropertyMethod.hasName("getProperty")
71+
|
72+
this.asExpr() = getPropertyCall.getArgument(0)
73+
)
74+
or
75+
// Call to `System.getProperties()` followed by property lookup
76+
exists(
77+
MethodAccess getPropertiesCall, Method getPropertiesMethod, MethodAccess propertiesMethodCall
78+
|
79+
getPropertiesCall.getMethod() = getPropertiesMethod and
80+
getPropertiesMethod.getDeclaringType() instanceof TypeSystem and
81+
getPropertiesMethod.hasName("getProperties")
82+
|
83+
DataFlow::localExprFlow(getPropertiesCall, propertiesMethodCall.getQualifier()) and
84+
this.asExpr() = getKeyArg(propertiesMethodCall)
85+
)
86+
}
87+
}
88+
89+
module EnvLookupConfig implements DataFlow::ConfigSig {
90+
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
91+
92+
predicate isSink(DataFlow::Node sink) {
93+
sink instanceof EnvSink or sink instanceof SystemPropertySink
94+
}
95+
96+
predicate isBarrier(DataFlow::Node node) {
97+
// Try to reduce false positives by ignoring method calls where the real receiver type
98+
// is unknown
99+
exists(MethodAccess call | call = node.asExpr() |
100+
call.getReceiverType() instanceof TypeObject and
101+
call.getMethod() instanceof ToStringMethod
102+
or
103+
call.getMethod().isAbstract()
104+
)
105+
}
106+
}
107+
108+
module EnvLookupFlow = TaintTracking::Global<EnvLookupConfig>;
109+
110+
import EnvLookupFlow::PathGraph
111+
112+
from EnvLookupFlow::PathNode source, EnvLookupFlow::PathNode sink
113+
where EnvLookupFlow::flowPath(source, sink)
114+
select sink.getNode(), source, sink,
115+
"Remote user input flows into lookup of environment variable or System property"

0 commit comments

Comments
 (0)