1

I'm trying to connect to a FTP server behind a firewall that allows incoming connections in the range 6100-6200 only. I have successfully connected to this server using curl like this:

curl --ftp-port :6100-6200 --list-only ftp.server

But I'd like to reproduce the behaviour of this curl command with other clients that are more friendly to use from Python. In principle Linux's ftp, but I'm open to other options if someone suggest a good one. I tried ftplib but it seems that this library does not allow you to select ports; I've tried it unsuccessfully.

Currently I can not make it work with ftp:

230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> passive
Passive mode on.
ftp> ls
227 Entering Passive Mode (XXX,XXX,XXX,XXX,202,251).
ftp: connect: Connection refused

The same set of commands work from my laptop, therefore it seems clear that the problem is the firewall.

How can I force ftp to negociate a data connection in a port in the range 6100-6200, so emulating the behaviour of curl?

10
  • why not simply use python's ftplib? That's part of the standard python library and certainly better than calling ftp and communicating with that process (especially considering how bad FTP as a protocol is, itself, I'd try to avoid every additional layer of uncertainty). Jul 20, 2021 at 10:38
  • I tried, but it does not work (I posted a question here: stackoverflow.com/questions/68374018/…). I'm still trying to understand what's going on, but the problem seems to be that this library does not allow to select the data ports..
    – Pythonist
    Jul 20, 2021 at 10:40
  • The answer that said it was impossible to do that is 11 years old and not true anymore, but I see you've tried that, and it failed since things said you couldn't bind to that address. Are we sure that's not the case because you tried to bind to a port that was in use just shortly before and is not ready for re-use yet? Jul 20, 2021 at 10:43
  • You mean that I was unlucky, but if I retry it could work now? I do not think that's possible, I tried several times. Anyway, if it is possible, how can I achieve that? How can I specify ports then? Maybe you want to reply in the other question?
    – Pythonist
    Jul 20, 2021 at 10:47
  • 2
    So the thing is, I'm not sure this is what you really want: You are sure you want passive mode, right? Because "target server behind firewall" does sound an awful lot like you want active mode. Jul 20, 2021 at 10:47

1 Answer 1

2

When you use FTP in passive mode, the server tells the client which (server-side) data port to use. The well-known FTP protocol includes no way for the client to express requests on which port range to use at the server end. There could be some extensions that could change that, but those are not necessarily widely supported.

In your example, the message

227 Entering Passive Mode (XXX,XXX,XXX,XXX,202,251).

comes directly from the FTP server, as it's telling the client: "I'm listening for a data connection from you at IP address XXX.XXX.XXX.XXX, port 51683" (= 202*256 + 251).

Each TCP connection has two port numbers: a local port number and a remote port number. Usually, an outgoing connection just picks the first free local port in the OS-specified range of ports to be used for outgoing connections, and the remote port is specified according to the service that's being used. In case of passive FTP, the server will pick the remote port according to its configuration and will tell it to the client in the form of a FTP 227 response.

There are generally two ways to handle passive FTP in firewalls:

a) The firewall and the FTP server need both be configured in cooperation to accept/use a specific range of ports for passive FTP data connections, so the server won't even try to select a port the firewall is not going to let through,

or b) the firewall needs to listen in on the FTP command channel traffic, determine the port numbers used for each data connection and dynamically allow passive FTP data connections between the FTP client and server using the port numbers declared on the command channel.

If you are using Linux iptables/netfilter firewall, this is exactly what the protocol-specific conntrack extension module for FTP does. You'll just need to tell it what control connections it's allowed to listen to, since the previous policy of listening on all FTP control connections passing through the firewall system turned out to be exploitable by bad guys, and now such extensions will no longer be used automatically. For details, see this page or this question here on U&L SE.


curl actually uses FTP in passive mode by default, but when you use the --ftp-port option it switches to active mode. From the man page (highlight mine):

-P, --ftp-port

(FTP) Reverses the default initiator/listener roles when connecting with FTP. This option makes curl use active mode. curl then tells the server to connect back to the client's specified address and port, while passive mode asks the server to setup an IP address and port for it to connect to.


Regarding Python and ftplib, note that the question you referred to is more than 10 years old, and there's now a new answer added by Marcus Müller:

Since Python 3.3, ftplib functions that establish connections take a source_addr argument that allows you to do exactly this.

1
  • Thanks for the explanation. It helped a lot. I realise now that what I was asking makes not much sense. Somehow I wanted to select the destination ports, which I now see is the role of the server in passive mode. You could mess with source ports, but it makes little sense. Actually, the source_address option of ftplib is NOT made for this, and therefore it does not work. The reason is that the same port is tried to be used for both control and data channels, which of course raises an exception when actually used. It is designed only for the control channel in active mode.
    – Pythonist
    Jul 23, 2021 at 7:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.