Skip to content

Commit 3e6b4a1

Browse files
committed
finalize Secondary server command injection queries and tests.
1 parent 95c9a3f commit 3e6b4a1

File tree

16 files changed

+215
-63
lines changed

16 files changed

+215
-63
lines changed

python/ql/src/experimental/semmle/python/Concepts.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ private import semmle.python.dataflow.new.TaintTracking
1515
private import experimental.semmle.python.Frameworks
1616
private import semmle.python.Concepts
1717

18+
/**
19+
* A data-flow node that responsible for a command that can be executed on a secondary remote system,
20+
*
21+
* Extend this class to model new APIs.
22+
*/
23+
abstract class SecondaryCommandInjection extends DataFlow::Node { }
24+
1825
/** Provides classes for modeling copying file related APIs. */
1926
module CopyFile {
2027
/**

python/ql/src/experimental/semmle/python/Frameworks.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
* Helper file that imports all framework modeling.
33
*/
44

5+
private import experimental.semmle.python.frameworks.AsyncSsh
56
private import experimental.semmle.python.frameworks.Stdlib
67
private import experimental.semmle.python.frameworks.Flask
78
private import experimental.semmle.python.frameworks.Django
89
private import experimental.semmle.python.frameworks.Werkzeug
910
private import experimental.semmle.python.frameworks.LDAP
11+
private import experimental.semmle.python.frameworks.Netmiko
12+
private import experimental.semmle.python.frameworks.Paramiko
13+
private import experimental.semmle.python.frameworks.Scrapli
1014
private import experimental.semmle.python.frameworks.JWT
1115
private import experimental.semmle.python.frameworks.Csv
1216
private import experimental.semmle.python.libraries.PyJWT
@@ -15,5 +19,6 @@ private import experimental.semmle.python.libraries.Authlib
1519
private import experimental.semmle.python.libraries.PythonJose
1620
private import experimental.semmle.python.frameworks.CopyFile
1721
private import experimental.semmle.python.frameworks.Sendgrid
22+
private import experimental.semmle.python.frameworks.Ssh2
1823
private import experimental.semmle.python.libraries.FlaskMail
1924
private import experimental.semmle.python.libraries.SmtpLib

python/ql/src/experimental/semmle/python/frameworks/AsyncSsh.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
88
private import semmle.python.dataflow.new.RemoteFlowSources
99
private import semmle.python.Concepts
1010
private import semmle.python.ApiGraphs
11-
import experimental.semmle.python.security.SecondaryServerCmdInjectionCustomizations
11+
import experimental.semmle.python.Concepts
1212

1313
/**
1414
* Provides models for the `asyncssh` PyPI package.
@@ -23,7 +23,7 @@ private module Asyncssh {
2323
/**
2424
* A `run` method responsible for executing commands on remote secondary servers.
2525
*/
26-
class AsyncsshRun extends SecondaryCommandInjection::Sink {
26+
class AsyncsshRun extends SecondaryCommandInjection {
2727
AsyncsshRun() {
2828
this =
2929
asyncssh()

python/ql/src/experimental/semmle/python/frameworks/Netmiko.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
88
private import semmle.python.dataflow.new.RemoteFlowSources
99
private import semmle.python.Concepts
1010
private import semmle.python.ApiGraphs
11-
import experimental.semmle.python.security.SecondaryServerCmdInjectionCustomizations
11+
import experimental.semmle.python.Concepts
1212

1313
/**
1414
* Provides models for the `netmiko` PyPI package.
@@ -30,7 +30,7 @@ private module Netmiko {
3030
/**
3131
* The `send_*` methods responsible for executing commands on remote secondary servers.
3232
*/
33-
class NetmikoSendCommand extends SecondaryCommandInjection::Sink {
33+
class NetmikoSendCommand extends SecondaryCommandInjection {
3434
NetmikoSendCommand() {
3535
this =
3636
netmikoConnectHandler()

python/ql/src/experimental/semmle/python/frameworks/Paramiko.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
88
private import semmle.python.dataflow.new.RemoteFlowSources
99
private import semmle.python.Concepts
1010
private import semmle.python.ApiGraphs
11-
import experimental.semmle.python.security.SecondaryServerCmdInjectionCustomizations
11+
import experimental.semmle.python.Concepts
1212

1313
/**
1414
* Provides models for the `paramiko` PyPI package.
@@ -28,7 +28,7 @@ private module Paramiko {
2828
/**
2929
* The `exec_command` of `paramiko.SSHClient` class execute command on ssh target server
3030
*/
31-
class ParamikoExecCommand extends SecondaryCommandInjection::Sink {
31+
class ParamikoExecCommand extends SecondaryCommandInjection {
3232
ParamikoExecCommand() {
3333
this =
3434
paramikoClient().getMember("exec_command").getACall().getParameter(0, "command").asSink()

python/ql/src/experimental/semmle/python/frameworks/Scrapli.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
88
private import semmle.python.dataflow.new.RemoteFlowSources
99
private import semmle.python.Concepts
1010
private import semmle.python.ApiGraphs
11-
import experimental.semmle.python.security.SecondaryServerCmdInjectionCustomizations
11+
import experimental.semmle.python.Concepts
1212

1313
/**
1414
* Provides models for the `scrapli` PyPI package.
@@ -33,7 +33,7 @@ private module Scrapli {
3333
/**
3434
* A `send_command` method responsible for executing commands on remote secondary servers.
3535
*/
36-
class ScrapliSendCommand extends SecondaryCommandInjection::Sink {
36+
class ScrapliSendCommand extends SecondaryCommandInjection {
3737
ScrapliSendCommand() {
3838
this =
3939
scrapliCore()

python/ql/src/experimental/semmle/python/frameworks/Ssh2Python renamed to python/ql/src/experimental/semmle/python/frameworks/Ssh2.qll

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
88
private import semmle.python.dataflow.new.RemoteFlowSources
99
private import semmle.python.Concepts
1010
private import semmle.python.ApiGraphs
11-
import experimental.semmle.python.security.SecondaryServerCmdInjectionCustomizations
11+
import experimental.semmle.python.Concepts
1212

1313
/**
1414
* Provides models for the `ssh2-python` PyPI package.
@@ -20,20 +20,17 @@ private module Ssh2 {
2020
*/
2121
private API::Node ssh2() { result = API::moduleImport("ssh2") }
2222

23-
/**
24-
* Gets `ssh2.session` package.
25-
*/
26-
private API::Node ssh2Session() { result = API::moduleImport("ssh2").getMember("session") }
27-
2823
/**
2924
* Gets `ssh2.session.Session` return value.
3025
*/
31-
private API::Node ssh2Session() { result = ssh2Session().getMember("Session").getReturn() }
26+
private API::Node ssh2Session() {
27+
result = ssh2().getMember("session").getMember("Session").getReturn()
28+
}
3229

3330
/**
3431
* A `execute` method responsible for executing commands on remote secondary servers.
3532
*/
36-
class Ssh2Execute extends SecondaryCommandInjection::Sink {
33+
class Ssh2Execute extends SecondaryCommandInjection {
3734
Ssh2Execute() {
3835
this =
3936
ssh2Session()

python/ql/src/experimental/semmle/python/security/SecondaryServerCmdInjection.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import semmle.python.dataflow.new.RemoteFlowSources
44
import semmle.python.ApiGraphs
55
import semmle.python.dataflow.new.internal.DataFlowPublic
66
import codeql.util.Unit
7-
import SecondaryServerCmdInjectionCustomizations
7+
import experimental.semmle.python.Concepts
88

99
module SecondaryCommandInjectionConfig implements DataFlow::ConfigSig {
10-
predicate isSource(DataFlow::Node source) { source instanceof SecondaryCommandInjection::Source }
10+
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
1111

12-
predicate isSink(DataFlow::Node sink) { sink instanceof SecondaryCommandInjection::Sink }
12+
predicate isSink(DataFlow::Node sink) { sink instanceof SecondaryCommandInjection }
1313
}
1414

1515
/** Global taint-tracking for detecting "paramiko command injection" vulnerabilities. */

python/ql/src/experimental/semmle/python/security/SecondaryServerCmdInjectionCustomizations.qll

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env python
2+
3+
from fastapi import FastAPI
4+
import asyncssh
5+
6+
7+
app = FastAPI()
8+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
9+
sock.connect(("host", "port"))
10+
session = Session()
11+
session.handshake(sock)
12+
session.userauth_password("user", "password")
13+
14+
@app.get("/bad1")
15+
async def bad1(cmd: str):
16+
async with asyncssh.connect('localhost') as conn:
17+
result = await conn.run(cmd, check=True) # $ result=BAD getSecondaryCommand=cmd
18+
print(result.stdout, end='')
19+
return {"success": "Dangerous"}

0 commit comments

Comments
 (0)