/** * @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 path-problem * @tags security * external/cwe/cwe-200 * @problem.severity error * @security-severity 6.5 * @sub-severity low * @precision high * @id py/bind-socket-all-network-interfaces */ import python import semmle.python.dataflow.new.DataFlow 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() { result in [ // IPv4 "0.0.0.0", "", // IPv6 "::", "::0" ] } 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") } } private module BindToAllInterfacesFlow = TaintTracking::Global; private import BindToAllInterfacesFlow /** * Holds if `sink` is the address argument of a `bind()` call on a * network socket (AF_INET or AF_INET6), as opposed to a Unix domain * socket (AF_UNIX) which takes a plain string path. * * Network socket addresses are tuples like `(host, port)`, so we check * that the sink argument is a tuple, by looking for flow from a tuple expression. */ private predicate isNetworkBind(DataFlow::Node sink) { any(DataFlow::LocalSourceNode n | n.asExpr() instanceof Tuple).flowsTo(sink) } from PathNode source, PathNode sink where flowPath(source, sink) and isNetworkBind(sink.getNode()) select sink.getNode(), source, sink, "Binding a socket to all interfaces (using $@) is a security risk.", source.getNode(), "'" + source.getNode().asExpr().(StringLiteral).getText() + "'"