Skip to content

Commit 3e6f8fb

Browse files
Add bind-socket-all-network-interfaces Python query (github#2048)
Add bind-socket-all-network-interfaces Python query
1 parent a019c45 commit 3e6f8fb

File tree

8 files changed

+119
-1
lines changed

8 files changed

+119
-1
lines changed

change-notes/1.23/analysis-python.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@
1111
|-----------|----------|-------------|
1212
| Clear-text logging of sensitive information (`py/clear-text-logging-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is logged without encryption or hashing. Results are shown on LGTM by default. |
1313
| Clear-text storage of sensitive information (`py/clear-text-storage-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is stored without encryption or hashing. Results are shown on LGTM by default. |
14-
14+
| Binding a socket to all network interfaces (`py/bind-socket-all-network-interfaces`) | security | Finds instances where a socket is bound to all network interfaces. Results are shown on LGTM by default. |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import socket
2+
3+
# binds to all interfaces, insecure
4+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
5+
s.bind(('0.0.0.0', 31137))
6+
7+
# binds to all interfaces, insecure
8+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
9+
s.bind(('', 4040))
10+
11+
# binds only to a dedicated interface, secure
12+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
13+
s.bind(('84.68.10.12', 8080))
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>Sockets can be used to communicate
8+
with other machines on a network.
9+
You can use the (IP address, port) pair
10+
to define the access restrictions for the socket you create.
11+
When using the built-in Python <code>socket</code> module
12+
(for instance, when building a message sender service
13+
or an FTP server data transmitter),
14+
one has to bind the port to some interface.
15+
When you bind the port to all interfaces
16+
using <code>0.0.0.0</code> as the IP address,
17+
you essentially allow it to accept connections from any IPv4 address
18+
provided that it can get to the socket via routing.
19+
Binding to all interfaces is therefore associated with security risks.</p>
20+
</overview>
21+
22+
<recommendation>
23+
<p>Bind your service incoming traffic only to a dedicated interface.
24+
If you need to bind more than one interface
25+
using the built-in <code>socket</code> module,
26+
create multiple sockets (instead of binding to one socket to all interfaces).</p>
27+
</recommendation>
28+
29+
<example>
30+
<p>In this example, two sockets are insecure because they are bound to all interfaces;
31+
one through the <code>0.0.0.0</code> notation
32+
and another one through an empty string <code>''</code>.
33+
</p>
34+
<sample src="BindToAllInterfaces.py" />
35+
</example>
36+
37+
<references>
38+
<li>Python reference: <a href="https://docs.python.org/3/library/socket.html#socket-families">
39+
Socket families</a>.</li>
40+
<li>Python reference: <a href="https://docs.python.org/3.7/howto/sockets.html">
41+
Socket Programming HOWTO</a>.</li>
42+
<li>Common Vulnerabilities and Exposures: <a href="https://nvd.nist.gov/vuln/detail/CVE-2018-1281">
43+
CVE-2018-1281 Detail</a>.</li>
44+
</references>
45+
</qhelp>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @name Binding a socket to all network interfaces
3+
* @description Binding a socket to all interfaces opens it up to traffic from any IPv4 address
4+
* and is therefore associated with security risks.
5+
* @kind problem
6+
* @tags security
7+
* @problem.severity error
8+
* @sub-severity low
9+
* @precision high
10+
* @id py/bind-socket-all-network-interfaces
11+
*/
12+
13+
import python
14+
15+
Value aSocket() { result.getClass() = Value::named("socket.socket") }
16+
17+
CallNode socketBindCall() {
18+
result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3
19+
or
20+
result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and
21+
major_version() = 2
22+
}
23+
24+
string allInterfaces() { result = "0.0.0.0" or result = "" }
25+
26+
Value getTextValue(string address) {
27+
result = Value::forUnicode(address) and major_version() = 3
28+
or
29+
result = Value::forString(address) and major_version() = 2
30+
}
31+
32+
from CallNode call, TupleValue args, string address
33+
where
34+
call = socketBindCall() and
35+
call.getArg(0).pointsTo(args) and
36+
args.getItem(0) = getTextValue(address) and
37+
address = allInterfaces()
38+
select call.getNode(), "'" + address + "' binds a socket to all interfaces."
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| BindToAllInterfaces_test.py:5:1:5:26 | Attribute() | '0.0.0.0' binds a socket to all interfaces. |
2+
| BindToAllInterfaces_test.py:9:1:9:18 | Attribute() | '' binds a socket to all interfaces. |
3+
| BindToAllInterfaces_test.py:17:1:17:26 | Attribute() | '0.0.0.0' binds a socket to all interfaces. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CVE-2018-1281/BindToAllInterfaces.ql
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import socket
2+
3+
# binds to all interfaces, insecure
4+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
5+
s.bind(('0.0.0.0', 31137))
6+
7+
# binds to all interfaces, insecure
8+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
9+
s.bind(('', 4040))
10+
11+
# binds only to a dedicated interface, secure
12+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
13+
s.bind(('84.68.10.12', 8080))
14+
15+
# binds to all interfaces, insecure
16+
ALL_LOCALS = "0.0.0.0"
17+
s.bind((ALL_LOCALS, 9090))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
semmle-extractor-options: --max-import-depth=3

0 commit comments

Comments
 (0)