mirror of
https://github.com/github/codeql.git
synced 2025-12-20 18:56:32 +01:00
Merge pull request #5710 from p0wn4j/jsch-os-injection
[Java] CWE-078: Add JSch lib OS Command Injection sink
This commit is contained in:
32
java/ql/src/experimental/Security/CWE/CWE-078/ExecCommon.qll
Normal file
32
java/ql/src/experimental/Security/CWE/CWE-078/ExecCommon.qll
Normal file
@@ -0,0 +1,32 @@
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.ExternalProcess
|
||||
import semmle.code.java.security.CommandArguments
|
||||
|
||||
private class RemoteUserInputToArgumentToExecFlowConfig extends TaintTracking::Configuration {
|
||||
RemoteUserInputToArgumentToExecFlowConfig() {
|
||||
this = "ExecCommon::RemoteUserInputToArgumentToExecFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ArgumentToExec }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType
|
||||
or
|
||||
node.getType() instanceof BoxedType
|
||||
or
|
||||
isSafeCommandArgument(node.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of `ExecTainted.ql`. It is extracted to a QLL
|
||||
* so that it can be excluded from `ExecUnescaped.ql` to avoid
|
||||
* reporting overlapping results.
|
||||
*/
|
||||
predicate execTainted(DataFlow::PathNode source, DataFlow::PathNode sink, ArgumentToExec execArg) {
|
||||
exists(RemoteUserInputToArgumentToExecFlowConfig conf |
|
||||
conf.hasFlowPath(source, sink) and sink.getNode() = DataFlow::exprNode(execArg)
|
||||
)
|
||||
}
|
||||
24
java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql
Normal file
24
java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @name Uncontrolled command line
|
||||
* @description Using externally controlled strings in a command line is vulnerable to malicious
|
||||
* changes in the strings.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/command-line-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-078
|
||||
* external/cwe/cwe-088
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.ExternalProcess
|
||||
import ExecCommon
|
||||
import JSchOSInjection
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, ArgumentToExec execArg
|
||||
where execTainted(source, sink, execArg)
|
||||
select execArg, source, sink, "$@ flows to here and is used in a command.", source.getNode(),
|
||||
"User-provided value"
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Provides classes for JSch OS command injection detection
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/** The class `com.jcraft.jsch.ChannelExec`. */
|
||||
private class JSchChannelExec extends RefType {
|
||||
JSchChannelExec() { this.hasQualifiedName("com.jcraft.jsch", "ChannelExec") }
|
||||
}
|
||||
|
||||
/** A method to set an OS Command for the execution. */
|
||||
private class ChannelExecSetCommandMethod extends Method, ExecCallable {
|
||||
ChannelExecSetCommandMethod() {
|
||||
this.hasName("setCommand") and
|
||||
this.getDeclaringType() instanceof JSchChannelExec
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
public class JSchOSInjectionBad {
|
||||
void jschOsExecution(HttpServletRequest request) {
|
||||
String command = request.getParameter("command");
|
||||
|
||||
JSch jsch = new JSch();
|
||||
Session session = jsch.getSession("user", "sshHost", 22);
|
||||
session.setPassword("password");
|
||||
session.connect();
|
||||
|
||||
Channel channel = session.openChannel("exec");
|
||||
// BAD - untrusted user data is used directly in a command
|
||||
((ChannelExec) channel).setCommand("ping " + command);
|
||||
|
||||
channel.connect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
public class JSchOSInjectionSanitized {
|
||||
void jschOsExecutionPing(HttpServletRequest request) {
|
||||
String untrusted = request.getParameter("command");
|
||||
|
||||
//GOOD - Validate user the input.
|
||||
if (!com.google.common.net.InetAddresses.isInetAddress(untrusted)) {
|
||||
System.out.println("Invalid IP address");
|
||||
return;
|
||||
}
|
||||
|
||||
JSch jsch = new JSch();
|
||||
Session session = jsch.getSession("user", "host", 22);
|
||||
session.setPassword("password");
|
||||
session.connect();
|
||||
|
||||
Channel channel = session.openChannel("exec");
|
||||
((ChannelExec) channel).setCommand("ping " + untrusted);
|
||||
|
||||
channel.connect();
|
||||
}
|
||||
|
||||
void jschOsExecutionDig(HttpServletRequest request) {
|
||||
String untrusted = request.getParameter("command");
|
||||
|
||||
//GOOD - check whether the user input doesn't contain dangerous shell characters.
|
||||
String[] badChars = new String[] {"^", "~" ," " , "&", "|", ";", "$", ">", "<", "`", "\\", ",", "!", "{", "}", "(", ")", "@", "%", "#", "%0A", "%0a", "\n", "\r\n"};
|
||||
|
||||
for (String badChar : badChars) {
|
||||
if (untrusted.contains(badChar)) {
|
||||
System.out.println("Invalid host");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JSch jsch = new JSch();
|
||||
Session session = jsch.getSession("user", "host", 22);
|
||||
session.setPassword("password");
|
||||
session.connect();
|
||||
|
||||
Channel channel = session.openChannel("exec");
|
||||
((ChannelExec) channel).setCommand("dig " + untrusted);
|
||||
|
||||
channel.connect();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user