mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
rename secondary to remote :), complete the previous commit changes
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Allowing users to execute arbitrary commands using an SSH connection on a secondary server can lead to security issues unless you implement proper authorization.
|
||||
Allowing users to execute arbitrary commands using an SSH connection on a remote server can lead to security issues unless you implement proper authorization.
|
||||
</p>
|
||||
<p>
|
||||
Assume that you connect to a secondary system via SSH connection from your main or local server that accepts user-controlled data and has interaction with users that you don't trust, passing these data to SSH API as a part of a command that will be executed on a secondary remote server can lead to security issues. You should consider proper authorization rules very carefully.
|
||||
Assume that you connect to a remote system via SSH connection from your main or local server that accepts user-controlled data and has interaction with users that you don't trust, passing these data to SSH API as a part of a command that will be executed on a secondary remote server can lead to security issues. You should consider proper authorization rules very carefully.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
@@ -12,10 +12,10 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import experimental.semmle.python.security.SecondaryServerCmdInjection
|
||||
import SecondaryCommandInjectionFlow::PathGraph
|
||||
import experimental.semmle.python.security.RemoteCommandExecution
|
||||
import RemoteCommandExecutionFlow::PathGraph
|
||||
|
||||
from SecondaryCommandInjectionFlow::PathNode source, SecondaryCommandInjectionFlow::PathNode sink
|
||||
where SecondaryCommandInjectionFlow::flowPath(source, sink)
|
||||
from RemoteCommandExecutionFlow::PathNode source, RemoteCommandExecutionFlow::PathNode sink
|
||||
where RemoteCommandExecutionFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This code execution depends on a $@.", source.getNode(),
|
||||
"a user-provided value"
|
||||
@@ -15,14 +15,29 @@ private import semmle.python.dataflow.new.TaintTracking
|
||||
private import experimental.semmle.python.Frameworks
|
||||
private import semmle.python.Concepts
|
||||
|
||||
/** Provides classes for modeling remote server command execution related APIs. */
|
||||
/**
|
||||
* A data-flow node that executes an operating system command,
|
||||
* on a remote server likely by SSH connections.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `RemoteCommandExecution::Range` instead.
|
||||
*/
|
||||
class RemoteCommandExecution extends DataFlow::Node instanceof RemoteCommandExecution::Range {
|
||||
/** Holds if a shell interprets `arg`. */
|
||||
predicate isShellInterpreted(DataFlow::Node arg) { super.isShellInterpreted(arg) }
|
||||
|
||||
/** Gets the argument that specifies the command to be executed. */
|
||||
DataFlow::Node getCommand() { result = super.getCommand() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling new remote server command execution APIs. */
|
||||
module RemoteCommandExecution {
|
||||
/**
|
||||
* A data-flow node that executes an operating system command,
|
||||
* on a remote server likely by SSH connections.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `SystemCommandExecution` instead.
|
||||
* extend `RemoteCommandExecution` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the argument that specifies the command to be executed. */
|
||||
|
||||
@@ -6,11 +6,11 @@ import semmle.python.dataflow.new.internal.DataFlowPublic
|
||||
import codeql.util.Unit
|
||||
import experimental.semmle.python.Concepts
|
||||
|
||||
module SecondaryCommandInjectionConfig implements DataFlow::ConfigSig {
|
||||
module RemoteCommandExecutionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof SecondaryCommandInjection }
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(RemoteCommandExecution rce).getCommand() }
|
||||
}
|
||||
|
||||
/** Global taint-tracking for detecting "secondary server command injection" vulnerabilities. */
|
||||
module SecondaryCommandInjectionFlow = TaintTracking::Global<SecondaryCommandInjectionConfig>;
|
||||
module RemoteCommandExecutionFlow = TaintTracking::Global<RemoteCommandExecutionConfig>;
|
||||
@@ -14,6 +14,6 @@ session.userauth_password("user", "password")
|
||||
@app.get("/bad1")
|
||||
async def bad1(cmd: str):
|
||||
async with asyncssh.connect('localhost') as conn:
|
||||
result = await conn.run(cmd, check=True) # $ result=BAD getSecondaryCommand=cmd
|
||||
result = await conn.run(cmd, check=True) # $ result=BAD getRemoteCommand=cmd
|
||||
print(result.stdout, end='')
|
||||
return {"success": "Dangerous"}
|
||||
@@ -6,16 +6,16 @@ private import semmle.python.dataflow.new.internal.PrintNode
|
||||
import experimental.semmle.python.Concepts
|
||||
|
||||
module SystemCommandExecutionTest implements TestSig {
|
||||
string getARelevantTag() { result = "getSecondaryCommand" }
|
||||
string getARelevantTag() { result = "getRemoteCommand" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(SecondaryCommandInjection sci, DataFlow::Node command |
|
||||
command = sci and
|
||||
exists(RemoteCommandExecution sci, DataFlow::Node command |
|
||||
command = sci.getCommand() and
|
||||
location = command.getLocation() and
|
||||
element = command.toString() and
|
||||
value = prettyNodeForInlineTest(command) and
|
||||
tag = "getSecondaryCommand"
|
||||
tag = "getRemoteCommand"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import python
|
||||
import TestUtilities.dataflow.DataflowQueryTest
|
||||
import experimental.semmle.python.security.RemoteCommandExecution
|
||||
import FromTaintTrackingConfig<RemoteCommandExecutionConfig>
|
||||
@@ -17,9 +17,9 @@ cisco_881 = {
|
||||
@app.get("/bad1")
|
||||
async def bad1(cmd: str):
|
||||
net_connect = ConnectHandler(**cisco_881)
|
||||
net_connect.send_command(command_string=cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
net_connect.send_command_expect(command_string=cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
net_connect.send_command_timing(command_string=cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
net_connect.send_multiline(commands=[[cmd, "expect"]]) # $ result=BAD getSecondaryCommand=List
|
||||
net_connect.send_multiline_timing(commands=cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
net_connect.send_command(command_string=cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
net_connect.send_command_expect(command_string=cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
net_connect.send_command_timing(command_string=cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
net_connect.send_multiline(commands=[[cmd, "expect"]]) # $ result=BAD getRemoteCommand=List
|
||||
net_connect.send_multiline_timing(commands=cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
return {"success": "Dangerous"}
|
||||
@@ -13,9 +13,9 @@ app = FastAPI()
|
||||
|
||||
@app.get("/bad1")
|
||||
async def bad1(cmd: str):
|
||||
ssh.send(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
ssh.send(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
ssh.prompt()
|
||||
ssh.sendline(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
ssh.sendline(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
ssh.prompt()
|
||||
ssh.logout()
|
||||
return {"success": stdout}
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-074/remoteCommandExecution/RemoteCommandExecution.ql
|
||||
@@ -21,19 +21,19 @@ async def bad1(cmd: str):
|
||||
}
|
||||
driver = AsyncIOSXEDriver
|
||||
async with driver(**dev_connect) as conn:
|
||||
output = await conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
output = await conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
driver = AsyncIOSXRDriver
|
||||
async with driver(**dev_connect) as conn:
|
||||
output = await conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
output = await conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
driver = AsyncNXOSDriver
|
||||
async with driver(**dev_connect) as conn:
|
||||
output = await conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
output = await conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
driver = AsyncEOSDriver
|
||||
async with driver(**dev_connect) as conn:
|
||||
output = await conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
output = await conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
driver = AsyncJunosDriver
|
||||
async with driver(**dev_connect) as conn:
|
||||
output = await conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
output = await conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
return {"success": "Dangerous"}
|
||||
|
||||
@app.get("/bad1")
|
||||
@@ -48,19 +48,19 @@ def bad2(cmd: str):
|
||||
}
|
||||
driver = NXOSDriver
|
||||
with driver(**dev_connect) as conn:
|
||||
output = conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
output = conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
driver = IOSXRDriver
|
||||
with driver(**dev_connect) as conn:
|
||||
output = conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
output = conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
driver = IOSXEDriver
|
||||
with driver(**dev_connect) as conn:
|
||||
output = conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
output = conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
driver = EOSDriver
|
||||
with driver(**dev_connect) as conn:
|
||||
output = conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
output = conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
driver = JunosDriver
|
||||
with driver(**dev_connect) as conn:
|
||||
output = conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
output = conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
|
||||
dev_connect = {
|
||||
"host": "65.65.65.65",
|
||||
@@ -71,7 +71,7 @@ def bad2(cmd: str):
|
||||
"platform": "cisco_iosxe",
|
||||
}
|
||||
with Scrapli(**dev_connect) as conn:
|
||||
result = conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
result = conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
|
||||
dev_connect = {
|
||||
"host": "65.65.65.65",
|
||||
@@ -81,5 +81,5 @@ def bad2(cmd: str):
|
||||
"transport": "ssh2",
|
||||
}
|
||||
with GenericDriver(**dev_connect) as conn:
|
||||
result = conn.send_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
result = conn.send_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
return {"success": "Dangerous"}
|
||||
@@ -13,7 +13,7 @@ app = FastAPI()
|
||||
async def bad1(cmd: bytes):
|
||||
endpoint = SSHCommandClientEndpoint.newConnection(
|
||||
reactor,
|
||||
cmd, # $ result=BAD getSecondaryCommand=cmd
|
||||
cmd, # $ result=BAD getRemoteCommand=cmd
|
||||
b"username",
|
||||
b"ssh.example.com",
|
||||
22,
|
||||
@@ -21,7 +21,7 @@ async def bad1(cmd: bytes):
|
||||
|
||||
SSHCommandClientEndpoint.existingConnection(
|
||||
endpoint,
|
||||
cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
|
||||
factory = Factory()
|
||||
d = endpoint.connect(factory)
|
||||
@@ -13,10 +13,10 @@ app = FastAPI()
|
||||
|
||||
@app.get("/bad1")
|
||||
async def bad1(cmd: str):
|
||||
stdin, stdout, stderr = paramiko_ssh_client.exec_command(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
stdin, stdout, stderr = paramiko_ssh_client.exec_command(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
return {"success": "Dangerous"}
|
||||
|
||||
@app.get("/bad2")
|
||||
async def bad2(cmd: str):
|
||||
stdin, stdout, stderr = paramiko_ssh_client.exec_command(command=cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
stdin, stdout, stderr = paramiko_ssh_client.exec_command(command=cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
return {"success": "Dangerous"}
|
||||
@@ -14,7 +14,7 @@ session.userauth_password("user", "password")
|
||||
@app.get("/bad1")
|
||||
async def bad1(cmd: str):
|
||||
channel = session.open_session()
|
||||
channel.execute(cmd) # $ result=BAD getSecondaryCommand=cmd
|
||||
channel.execute(cmd) # $ result=BAD getRemoteCommand=cmd
|
||||
channel.wait_eof()
|
||||
channel.close()
|
||||
channel.wait_closed()
|
||||
@@ -1,4 +0,0 @@
|
||||
import python
|
||||
import TestUtilities.dataflow.DataflowQueryTest
|
||||
import experimental.semmle.python.security.SecondaryServerCmdInjection
|
||||
import FromTaintTrackingConfig<SecondaryCommandInjectionConfig>
|
||||
@@ -1 +0,0 @@
|
||||
experimental/Security/CWE-074/secondaryCommandInjection/SecondaryServerCmdInjection.ql
|
||||
Reference in New Issue
Block a user