-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathStackTraceExposure.ql
More file actions
146 lines (127 loc) · 5.24 KB
/
StackTraceExposure.ql
File metadata and controls
146 lines (127 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
* @name Information exposure through a stack trace
* @description Information from a stack trace propagates to an external user.
* Stack traces can unintentionally reveal implementation details
* that are useful to an attacker for developing a subsequent exploit.
* @kind problem
* @problem.severity error
* @security-severity 5.4
* @precision high
* @id java/stack-trace-exposure
* @tags security
* external/cwe/cwe-209
* external/cwe/cwe-497
*/
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.InformationLeak
/**
* One of the `printStackTrace()` overloads on `Throwable`.
*/
class PrintStackTraceMethod extends Method {
PrintStackTraceMethod() {
this.getDeclaringType()
.getSourceDeclaration()
.getASourceSupertype*()
.hasQualifiedName("java.lang", "Throwable") and
this.getName() = "printStackTrace"
}
}
module ServletWriterSourceToPrintStackTraceMethodFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof XssVulnerableWriterSource }
predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
sink.asExpr() = ma.getAnArgument() and ma.getMethod() instanceof PrintStackTraceMethod
)
}
}
module ServletWriterSourceToPrintStackTraceMethodFlow =
TaintTracking::Make<ServletWriterSourceToPrintStackTraceMethodFlowConfig>;
/**
* A call that uses `Throwable.printStackTrace()` on a stream that is connected
* to external output.
*/
predicate printsStackToWriter(MethodAccess call) {
exists(PrintStackTraceMethod printStackTrace |
call.getMethod() = printStackTrace and
ServletWriterSourceToPrintStackTraceMethodFlow::hasFlowToExpr(call.getAnArgument())
)
}
/**
* A `PrintWriter` that wraps a given string writer. This pattern is used
* in the most common idiom for converting a `Throwable` to a string.
*/
predicate printWriterOnStringWriter(Expr printWriter, Variable stringWriterVar) {
printWriter.getType().(Class).hasQualifiedName("java.io", "PrintWriter") and
stringWriterVar.getType().(Class).hasQualifiedName("java.io", "StringWriter") and
(
printWriter.(ClassInstanceExpr).getAnArgument() = stringWriterVar.getAnAccess() or
printWriterOnStringWriter(printWriter.(VarAccess).getVariable().getInitializer(),
stringWriterVar)
)
}
predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
exists(Expr printWriter, Variable stringWriterVar, MethodAccess printStackCall |
printWriterOnStringWriter(printWriter, stringWriterVar) and
printStackCall.getMethod() instanceof PrintStackTraceMethod and
printStackCall.getAnArgument() = printWriter and
printStackCall.getQualifier() = exception and
stackTraceString.getQualifier() = stringWriterVar.getAnAccess() and
stackTraceString.getMethod() instanceof ToStringMethod
)
}
module StackTraceStringToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
}
module StackTraceStringToHttpResponseSinkFlow =
TaintTracking::Make<StackTraceStringToHttpResponseSinkFlowConfig>;
/**
* A write of stack trace data to an external stream.
*/
predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
printsStackToWriter(call) and
call.getQualifier() = stackTrace and
not call.getQualifier() instanceof SuperAccess
}
/**
* A stringified stack trace flows to an external sink.
*/
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
exists(MethodAccess stackTraceString |
stackTraceExpr(stackTrace, stackTraceString) and
StackTraceStringToHttpResponseSinkFlow::hasFlow(DataFlow::exprNode(stackTraceString),
externalExpr)
)
}
class GetMessageFlowSource extends MethodAccess {
GetMessageFlowSource() {
exists(Method method |
method = this.getMethod() and
method.hasName("getMessage") and
method.hasNoParameters() and
method.getDeclaringType().hasQualifiedName("java.lang", "Throwable")
)
}
}
class GetMessageFlowSourceToHttpResponseSinkFlowConfig extends TaintTracking::Configuration {
GetMessageFlowSourceToHttpResponseSinkFlowConfig() {
this = "StackTraceExposure::GetMessageFlowSourceToHttpResponseSinkFlowConfig"
}
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof GetMessageFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
}
/**
* A call to `getMessage()` that then flows to a servlet response.
*/
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
any(GetMessageFlowSourceToHttpResponseSinkFlowConfig conf)
.hasFlow(DataFlow::exprNode(getMessage), externalExpr)
}
from Expr externalExpr, Expr errorInformation
where
printsStackExternally(externalExpr, errorInformation) or
stringifiedStackFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation) or
getMessageFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation)
select externalExpr, "$@ can be exposed to an external user.", errorInformation, "Error information"