Convert all command injection sinks to MaD format

This commit is contained in:
Tony Torralba
2023-04-19 17:09:24 +02:00
parent f5070bb082
commit a276cc3094
13 changed files with 81 additions and 127 deletions

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* The `ExecCallable` class in `ExternalProcess.qll` has been deprecated.

View File

@@ -0,0 +1,6 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: experimentalSinkModel
data:
- ["com.jcraft.jsch", "ChannelExec", True, "setCommand", "", "", "Argument[0]", "command-injection", "manual", "jsch-os-injection"]

View File

@@ -8,19 +8,20 @@ extensions:
- ["java.lang", "ClassLoader", True, "getSystemResource", "(String)", "", "Argument[0]", "read-file", "ai-manual"]
- ["java.lang", "ClassLoader", True, "getSystemResourceAsStream", "(String)", "", "Argument[0]", "read-file", "ai-manual"]
- ["java.lang", "Module", True, "getResourceAsStream", "(String)", "", "Argument[0]", "read-file", "ai-manual"]
# These are modeled in plain CodeQL. TODO: migrate them.
# - ["java.lang", "ProcessBuilder", False, "command", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"]
# - ["java.lang", "ProcessBuilder", False, "directory", "(File)", "", "Argument[0]", "command-injection", "ai-manual"]
# - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(List)", "", "Argument[0]", "command-injection", "ai-manual"]
# - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"]
# - ["java.lang", "Runtime", True, "exec", "(String,String[])", "", "Argument[0]", "command-injection", "ai-manual"]
# - ["java.lang", "Runtime", True, "exec", "(String[],String[])", "", "Argument[0]", "command-injection", "ai-manual"]
# - ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[0]", "command-injection", "ai-manual"]
# - ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[2]", "command-injection", "ai-manual"]
# - ["java.lang", "Runtime", True, "exec", "(String)", "", "Argument[0]", "command-injection", "ai-manual"]
# - ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[0]", "command-injection", "ai-manual"]
# - ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[2]", "command-injection", "ai-manual"]
# - ["java.lang", "Runtime", True, "exec", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "ProcessBuilder", False, "command", "(List)", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "ProcessBuilder", False, "command", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "ProcessBuilder", False, "command", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "ProcessBuilder", False, "directory", "(File)", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(List)", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String)", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String[],String[])", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[2]", "command-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String,String[])", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[2]", "command-injection", "ai-manual"]
- ["java.lang", "String", False, "matches", "(String)", "", "Argument[0]", "regex-use[f-1]", "manual"]
- ["java.lang", "String", False, "replaceAll", "(String,String)", "", "Argument[0]", "regex-use[-1]", "manual"]
- ["java.lang", "String", False, "replaceFirst", "(String,String)", "", "Argument[0]", "regex-use[-1]", "manual"]

View File

@@ -0,0 +1,9 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: sinkModel
data:
- ["org.apache.commons.exec", "CommandLine", True, "parse", "(String)", "", "Argument[0]", "command-injection", "manual"]
- ["org.apache.commons.exec", "CommandLine", True, "parse", "(String,Map)", "", "Argument[0]", "command-injection", "manual"]
- ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String)", "", "Argument[0]", "command-injection", "manual"]
- ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String,boolean)", "", "Argument[0]", "command-injection", "manual"]

View File

@@ -3,7 +3,6 @@
*/
import Member
import semmle.code.java.security.ExternalProcess
private import semmle.code.java.dataflow.FlowSteps
// --- Standard types ---
@@ -198,39 +197,6 @@ class TypeFile extends Class {
}
// --- Standard methods ---
/**
* Any constructor of class `java.lang.ProcessBuilder`.
*/
class ProcessBuilderConstructor extends Constructor, ExecCallable {
ProcessBuilderConstructor() { this.getDeclaringType() instanceof TypeProcessBuilder }
override int getAnExecutedArgument() { result = 0 }
}
/**
* Any of the methods named `command` on class `java.lang.ProcessBuilder`.
*/
class MethodProcessBuilderCommand extends Method, ExecCallable {
MethodProcessBuilderCommand() {
this.hasName("command") and
this.getDeclaringType() instanceof TypeProcessBuilder
}
override int getAnExecutedArgument() { result = 0 }
}
/**
* Any method named `exec` on class `java.lang.Runtime`.
*/
class MethodRuntimeExec extends Method, ExecCallable {
MethodRuntimeExec() {
this.hasName("exec") and
this.getDeclaringType() instanceof TypeRuntime
}
override int getAnExecutedArgument() { result = 0 }
}
/**
* Any method named `getenv` on class `java.lang.System`.
*/

View File

@@ -1,29 +0,0 @@
/** Definitions related to the Apache Commons Exec library. */
import semmle.code.java.Type
import semmle.code.java.security.ExternalProcess
/** The class `org.apache.commons.exec.CommandLine`. */
private class TypeCommandLine extends Class {
TypeCommandLine() { this.hasQualifiedName("org.apache.commons.exec", "CommandLine") }
}
/** The `parse()` method of the class `org.apache.commons.exec.CommandLine`. */
private class MethodCommandLineParse extends Method, ExecCallable {
MethodCommandLineParse() {
this.getDeclaringType() instanceof TypeCommandLine and
this.hasName("parse")
}
override int getAnExecutedArgument() { result = 0 }
}
/** The `addArguments()` method of the class `org.apache.commons.exec.CommandLine`. */
private class MethodCommandLineAddArguments extends Method, ExecCallable {
MethodCommandLineAddArguments() {
this.getDeclaringType() instanceof TypeCommandLine and
this.hasName("addArguments")
}
override int getAnExecutedArgument() { result = 0 }
}

View File

@@ -10,8 +10,8 @@
import java
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.security.ExternalProcess
private import semmle.code.java.security.CommandArguments
private import semmle.code.java.security.ExternalProcess
/** A sink for command injection vulnerabilities. */
abstract class CommandInjectionSink extends DataFlow::Node { }
@@ -33,9 +33,7 @@ class CommandInjectionAdditionalTaintStep extends Unit {
}
private class DefaultCommandInjectionSink extends CommandInjectionSink {
DefaultCommandInjectionSink() {
this.asExpr() instanceof ArgumentToExec or sinkNode(this, "command-injection")
}
DefaultCommandInjectionSink() { sinkNode(this, "command-injection") }
}
private class DefaultCommandInjectionSanitizer extends CommandInjectionSanitizer {
@@ -100,7 +98,7 @@ predicate execIsTainted(
RemoteUserInputToArgumentToExecFlow::PathNode sink, Expr execArg
) {
RemoteUserInputToArgumentToExecFlow::flowPath(source, sink) and
sink.getNode().asExpr() = execArg
argumentToExec(execArg, sink.getNode())
}
/**
@@ -112,7 +110,7 @@ predicate execIsTainted(
*/
deprecated predicate execTainted(DataFlow::PathNode source, DataFlow::PathNode sink, Expr execArg) {
exists(RemoteUserInputToArgumentToExecFlowConfig conf |
conf.hasFlowPath(source, sink) and sink.getNode().asExpr() = execArg
conf.hasFlowPath(source, sink) and argumentToExec(execArg, sink.getNode())
)
}

View File

@@ -1,16 +1,13 @@
/** Definitions related to external processes. */
import semmle.code.java.Member
private module Instances {
private import semmle.code.java.JDK
private import semmle.code.java.frameworks.apache.Exec
}
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.security.CommandLineQuery
/**
* A callable that executes a command.
* DEPRECATED: A callable that executes a command.
*/
abstract class ExecCallable extends Callable {
abstract deprecated class ExecCallable extends Callable {
/**
* Gets the index of an argument that will be part of the command that is executed.
*/
@@ -23,13 +20,19 @@ abstract class ExecCallable extends Callable {
* to be executed.
*/
class ArgumentToExec extends Expr {
ArgumentToExec() {
exists(Call execCall, ExecCallable execCallable, int i |
execCall.getArgument(pragma[only_bind_into](i)) = this and
execCallable = execCall.getCallee() and
i = execCallable.getAnExecutedArgument()
)
}
ArgumentToExec() { argumentToExec(this, _) }
}
/**
* Holds if `e` is an expression used as an argument to a call that executes an external command.
* For calls to varargs method calls, this only includes the first argument, which will be the command
* to be executed.
*/
predicate argumentToExec(Expr e, CommandInjectionSink s) {
s.asExpr() = e
or
e.(Argument).isNthVararg(0) and
s.(DataFlow::ImplicitVarargsArray).getCall() = e.(Argument).getCall()
}
/**

View File

@@ -14,11 +14,14 @@
import java
import semmle.code.java.security.CommandLineQuery
import semmle.code.java.security.ExternalProcess
import LocalUserInputToArgumentToExecFlow::PathGraph
from
LocalUserInputToArgumentToExecFlow::PathNode source,
LocalUserInputToArgumentToExecFlow::PathNode sink
where LocalUserInputToArgumentToExecFlow::flowPath(source, sink)
select sink.getNode().asExpr(), source, sink, "This command line depends on a $@.",
source.getNode(), "user-provided value"
LocalUserInputToArgumentToExecFlow::PathNode sink, Expr e
where
LocalUserInputToArgumentToExecFlow::flowPath(source, sink) and
argumentToExec(e, sink.getNode())
select e, source, sink, "This command line depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -14,6 +14,7 @@
import java
import semmle.code.java.security.CommandLineQuery
import semmle.code.java.security.ExternalProcess
/**
* Strings that are known to be sane by some simple local analysis. Such strings

View File

@@ -15,7 +15,11 @@
import java
import semmle.code.java.security.CommandLineQuery
import RemoteUserInputToArgumentToExecFlow::PathGraph
import JSchOSInjection
private import semmle.code.java.dataflow.ExternalFlow
private class ActivateModels extends ActiveExperimentalModels {
ActivateModels() { this = "jsch-os-injection" }
}
// This is a clone of query `java/command-line-injection` that also includes experimental sinks.
from

View File

@@ -1,20 +0,0 @@
/**
* 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 }
}

View File

@@ -1,22 +1,28 @@
edges
| Test.java:6:35:6:44 | arg : String | Test.java:7:44:7:69 | ... + ... |
| Test.java:6:35:6:44 | arg : String | Test.java:7:44:7:69 | ... + ... : String |
| Test.java:6:35:6:44 | arg : String | Test.java:10:61:10:73 | ... + ... : String |
| Test.java:6:35:6:44 | arg : String | Test.java:16:13:16:25 | ... + ... : String |
| Test.java:6:35:6:44 | arg : String | Test.java:22:15:22:27 | ... + ... : String |
| Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | Test.java:7:25:7:70 | new ..[] { .. } |
| Test.java:7:44:7:69 | ... + ... : String | Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String |
| Test.java:10:29:10:74 | {...} : String[] [[]] : String | Test.java:10:29:10:74 | new String[] |
| Test.java:10:61:10:73 | ... + ... : String | Test.java:10:29:10:74 | {...} : String[] [[]] : String |
| Test.java:16:5:16:7 | cmd [post update] : List [<element>] : String | Test.java:18:29:18:31 | cmd |
| Test.java:16:13:16:25 | ... + ... : String | Test.java:16:5:16:7 | cmd [post update] : List [<element>] : String |
| Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | Test.java:24:29:24:32 | cmd1 |
| Test.java:22:15:22:27 | ... + ... : String | Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String |
| Test.java:28:38:28:47 | arg : String | Test.java:29:44:29:64 | ... + ... |
| Test.java:28:38:28:47 | arg : String | Test.java:29:44:29:64 | ... + ... : String |
| Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | Test.java:29:25:29:65 | new ..[] { .. } |
| Test.java:29:44:29:64 | ... + ... : String | Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String |
| Test.java:57:27:57:39 | args : String[] | Test.java:60:20:60:22 | arg : String |
| Test.java:57:27:57:39 | args : String[] | Test.java:61:23:61:25 | arg : String |
| Test.java:60:20:60:22 | arg : String | Test.java:6:35:6:44 | arg : String |
| Test.java:61:23:61:25 | arg : String | Test.java:28:38:28:47 | arg : String |
nodes
| Test.java:6:35:6:44 | arg : String | semmle.label | arg : String |
| Test.java:7:44:7:69 | ... + ... | semmle.label | ... + ... |
| Test.java:7:25:7:70 | new ..[] { .. } | semmle.label | new ..[] { .. } |
| Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | semmle.label | new ..[] { .. } : String[] [[]] : String |
| Test.java:7:44:7:69 | ... + ... : String | semmle.label | ... + ... : String |
| Test.java:10:29:10:74 | new String[] | semmle.label | new String[] |
| Test.java:10:29:10:74 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String |
| Test.java:10:61:10:73 | ... + ... : String | semmle.label | ... + ... : String |
@@ -27,14 +33,16 @@ nodes
| Test.java:22:15:22:27 | ... + ... : String | semmle.label | ... + ... : String |
| Test.java:24:29:24:32 | cmd1 | semmle.label | cmd1 |
| Test.java:28:38:28:47 | arg : String | semmle.label | arg : String |
| Test.java:29:44:29:64 | ... + ... | semmle.label | ... + ... |
| Test.java:29:25:29:65 | new ..[] { .. } | semmle.label | new ..[] { .. } |
| Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | semmle.label | new ..[] { .. } : String[] [[]] : String |
| Test.java:29:44:29:64 | ... + ... : String | semmle.label | ... + ... : String |
| Test.java:57:27:57:39 | args : String[] | semmle.label | args : String[] |
| Test.java:60:20:60:22 | arg : String | semmle.label | arg : String |
| Test.java:61:23:61:25 | arg : String | semmle.label | arg : String |
subpaths
#select
| Test.java:7:44:7:69 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:7:44:7:69 | ... + ... | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:7:44:7:69 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:7:25:7:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:10:29:10:74 | new String[] | Test.java:57:27:57:39 | args : String[] | Test.java:10:29:10:74 | new String[] | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:18:29:18:31 | cmd | Test.java:57:27:57:39 | args : String[] | Test.java:18:29:18:31 | cmd | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:24:29:24:32 | cmd1 | Test.java:57:27:57:39 | args : String[] | Test.java:24:29:24:32 | cmd1 | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:29:44:29:64 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:29:44:29:64 | ... + ... | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |
| Test.java:29:44:29:64 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:29:25:29:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value |