Add bind-socket-all-network-interfaces Python query (#2048)

Add bind-socket-all-network-interfaces Python query
This commit is contained in:
AlexTereshenkov
2019-10-03 11:23:11 +01:00
committed by GitHub
parent a019c456e9
commit 3e6f8fb6be
8 changed files with 119 additions and 1 deletions

View File

@@ -0,0 +1,13 @@
import socket
# binds to all interfaces, insecure
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 31137))
# binds to all interfaces, insecure
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 4040))
# binds only to a dedicated interface, secure
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('84.68.10.12', 8080))

View File

@@ -0,0 +1,45 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Sockets can be used to communicate
with other machines on a network.
You can use the (IP address, port) pair
to define the access restrictions for the socket you create.
When using the built-in Python <code>socket</code> module
(for instance, when building a message sender service
or an FTP server data transmitter),
one has to bind the port to some interface.
When you bind the port to all interfaces
using <code>0.0.0.0</code> as the IP address,
you essentially allow it to accept connections from any IPv4 address
provided that it can get to the socket via routing.
Binding to all interfaces is therefore associated with security risks.</p>
</overview>
<recommendation>
<p>Bind your service incoming traffic only to a dedicated interface.
If you need to bind more than one interface
using the built-in <code>socket</code> module,
create multiple sockets (instead of binding to one socket to all interfaces).</p>
</recommendation>
<example>
<p>In this example, two sockets are insecure because they are bound to all interfaces;
one through the <code>0.0.0.0</code> notation
and another one through an empty string <code>''</code>.
</p>
<sample src="BindToAllInterfaces.py" />
</example>
<references>
<li>Python reference: <a href="https://docs.python.org/3/library/socket.html#socket-families">
Socket families</a>.</li>
<li>Python reference: <a href="https://docs.python.org/3.7/howto/sockets.html">
Socket Programming HOWTO</a>.</li>
<li>Common Vulnerabilities and Exposures: <a href="https://nvd.nist.gov/vuln/detail/CVE-2018-1281">
CVE-2018-1281 Detail</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,38 @@
/**
* @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
* @tags security
* @problem.severity error
* @sub-severity low
* @precision high
* @id py/bind-socket-all-network-interfaces
*/
import python
Value aSocket() { result.getClass() = Value::named("socket.socket") }
CallNode socketBindCall() {
result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3
or
result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and
major_version() = 2
}
string allInterfaces() { result = "0.0.0.0" or result = "" }
Value getTextValue(string address) {
result = Value::forUnicode(address) and major_version() = 3
or
result = Value::forString(address) and major_version() = 2
}
from CallNode call, TupleValue args, string address
where
call = socketBindCall() and
call.getArg(0).pointsTo(args) and
args.getItem(0) = getTextValue(address) and
address = allInterfaces()
select call.getNode(), "'" + address + "' binds a socket to all interfaces."

View File

@@ -0,0 +1,3 @@
| BindToAllInterfaces_test.py:5:1:5:26 | Attribute() | '0.0.0.0' binds a socket to all interfaces. |
| BindToAllInterfaces_test.py:9:1:9:18 | Attribute() | '' binds a socket to all interfaces. |
| BindToAllInterfaces_test.py:17:1:17:26 | Attribute() | '0.0.0.0' binds a socket to all interfaces. |

View File

@@ -0,0 +1 @@
Security/CVE-2018-1281/BindToAllInterfaces.ql

View File

@@ -0,0 +1,17 @@
import socket
# binds to all interfaces, insecure
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 31137))
# binds to all interfaces, insecure
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 4040))
# binds only to a dedicated interface, secure
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('84.68.10.12', 8080))
# binds to all interfaces, insecure
ALL_LOCALS = "0.0.0.0"
s.bind((ALL_LOCALS, 9090))

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=3