mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge branch 'main' into atorralba/promote-missing-jwt-signature-check
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
* Such operations could interfere with the EJB container's operation.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/container-interference
|
||||
* @tags reliability
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* for enterprise components.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/file-io
|
||||
* @tags reliability
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Such use could compromise security and system stability.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/native-code
|
||||
* @tags reliability
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* as this could compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/reflection
|
||||
* @tags external/cwe/cwe-573
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* This functionality is reserved for the EJB container for security reasons.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/security-configuration-access
|
||||
* @tags external/cwe/cwe-573
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* the Java serialization protocol, since their use could compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/substitution-in-serialization
|
||||
* @tags external/cwe/cwe-573
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* compromise security or interfere with the EJB container's operation.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision low
|
||||
* @id java/ejb/socket-or-stream-handler-factory
|
||||
* @tags reliability
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* numeric errors such as overflows.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision very-high
|
||||
* @id java/implicit-cast-in-compound-assignment
|
||||
* @tags reliability
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* guarantee an evenly distributed sequence of random numbers.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/random-used-once
|
||||
* @tags reliability
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* may cause a deadlock.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.9
|
||||
* @precision medium
|
||||
* @id java/unreleased-lock
|
||||
* @tags reliability
|
||||
|
||||
@@ -14,7 +14,7 @@ but not closed may cause a resource leak.
|
||||
|
||||
<p>Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions,
|
||||
it is safest to close a resource in a <code>finally</code> block. (However, this is unnecessary for
|
||||
subclasses of <code>StringReader</code> and <code>ByteArrayInputStream</code>.)
|
||||
subclasses of <code>CharArrayReader</code>, <code>StringReader</code> and <code>ByteArrayInputStream</code>.)
|
||||
</p>
|
||||
|
||||
<p>For Java 7 or later, the recommended way to close resources that implement <code>java.lang.AutoCloseable</code>
|
||||
|
||||
@@ -17,16 +17,16 @@ import CloseType
|
||||
|
||||
predicate readerType(RefType t) {
|
||||
exists(RefType sup | sup = t.getASupertype*() |
|
||||
sup.hasName("Reader") or
|
||||
sup.hasName("InputStream") or
|
||||
sup.hasQualifiedName("java.io", ["Reader", "InputStream"]) or
|
||||
sup.hasQualifiedName("java.util.zip", "ZipFile")
|
||||
)
|
||||
}
|
||||
|
||||
predicate safeReaderType(RefType t) {
|
||||
exists(RefType sup | sup = t.getASupertype*() |
|
||||
sup.hasName("StringReader") or
|
||||
sup.hasName("ByteArrayInputStream") or
|
||||
sup.hasQualifiedName("java.io", ["CharArrayReader", "StringReader", "ByteArrayInputStream"])
|
||||
or
|
||||
// Note: It is unclear which specific class this is supposed to match
|
||||
sup.hasName("StringInputStream")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ but not properly closed later may cause a resource leak.
|
||||
|
||||
<p>Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions,
|
||||
it is safest to close a resource properly in a <code>finally</code> block. (However, this is unnecessary for
|
||||
subclasses of <code>StringWriter</code> and <code>ByteArrayOutputStream</code>.)</p>
|
||||
subclasses of <code>CharArrayWriter</code>, <code>StringWriter</code> and <code>ByteArrayOutputStream</code>.)</p>
|
||||
|
||||
<p>For Java 7 or later, the recommended way to close resources that implement <code>java.lang.AutoCloseable</code>
|
||||
is to declare them within a <code>try-with-resources</code> statement, so that they are closed implicitly.</p>
|
||||
|
||||
@@ -17,15 +17,13 @@ import CloseType
|
||||
|
||||
predicate writerType(RefType t) {
|
||||
exists(RefType sup | sup = t.getASupertype*() |
|
||||
sup.hasName("Writer") or
|
||||
sup.hasName("OutputStream")
|
||||
sup.hasQualifiedName("java.io", ["Writer", "OutputStream"])
|
||||
)
|
||||
}
|
||||
|
||||
predicate safeWriterType(RefType t) {
|
||||
exists(RefType sup | sup = t.getASupertype*() |
|
||||
sup.hasName("StringWriter") or
|
||||
sup.hasName("ByteArrayOutputStream")
|
||||
sup.hasQualifiedName("java.io", ["CharArrayWriter", "StringWriter", "ByteArrayOutputStream"])
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* @kind path-problem
|
||||
* @precision low
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Accessing paths influenced by users can allow an attacker to access unexpected resources.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.4
|
||||
* @precision high
|
||||
* @id java/path-injection
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Accessing paths influenced by users can allow an attacker to access unexpected resources.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 6.4
|
||||
* @precision medium
|
||||
* @id java/path-injection-local
|
||||
* @tags security
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* @kind path-problem
|
||||
* @id java/zipslip
|
||||
* @problem.severity error
|
||||
* @security-severity 6.4
|
||||
* @precision high
|
||||
* @tags security
|
||||
* external/cwe/cwe-022
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* malicious changes in the PATH environment variable.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/relative-path-command
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* changes in the strings.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/command-line-injection
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* changes in the strings.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/command-line-injection-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* insertion of special characters in the strings.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/concatenated-command-line
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* allows for a cross-site scripting vulnerability.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 2.9
|
||||
* @precision high
|
||||
* @id java/xss
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* allows for a cross-site scripting vulnerability.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 2.9
|
||||
* @precision medium
|
||||
* @id java/xss-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* malicious code by the user.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.4
|
||||
* @precision high
|
||||
* @id java/sql-injection
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* malicious code by the user.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 6.4
|
||||
* @precision medium
|
||||
* @id java/sql-injection-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* characters is vulnerable to insertion of malicious code.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.4
|
||||
* @precision high
|
||||
* @id java/concatenated-sql-query
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* malicious LDAP code by the user.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/ldap-injection
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description User-controlled data may be evaluated as a Java EL expression, leading to arbitrary code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 10.0
|
||||
* @precision high
|
||||
* @id java/insecure-bean-validation
|
||||
* @tags security
|
||||
|
||||
38
java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
Normal file
38
java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @name Expression language injection (JEXL)
|
||||
* @description Evaluation of a user-controlled JEXL expression
|
||||
* may lead to arbitrary code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 10.0
|
||||
* @precision high
|
||||
* @id java/jexl-expression-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.JexlInjection
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe user input
|
||||
* that is used to construct and evaluate a JEXL expression.
|
||||
* It supports both JEXL 2 and 3.
|
||||
*/
|
||||
class JexlInjectionConfig extends TaintTracking::Configuration {
|
||||
JexlInjectionConfig() { this = "JexlInjectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
any(JexlInjectionAdditionalTaintStep c).step(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, JexlInjectionConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "JEXL injection from $@.", source.getNode(), "this user input"
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using a deprecated artifact repository may eventually give attackers access for a supply chain attack.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.5
|
||||
* @precision very-high
|
||||
* @id java/maven/dependency-upon-bintray
|
||||
* @tags security
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* an HTTP header.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 3.6
|
||||
* @precision high
|
||||
* @id java/netty-http-response-splitting
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* makes code vulnerable to attack by header splitting.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 3.6
|
||||
* @precision high
|
||||
* @id java/http-response-splitting
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* makes code vulnerable to attack by header splitting.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 3.6
|
||||
* @precision medium
|
||||
* @id java/http-response-splitting-local
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using unvalidated external input as the argument to a construction of an array can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-construction
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* a construction of an array can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-construction-code-specified
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* a construction of an array can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-construction-local
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using external input as an index to an array, without proper validation, can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-index
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* proper validation, can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-index-code-specified
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* proper validation, can lead to index out of bound exceptions.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/improper-validation-of-array-index-local
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using external input in format strings can lead to exceptions or information leaks.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.9
|
||||
* @precision high
|
||||
* @id java/tainted-format-string
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using external input in format strings can lead to exceptions or information leaks.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 6.9
|
||||
* @precision medium
|
||||
* @id java/tainted-format-string-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* overflows.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/tainted-arithmetic
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* overflows.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/tainted-arithmetic-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* overflows.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/uncontrolled-arithmetic
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* is then used in an arithmetic expression, this may result in an overflow.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/extreme-value-arithmetic
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* to behave unexpectedly.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/comparison-with-wider-type
|
||||
* @tags reliability
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* that are useful to an attacker for developing a subsequent exploit.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 3.6
|
||||
* @precision high
|
||||
* @id java/stack-trace-exposure
|
||||
* @tags security
|
||||
@@ -15,7 +16,7 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.security.XSS
|
||||
import semmle.code.java.security.InformationLeak
|
||||
|
||||
/**
|
||||
* One of the `printStackTrace()` overloads on `Throwable`.
|
||||
@@ -83,14 +84,14 @@ predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
|
||||
)
|
||||
}
|
||||
|
||||
class StackTraceStringToXssSinkFlowConfig extends TaintTracking::Configuration {
|
||||
StackTraceStringToXssSinkFlowConfig() {
|
||||
this = "StackTraceExposure::StackTraceStringToXssSinkFlowConfig"
|
||||
class StackTraceStringToHttpResponseSinkFlowConfig extends TaintTracking::Configuration {
|
||||
StackTraceStringToHttpResponseSinkFlowConfig() {
|
||||
this = "StackTraceExposure::StackTraceStringToHttpResponseSinkFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,8 +106,8 @@ predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
|
||||
/**
|
||||
* A stringified stack trace flows to an external sink.
|
||||
*/
|
||||
predicate stringifiedStackFlowsExternally(XssSink externalExpr, Expr stackTrace) {
|
||||
exists(MethodAccess stackTraceString, StackTraceStringToXssSinkFlowConfig conf |
|
||||
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
|
||||
exists(MethodAccess stackTraceString, StackTraceStringToHttpResponseSinkFlowConfig conf |
|
||||
stackTraceExpr(stackTrace, stackTraceString) and
|
||||
conf.hasFlow(DataFlow::exprNode(stackTraceString), externalExpr)
|
||||
)
|
||||
@@ -123,21 +124,21 @@ class GetMessageFlowSource extends MethodAccess {
|
||||
}
|
||||
}
|
||||
|
||||
class GetMessageFlowSourceToXssSinkFlowConfig extends TaintTracking::Configuration {
|
||||
GetMessageFlowSourceToXssSinkFlowConfig() {
|
||||
this = "StackTraceExposure::GetMessageFlowSourceToXssSinkFlowConfig"
|
||||
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 XssSink }
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `getMessage()` that then flows to a servlet response.
|
||||
*/
|
||||
predicate getMessageFlowsExternally(XssSink externalExpr, GetMessageFlowSource getMessage) {
|
||||
any(GetMessageFlowSourceToXssSinkFlowConfig conf)
|
||||
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
|
||||
any(GetMessageFlowSourceToHttpResponseSinkFlowConfig conf)
|
||||
.hasFlow(DataFlow::exprNode(getMessage), externalExpr)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Marking a certificate as valid for a host without checking the certificate hostname allows an attacker to perform a machine-in-the-middle attack.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 4.9
|
||||
* @precision high
|
||||
* @id java/unsafe-hostname-verification
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Storing sensitive information in cleartext can expose it to an attacker.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/cleartext-storage-in-class
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Storing sensitive information in cleartext can expose it to an attacker.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 2.9
|
||||
* @precision high
|
||||
* @id java/cleartext-storage-in-cookie
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Storing sensitive information in cleartext can expose it to an attacker.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 6.4
|
||||
* @precision medium
|
||||
* @id java/cleartext-storage-in-properties
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Non-HTTPS connections can be intercepted by third parties.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.2
|
||||
* @precision medium
|
||||
* @id java/non-https-url
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Non-SSL connections can be intercepted by third parties.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.2
|
||||
* @precision medium
|
||||
* @id java/non-ssl-connection
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* third parties.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.2
|
||||
* @precision medium
|
||||
* @id java/non-ssl-socket-factory
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.2
|
||||
* @precision high
|
||||
* @id java/weak-cryptographic-algorithm
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.2
|
||||
* @precision medium
|
||||
* @id java/potentially-weak-cryptographic-algorithm
|
||||
* @tags security
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
* @description Using a predictable seed in a pseudo-random number generator can lead to predictability of the numbers generated by it.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/predictable-seed
|
||||
* @tags security
|
||||
* external/cwe/cwe-335
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using a vulnerable version of JHipster to generate random numbers makes it easier for attackers to take over accounts.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision very-high
|
||||
* @id java/jhipster-prng
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* a Cross-Site Request Forgery (CSRF) attack.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.4
|
||||
* @precision high
|
||||
* @id java/spring-disabled-csrf-protection
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* if the state may be changed between the check and use.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/toctou-race-condition
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Opening a socket after authenticating via a different channel may allow an attacker to connect to the port first.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 10.0
|
||||
* @precision medium
|
||||
* @id java/socket-auth-race-condition
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* execute arbitrary code.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/unsafe-deserialization
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* may cause redirection to malicious web sites.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 2.7
|
||||
* @precision high
|
||||
* @id java/unvalidated-url-redirection
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* may cause redirection to malicious web sites.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 2.7
|
||||
* @precision medium
|
||||
* @id java/unvalidated-url-redirection-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* references may lead to disclosure of confidential data or denial of service.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/xxe
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* interception.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 2.9
|
||||
* @precision high
|
||||
* @id java/insecure-cookie
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* malicious code by the user.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/xml/xpath-injection
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Certain standard library routines are dangerous to call.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 10.0
|
||||
* @precision medium
|
||||
* @id java/potentially-dangerous-function
|
||||
* @tags reliability
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* can cause unexpected truncation.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/tainted-numeric-cast
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* can cause unexpected truncation.
|
||||
* @kind path-problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/tainted-numeric-cast-local
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* the file may be modified or removed by external actors.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/world-writable-file-read
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using a hard-coded credential in a call to a sensitive Java API may compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/hardcoded-credential-api-call
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Comparing a parameter to a hard-coded credential may compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision low
|
||||
* @id java/hardcoded-credential-comparison
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Using a hard-coded credential in a sensitive call may compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision low
|
||||
* @id java/hardcoded-credential-sensitive-call
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Hard-coding a password string may compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision low
|
||||
* @id java/hardcoded-password-field
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* passing through authentication systems.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision medium
|
||||
* @id java/user-controlled-bypass
|
||||
* @tags security
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* permissions being granted.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision high
|
||||
* @id java/tainted-permissions-check
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Non-HTTPS connections can be intercepted by third parties.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.9
|
||||
* @precision very-high
|
||||
* @id java/maven/non-https-url
|
||||
* @tags security
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* @description Acquiring multiple locks in a different order may cause deadlock.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 6.9
|
||||
* @precision medium
|
||||
* @id java/lock-order-inconsistency
|
||||
* @tags security
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* looping.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 3.6
|
||||
* @precision medium
|
||||
* @id java/unreachable-exit-in-loop
|
||||
* @tags security
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @name Expression language injection (JEXL)
|
||||
* @description Evaluation of a user-controlled JEXL expression
|
||||
* may lead to arbitrary code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/jexl-expression-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import JexlInjectionLib
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, JexlInjectionConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "JEXL injection from $@.", source.getNode(), "this user input"
|
||||
@@ -1,277 +0,0 @@
|
||||
import java
|
||||
import FlowUtils
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe user input
|
||||
* that is used to construct and evaluate a JEXL expression.
|
||||
* It supports both JEXL 2 and 3.
|
||||
*/
|
||||
class JexlInjectionConfig extends TaintTracking::Configuration {
|
||||
JexlInjectionConfig() { this = "JexlInjectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
any(TaintPropagatingJexlMethodCall c).taintFlow(fromNode, toNode) or
|
||||
hasGetterFlow(fromNode, toNode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for Expresssion Language injection vulnerabilities via Jexl,
|
||||
* i.e. method calls that run evaluation of a JEXL expression.
|
||||
*
|
||||
* Creating a `Callable` from a tainted JEXL expression or script is considered as a sink
|
||||
* although the tainted expression is not executed at this point.
|
||||
* Here we assume that it will get executed at some point,
|
||||
* maybe stored in an object field and then reached by a different flow.
|
||||
*/
|
||||
private class JexlEvaluationSink extends DataFlow::ExprNode {
|
||||
JexlEvaluationSink() {
|
||||
exists(MethodAccess ma, Method m, Expr taintFrom |
|
||||
ma.getMethod() = m and taintFrom = this.asExpr()
|
||||
|
|
||||
m instanceof DirectJexlEvaluationMethod and ma.getQualifier() = taintFrom
|
||||
or
|
||||
m instanceof CreateJexlCallableMethod and ma.getQualifier() = taintFrom
|
||||
or
|
||||
m instanceof JexlEngineGetSetPropertyMethod and
|
||||
taintFrom.getType() instanceof TypeString and
|
||||
ma.getAnArgument() = taintFrom
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines method calls that propagate tainted data via one of the methods
|
||||
* from JEXL library.
|
||||
*/
|
||||
private class TaintPropagatingJexlMethodCall extends MethodAccess {
|
||||
Expr taintFromExpr;
|
||||
|
||||
TaintPropagatingJexlMethodCall() {
|
||||
exists(Method m, RefType taintType |
|
||||
this.getMethod() = m and
|
||||
taintType = taintFromExpr.getType()
|
||||
|
|
||||
isUnsafeEngine(this.getQualifier()) and
|
||||
(
|
||||
m instanceof CreateJexlScriptMethod and
|
||||
taintFromExpr = this.getArgument(0) and
|
||||
taintType instanceof TypeString
|
||||
or
|
||||
m instanceof CreateJexlExpressionMethod and
|
||||
taintFromExpr = this.getAnArgument() and
|
||||
taintType instanceof TypeString
|
||||
or
|
||||
m instanceof CreateJexlTemplateMethod and
|
||||
(taintType instanceof TypeString or taintType instanceof Reader) and
|
||||
taintFromExpr = this.getArgument([0, 1])
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that propagates
|
||||
* tainted data.
|
||||
*/
|
||||
predicate taintFlow(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
fromNode.asExpr() = taintFromExpr and toNode.asExpr() = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is a JEXL engine that is not configured with a sandbox.
|
||||
*/
|
||||
private predicate isUnsafeEngine(Expr expr) {
|
||||
not exists(SandboxedJexlFlowConfig config | config.hasFlowTo(DataFlow::exprNode(expr)))
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration for a tracking sandboxed JEXL engines.
|
||||
*/
|
||||
private class SandboxedJexlFlowConfig extends DataFlow2::Configuration {
|
||||
SandboxedJexlFlowConfig() { this = "JexlInjection::SandboxedJexlFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof SandboxedJexlSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
(
|
||||
m instanceof CreateJexlScriptMethod or
|
||||
m instanceof CreateJexlExpressionMethod or
|
||||
m instanceof CreateJexlTemplateMethod
|
||||
) and
|
||||
ma.getQualifier() = node.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
createsJexlEngine(fromNode, toNode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a data flow source for JEXL engines configured with a sandbox.
|
||||
*/
|
||||
private class SandboxedJexlSource extends DataFlow::ExprNode {
|
||||
SandboxedJexlSource() {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m.getDeclaringType() instanceof JexlBuilder and
|
||||
m.hasName(["uberspect", "sandbox"]) and
|
||||
m.getReturnType() instanceof JexlBuilder and
|
||||
this.asExpr() = [ma, ma.getQualifier()]
|
||||
)
|
||||
or
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof JexlEngine and
|
||||
cc.getArgument(0).getType() instanceof JexlUberspect and
|
||||
cc = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that creates one of the JEXL engines.
|
||||
*/
|
||||
private predicate createsJexlEngine(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
(m.getDeclaringType() instanceof JexlBuilder or m.getDeclaringType() instanceof JexlEngine) and
|
||||
m.hasName(["create", "createJxltEngine"]) and
|
||||
ma.getQualifier() = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
or
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof UnifiedJexl and
|
||||
cc.getArgument(0) = fromNode.asExpr() and
|
||||
cc = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A methods in the `JexlEngine` class that gets or sets a property with a JEXL expression.
|
||||
*/
|
||||
private class JexlEngineGetSetPropertyMethod extends Method {
|
||||
JexlEngineGetSetPropertyMethod() {
|
||||
getDeclaringType() instanceof JexlEngine and
|
||||
hasName(["getProperty", "setProperty"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that triggers direct evaluation of JEXL expressions.
|
||||
*/
|
||||
private class DirectJexlEvaluationMethod extends Method {
|
||||
DirectJexlEvaluationMethod() {
|
||||
getDeclaringType() instanceof JexlExpression and hasName("evaluate")
|
||||
or
|
||||
getDeclaringType() instanceof JexlScript and hasName("execute")
|
||||
or
|
||||
getDeclaringType() instanceof JxltEngineExpression and hasName(["evaluate", "prepare"])
|
||||
or
|
||||
getDeclaringType() instanceof JxltEngineTemplate and hasName("evaluate")
|
||||
or
|
||||
getDeclaringType() instanceof UnifiedJexlExpression and hasName(["evaluate", "prepare"])
|
||||
or
|
||||
getDeclaringType() instanceof UnifiedJexlTemplate and hasName("evaluate")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a JEXL script.
|
||||
*/
|
||||
private class CreateJexlScriptMethod extends Method {
|
||||
CreateJexlScriptMethod() { getDeclaringType() instanceof JexlEngine and hasName("createScript") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a `Callable` for a JEXL expression or script.
|
||||
*/
|
||||
private class CreateJexlCallableMethod extends Method {
|
||||
CreateJexlCallableMethod() {
|
||||
(getDeclaringType() instanceof JexlExpression or getDeclaringType() instanceof JexlScript) and
|
||||
hasName("callable")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a JEXL template.
|
||||
*/
|
||||
private class CreateJexlTemplateMethod extends Method {
|
||||
CreateJexlTemplateMethod() {
|
||||
(getDeclaringType() instanceof JxltEngine or getDeclaringType() instanceof UnifiedJexl) and
|
||||
hasName("createTemplate")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that creates a JEXL expression.
|
||||
*/
|
||||
private class CreateJexlExpressionMethod extends Method {
|
||||
CreateJexlExpressionMethod() {
|
||||
(getDeclaringType() instanceof JexlEngine or getDeclaringType() instanceof JxltEngine) and
|
||||
hasName("createExpression")
|
||||
or
|
||||
getDeclaringType() instanceof UnifiedJexl and hasName("parse")
|
||||
}
|
||||
}
|
||||
|
||||
private class JexlRefType extends RefType {
|
||||
JexlRefType() { getPackage().hasName(["org.apache.commons.jexl2", "org.apache.commons.jexl3"]) }
|
||||
}
|
||||
|
||||
private class JexlExpression extends JexlRefType {
|
||||
JexlExpression() { hasName(["Expression", "JexlExpression"]) }
|
||||
}
|
||||
|
||||
private class JexlScript extends JexlRefType {
|
||||
JexlScript() { hasName(["Script", "JexlScript"]) }
|
||||
}
|
||||
|
||||
private class JexlBuilder extends JexlRefType {
|
||||
JexlBuilder() { hasName("JexlBuilder") }
|
||||
}
|
||||
|
||||
private class JexlEngine extends JexlRefType {
|
||||
JexlEngine() { hasName("JexlEngine") }
|
||||
}
|
||||
|
||||
private class JxltEngine extends JexlRefType {
|
||||
JxltEngine() { hasName("JxltEngine") }
|
||||
}
|
||||
|
||||
private class UnifiedJexl extends JexlRefType {
|
||||
UnifiedJexl() { hasName("UnifiedJEXL") }
|
||||
}
|
||||
|
||||
private class JexlUberspect extends Interface {
|
||||
JexlUberspect() {
|
||||
hasQualifiedName("org.apache.commons.jexl2.introspection", "Uberspect") or
|
||||
hasQualifiedName("org.apache.commons.jexl3.introspection", "JexlUberspect")
|
||||
}
|
||||
}
|
||||
|
||||
private class JxltEngineExpression extends NestedType {
|
||||
JxltEngineExpression() { getEnclosingType() instanceof JxltEngine and hasName("Expression") }
|
||||
}
|
||||
|
||||
private class JxltEngineTemplate extends NestedType {
|
||||
JxltEngineTemplate() { getEnclosingType() instanceof JxltEngine and hasName("Template") }
|
||||
}
|
||||
|
||||
private class UnifiedJexlExpression extends NestedType {
|
||||
UnifiedJexlExpression() { getEnclosingType() instanceof UnifiedJexl and hasName("Expression") }
|
||||
}
|
||||
|
||||
private class UnifiedJexlTemplate extends NestedType {
|
||||
UnifiedJexlTemplate() { getEnclosingType() instanceof UnifiedJexl and hasName("Template") }
|
||||
}
|
||||
|
||||
private class Reader extends RefType {
|
||||
Reader() { hasQualifiedName("java.io", "Reader") }
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import org.python.util.PythonInterpreter;
|
||||
|
||||
public class JythonInjection extends HttpServlet {
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
PythonInterpreter interpreter = null;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
interpreter = new PythonInterpreter();
|
||||
interpreter.setOut(out);
|
||||
interpreter.setErr(out);
|
||||
|
||||
// BAD: allow execution of arbitrary Python code
|
||||
interpreter.exec(code);
|
||||
out.flush();
|
||||
|
||||
response.getWriter().print(out.toString());
|
||||
} catch(PyException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
if (interpreter != null) {
|
||||
interpreter.close();
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
PythonInterpreter interpreter = null;
|
||||
|
||||
try {
|
||||
interpreter = new PythonInterpreter();
|
||||
// BAD: allow execution of arbitrary Python code
|
||||
PyObject py = interpreter.eval(code);
|
||||
|
||||
response.getWriter().print(py.toString());
|
||||
} catch(PyException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
if (interpreter != null) {
|
||||
interpreter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>Python has been the most widely used programming language in recent years, and Jython
|
||||
(formerly known as JPython) is a popular Java implementation of Python. It allows
|
||||
embedded Python scripting inside Java applications and provides an interactive interpreter
|
||||
that can be used to interact with Java packages or with running Java applications. If an
|
||||
expression is built using attacker-controlled data and then evaluated, it may allow the
|
||||
attacker to run arbitrary code.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>In general, including user input in Jython expression should be avoided. If user input
|
||||
must be included in an expression, it should be then evaluated in a safe context that
|
||||
doesn't allow arbitrary code invocation.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following code could execute arbitrary code in Jython Interpreter</p>
|
||||
<sample src="JythonInjection.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Jython Organization: <a href="https://jython.readthedocs.io/en/latest/JythonAndJavaIntegration/">Jython and Java Integration</a>
|
||||
</li>
|
||||
<li>
|
||||
PortSwigger: <a href="https://portswigger.net/kb/issues/00100f10_python-code-injection">Python code injection</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
114
java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql
Normal file
114
java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* @name Injection in Jython
|
||||
* @description Evaluation of a user-controlled malicious expression in Java Python
|
||||
* interpreter may lead to remote code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/jython-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
* external/cwe/cwe-095
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.frameworks.spring.SpringController
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** The class `org.python.util.PythonInterpreter`. */
|
||||
class PythonInterpreter extends RefType {
|
||||
PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") }
|
||||
}
|
||||
|
||||
/** A method that evaluates, compiles or executes a Jython expression. */
|
||||
class InterpretExprMethod extends Method {
|
||||
InterpretExprMethod() {
|
||||
this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and
|
||||
getName().matches(["exec%", "run%", "eval", "compile"])
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `org.python.core.BytecodeLoader`. */
|
||||
class BytecodeLoader extends RefType {
|
||||
BytecodeLoader() { this.hasQualifiedName("org.python.core", "BytecodeLoader") }
|
||||
}
|
||||
|
||||
/** Holds if a Jython expression if evaluated, compiled or executed. */
|
||||
predicate runsCode(MethodAccess ma, Expr sink) {
|
||||
exists(Method m | m = ma.getMethod() |
|
||||
m instanceof InterpretExprMethod and
|
||||
sink = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
/** A method that loads Java class data. */
|
||||
class LoadClassMethod extends Method {
|
||||
LoadClassMethod() {
|
||||
this.getDeclaringType().getAnAncestor*() instanceof BytecodeLoader and
|
||||
hasName(["makeClass", "makeCode"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ma` is a call to a class-loading method, and `sink` is the byte array
|
||||
* representing the class to be loaded.
|
||||
*/
|
||||
predicate loadsClass(MethodAccess ma, Expr sink) {
|
||||
exists(Method m, int i | m = ma.getMethod() |
|
||||
m instanceof LoadClassMethod and
|
||||
m.getParameter(i).getType() instanceof Array and // makeClass(java.lang.String name, byte[] data, ...)
|
||||
sink = ma.getArgument(i)
|
||||
)
|
||||
}
|
||||
|
||||
/** The class `org.python.core.Py`. */
|
||||
class Py extends RefType {
|
||||
Py() { this.hasQualifiedName("org.python.core", "Py") }
|
||||
}
|
||||
|
||||
/** A method declared on class `Py` or one of its descendants that compiles Python code. */
|
||||
class PyCompileMethod extends Method {
|
||||
PyCompileMethod() {
|
||||
this.getDeclaringType().getAnAncestor*() instanceof Py and
|
||||
getName().matches("compile%")
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if source code is compiled with `PyCompileMethod`. */
|
||||
predicate compile(MethodAccess ma, Expr sink) {
|
||||
exists(Method m | m = ma.getMethod() |
|
||||
m instanceof PyCompileMethod and
|
||||
sink = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
/** An expression loaded by Jython. */
|
||||
class CodeInjectionSink extends DataFlow::ExprNode {
|
||||
MethodAccess methodAccess;
|
||||
|
||||
CodeInjectionSink() {
|
||||
runsCode(methodAccess, this.getExpr()) or
|
||||
loadsClass(methodAccess, this.getExpr()) or
|
||||
compile(methodAccess, this.getExpr())
|
||||
}
|
||||
|
||||
MethodAccess getMethodAccess() { result = methodAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint configuration for tracking flow from `RemoteFlowSource` to a Jython method call
|
||||
* `CodeInjectionSink` that executes injected code.
|
||||
*/
|
||||
class CodeInjectionConfiguration extends TaintTracking::Configuration {
|
||||
CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode().(CodeInjectionSink).getMethodAccess(), source, sink, "Jython evaluate $@.",
|
||||
source.getNode(), "user input"
|
||||
@@ -0,0 +1,40 @@
|
||||
import org.mozilla.javascript.ClassShutter;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
public class RhinoInjection extends HttpServlet {
|
||||
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
Context ctx = Context.enter();
|
||||
try {
|
||||
{
|
||||
// BAD: allow arbitrary Java and JavaScript code to be executed
|
||||
Scriptable scope = ctx.initStandardObjects();
|
||||
}
|
||||
|
||||
{
|
||||
// GOOD: enable the safe mode
|
||||
Scriptable scope = ctx.initSafeStandardObjects();
|
||||
}
|
||||
|
||||
{
|
||||
// GOOD: enforce a constraint on allowed classes
|
||||
Scriptable scope = ctx.initStandardObjects();
|
||||
ctx.setClassShutter(new ClassShutter() {
|
||||
public boolean visibleToScripts(String className) {
|
||||
return className.startsWith("com.example.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Object result = ctx.evaluateString(scope, code, "<code>", 1, null);
|
||||
response.getWriter().print(Context.toString(result));
|
||||
} catch(RhinoException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>The ScriptEngine API has been available since the release of Java 6.
|
||||
It allows applications to interact with scripts written in languages such as JavaScript.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Use "Cloudbees Rhino Sandbox" or sandboxing with SecurityManager or use <a href="https://www.graalvm.org/">graalvm</a> instead.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following code could execute random JavaScript code</p>
|
||||
<sample src="ScriptEngine.java" />
|
||||
<sample src="NashornScriptEngine.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
CERT coding standard: <a href="https://wiki.sei.cmu.edu/confluence/display/java/IDS52-J.+Prevent+code+injection">ScriptEngine code injection</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* @name ScriptEngine evaluation
|
||||
* @description Malicious Javascript code could cause arbitrary command execution at the OS level
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/unsafe-eval
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class ScriptEngineMethod extends Method {
|
||||
ScriptEngineMethod() {
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and
|
||||
this.hasName("eval")
|
||||
}
|
||||
}
|
||||
|
||||
predicate scriptEngine(MethodAccess ma, Expr sink) {
|
||||
exists(Method m | m = ma.getMethod() |
|
||||
m instanceof ScriptEngineMethod and
|
||||
sink = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
class ScriptEngineSink extends DataFlow::ExprNode {
|
||||
ScriptEngineSink() { scriptEngine(_, this.getExpr()) }
|
||||
|
||||
MethodAccess getMethodAccess() { scriptEngine(result, this.getExpr()) }
|
||||
}
|
||||
|
||||
class ScriptEngineConfiguration extends TaintTracking::Configuration {
|
||||
ScriptEngineConfiguration() { this = "ScriptEngineConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof RemoteFlowSource
|
||||
or
|
||||
source instanceof LocalUserInput
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptEngineSink }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptEngineConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode().(ScriptEngineSink).getMethodAccess(), source, sink, "ScriptEngine eval $@.",
|
||||
source.getNode(), "user input"
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>The Java Scripting API has been available since the release of Java 6. It allows
|
||||
applications to interact with scripts written in languages such as JavaScript. It serves
|
||||
as an embedded scripting engine inside Java applications which allows Java-to-JavaScript
|
||||
interoperability and provides a seamless integration between the two languages. If an
|
||||
expression is built using attacker-controlled data, and then evaluated in a powerful
|
||||
context, it may allow the attacker to run arbitrary code.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>In general, including user input in a Java Script Engine expression should be avoided.
|
||||
If user input must be included in the expression, it should be then evaluated in a safe
|
||||
context that doesn't allow arbitrary code invocation. Use "Cloudbees Rhino Sandbox" or
|
||||
sandboxing with SecurityManager, which will be deprecated in a future release, or use
|
||||
<a href="https://www.graalvm.org/">GraalVM</a> instead.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following code could execute user-supplied JavaScript code in <code>ScriptEngine</code></p>
|
||||
<sample src="ScriptEngine.java" />
|
||||
<sample src="NashornScriptEngine.java" />
|
||||
|
||||
<p>The following example shows two ways of using Rhino expression. In the 'BAD' case,
|
||||
an unsafe context is initialized with <code>initStandardObjects</code> that allows arbitrary
|
||||
Java code to be executed. In the 'GOOD' case, a safe context is initialized with
|
||||
<code>initSafeStandardObjects</code> or <code>setClassShutter</code>.</p>
|
||||
<sample src="RhinoInjection.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
CERT coding standard: <a href="https://wiki.sei.cmu.edu/confluence/display/java/IDS52-J.+Prevent+code+injection">ScriptEngine code injection</a>
|
||||
</li>
|
||||
<li>
|
||||
GraalVM: <a href="https://www.graalvm.org/reference-manual/js/NashornMigrationGuide/#secure-by-default">Secure by Default</a>
|
||||
</li>
|
||||
<li>
|
||||
Mozilla Rhino: <a href="https://github.com/mozilla/rhino">Rhino: JavaScript in Java</a>
|
||||
</li>
|
||||
<li>
|
||||
Rhino Sandbox: <a href="https://github.com/javadelight/delight-rhino-sandbox">A sandbox to execute JavaScript code with Rhino in Java</a>
|
||||
</li>
|
||||
<li>
|
||||
GuardRails: <a href="https://docs.guardrails.io/docs/en/vulnerabilities/java/insecure_use_of_dangerous_function#code-injection">Code Injection</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
146
java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql
Normal file
146
java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @name Injection in Java Script Engine
|
||||
* @description Evaluation of user-controlled data using the Java Script Engine may
|
||||
* lead to remote code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/unsafe-eval
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** A method of ScriptEngine that allows code injection. */
|
||||
class ScriptEngineMethod extends Method {
|
||||
ScriptEngineMethod() {
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and
|
||||
this.hasName("eval")
|
||||
or
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "Compilable") and
|
||||
this.hasName("compile")
|
||||
or
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory") and
|
||||
this.hasName(["getProgram", "getMethodCallSyntax"])
|
||||
}
|
||||
}
|
||||
|
||||
/** The context class `org.mozilla.javascript.Context` of Rhino Java Script Engine. */
|
||||
class RhinoContext extends RefType {
|
||||
RhinoContext() { this.hasQualifiedName("org.mozilla.javascript", "Context") }
|
||||
}
|
||||
|
||||
/** A method that evaluates a Rhino expression with `org.mozilla.javascript.Context`. */
|
||||
class RhinoEvaluateExpressionMethod extends Method {
|
||||
RhinoEvaluateExpressionMethod() {
|
||||
this.getDeclaringType().getAnAncestor*() instanceof RhinoContext and
|
||||
this.hasName([
|
||||
"evaluateString", "evaluateReader", "compileFunction", "compileReader", "compileString"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that compiles a Rhino expression with
|
||||
* `org.mozilla.javascript.optimizer.ClassCompiler`.
|
||||
*/
|
||||
class RhinoCompileClassMethod extends Method {
|
||||
RhinoCompileClassMethod() {
|
||||
this.getDeclaringType()
|
||||
.getASupertype*()
|
||||
.hasQualifiedName("org.mozilla.javascript.optimizer", "ClassCompiler") and
|
||||
this.hasName("compileToClassFiles")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that defines a Java class from a Rhino expression with
|
||||
* `org.mozilla.javascript.GeneratedClassLoader`.
|
||||
*/
|
||||
class RhinoDefineClassMethod extends Method {
|
||||
RhinoDefineClassMethod() {
|
||||
this.getDeclaringType()
|
||||
.getASupertype*()
|
||||
.hasQualifiedName("org.mozilla.javascript", "GeneratedClassLoader") and
|
||||
this.hasName("defineClass")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ma` is a call to a `ScriptEngineMethod` and `sink` is an argument that
|
||||
* will be executed.
|
||||
*/
|
||||
predicate isScriptArgument(MethodAccess ma, Expr sink) {
|
||||
exists(ScriptEngineMethod m |
|
||||
m = ma.getMethod() and
|
||||
if m.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory")
|
||||
then sink = ma.getArgument(_) // all arguments allow script injection
|
||||
else sink = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a Rhino expression evaluation method is vulnerable to code injection.
|
||||
*/
|
||||
predicate evaluatesRhinoExpression(MethodAccess ma, Expr sink) {
|
||||
exists(RhinoEvaluateExpressionMethod m | m = ma.getMethod() |
|
||||
(
|
||||
if ma.getMethod().getName() = "compileReader"
|
||||
then sink = ma.getArgument(0) // The first argument is the input reader
|
||||
else sink = ma.getArgument(1) // The second argument is the JavaScript or Java input
|
||||
) and
|
||||
not exists(MethodAccess ca |
|
||||
ca.getMethod().hasName(["initSafeStandardObjects", "setClassShutter"]) and // safe mode or `ClassShutter` constraint is enforced
|
||||
ma.getQualifier() = ca.getQualifier().(VarAccess).getVariable().getAnAccess()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a Rhino expression compilation method is vulnerable to code injection.
|
||||
*/
|
||||
predicate compilesScript(MethodAccess ma, Expr sink) {
|
||||
exists(RhinoCompileClassMethod m | m = ma.getMethod() | sink = ma.getArgument(0))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a Rhino class loading method is vulnerable to code injection.
|
||||
*/
|
||||
predicate definesRhinoClass(MethodAccess ma, Expr sink) {
|
||||
exists(RhinoDefineClassMethod m | m = ma.getMethod() | sink = ma.getArgument(1))
|
||||
}
|
||||
|
||||
/** A script injection sink. */
|
||||
class ScriptInjectionSink extends DataFlow::ExprNode {
|
||||
MethodAccess methodAccess;
|
||||
|
||||
ScriptInjectionSink() {
|
||||
isScriptArgument(methodAccess, this.getExpr()) or
|
||||
evaluatesRhinoExpression(methodAccess, this.getExpr()) or
|
||||
compilesScript(methodAccess, this.getExpr()) or
|
||||
definesRhinoClass(methodAccess, this.getExpr())
|
||||
}
|
||||
|
||||
/** An access to the method associated with this sink. */
|
||||
MethodAccess getMethodAccess() { result = methodAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint tracking configuration that tracks flow from `RemoteFlowSource` to an argument
|
||||
* of a method call that executes injected script.
|
||||
*/
|
||||
class ScriptInjectionConfiguration extends TaintTracking::Configuration {
|
||||
ScriptInjectionConfiguration() { this = "ScriptInjectionConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptInjectionSink }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptInjectionConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode().(ScriptInjectionSink).getMethodAccess(), source, sink,
|
||||
"Java Script Engine evaluate $@.", source.getNode(), "user input"
|
||||
@@ -0,0 +1,9 @@
|
||||
public void bindRemoteObject(Registry registry, int port) throws Exception {
|
||||
ObjectInputFilter filter = info -> {
|
||||
if (info.serialClass().getCanonicalName().startsWith("com.safe.package.")) {
|
||||
return ObjectInputFilter.Status.ALLOWED;
|
||||
}
|
||||
return ObjectInputFilter.Status.REJECTED;
|
||||
};
|
||||
registry.bind("safer", UnicastRemoteObject.exportObject(new RemoteObjectImpl(), port, filter));
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
public class Server {
|
||||
public void bindRemoteObject(Registry registry) throws Exception {
|
||||
registry.bind("safe", new RemoteObjectImpl());
|
||||
}
|
||||
}
|
||||
|
||||
interface RemoteObject extends Remote {
|
||||
void calculate(int a, double b) throws RemoteException;
|
||||
void save(String s) throws RemoteException;
|
||||
}
|
||||
|
||||
class RemoteObjectImpl implements RemoteObject {
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
public class Server {
|
||||
public void bindRemoteObject(Registry registry) throws Exception {
|
||||
registry.bind("unsafe", new RemoteObjectImpl());
|
||||
}
|
||||
}
|
||||
|
||||
interface RemoteObject extends Remote {
|
||||
void action(Object obj) throws RemoteException;
|
||||
}
|
||||
|
||||
class RemoteObjectImpl implements RemoteObject {
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Java RMI uses the default Java serialization mechanism (in other words, <code>ObjectInputStream</code>)
|
||||
to pass parameters in remote method invocations. This mechanism is known to be unsafe when deserializing
|
||||
untrusted data. If a registered remote object has a method that accepts a complex object,
|
||||
an attacker can take advantage of the unsafe deserialization mechanism.
|
||||
In the worst case, it results in remote code execution.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Use only strings and primitive types for parameters of remotely invokable methods.
|
||||
</p>
|
||||
<p>
|
||||
Set a filter for incoming serialized data by wrapping remote objects using either <code>UnicastRemoteObject.exportObject(Remote, int, ObjectInputFilter)</code>
|
||||
or <code>UnicastRemoteObject.exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory, ObjectInputFilter)</code> methods.
|
||||
Those methods accept an <code>ObjectInputFilter</code> that decides which classes are allowed for deserialization.
|
||||
The filter should allow deserializing only safe classes.
|
||||
</p>
|
||||
<p>
|
||||
It is also possible to set a process-wide deserialization filter.
|
||||
The filter can be set by with <code>ObjectInputFilter.Config.setSerialFilter(ObjectInputFilter)</code> method,
|
||||
or by setting system or security property <code>jdk.serialFilter</code>.
|
||||
Make sure that you use the latest Java versions that include JEP 290.
|
||||
Please note that the query is not sensitive to this mitigation.
|
||||
</p>
|
||||
<p>
|
||||
If switching to the latest Java versions is not possible,
|
||||
consider using other implementations of remote procedure calls. For example, HTTP API with JSON.
|
||||
Make sure that the underlying deserialization mechanism is properly configured
|
||||
so that deserialization attacks are not possible.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following code registers a remote object
|
||||
with a vulnerable method that accepts a complex object:
|
||||
</p>
|
||||
<sample src="RmiUnsafeRemoteObject.java" />
|
||||
|
||||
<p>
|
||||
The next example registers a safe remote object
|
||||
whose methods use only primitive types and strings:
|
||||
</p>
|
||||
<sample src="RmiSafeRemoteObject.java" />
|
||||
|
||||
<p>
|
||||
The next example shows how to set a deserilization filter for a remote object:
|
||||
</p>
|
||||
<sample src="RmiRemoteObjectWithFilter.java" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Oracle:
|
||||
<a href="https://www.oracle.com/java/technologies/javase/remote-method-invocation-home.html">Remote Method Invocation (RMI)</a>.
|
||||
</li>
|
||||
<li>
|
||||
ITNEXT:
|
||||
<a href="https://itnext.io/java-rmi-for-pentesters-part-two-reconnaissance-attack-against-non-jmx-registries-187a6561314d">Java RMI for pentesters part two - reconnaissance & attack against non-JMX registries</a>.
|
||||
</li>
|
||||
<li>
|
||||
MOGWAI LABS:
|
||||
<a href="https://mogwailabs.de/en/blog/2019/03/attacking-java-rmi-services-after-jep-290">Attacking Java RMI services after JEP 290</a>
|
||||
</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Deserialization_of_untrusted_data">Deserialization of untrusted data</a>.
|
||||
</li>
|
||||
<li>
|
||||
OpenJDK:
|
||||
<a href="https://openjdk.java.net/jeps/290">JEP 290: Filter Incoming Serialization Data</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @name Unsafe deserialization in a remotely callable method.
|
||||
* @description If a registered remote object has a method that accepts a complex object,
|
||||
* an attacker can take advantage of the unsafe deserialization mechanism
|
||||
* which is used to pass parameters in RMI.
|
||||
* In the worst case, it results in remote code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/unsafe-deserialization-rmi
|
||||
* @tags security
|
||||
* external/cwe/cwe-502
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.frameworks.Rmi
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A method that binds a name to a remote object.
|
||||
*/
|
||||
private class BindMethod extends Method {
|
||||
BindMethod() {
|
||||
(
|
||||
getDeclaringType().hasQualifiedName("java.rmi", "Naming") or
|
||||
getDeclaringType().hasQualifiedName("java.rmi.registry", "Registry")
|
||||
) and
|
||||
hasName(["bind", "rebind"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `type` has an vulnerable remote method.
|
||||
*/
|
||||
private predicate hasVulnerableMethod(RefType type) {
|
||||
exists(RemoteCallableMethod m, Type parameterType |
|
||||
m.getDeclaringType() = type and parameterType = m.getAParamType()
|
||||
|
|
||||
not parameterType instanceof PrimitiveType and
|
||||
not parameterType instanceof TypeString and
|
||||
not parameterType.(RefType).hasQualifiedName("java.io", "ObjectInputStream")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe remote objects
|
||||
* that are vulnerable to deserialization attacks.
|
||||
*/
|
||||
private class BindingUnsafeRemoteObjectConfig extends TaintTracking::Configuration {
|
||||
BindingUnsafeRemoteObjectConfig() { this = "BindingUnsafeRemoteObjectConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(ConstructorCall cc | cc = source.asExpr() |
|
||||
hasVulnerableMethod(cc.getConstructedType().getASupertype*())
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma | ma.getArgument(1) = sink.asExpr() |
|
||||
ma.getMethod() instanceof BindMethod
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m.getDeclaringType().hasQualifiedName("java.rmi.server", "UnicastRemoteObject") and
|
||||
m.hasName("exportObject") and
|
||||
not m.getParameterType([2, 4]).(RefType).hasQualifiedName("java.io", "ObjectInputFilter") and
|
||||
ma.getArgument(0) = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, BindingUnsafeRemoteObjectConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Unsafe deserialization in a remote object."
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user