Jshell Injection

This commit is contained in:
haby0
2021-05-26 08:58:29 +08:00
parent 081fd28090
commit 921b8e80a2
7 changed files with 152 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
import javax.servlet.http.HttpServletRequest;
import jdk.jshell.JShell;
import jdk.jshell.SourceCodeAnalysis;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class JShellInjection {
@GetMapping(value = "bad1")
public void bad1(HttpServletRequest request) {
String input = request.getParameter("code");
JShell jShell = JShell.builder().build();
// BAD: allow execution of arbitrary Java code
jShell.eval(input);
}
@GetMapping(value = "bad2")
public void bad2(HttpServletRequest request) {
String input = request.getParameter("code");
JShell jShell = JShell.builder().build();
SourceCodeAnalysis sourceCodeAnalysis = jShell.sourceCodeAnalysis();
// BAD: allow execution of arbitrary Java code
sourceCodeAnalysis.wrappers(input);
}
}

View File

@@ -0,0 +1,31 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The Java Shell tool (JShell) is an interactive tool for learning the Java programming
language and prototyping Java code. JShell is a Read-Evaluate-Print Loop (REPL), which
evaluates declarations, statements, and expressions as they are entered and immediately
shows the results. 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>It is generally recommended to avoid using untrusted input in a JShell expression.
If it is not possible,JShell expressions should be run in a sandbox that allows accessing only
explicitly allowed classes.</p>
</recommendation>
<example>
<p>The following example calls <code>JShell.eval(...)</code> or <code>SourceCodeAnalysis.wrappers(...)</code>
to execute untrusted data.</p>
<sample src="JShellInjection.java" />
</example>
<references>
<li>
Java 9 jshell tutorial: <a href="https://examples.javacodegeeks.com/core-java/java-9-jshell-tutorial/">JShell introduction</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,29 @@
/**
* @name JShell injection
* @description Evaluation of a user-controlled JShell expression
* may lead to arbitrary code execution.
* @kind path-problem
* @problem.severity error
* @precision high
* @id java/jshell-injection
* @tags security
* external/cwe-094
*/
import java
import JShellInjection
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
class JShellInjectionConfiguration extends TaintTracking::Configuration {
JShellInjectionConfiguration() { this = "JShellInjectionConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof JShellInjectionSink }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, JShellInjectionConfiguration conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "JShell injection from $@.", source.getNode(),
"this user input"

View File

@@ -0,0 +1,28 @@
import java
import semmle.code.java.dataflow.FlowSources
/** A sink for JShell expression injection vulnerabilities. */
class JShellInjectionSink extends DataFlow::Node {
JShellInjectionSink() {
this.asExpr() = any(JShellEvalCall jsec).getArgument(0) or
this.asExpr() = any(SourceCodeAnalysisWrappersCall scawc).getArgument(0)
}
}
/** A call to `JShell.eval`. */
class JShellEvalCall extends MethodAccess {
JShellEvalCall() {
this.getMethod().hasName("eval") and
this.getMethod().getDeclaringType().hasQualifiedName("jdk.jshell", "JShell") and
this.getMethod().getNumberOfParameters() = 1
}
}
/** A call to `SourceCodeAnalysis.wrappers`. */
class SourceCodeAnalysisWrappersCall extends MethodAccess {
SourceCodeAnalysisWrappersCall() {
this.getMethod().hasName("wrappers") and
this.getMethod().getDeclaringType().hasQualifiedName("jdk.jshell", "SourceCodeAnalysis") and
this.getMethod().getNumberOfParameters() = 1
}
}

View File

@@ -0,0 +1,11 @@
edges
| JShellInjection.java:12:18:12:45 | getParameter(...) : String | JShellInjection.java:15:15:15:19 | input |
| JShellInjection.java:20:18:20:45 | getParameter(...) : String | JShellInjection.java:24:31:24:35 | input |
nodes
| JShellInjection.java:12:18:12:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| JShellInjection.java:15:15:15:19 | input | semmle.label | input |
| JShellInjection.java:20:18:20:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| JShellInjection.java:24:31:24:35 | input | semmle.label | input |
#select
| JShellInjection.java:15:15:15:19 | input | JShellInjection.java:12:18:12:45 | getParameter(...) : String | JShellInjection.java:15:15:15:19 | input | JShell injection from $@. | JShellInjection.java:12:18:12:45 | getParameter(...) | this user input |
| JShellInjection.java:24:31:24:35 | input | JShellInjection.java:20:18:20:45 | getParameter(...) : String | JShellInjection.java:24:31:24:35 | input | JShell injection from $@. | JShellInjection.java:20:18:20:45 | getParameter(...) | this user input |

View File

@@ -0,0 +1,26 @@
import javax.servlet.http.HttpServletRequest;
import jdk.jshell.JShell;
import jdk.jshell.SourceCodeAnalysis;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class JShellInjection {
@GetMapping(value = "bad1")
public void bad1(HttpServletRequest request) {
String input = request.getParameter("code");
JShell jShell = JShell.builder().build();
// BAD: allow execution of arbitrary Java code
jShell.eval(input);
}
@GetMapping(value = "bad2")
public void bad2(HttpServletRequest request) {
String input = request.getParameter("code");
JShell jShell = JShell.builder().build();
SourceCodeAnalysis sourceCodeAnalysis = jShell.sourceCodeAnalysis();
// BAD: allow execution of arbitrary Java code
sourceCodeAnalysis.wrappers(input);
}
}

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-094/JShellInjection.ql