Skip to content

Commit 0d18e4f

Browse files
committed
BeanShell Injection
1 parent 0545bcf commit 0d18e4f

File tree

17 files changed

+435
-0
lines changed

17 files changed

+435
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import bsh.Interpreter;
2+
import javax.servlet.http.HttpServletRequest;
3+
import org.springframework.scripting.bsh.BshScriptEvaluator;
4+
import org.springframework.scripting.support.StaticScriptSource;
5+
import org.springframework.stereotype.Controller;
6+
import org.springframework.web.bind.annotation.GetMapping;
7+
8+
@Controller
9+
public class BeanShellInjection {
10+
11+
@GetMapping(value = "bad1")
12+
public void bad1(HttpServletRequest request) {
13+
String code = request.getParameter("code");
14+
BshScriptEvaluator evaluator = new BshScriptEvaluator();
15+
evaluator.evaluate(new StaticScriptSource(code)); //bad
16+
}
17+
18+
@GetMapping(value = "bad2")
19+
public void bad2(HttpServletRequest request) throws Exception {
20+
String code = request.getParameter("code");
21+
Interpreter interpreter = new Interpreter();
22+
interpreter.eval(code); //bad
23+
}
24+
25+
@GetMapping(value = "bad3")
26+
public void bad3(HttpServletRequest request) {
27+
String code = request.getParameter("code");
28+
StaticScriptSource staticScriptSource = new StaticScriptSource("test");
29+
staticScriptSource.setScript(code);
30+
BshScriptEvaluator evaluator = new BshScriptEvaluator();
31+
evaluator.evaluate(staticScriptSource); //bad
32+
}
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
4+
<overview>
5+
<p>
6+
BeanShell is a small, free, embeddable Java source interpreter with object scripting language
7+
features, written in Java. BeanShell dynamically executes standard Java syntax and extends it
8+
with common scripting conveniences such as loose types, commands, and method closures like
9+
those in Perl and JavaScript. If a BeanShell expression is built using attacker-controlled data,
10+
and then evaluated, then it may allow the attacker to run arbitrary code.
11+
</p>
12+
</overview>
13+
14+
<recommendation>
15+
<p>
16+
It is generally recommended to avoid using untrusted input in a BeanShell expression.
17+
If it is not possible, BeanShell expressions should be run in a sandbox that allows accessing only
18+
explicitly allowed classes.
19+
</p>
20+
</recommendation>
21+
22+
<example>
23+
<p>
24+
The following example uses untrusted data to build and run a BeanShell expression.
25+
</p>
26+
<sample src="BeanShellInjection.java" />
27+
28+
<references>
29+
<li>
30+
CVE-2016-2510:<a href="https://nvd.nist.gov/vuln/detail/CVE-2016-2510">BeanShell Injection</a>.
31+
</li>
32+
</references>
33+
</qhelp>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @name BeanShell injection
3+
* @description Evaluation of a user-controlled BeanShell expression
4+
* may lead to arbitrary code execution.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @precision high
8+
* @id java/beanshell-injection
9+
* @tags security
10+
* external/cwe/cwe-094
11+
*/
12+
13+
import java
14+
import BeanShellInjection
15+
import semmle.code.java.dataflow.FlowSources
16+
import DataFlow::PathGraph
17+
18+
class BeanShellInjectionConfig extends TaintTracking::Configuration {
19+
BeanShellInjectionConfig() { this = "BeanShellInjectionConfig" }
20+
21+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
22+
23+
override predicate isSink(DataFlow::Node sink) { sink instanceof BeanShellInjectionSink }
24+
25+
override predicate isAdditionalTaintStep(DataFlow::Node prod, DataFlow::Node succ) {
26+
exists(ClassInstanceExpr cie |
27+
cie.getConstructedType()
28+
.hasQualifiedName("org.springframework.scripting.support", "StaticScriptSource") and
29+
cie.getArgument(0) = prod.asExpr() and
30+
cie = succ.asExpr()
31+
)
32+
or
33+
exists(MethodAccess ma |
34+
ma.getMethod().hasName("setScript") and
35+
ma.getMethod()
36+
.getDeclaringType()
37+
.hasQualifiedName("org.springframework.scripting.support", "StaticScriptSource") and
38+
ma.getArgument(0) = prod.asExpr() and
39+
ma.getQualifier() = succ.asExpr()
40+
)
41+
}
42+
}
43+
44+
from DataFlow::PathNode source, DataFlow::PathNode sink, BeanShellInjectionConfig conf
45+
where conf.hasFlowPath(source, sink)
46+
select sink.getNode(), source, sink, "BeanShell injection from $@.", source.getNode(),
47+
"this user input"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import java
2+
import semmle.code.java.dataflow.FlowSources
3+
4+
/** A call to `Interpreter.eval`. */
5+
class InterpreterEvalCall extends MethodAccess {
6+
InterpreterEvalCall() {
7+
this.getMethod().hasName("eval") and
8+
this.getMethod().getDeclaringType().hasQualifiedName("bsh", "Interpreter")
9+
}
10+
}
11+
12+
/** A call to `BshScriptEvaluator.evaluate`. */
13+
class BshScriptEvaluatorEvaluateCall extends MethodAccess {
14+
BshScriptEvaluatorEvaluateCall() {
15+
this.getMethod().hasName("evaluate") and
16+
this.getMethod()
17+
.getDeclaringType()
18+
.hasQualifiedName("org.springframework.scripting.bsh", "BshScriptEvaluator")
19+
}
20+
}
21+
22+
/** A sink for BeanShell expression injection vulnerabilities. */
23+
class BeanShellInjectionSink extends DataFlow::Node {
24+
BeanShellInjectionSink() {
25+
this.asExpr() = any(InterpreterEvalCall iec).getArgument(0) or
26+
this.asExpr() = any(BshScriptEvaluatorEvaluateCall bseec).getArgument(0)
27+
}
28+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
edges
2+
| BeanShellInjection.java:13:17:13:44 | getParameter(...) : String | BeanShellInjection.java:15:22:15:49 | new StaticScriptSource(...) |
3+
| BeanShellInjection.java:20:17:20:44 | getParameter(...) : String | BeanShellInjection.java:22:20:22:23 | code |
4+
| BeanShellInjection.java:27:17:27:44 | getParameter(...) : String | BeanShellInjection.java:31:22:31:39 | staticScriptSource |
5+
nodes
6+
| BeanShellInjection.java:13:17:13:44 | getParameter(...) : String | semmle.label | getParameter(...) : String |
7+
| BeanShellInjection.java:15:22:15:49 | new StaticScriptSource(...) | semmle.label | new StaticScriptSource(...) |
8+
| BeanShellInjection.java:20:17:20:44 | getParameter(...) : String | semmle.label | getParameter(...) : String |
9+
| BeanShellInjection.java:22:20:22:23 | code | semmle.label | code |
10+
| BeanShellInjection.java:27:17:27:44 | getParameter(...) : String | semmle.label | getParameter(...) : String |
11+
| BeanShellInjection.java:31:22:31:39 | staticScriptSource | semmle.label | staticScriptSource |
12+
#select
13+
| BeanShellInjection.java:15:22:15:49 | new StaticScriptSource(...) | BeanShellInjection.java:13:17:13:44 | getParameter(...) : String | BeanShellInjection.java:15:22:15:49 | new StaticScriptSource(...) | BeanShell injection from $@. | BeanShellInjection.java:13:17:13:44 | getParameter(...) | this user input |
14+
| BeanShellInjection.java:22:20:22:23 | code | BeanShellInjection.java:20:17:20:44 | getParameter(...) : String | BeanShellInjection.java:22:20:22:23 | code | BeanShell injection from $@. | BeanShellInjection.java:20:17:20:44 | getParameter(...) | this user input |
15+
| BeanShellInjection.java:31:22:31:39 | staticScriptSource | BeanShellInjection.java:27:17:27:44 | getParameter(...) : String | BeanShellInjection.java:31:22:31:39 | staticScriptSource | BeanShell injection from $@. | BeanShellInjection.java:27:17:27:44 | getParameter(...) | this user input |
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import bsh.Interpreter;
2+
import javax.servlet.http.HttpServletRequest;
3+
import org.springframework.scripting.bsh.BshScriptEvaluator;
4+
import org.springframework.scripting.support.StaticScriptSource;
5+
import org.springframework.stereotype.Controller;
6+
import org.springframework.web.bind.annotation.GetMapping;
7+
8+
@Controller
9+
public class BeanShellInjection {
10+
11+
@GetMapping(value = "bad1")
12+
public void bad1(HttpServletRequest request) {
13+
String code = request.getParameter("code");
14+
BshScriptEvaluator evaluator = new BshScriptEvaluator();
15+
evaluator.evaluate(new StaticScriptSource(code)); //bad
16+
}
17+
18+
@GetMapping(value = "bad2")
19+
public void bad2(HttpServletRequest request) throws Exception {
20+
String code = request.getParameter("code");
21+
Interpreter interpreter = new Interpreter();
22+
interpreter.eval(code); //bad
23+
}
24+
25+
@GetMapping(value = "bad3")
26+
public void bad3(HttpServletRequest request) {
27+
String code = request.getParameter("code");
28+
StaticScriptSource staticScriptSource = new StaticScriptSource("test");
29+
staticScriptSource.setScript(code);
30+
BshScriptEvaluator evaluator = new BshScriptEvaluator();
31+
evaluator.evaluate(staticScriptSource); //bad
32+
}
33+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-094/BeanShellInjection.ql
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package bsh;
2+
3+
import java.io.PrintStream;
4+
import java.io.Reader;
5+
6+
public interface ConsoleInterface {
7+
Reader getIn();
8+
9+
PrintStream getOut();
10+
11+
PrintStream getErr();
12+
13+
void println(Object var1);
14+
15+
void print(Object var1);
16+
17+
void error(Object var1);
18+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package bsh;
2+
3+
public class EvalError extends Exception {
4+
5+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package bsh;
2+
3+
import java.io.BufferedReader;
4+
import java.io.File;
5+
import java.io.FileNotFoundException;
6+
import java.io.FileOutputStream;
7+
import java.io.FileReader;
8+
import java.io.FilterInputStream;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
import java.io.InputStreamReader;
12+
import java.io.ObjectInputStream;
13+
import java.io.PrintStream;
14+
import java.io.Reader;
15+
import java.io.Serializable;
16+
import java.io.StringReader;
17+
import java.lang.reflect.InvocationTargetException;
18+
import java.lang.reflect.Method;
19+
20+
public class Interpreter implements Runnable, ConsoleInterface, Serializable {
21+
22+
public Interpreter(Reader in, PrintStream out, PrintStream err, boolean interactive, NameSpace namespace, Interpreter parent, String sourceFileInfo) { }
23+
24+
public Interpreter(Reader in, PrintStream out, PrintStream err, boolean interactive, NameSpace namespace) { }
25+
26+
public Interpreter(Reader in, PrintStream out, PrintStream err, boolean interactive) { }
27+
28+
public Interpreter(ConsoleInterface console, NameSpace globalNameSpace) { }
29+
30+
public Interpreter(ConsoleInterface console) { }
31+
32+
public Interpreter() { }
33+
34+
public void setConsole(ConsoleInterface console) { }
35+
36+
private void initRootSystemObject() { }
37+
38+
public void setNameSpace(NameSpace globalNameSpace) { }
39+
40+
public NameSpace getNameSpace() {
41+
return null;
42+
}
43+
44+
public static void main(String[] args) { }
45+
46+
public static void invokeMain(Class clas, String[] args) throws Exception { }
47+
48+
public void run() { }
49+
50+
public Object source(String filename, NameSpace nameSpace) throws FileNotFoundException, IOException, EvalError {
51+
return null;
52+
}
53+
54+
public Object source(String filename) throws FileNotFoundException, IOException, EvalError {
55+
return null;
56+
}
57+
58+
public Object eval(Reader in, NameSpace nameSpace, String sourceFileInfo) throws EvalError {
59+
return null;
60+
}
61+
62+
public Object eval(Reader in) throws EvalError {
63+
return null;
64+
}
65+
66+
public Object eval(String statements) throws EvalError {
67+
return null;
68+
}
69+
70+
public Object eval(String statements, NameSpace nameSpace) throws EvalError {
71+
return null;
72+
}
73+
74+
private String showEvalString(String s) {
75+
return null;
76+
}
77+
78+
public final void error(Object o) { }
79+
80+
public Reader getIn() {
81+
return null;
82+
}
83+
84+
public PrintStream getOut() {
85+
return null;
86+
}
87+
88+
public PrintStream getErr() {
89+
return null;
90+
}
91+
92+
public final void println(Object o) { }
93+
94+
public final void print(Object o) { }
95+
96+
public static final void debug(String s) { }
97+
98+
public Object get(String name) throws EvalError {
99+
return null;
100+
}
101+
102+
Object getu(String name) {
103+
return null;
104+
}
105+
106+
public void set(String name, Object value) throws EvalError { }
107+
108+
void setu(String name, Object value) { }
109+
110+
public void set(String name, long value) throws EvalError { }
111+
112+
public void set(String name, int value) throws EvalError { }
113+
114+
public void set(String name, double value) throws EvalError { }
115+
116+
public void set(String name, float value) throws EvalError { }
117+
118+
public void set(String name, boolean value) throws EvalError { }
119+
120+
public void unset(String name) throws EvalError { }
121+
122+
public Object getInterface(Class interf) throws EvalError {
123+
return null;
124+
}
125+
}

0 commit comments

Comments
 (0)