[php-src] Issue #24113: [FTP stream wrapper] Validate the PASV-advertised data-channel host
| From: | bupt-Yy-young | Date: | Sun, 04 Oct 2026 11:54:27 +0000 |
| Subject: | [php-src] Issue #24113: [FTP stream wrapper] Validate the PASV-advertised data-channel host | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-252896@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24113
Author: bupt-Yy-young
### Description
In the php-src master snapshot
63b0af4a5c400d93510dca7aa998769609fdc26c (2026-10-04),
the FTP stream wrapper uses the host returned in an FTP server's 227 PASV response
as the data-channel destination.
php_fopen_do_pasv() parses the 227 host into hoststart
(ext/standard/ftp_fopen_wrapper.c:341-361), and the open path then connects to
tcp://%s:%d using that value (:551-557). I could not find a comparison
with the control connection's peer address or a stream-context option to constrain the PASV
host. The only effective port rejection in this path is port 0.
For an application that fetches attacker-influenced ftp://` URLs,
an attacker-operated FTP server can return a PASV tuple for a loopback or private-network address.
PHP then attempts the data-channel connection to that address while the attacker controls the FTP
control channel. This can provide server-side reachability/port probing of targets reachable from
the PHP host; data returned by a target may also be exposed through the caller's read flow when
that service sends an unsolicited banner. Any stronger read/write impact depends on how the
application consumes the stream.
This is a conditional issue: it requires an application to use the FTP wrapper with an
attacker-influenced URL and a PHP host that can route to the target. allow_url_fopen is
only a scheme gate, not a destination policy.
### Expected behavior
Could the stream wrapper bind the data connection to the control connection's peer address by
default, or expose a documented opt-in equivalent to the FTP_USEPASVADDRESS setting
available to the separate FTP extension API?
### Reproduction outline
Run a controlled FTP server and a listener on a local/private address. Have the FTP server return a
227 reply whose tuple names the listener, then request a file through PHP's FTP
stream wrapper. The expected observation is whether PHP connects to the PASV-advertised listener
rather than to the FTP control peer. I have traced this statically in the referenced source; I have
not run this outline against a compiled build.
### Related work
This is distinct from [PR #22203](https://github.com/php/php-src/pull/22203), which addresses the
bounded-copy/OOB-read issue in the same PASV parser, and from [PR
#1668](https://github.com/php/php-src/pull/1668), which added a PASV-address option to the separate
FTP extension API.