FTP#

FTP (File Transfer Protocol, RFC 959) is the original file transfer protocol from 1971, predates TCP/IP itself. Plaintext, two connections per session (control + data), and a NAT-hostile design that’s kept network engineers up at night for decades. For new work the operator should reach for SFTP (SSH-tunneled), HTTPS, or rsync over SSH. For legacy systems, embedded devices, anonymous software mirrors, and CTF boxes, FTP is still everywhere, which is why operators still need to read it. The protocol quirks, ftp / lftp / curl clients, vsftpd / proftpd servers, FTPS (TLS-wrapped FTP), and what makes FTP fundamentally insecure.

Ports:

Port

Use

21/TCP

Control channel, commands and responses.

20/TCP

Active-mode data channel (server-initiated).

ephemeral

Passive-mode data channel (client picks server side).

990/TCP

Implicit FTPS (TLS at connect; deprecated).

989/TCP

Implicit FTPS data.

Active vs passive mode#

The old design that breaks NATs and firewalls. The control channel (client → server:21) is normal; the data channel direction depends on mode:

Mode

How the data channel opens

Active

Client tells server “connect back to my IP:port” via PORT command. Server initiates the data connection from its port 20 to the client’s high port. Breaks behind any NAT or stateful firewall on the client side.

Passive

Client says PASV; server replies with its own IP and port. Client initiates the data connection. Works behind client-side NAT. Server-side firewall must allow the configured passive port range.

        sequenceDiagram
    participant C as Client
    participant S as Server port 21
    Note over C,S: Passive mode (PASV), the modern norm
    C->>S: TCP connect to port 21
    S-->>C: 220 banner
    C->>S: USER alice
    S-->>C: 331 password please
    C->>S: PASS p@ss
    S-->>C: 230 logged in
    C->>S: PASV
    S-->>C: 227 reply with IP and port (10,0,0,5,200,15)
    C->>S: TCP connect to data channel 10.0.0.5 port 51215
    C->>S: RETR file.bin
    S-->>C: 150 sending, data flows on data channel
    S-->>C: 226 transfer complete
    

Common response codes#

Code

Meaning

220

Service ready (banner)

230

User logged in

331

Username OK, password required

530

Not logged in (auth failed / required)

150

Opening data connection

226

Closing data connection (transfer OK)

425

Can’t open data connection (firewall? port range?)

450

File unavailable (busy)

550

File / permission error

Clients#

Tool

Use

ftp (BSD / Linux)

Original interactive client; keeps a session.

lftp

Operator’s choice; scriptable, mirror, segmented downloads, FTPS, SFTP, FXP.

curl

Quick one-shot get / put with TLS support.

wget

Recursive fetch (-r -np follows listings).

ncftp

Friendly interactive; auto-resume.

filezilla

GUI; FTP / FTPS / SFTP.

# one-shot fetch (anonymous)
$ curl -O ftp://ftp.gnu.org/gnu/wget/wget-1.21.tar.gz

# authenticated put
$ curl -T report.pdf -u alice:pwd ftp://files.example.com/incoming/

# interactive session
$ ftp -inv ftp.example.com
ftp> user alice
ftp> bin                     # binary mode
ftp> passive on
ftp> cd /pub
ftp> ls
ftp> get file.bin
ftp> mput *.txt
ftp> bye

# lftp: scriptable, recursive
$ lftp -u alice,'pwd' -e '
    set ftp:passive-mode on
    cd /backup
    mirror -R /var/backups /backup/host01
    quit
  ' files.example.com

FTPS (FTP over TLS)#

Two flavours, with the usual TLS bolted-on confusion:

Flavour

Notes

Explicit

Plain FTP on port 21; client issues AUTH TLS to upgrade. Modern, RFC 4217. The default in vsftpd.

Implicit

TLS from byte one on port 990. Deprecated by RFC 2228 but still found on appliances.

# explicit FTPS via curl
$ curl --ftp-ssl -u alice:pwd ftp://files.example.com/path/file

# explicit FTPS via lftp
$ lftp -u alice,pwd -e 'set ftp:ssl-force true; ls; quit' files.example.com

Note: don’t confuse FTPS with SFTP. SFTP is a different protocol, the file transfer subsystem of SSH on port 22, no relation to FTP. Use SFTP whenever possible.

Servers#

Server

Notes

vsftpd

“Very Secure FTPd”; default on many distros; /etc/vsftpd.conf.

proftpd

Apache-style modular config; /etc/proftpd/proftpd.conf.

pure-ftpd

Light, command-line configured.

Filezilla Server

Mostly Windows.

Minimum-viable vsftpd config:

# /etc/vsftpd.conf
listen=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
chroot_local_user=YES
pasv_enable=YES
pasv_min_port=40000
pasv_max_port=40100

# FTPS (explicit)
ssl_enable=YES
rsa_cert_file=/etc/ssl/certs/vsftpd.pem
rsa_private_key_file=/etc/ssl/private/vsftpd.key
force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1_2=YES

Open ports 21 + the passive range on the host firewall.

Pentest / audit#

# find listeners
$ nmap -p 21 --open 10.0.0.0/24

# banner, anon login, syst, bounce-attack vector
$ nmap -p 21 --script ftp-anon,ftp-syst,ftp-bounce,ftp-vuln-cve2010-4221 host

# anonymous probe by hand
$ curl -v ftp://host/                    # lists / on anon-enabled servers

# password spray
$ hydra -L users.txt -P passwords.txt ftp://host
$ medusa -h host -U users.txt -P pass.txt -M ftp

Why FTP is broken#

  • Plaintext credentials and data, a single passive sniff sees USER, PASS, and the file. Use FTPS, SFTP, or HTTPS instead.

  • NAT / firewall hostility, two channels per session; passive port range must be punched through the firewall.

  • FTP bounce (RFC 2577), the PORT command historically let an attacker have a server connect anywhere on the attacker’s behalf; modern servers refuse this, but the legacy Nmap -b flag still works on weird gear.

  • Anonymous read often == anonymous write, vsftpd’s anon_upload_enable plus a writable /incoming/ plus default config has been a recurring data-leak pattern.

  • No good auth, no MFA, no key auth, no SSO. SFTP solves all of this.

Modern operator default: ban port 21 inbound; offer SFTP (or HTTPS upload, or rsync over SSH) instead.

See also#

  • SSH, SFTP, the secure replacement.

  • SSL/TLS, TLS for FTPS.

  • 0X42 - Networks, finding FTP listeners.

  • man vsftpd.conf, man lftp, man curl (FTP options).

  • RFC 959 (FTP), RFC 2228 (security extensions), RFC 4217 (FTP over TLS).