Skip to content

Commit a0f7575

Browse files
committed
Add StackTraceExposureQuery
1 parent aff299e commit a0f7575

File tree

3 files changed

+126
-122
lines changed

3 files changed

+126
-122
lines changed

java/ql/lib/change-notes/2023-03-30-add-libraries-for-query-configurations.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ category: minorAnalysis
77
* Added the `XssLocalQuery.qll` library to provide the `XssLocalFlow` taint-tracking module to reason about XSS vulnerabilities caused by local data flow.
88
* Added the `ExternallyControlledFormatStringLocalQuery.qll` library to provide the `ExternallyControlledFormatStringLocalFlow` taint-tracking module to reason about format string vulnerabilities caused by local data flow.
99
* Added the `InsecureCookieQuery.qll` library to provide the `SecureCookieFlow` taint-tracking module to reason about insecure cookie vulnerabilities.
10-
* Added the `ExecTaintedLocalQuery.qll` library to provide the `LocalUserInputToArgumentToExecFlow` taint-tracking module to reason about command injection vulnerabilities caused by local data flow.
10+
* Added the `ExecTaintedLocalQuery.qll` library to provide the `LocalUserInputToArgumentToExecFlow` taint-tracking module to reason about command injection vulnerabilities caused by local data flow.
11+
* Added the `StackTraceExposureQuery.qll` library to provide the `printsStackExternally`, `stringifiedStackFlowsExternally`, and `getMessageFlowsExternally` predicates to reason about stack trace exposure vulnerabilities.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/** Provides predicates to reason about exposure of stack-traces. */
2+
3+
import java
4+
import semmle.code.java.dataflow.DataFlow
5+
import semmle.code.java.dataflow.TaintTracking
6+
private import semmle.code.java.security.InformationLeak
7+
8+
/**
9+
* One of the `printStackTrace()` overloads on `Throwable`.
10+
*/
11+
private class PrintStackTraceMethod extends Method {
12+
PrintStackTraceMethod() {
13+
this.getDeclaringType()
14+
.getSourceDeclaration()
15+
.getASourceSupertype*()
16+
.hasQualifiedName("java.lang", "Throwable") and
17+
this.getName() = "printStackTrace"
18+
}
19+
}
20+
21+
private module ServletWriterSourceToPrintStackTraceMethodFlowConfig implements DataFlow::ConfigSig {
22+
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof XssVulnerableWriterSource }
23+
24+
predicate isSink(DataFlow::Node sink) {
25+
exists(MethodAccess ma |
26+
sink.asExpr() = ma.getAnArgument() and ma.getMethod() instanceof PrintStackTraceMethod
27+
)
28+
}
29+
}
30+
31+
private module ServletWriterSourceToPrintStackTraceMethodFlow =
32+
TaintTracking::Global<ServletWriterSourceToPrintStackTraceMethodFlowConfig>;
33+
34+
/**
35+
* A call that uses `Throwable.printStackTrace()` on a stream that is connected
36+
* to external output.
37+
*/
38+
private predicate printsStackToWriter(MethodAccess call) {
39+
exists(PrintStackTraceMethod printStackTrace |
40+
call.getMethod() = printStackTrace and
41+
ServletWriterSourceToPrintStackTraceMethodFlow::flowToExpr(call.getAnArgument())
42+
)
43+
}
44+
45+
/**
46+
* A `PrintWriter` that wraps a given string writer. This pattern is used
47+
* in the most common idiom for converting a `Throwable` to a string.
48+
*/
49+
private predicate printWriterOnStringWriter(Expr printWriter, Variable stringWriterVar) {
50+
printWriter.getType().(Class).hasQualifiedName("java.io", "PrintWriter") and
51+
stringWriterVar.getType().(Class).hasQualifiedName("java.io", "StringWriter") and
52+
(
53+
printWriter.(ClassInstanceExpr).getAnArgument() = stringWriterVar.getAnAccess() or
54+
printWriterOnStringWriter(printWriter.(VarAccess).getVariable().getInitializer(),
55+
stringWriterVar)
56+
)
57+
}
58+
59+
private predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
60+
exists(Expr printWriter, Variable stringWriterVar, MethodAccess printStackCall |
61+
printWriterOnStringWriter(printWriter, stringWriterVar) and
62+
printStackCall.getMethod() instanceof PrintStackTraceMethod and
63+
printStackCall.getAnArgument() = printWriter and
64+
printStackCall.getQualifier() = exception and
65+
stackTraceString.getQualifier() = stringWriterVar.getAnAccess() and
66+
stackTraceString.getMethod() instanceof ToStringMethod
67+
)
68+
}
69+
70+
private module StackTraceStringToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
71+
predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
72+
73+
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
74+
}
75+
76+
private module StackTraceStringToHttpResponseSinkFlow =
77+
TaintTracking::Global<StackTraceStringToHttpResponseSinkFlowConfig>;
78+
79+
/**
80+
* Holds if `call` writes the data of `stackTrace` to an external stream.
81+
*/
82+
predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
83+
printsStackToWriter(call) and
84+
call.getQualifier() = stackTrace and
85+
not call.getQualifier() instanceof SuperAccess
86+
}
87+
88+
/**
89+
* Holds if `stackTrace` is a stringified stack trace which flows to an external sink.
90+
*/
91+
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
92+
exists(MethodAccess stackTraceString |
93+
stackTraceExpr(stackTrace, stackTraceString) and
94+
StackTraceStringToHttpResponseSinkFlow::flow(DataFlow::exprNode(stackTraceString), externalExpr)
95+
)
96+
}
97+
98+
private class GetMessageFlowSource extends DataFlow::Node {
99+
GetMessageFlowSource() {
100+
exists(Method method | this.asExpr().(MethodAccess).getMethod() = method |
101+
method.hasName("getMessage") and
102+
method.hasNoParameters() and
103+
method.getDeclaringType().hasQualifiedName("java.lang", "Throwable")
104+
)
105+
}
106+
}
107+
108+
private module GetMessageFlowSourceToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
109+
predicate isSource(DataFlow::Node src) { src instanceof GetMessageFlowSource }
110+
111+
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
112+
}
113+
114+
private module GetMessageFlowSourceToHttpResponseSinkFlow =
115+
TaintTracking::Global<GetMessageFlowSourceToHttpResponseSinkFlowConfig>;
116+
117+
/**
118+
* Holds if there is a call to `getMessage()` that then flows to a servlet response.
119+
*/
120+
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
121+
GetMessageFlowSourceToHttpResponseSinkFlow::flow(getMessage, externalExpr)
122+
}

java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql

Lines changed: 2 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -14,130 +14,11 @@
1414
*/
1515

1616
import java
17-
import semmle.code.java.dataflow.DataFlow
18-
import semmle.code.java.dataflow.TaintTracking
19-
import semmle.code.java.security.InformationLeak
20-
21-
/**
22-
* One of the `printStackTrace()` overloads on `Throwable`.
23-
*/
24-
class PrintStackTraceMethod extends Method {
25-
PrintStackTraceMethod() {
26-
this.getDeclaringType()
27-
.getSourceDeclaration()
28-
.getASourceSupertype*()
29-
.hasQualifiedName("java.lang", "Throwable") and
30-
this.getName() = "printStackTrace"
31-
}
32-
}
33-
34-
module ServletWriterSourceToPrintStackTraceMethodFlowConfig implements DataFlow::ConfigSig {
35-
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof XssVulnerableWriterSource }
36-
37-
predicate isSink(DataFlow::Node sink) {
38-
exists(MethodAccess ma |
39-
sink.asExpr() = ma.getAnArgument() and ma.getMethod() instanceof PrintStackTraceMethod
40-
)
41-
}
42-
}
43-
44-
module ServletWriterSourceToPrintStackTraceMethodFlow =
45-
TaintTracking::Global<ServletWriterSourceToPrintStackTraceMethodFlowConfig>;
46-
47-
/**
48-
* A call that uses `Throwable.printStackTrace()` on a stream that is connected
49-
* to external output.
50-
*/
51-
predicate printsStackToWriter(MethodAccess call) {
52-
exists(PrintStackTraceMethod printStackTrace |
53-
call.getMethod() = printStackTrace and
54-
ServletWriterSourceToPrintStackTraceMethodFlow::flowToExpr(call.getAnArgument())
55-
)
56-
}
57-
58-
/**
59-
* A `PrintWriter` that wraps a given string writer. This pattern is used
60-
* in the most common idiom for converting a `Throwable` to a string.
61-
*/
62-
predicate printWriterOnStringWriter(Expr printWriter, Variable stringWriterVar) {
63-
printWriter.getType().(Class).hasQualifiedName("java.io", "PrintWriter") and
64-
stringWriterVar.getType().(Class).hasQualifiedName("java.io", "StringWriter") and
65-
(
66-
printWriter.(ClassInstanceExpr).getAnArgument() = stringWriterVar.getAnAccess() or
67-
printWriterOnStringWriter(printWriter.(VarAccess).getVariable().getInitializer(),
68-
stringWriterVar)
69-
)
70-
}
71-
72-
predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
73-
exists(Expr printWriter, Variable stringWriterVar, MethodAccess printStackCall |
74-
printWriterOnStringWriter(printWriter, stringWriterVar) and
75-
printStackCall.getMethod() instanceof PrintStackTraceMethod and
76-
printStackCall.getAnArgument() = printWriter and
77-
printStackCall.getQualifier() = exception and
78-
stackTraceString.getQualifier() = stringWriterVar.getAnAccess() and
79-
stackTraceString.getMethod() instanceof ToStringMethod
80-
)
81-
}
82-
83-
module StackTraceStringToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
84-
predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
85-
86-
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
87-
}
88-
89-
module StackTraceStringToHttpResponseSinkFlow =
90-
TaintTracking::Global<StackTraceStringToHttpResponseSinkFlowConfig>;
91-
92-
/**
93-
* A write of stack trace data to an external stream.
94-
*/
95-
predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
96-
printsStackToWriter(call) and
97-
call.getQualifier() = stackTrace and
98-
not call.getQualifier() instanceof SuperAccess
99-
}
100-
101-
/**
102-
* A stringified stack trace flows to an external sink.
103-
*/
104-
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
105-
exists(MethodAccess stackTraceString |
106-
stackTraceExpr(stackTrace, stackTraceString) and
107-
StackTraceStringToHttpResponseSinkFlow::flow(DataFlow::exprNode(stackTraceString), externalExpr)
108-
)
109-
}
110-
111-
class GetMessageFlowSource extends MethodAccess {
112-
GetMessageFlowSource() {
113-
exists(Method method |
114-
method = this.getMethod() and
115-
method.hasName("getMessage") and
116-
method.hasNoParameters() and
117-
method.getDeclaringType().hasQualifiedName("java.lang", "Throwable")
118-
)
119-
}
120-
}
121-
122-
module GetMessageFlowSourceToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
123-
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof GetMessageFlowSource }
124-
125-
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
126-
}
127-
128-
module GetMessageFlowSourceToHttpResponseSinkFlow =
129-
TaintTracking::Global<GetMessageFlowSourceToHttpResponseSinkFlowConfig>;
130-
131-
/**
132-
* A call to `getMessage()` that then flows to a servlet response.
133-
*/
134-
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
135-
GetMessageFlowSourceToHttpResponseSinkFlow::flow(DataFlow::exprNode(getMessage), externalExpr)
136-
}
17+
import semmle.code.java.security.StackTraceExposureQuery
13718

13819
from Expr externalExpr, Expr errorInformation
13920
where
14021
printsStackExternally(externalExpr, errorInformation) or
14122
stringifiedStackFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation) or
142-
getMessageFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation)
23+
getMessageFlowsExternally(DataFlow::exprNode(externalExpr), DataFlow::exprNode(errorInformation))
14324
select externalExpr, "$@ can be exposed to an external user.", errorInformation, "Error information"

0 commit comments

Comments
 (0)