+42
-2
lines changedFilter options
+42
-2
lines changed Original file line number Diff line number Diff line change
@@ -104,6 +104,8 @@ class FTP:
104
104
welcome = None
105
105
passiveserver = 1
106
106
encoding = "latin-1"
107
+
# Disables https://bugs.python.org/issue43285 security if set to True.
108
+
trust_server_pasv_ipv4_address = False
107
109
108
110
# Initialization method (called by class instantiation).
109
111
# Initialize host to localhost, port to standard ftp port
@@ -316,8 +318,13 @@ def makeport(self):
316
318
return sock
317
319
318
320
def makepasv(self):
321
+
"""Internal: Does the PASV or EPSV handshake -> (address, port)"""
319
322
if self.af == socket.AF_INET:
320
-
host, port = parse227(self.sendcmd('PASV'))
323
+
untrusted_host, port = parse227(self.sendcmd('PASV'))
324
+
if self.trust_server_pasv_ipv4_address:
325
+
host = untrusted_host
326
+
else:
327
+
host = self.sock.getpeername()[0]
321
328
else:
322
329
host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
323
330
return host, port
Original file line number Diff line number Diff line change
@@ -94,6 +94,10 @@ def __init__(self, conn):
94
94
self.rest = None
95
95
self.next_retr_data = RETR_DATA
96
96
self.push('220 welcome')
97
+
# We use this as the string IPv4 address to direct the client
98
+
# to in response to a PASV command. To test security behavior.
99
+
# https://bugs.python.org/issue43285/.
100
+
self.fake_pasv_server_ip = '252.253.254.255'
97
101
98
102
def collect_incoming_data(self, data):
99
103
self.in_buffer.append(data)
@@ -134,7 +138,8 @@ def cmd_port(self, arg):
134
138
def cmd_pasv(self, arg):
135
139
with socket.create_server((self.socket.getsockname()[0], 0)) as sock:
136
140
sock.settimeout(TIMEOUT)
137
-
ip, port = sock.getsockname()[:2]
141
+
port = sock.getsockname()[1]
142
+
ip = self.fake_pasv_server_ip
138
143
ip = ip.replace('.', ','); p1 = port / 256; p2 = port % 256
139
144
self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
140
145
conn, addr = sock.accept()
@@ -695,6 +700,26 @@ def test_makepasv(self):
695
700
# IPv4 is in use, just make sure send_epsv has not been used
696
701
self.assertEqual(self.server.handler_instance.last_received_cmd, 'pasv')
697
702
703
+
def test_makepasv_issue43285_security_disabled(self):
704
+
"""Test the opt-in to the old vulnerable behavior."""
705
+
self.client.trust_server_pasv_ipv4_address = True
706
+
bad_host, port = self.client.makepasv()
707
+
self.assertEqual(
708
+
bad_host, self.server.handler_instance.fake_pasv_server_ip)
709
+
# Opening and closing a connection keeps the dummy server happy
710
+
# instead of timing out on accept.
711
+
socket.create_connection((self.client.sock.getpeername()[0], port),
712
+
timeout=TIMEOUT).close()
713
+
714
+
def test_makepasv_issue43285_security_enabled_default(self):
715
+
self.assertFalse(self.client.trust_server_pasv_ipv4_address)
716
+
trusted_host, port = self.client.makepasv()
717
+
self.assertNotEqual(
718
+
trusted_host, self.server.handler_instance.fake_pasv_server_ip)
719
+
# Opening and closing a connection keeps the dummy server happy
720
+
# instead of timing out on accept.
721
+
socket.create_connection((trusted_host, port), timeout=TIMEOUT).close()
722
+
698
723
def test_with_statement(self):
699
724
self.client.quit()
700
725
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
1
+
:mod:`ftplib` no longer trusts the IP address value returned from the server
2
+
in response to the PASV command by default. This prevents a malicious FTP
3
+
server from using the response to probe IPv4 address and port combinations
4
+
on the client network.
5
+
6
+
Code that requires the former vulnerable behavior may set a
7
+
``trust_server_pasv_ipv4_address`` attribute on their
8
+
:class:`ftplib.FTP` instances to ``True`` to re-enable it.
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4