mirror of
https://github.com/github/codeql.git
synced 2026-04-17 13:04:02 +02:00
Merge pull request #21590 from github/tausbn/python-improve-bind-all-interfaces-query
Python: Improve "bind all interfaces" query
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* @name Binding a socket to all network interfaces
|
||||
* @description Binding a socket to all interfaces opens it up to traffic from any IPv4 address
|
||||
* and is therefore associated with security risks.
|
||||
* @kind problem
|
||||
* @kind path-problem
|
||||
* @tags security
|
||||
* external/cwe/cwe-200
|
||||
* @problem.severity error
|
||||
@@ -14,7 +14,9 @@
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.ApiGraphs
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
import BindToAllInterfacesFlow::PathGraph
|
||||
|
||||
/** Gets a hostname that can be used to bind to all interfaces. */
|
||||
private string vulnerableHostname() {
|
||||
@@ -26,45 +28,26 @@ private string vulnerableHostname() {
|
||||
]
|
||||
}
|
||||
|
||||
/** Gets a reference to a hostname that can be used to bind to all interfaces. */
|
||||
private DataFlow::TypeTrackingNode vulnerableHostnameRef(DataFlow::TypeTracker t, string hostname) {
|
||||
t.start() and
|
||||
exists(StringLiteral allInterfacesStringLiteral | hostname = vulnerableHostname() |
|
||||
allInterfacesStringLiteral.getText() = hostname and
|
||||
result.asExpr() = allInterfacesStringLiteral
|
||||
)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = vulnerableHostnameRef(t2, hostname).track(t2, t))
|
||||
private module BindToAllInterfacesConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(StringLiteral).getText() = vulnerableHostname()
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
ModelOutput::sinkNode(sink, "bind-socket-all-interfaces") and
|
||||
// Network socket addresses are tuples like (host, port), so we require
|
||||
// the bind() argument to originate from a tuple expression. This excludes
|
||||
// AF_UNIX sockets, which pass a plain string path to bind().
|
||||
any(DataFlow::LocalSourceNode n | n.asExpr() instanceof Tuple).flowsTo(sink)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a reference to a hostname that can be used to bind to all interfaces. */
|
||||
DataFlow::Node vulnerableHostnameRef(string hostname) {
|
||||
vulnerableHostnameRef(DataFlow::TypeTracker::end(), hostname).flowsTo(result)
|
||||
}
|
||||
private module BindToAllInterfacesFlow = TaintTracking::Global<BindToAllInterfacesConfig>;
|
||||
|
||||
/** Gets a reference to a tuple for which the first element is a hostname that can be used to bind to all interfaces. */
|
||||
private DataFlow::TypeTrackingNode vulnerableAddressTuple(DataFlow::TypeTracker t, string hostname) {
|
||||
t.start() and
|
||||
result.asExpr() = any(Tuple tup | tup.getElt(0) = vulnerableHostnameRef(hostname).asExpr())
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = vulnerableAddressTuple(t2, hostname).track(t2, t))
|
||||
}
|
||||
private import BindToAllInterfacesFlow
|
||||
|
||||
/** Gets a reference to a tuple for which the first element is a hostname that can be used to bind to all interfaces. */
|
||||
DataFlow::Node vulnerableAddressTuple(string hostname) {
|
||||
vulnerableAddressTuple(DataFlow::TypeTracker::end(), hostname).flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of `socket.socket` using _some_ address family.
|
||||
*
|
||||
* See https://docs.python.org/3/library/socket.html
|
||||
*/
|
||||
API::Node socketInstance() { result = API::moduleImport("socket").getMember("socket").getReturn() }
|
||||
|
||||
from DataFlow::CallCfgNode bindCall, DataFlow::Node addressArg, string hostname
|
||||
where
|
||||
bindCall = socketInstance().getMember("bind").getACall() and
|
||||
addressArg = bindCall.getArg(0) and
|
||||
addressArg = vulnerableAddressTuple(hostname)
|
||||
select bindCall.asExpr(), "'" + hostname + "' binds a socket to all interfaces."
|
||||
from PathNode source, PathNode sink
|
||||
where flowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
"Binding a socket to all interfaces (using $@) is a security risk.", source.getNode(),
|
||||
"'" + source.getNode().asExpr().(StringLiteral).getText() + "'"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
|
||||
- The `py/bind-socket-all-network-interfaces` query now uses the global data-flow library, leading to better precision and more results. Also, wrappers of `socket.socket` in the `eventlet` and `gevent` libraries are now also recognized as socket binding operations.
|
||||
Reference in New Issue
Block a user