Skip to content

Commit

Permalink
feat: add custom codeql queries (#2919)
Browse files Browse the repository at this point in the history
* add custom codeql queries

* modify severity level

* modify precision and severity

* try changing error case

* clean up queries
  • Loading branch information
QxBytes authored Aug 23, 2024
1 parent 4c66f48 commit 98466f4
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .github/workflows/codeql.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@ jobs:
- name: Setup go
uses: actions/setup-go@v5
with:
go-version: '1.21'
go-version: '1.21.0'
check-latest: true
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: ./codeql/
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
Expand Down
52 changes: 52 additions & 0 deletions codeql/addipamconfig-to-exec.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* @name Command Injection From CNS ipam add result / CNS multitenancy ipam add result
* @description Flow exists from CNS ipam add result / CNS multitenancy ipam add result (untrusted) to exec command
* @kind path-problem
* @problem.severity error
* @id go/cmd-inject-ipam-add-result
* @tags security
* @security-severity 9.8
* @precision high
*/

// Detect inputs from CNS add ipam result / CNS multitenancy ipam add result to command injection
import go

private class Sink extends DataFlow2::Node {
Sink() {
exists(DataFlow::CallNode c |
c.getTarget().hasQualifiedName("os/exec", "CommandContext") and
(c.getArgument(2) = this or c.getArgument(1) = this)
or
c.getTarget().hasQualifiedName("os/exec", "Command") and
(c.getArgument(0) = this or c.getArgument(1) = this)
)
}
}

private class Source extends DataFlow2::Node {
Source() {
exists(DataFlow::CallNode c, Method m |
//m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "NetPlugin",
// "addIpamInvoker") or // this is not necessary since we call GetAllNetworkContainers right next to this = duplicated results, but if this call moves, uncomment this
m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "Multitenancy",
"GetAllNetworkContainers") and
c = m.getACall() and
c.getResult(0) = this
)
}
}

module MyConfiguration implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }

predicate isSource(DataFlow::Node source) { source instanceof Source }
}

module Flow = TaintTracking::Global<MyConfiguration>;

import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "potential command injection"
58 changes: 58 additions & 0 deletions codeql/cni-args-to-exec.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @name Command Injection From CNI Args
* @description Flow exists from CNI Args (untrusted) to exec command
* @kind path-problem
* @problem.severity error
* @id go/cmd-inject-cni
* @tags security
* @security-severity 9.8
* @precision high
*/

// Detect inputs from CNI ARGS to command injection
import go

private class Sink extends DataFlow2::Node {
Sink() {
exists(DataFlow::CallNode c |
c.getTarget().hasQualifiedName("os/exec", "CommandContext") and
(c.getArgument(2) = this or c.getArgument(1) = this)
or
c.getTarget().hasQualifiedName("os/exec", "Command") and
(c.getArgument(0) = this or c.getArgument(1) = this)
)
}
}

private class Source extends DataFlow2::Node {
Source() {
exists(DataFlow::CallNode c, Method m |
(
m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "NetPlugin",
"Add") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "NetPlugin",
"Delete") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "NetPlugin",
"Update") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "NetPlugin",
"Get")
) and
c = m.getACall() and
c.getArgument(0) = this
)
}
}

module MyConfiguration implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }

predicate isSource(DataFlow::Node source) { source instanceof Source }
}

module Flow = TaintTracking::Global<MyConfiguration>;

import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "potential command injection"
59 changes: 59 additions & 0 deletions codeql/cns-invoker-to-exec.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @name Command Injection From CNS Invoker
* @description Flow exists from CNS Invoker (untrusted) to exec command
* @kind path-problem
* @problem.severity error
* @id go/cmd-inject-cns-invoker
* @tags security
* @security-severity 9.8
* @precision high
*/

// Detect inputs from CNS Invoker to command injection
// Does not detect flow to outside the enclosed method (which is why we analyze addIpamInvoker's results too)
import go

private class Sink extends DataFlow2::Node {
Sink() {
exists(DataFlow::CallNode c |
c.getTarget().hasQualifiedName("os/exec", "CommandContext") and
(c.getArgument(2) = this or c.getArgument(1) = this)
or
c.getTarget().hasQualifiedName("os/exec", "Command") and
(c.getArgument(0) = this or c.getArgument(1) = this)
)
}
}

private class Source extends DataFlow2::Node {
Source() {
exists(DataFlow::CallNode c, Method m |
(
m.hasQualifiedName("github.com/Azure/azure-container-networking/cns/client", "Client",
"RequestIPs") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cns/client", "Client",
"RequestIPAddress") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cns/client", "Client",
"GetNetworkContainer") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cns/client", "Client",
"GetAllNetworkContainers")
) and
c = m.getACall() and
c.getResult(0) = this
)
}
}

module MyConfiguration implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }

predicate isSource(DataFlow::Node source) { source instanceof Source }
}

module Flow = TaintTracking::Global<MyConfiguration>;

import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "potential command injection"
4 changes: 4 additions & 0 deletions codeql/codeql-pack.lock.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
lockVersion: 1.0.0
dependencies: {}
compiled: false
48 changes: 48 additions & 0 deletions codeql/decode-to-exec.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* @name Command Injection From Decode
* @description Flow exists from decodes (untrusted) to exec command
* @kind path-problem
* @problem.severity error
* @id go/cmd-inject-decode
* @tags security
* @security-severity 9.8
* @precision high
*/

// Detect flow from the DECODE method (which decodes http requests) to a command execution
import go

private class Sink extends DataFlow2::Node {
Sink() {
exists(DataFlow::CallNode c |
c.getTarget().hasQualifiedName("os/exec", "CommandContext") and
(c.getArgument(2) = this or c.getArgument(1) = this)
or
c.getTarget().hasQualifiedName("os/exec", "Command") and
(c.getArgument(0) = this or c.getArgument(1) = this)
)
}
}

private class Source extends DataFlow2::Node {
Source() {
exists(DataFlow::CallNode c |
c.getTarget().hasQualifiedName("github.com/Azure/azure-container-networking/common", "Decode") and
c.getArgument(2) = this
)
}
}

module MyConfiguration implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }

predicate isSource(DataFlow::Node source) { source instanceof Source }
}

module Flow = TaintTracking::Global<MyConfiguration>;

import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "potential command injection"
7 changes: 7 additions & 0 deletions codeql/qlpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
library: false
warnOnImplicitThis: false
name: codeql
version: 0.0.1
dependencies:
codeql/go-all: ^1.1.3

0 comments on commit 98466f4

Please sign in to comment.