From 95c9a3fc9a8be31db9e78b66d1ec200b9e282473 Mon Sep 17 00:00:00 2001 From: amammad <77095239+amammad@users.noreply.github.com> Date: Sun, 25 Feb 2024 12:50:12 +0400 Subject: [PATCH] add ssh client libraries, add SecondaryServerCmdInjectionCustomizations --- .../semmle/python/frameworks/AsyncSsh.qll | 38 ++++++++++ .../semmle/python/frameworks/Netmiko.qll | 50 +++++++++++++ .../semmle/python/frameworks/Paramiko.qll | 37 ++++++++++ .../semmle/python/frameworks/Scrapli.qll | 70 +++++++++++++++++++ .../semmle/python/frameworks/Ssh2Python | 48 +++++++++++++ .../security/SecondaryServerCmdInjection.qll | 36 +--------- ...ondaryServerCmdInjectionCustomizations.qll | 35 ++++++++++ 7 files changed, 280 insertions(+), 34 deletions(-) create mode 100644 python/ql/src/experimental/semmle/python/frameworks/AsyncSsh.qll create mode 100644 python/ql/src/experimental/semmle/python/frameworks/Netmiko.qll create mode 100644 python/ql/src/experimental/semmle/python/frameworks/Paramiko.qll create mode 100644 python/ql/src/experimental/semmle/python/frameworks/Scrapli.qll create mode 100644 python/ql/src/experimental/semmle/python/frameworks/Ssh2Python create mode 100644 python/ql/src/experimental/semmle/python/security/SecondaryServerCmdInjectionCustomizations.qll diff --git a/python/ql/src/experimental/semmle/python/frameworks/AsyncSsh.qll b/python/ql/src/experimental/semmle/python/frameworks/AsyncSsh.qll new file mode 100644 index 00000000000..b0536fcbb9d --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/AsyncSsh.qll @@ -0,0 +1,38 @@ +/** + * Provides classes modeling security-relevant aspects of the `asyncssh` PyPI package. + * See https://pypi.org/project/asyncssh/. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.RemoteFlowSources +private import semmle.python.Concepts +private import semmle.python.ApiGraphs +import experimental.semmle.python.security.SecondaryServerCmdInjectionCustomizations + +/** + * Provides models for the `asyncssh` PyPI package. + * See https://pypi.org/project/asyncssh/. + */ +private module Asyncssh { + /** + * Gets `asyncssh` package. + */ + private API::Node asyncssh() { result = API::moduleImport("asyncssh") } + + /** + * A `run` method responsible for executing commands on remote secondary servers. + */ + class AsyncsshRun extends SecondaryCommandInjection::Sink { + AsyncsshRun() { + this = + asyncssh() + .getMember("connect") + .getReturn() + .getMember("run") + .getACall() + .getParameter(0, "command") + .asSink() + } + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Netmiko.qll b/python/ql/src/experimental/semmle/python/frameworks/Netmiko.qll new file mode 100644 index 00000000000..e7833723f9c --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/Netmiko.qll @@ -0,0 +1,50 @@ +/** + * Provides classes modeling security-relevant aspects of the `netmiko` PyPI package. + * See https://pypi.org/project/netmiko/. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.RemoteFlowSources +private import semmle.python.Concepts +private import semmle.python.ApiGraphs +import experimental.semmle.python.security.SecondaryServerCmdInjectionCustomizations + +/** + * Provides models for the `netmiko` PyPI package. + * See https://pypi.org/project/netmiko/. + */ +private module Netmiko { + /** + * Gets `netmiko` package. + */ + private API::Node netmiko() { result = API::moduleImport("netmiko") } + + /** + * Gets `netmiko.ConnectHandler` return value. + */ + private API::Node netmikoConnectHandler() { + result = netmiko().getMember("ConnectHandler").getReturn() + } + + /** + * The `send_*` methods responsible for executing commands on remote secondary servers. + */ + class NetmikoSendCommand extends SecondaryCommandInjection::Sink { + NetmikoSendCommand() { + this = + netmikoConnectHandler() + .getMember(["send_command", "send_command_expect", "send_command_timing"]) + .getACall() + .getParameter(0, "command_string") + .asSink() + or + this = + netmikoConnectHandler() + .getMember(["send_multiline", "send_multiline_timing"]) + .getACall() + .getParameter(0, "commands") + .asSink() + } + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Paramiko.qll b/python/ql/src/experimental/semmle/python/frameworks/Paramiko.qll new file mode 100644 index 00000000000..83094adb991 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/Paramiko.qll @@ -0,0 +1,37 @@ +/** + * Provides classes modeling security-relevant aspects of the `paramiko` PyPI package. + * See https://pypi.org/project/paramiko/. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.RemoteFlowSources +private import semmle.python.Concepts +private import semmle.python.ApiGraphs +import experimental.semmle.python.security.SecondaryServerCmdInjectionCustomizations + +/** + * Provides models for the `paramiko` PyPI package. + * See https://pypi.org/project/paramiko/. + */ +private module Paramiko { + /** + * Gets `paramiko` package. + */ + private API::Node paramiko() { result = API::moduleImport("paramiko") } + + /** + * Gets `paramiko.SSHClient` return value. + */ + private API::Node paramikoClient() { result = paramiko().getMember("SSHClient").getReturn() } + + /** + * The `exec_command` of `paramiko.SSHClient` class execute command on ssh target server + */ + class ParamikoExecCommand extends SecondaryCommandInjection::Sink { + ParamikoExecCommand() { + this = + paramikoClient().getMember("exec_command").getACall().getParameter(0, "command").asSink() + } + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Scrapli.qll b/python/ql/src/experimental/semmle/python/frameworks/Scrapli.qll new file mode 100644 index 00000000000..072f1e1504c --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/Scrapli.qll @@ -0,0 +1,70 @@ +/** + * Provides classes modeling security-relevant aspects of the `scrapli` PyPI package. + * See https://pypi.org/project/scrapli/. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.RemoteFlowSources +private import semmle.python.Concepts +private import semmle.python.ApiGraphs +import experimental.semmle.python.security.SecondaryServerCmdInjectionCustomizations + +/** + * Provides models for the `scrapli` PyPI package. + * See https://pypi.org/project/scrapli/. + */ +private module Scrapli { + /** + * Gets `scrapli` package. + */ + private API::Node scrapli() { result = API::moduleImport("scrapli") } + + /** + * Gets `scrapli.driver` package. + */ + private API::Node scrapliDriver() { result = scrapli().getMember("driver") } + + /** + * Gets `scrapli.driver.core` package. + */ + private API::Node scrapliCore() { result = scrapliDriver().getMember("core") } + + /** + * A `send_command` method responsible for executing commands on remote secondary servers. + */ + class ScrapliSendCommand extends SecondaryCommandInjection::Sink { + ScrapliSendCommand() { + this = + scrapliCore() + .getMember([ + "AsyncNXOSDriver", "AsyncJunosDriver", "AsyncEOSDriver", "AsyncIOSXEDriver", + "AsyncIOSXRDriver", "NXOSDriver", "JunosDriver", "EOSDriver", "IOSXEDriver", + "IOSXRDriver" + ]) + .getReturn() + .getMember("send_command") + .getACall() + .getParameter(0, "command") + .asSink() + or + this = + scrapli() + .getMember("Scrapli") + .getReturn() + .getMember("send_command") + .getACall() + .getParameter(0, "command") + .asSink() + or + this = + scrapliDriver() + .getMember("GenericDriver") + .getReturn() + .getMember("send_command") + .getACall() + .getParameter(0, "command") + .asSink() + } + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Ssh2Python b/python/ql/src/experimental/semmle/python/frameworks/Ssh2Python new file mode 100644 index 00000000000..956d3126ab7 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/Ssh2Python @@ -0,0 +1,48 @@ +/** + * Provides classes modeling security-relevant aspects of the `ssh2-python` PyPI package. + * See https://pypi.org/project/ssh2-python/. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.RemoteFlowSources +private import semmle.python.Concepts +private import semmle.python.ApiGraphs +import experimental.semmle.python.security.SecondaryServerCmdInjectionCustomizations + +/** + * Provides models for the `ssh2-python` PyPI package. + * See https://pypi.org/project/ssh2-python/. + */ +private module Ssh2 { + /** + * Gets `ssh2` package. + */ + private API::Node ssh2() { result = API::moduleImport("ssh2") } + + /** + * Gets `ssh2.session` package. + */ + private API::Node ssh2Session() { result = API::moduleImport("ssh2").getMember("session") } + + /** + * Gets `ssh2.session.Session` return value. + */ + private API::Node ssh2Session() { result = ssh2Session().getMember("Session").getReturn() } + + /** + * A `execute` method responsible for executing commands on remote secondary servers. + */ + class Ssh2Execute extends SecondaryCommandInjection::Sink { + Ssh2Execute() { + this = + ssh2Session() + .getMember("open_session") + .getReturn() + .getMember("execute") + .getACall() + .getParameter(0, "command") + .asSink() + } + } +} diff --git a/python/ql/src/experimental/semmle/python/security/SecondaryServerCmdInjection.qll b/python/ql/src/experimental/semmle/python/security/SecondaryServerCmdInjection.qll index 7f3b29e7de7..4ff1396d81f 100644 --- a/python/ql/src/experimental/semmle/python/security/SecondaryServerCmdInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/SecondaryServerCmdInjection.qll @@ -4,42 +4,10 @@ import semmle.python.dataflow.new.RemoteFlowSources import semmle.python.ApiGraphs import semmle.python.dataflow.new.internal.DataFlowPublic import codeql.util.Unit - -/** - * Provides sinks and additional taint steps for the secondary command injection configuration - */ -module SecondaryCommandInjection { - /** - * The additional taint steps that need for creating taint tracking or dataflow. - */ - class AdditionalTaintStep extends Unit { - /** - * Holds if there is a additional taint step between pred and succ. - */ - abstract predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ); - } - - /** - * A abstract class responsible for extending new decompression sinks - */ - abstract class Sink extends DataFlow::Node { } -} - -/** - * The exec_command of `paramiko.SSHClient` class execute command on ssh target server - */ -class ParamikoExecCommand extends SecondaryCommandInjection::Sink { - ParamikoExecCommand() { - this = paramikoClient().getMember("exec_command").getACall().getParameter(0, "command").asSink() - } -} - -private API::Node paramikoClient() { - result = API::moduleImport("paramiko").getMember("SSHClient").getReturn() -} +import SecondaryServerCmdInjectionCustomizations module SecondaryCommandInjectionConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + predicate isSource(DataFlow::Node source) { source instanceof SecondaryCommandInjection::Source } predicate isSink(DataFlow::Node sink) { sink instanceof SecondaryCommandInjection::Sink } } diff --git a/python/ql/src/experimental/semmle/python/security/SecondaryServerCmdInjectionCustomizations.qll b/python/ql/src/experimental/semmle/python/security/SecondaryServerCmdInjectionCustomizations.qll new file mode 100644 index 00000000000..2e70e02f985 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/security/SecondaryServerCmdInjectionCustomizations.qll @@ -0,0 +1,35 @@ +import python +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.RemoteFlowSources +import semmle.python.ApiGraphs +import semmle.python.dataflow.new.internal.DataFlowPublic +import codeql.util.Unit + +/** + * Provides sinks and additional taint steps for the secondary command injection configuration + */ +module SecondaryCommandInjection { + /** + * The additional taint steps that need for creating taint tracking or dataflow. + */ + class AdditionalTaintStep extends Unit { + /** + * Holds if there is a additional taint step between pred and succ. + */ + abstract predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ); + } + + /** + * A abstract class responsible for extending secondary command injection dataflow sinks. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A data flow source for secondary command injection data flow queries. + */ + abstract class Source extends DataFlow::Node { } + + class RemoteSources extends Source { + RemoteSources() { this instanceof RemoteFlowSource } + } +}