Add StackTraceExposureQuery

This commit is contained in:
Ed Minnix
2023-04-05 11:48:29 -04:00
parent aff299eafd
commit a0f7575b34
3 changed files with 126 additions and 122 deletions

View File

@@ -7,4 +7,5 @@ category: minorAnalysis
* Added the `XssLocalQuery.qll` library to provide the `XssLocalFlow` taint-tracking module to reason about XSS vulnerabilities caused by local data flow. * Added the `XssLocalQuery.qll` library to provide the `XssLocalFlow` taint-tracking module to reason about XSS vulnerabilities caused by local data flow.
* Added the `ExternallyControlledFormatStringLocalQuery.qll` library to provide the `ExternallyControlledFormatStringLocalFlow` taint-tracking module to reason about format string vulnerabilities caused by local data flow. * Added the `ExternallyControlledFormatStringLocalQuery.qll` library to provide the `ExternallyControlledFormatStringLocalFlow` taint-tracking module to reason about format string vulnerabilities caused by local data flow.
* Added the `InsecureCookieQuery.qll` library to provide the `SecureCookieFlow` taint-tracking module to reason about insecure cookie vulnerabilities. * Added the `InsecureCookieQuery.qll` library to provide the `SecureCookieFlow` taint-tracking module to reason about insecure cookie vulnerabilities.
* Added the `ExecTaintedLocalQuery.qll` library to provide the `LocalUserInputToArgumentToExecFlow` taint-tracking module to reason about command injection vulnerabilities caused by local data flow. * Added the `ExecTaintedLocalQuery.qll` library to provide the `LocalUserInputToArgumentToExecFlow` taint-tracking module to reason about command injection vulnerabilities caused by local data flow.
* Added the `StackTraceExposureQuery.qll` library to provide the `printsStackExternally`, `stringifiedStackFlowsExternally`, and `getMessageFlowsExternally` predicates to reason about stack trace exposure vulnerabilities.

View File

@@ -0,0 +1,122 @@
/** Provides predicates to reason about exposure of stack-traces. */
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
private import semmle.code.java.security.InformationLeak
/**
* One of the `printStackTrace()` overloads on `Throwable`.
*/
private class PrintStackTraceMethod extends Method {
PrintStackTraceMethod() {
this.getDeclaringType()
.getSourceDeclaration()
.getASourceSupertype*()
.hasQualifiedName("java.lang", "Throwable") and
this.getName() = "printStackTrace"
}
}
private 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
)
}
}
private module ServletWriterSourceToPrintStackTraceMethodFlow =
TaintTracking::Global<ServletWriterSourceToPrintStackTraceMethodFlowConfig>;
/**
* A call that uses `Throwable.printStackTrace()` on a stream that is connected
* to external output.
*/
private predicate printsStackToWriter(MethodAccess call) {
exists(PrintStackTraceMethod printStackTrace |
call.getMethod() = printStackTrace and
ServletWriterSourceToPrintStackTraceMethodFlow::flowToExpr(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.
*/
private 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)
)
}
private 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
)
}
private module StackTraceStringToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
}
private module StackTraceStringToHttpResponseSinkFlow =
TaintTracking::Global<StackTraceStringToHttpResponseSinkFlowConfig>;
/**
* Holds if `call` writes the data of `stackTrace` to an external stream.
*/
predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
printsStackToWriter(call) and
call.getQualifier() = stackTrace and
not call.getQualifier() instanceof SuperAccess
}
/**
* Holds if `stackTrace` is a stringified stack trace which flows to an external sink.
*/
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
exists(MethodAccess stackTraceString |
stackTraceExpr(stackTrace, stackTraceString) and
StackTraceStringToHttpResponseSinkFlow::flow(DataFlow::exprNode(stackTraceString), externalExpr)
)
}
private class GetMessageFlowSource extends DataFlow::Node {
GetMessageFlowSource() {
exists(Method method | this.asExpr().(MethodAccess).getMethod() = method |
method.hasName("getMessage") and
method.hasNoParameters() and
method.getDeclaringType().hasQualifiedName("java.lang", "Throwable")
)
}
}
private module GetMessageFlowSourceToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src instanceof GetMessageFlowSource }
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
}
private module GetMessageFlowSourceToHttpResponseSinkFlow =
TaintTracking::Global<GetMessageFlowSourceToHttpResponseSinkFlowConfig>;
/**
* Holds if there is a call to `getMessage()` that then flows to a servlet response.
*/
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
GetMessageFlowSourceToHttpResponseSinkFlow::flow(getMessage, externalExpr)
}

View File

@@ -14,130 +14,11 @@
*/ */
import java import java
import semmle.code.java.dataflow.DataFlow import semmle.code.java.security.StackTraceExposureQuery
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::Global<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::flowToExpr(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::Global<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::flow(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")
)
}
}
module GetMessageFlowSourceToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof GetMessageFlowSource }
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
}
module GetMessageFlowSourceToHttpResponseSinkFlow =
TaintTracking::Global<GetMessageFlowSourceToHttpResponseSinkFlowConfig>;
/**
* A call to `getMessage()` that then flows to a servlet response.
*/
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
GetMessageFlowSourceToHttpResponseSinkFlow::flow(DataFlow::exprNode(getMessage), externalExpr)
}
from Expr externalExpr, Expr errorInformation from Expr externalExpr, Expr errorInformation
where where
printsStackExternally(externalExpr, errorInformation) or printsStackExternally(externalExpr, errorInformation) or
stringifiedStackFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation) or stringifiedStackFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation) or
getMessageFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation) getMessageFlowsExternally(DataFlow::exprNode(externalExpr), DataFlow::exprNode(errorInformation))
select externalExpr, "$@ can be exposed to an external user.", errorInformation, "Error information" select externalExpr, "$@ can be exposed to an external user.", errorInformation, "Error information"