Skip to content

2.7.18.16: ftplib.ftpcp() trusts the PASV host (CVE-2026-8328) - #94

Merged
icanhasmath merged 1 commit into
2.7.18.16from
2.7.18.16-ftpcp-pasv
Oct 28, 2026
Merged

icanhasmath merged 1 commit into
2.7.18.16from
2.7.18.16-ftpcp-pasv

Conversation

@icanhasmath

Copy link
Copy Markdown

Backport of python/cpython pythongh-87451 (CVE-2026-8328, PSF-2026-24) to 2.7.18.x.

Problem: 2.7.18.x already has the CVE-2021-4189 fix in FTP.makepasv(), but ftpcp() still passed the host from the source server's PASV reply straight to target.sendport(). A malicious source server could therefore redirect the target's data connection to any host:port (SSRF).

Fix: ftpcp() now follows the same rule as makepasv(). It uses the source's real peer address unless trust_server_pasv_ipv4_address is set.

Test: TestFtpcpSecurity. Python 2.7 has no unittest.mock, so the test uses small fake FTP objects. test_ftpcp_ignores_untrusted_pasv_host fails on 2.7.18.15 and passes with the fix. The other 6 test_ftplib failures are the existing source-address and with-statement failures from my local environment, and they happen with or without this change.

Part of 2.7.18.16. It targets the 2.7.18.16 integration branch.

…026-8328)

Backport of python/cpython pythongh-87451 (3.15 5dadc64).

2.7.18.x already carries the CVE-2021-4189 fix in FTP.makepasv(): it
ignores the IPv4 address a server advertises in its PASV reply and uses
the control connection's peer address unless
trust_server_pasv_ipv4_address is set.  ftpcp() still called parse227()
directly and passed the server-supplied host to target.sendport(), so a
malicious source server could point the target's data connection at an
arbitrary host:port (SSRF).  ftpcp() now applies the same rule.

2.7.18.x: no unittest.mock in 2.7, so TestFtpcpSecurity uses small fake
FTP/socket classes; % formatting instead of f-strings; the class is
added to test_main().

Co-Authored-By: Claude Opus 5.5 <[email protected]>

@ezequielp-activestate ezequielp-activestate left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers.

  • Fix matches upstream. The new ftpcp() code is line-for-line upstream pythongh-87451 (eac4fe3). There are no later upstream changes to ftpcp().
  • Repro: a source server whose PASV reply advertises 10.0.0.99 while its real peer address is 203.0.113.5:
    • Base: target.sendport('10.0.0.99', 1930).
    • This branch: target.sendport('203.0.113.5', 1930).
  • Tests: test_ftplib on this branch: 85 run. The 2 failures and 1 error (test_source_address, test_source_address_passive_connection, test_with_statement) are identical on the base, which runs 83.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants