Perché Questo Progetto?
Durante i penetration test black-box su applicazioni web, mi sono reso conto che le wordlist standard, solitamente in inglese o in altre lingue, non sono sempre adatte al contesto italiano. Così ho pensato all’utilità di avere delle liste di username e password più specifiche per l’italiano, evitando di fare affidamento su credenziali troppo comuni, come quelle presenti nelle raccolte note (SecLists e simili), che contengono comunque validissime wordlist utilizzabili in tantissime situazioni diverse. Volevo inoltre evitare approcci conosciuti come l’utilizzo di tool noti per la creazione di wordlist come: Crunch, CeWL, Hashcat, ecc.
Per questo motivo ho sviluppato due script Python:
- CombolistScraper: per scaricare combolist da canali Telegram usando Telethon.
- Wordlist Generator: add-on che estrae username e password dalle combolist, generando wordlist sempre aggiornate.
Questi script sono semplici e senza pretese, ma si sono rivelati utili per automatizzare task ripetitivi.
Cosa è una Combolist?
Per chi se lo fosse chiesto: una combolist è una lista che contiene combinazioni di username e password, solitamente utilizzate per effettuare attacchi di brute force o credential stuffing. In pratica, una combolist raccoglie coppie di credenziali che possono essere sfruttate per tentare l’accesso a diversi servizi o per il cracking di hash/password.
Queste liste possono derivare da data leaks o da altre fonti in cui sono stati esposti account con username e password. Sebbene vengano utilizzate principalmente dai pentester e dai red teamer per testare la sicurezza di applicazioni o sistemi, spesso vengono usate anche da attori malevoli per effettuare attacchi non propriamente etici.
Molte di queste combolist si possono reperire facilmente su Telegram, tramite accesso a canali privati.
⚠️ Disclaimer: Scaricare o utilizzare dati provenienti da data leaks senza il consenso del proprietario è illegale secondo la normativa italiana vigente in materia di sicurezza informatica e protezione dei dati personali. Gli script descritti in questo articolo sono destinati esclusivamente a scopi legali ed etici. L’autore declina ogni responsabilità per un uso improprio del codice.
Il Codice
Di seguito è illustrato il flusso di lavoro dei due script, dal download delle combolist fino alla generazione delle wordlist pronte all’uso.
![]() |
---|
Diagramma del flusso di lavoro dello script Combolist Scraper |
CombolistScraper: Scaricare Combolist da Telegram
Lo script si connette ai canali Telegram, legge i messaggi e scarica i file che contengono le parole chiave definite. Nel caso specifico, ho notato che la maggior parte delle combolist di mio interesse servite sui canali Telegram solitamnte contiene le parole chiave: IT, ita, italy, italian.
Configurazione
Il file di configurazione config/config.ini
consente la configurazione rapida del bot Telegram con i seguenti parametri:
# Replace with your Telegram API info and your phone number
[Telegram]
api_id = YOUR_API_ID
api_hash = YOUR_API_HASH
phone_number = YOUR_PHONE_NUMBER
# Replace with your telegram channel IDs
[Channels]
"channel1" = <CHANNEL_ID>
"channel2" = <CHANNEL_ID>
"channel3" = <CHANNEL_ID>
# Replace with the keywords you want to use to search combolist files.
[Keywords]
keywords = IT,ita,italy,italian
Puoi ottenere facilmente API ID, API hash e phone number direttamente da Telegram (https://my.telegram.org/auth).
Da notare che, volendo includere nuove keyword è possibile farlo direttamente dal file di configurazione senza dover mettere mano al codice. Lo trovo molto comodo, perché può succedere che in base al contesto si voglia scaricare combolist di altre lingue, o semplicemente aggiungere una keyword ricorrente nei nomi file disponibili su Telegram.
1. Pulizia e Creazione delle Cartelle per i File Scaricati
Una delle prime operazioni che il codice esegue è creare una cartella dedicata per ogni canale Telegram da cui vengono scaricati i file. Tuttavia, i nomi dei canali potrebbero contenere caratteri non validi nei nomi dei file (ad esempio, <>:"/|?*
). La funzione clean_folder_name()
si occupa di rimuovere questi caratteri e creare un nome di cartella sicuro e unico.
def clean_folder_name(channel_name, channel_id, main_folder):
clean_name = re.sub(r'[<>:"/\\|?*]', '', channel_name)
return os.path.join(main_folder, f"{clean_name} - {channel_id}")
In questo modo, ogni canale avrà la propria cartella dedicata all’interno della cartella principale, assicurando che non ci siano conflitti tra i nomi.
2. Verifica della Presenza di File Duplicati
Una delle problematiche più comuni durante il download di file è la duplicazione: se un file è già stato scaricato, il sistema potrebbe scaricarlo di nuovo. Per evitare questo, la funzione file_exists_with_same_size()
verifica se un file con lo stesso nome e la stessa dimensione è già presente nella cartella di destinazione. Se il file esiste, non viene scaricato nuovamente.
def file_exists_with_same_size(directory, file_name, file_size):
file_path = os.path.join(directory, file_name)
return os.path.exists(file_path) and os.path.getsize(file_path) == file_size
Questo permette di risparmiare tempo e banda, evitando download inutili.
3. Generazione di Nuovi Nomi per i File
In alcuni casi, potresti trovare un file con lo stesso nome ma una dimensione diversa. In questi casi, la funzione generate_new_file_name()
rinomina il file aggiungendo un suffisso numerico per evitare conflitti con i file già esistenti.
def generate_new_file_name(directory, file_name):
base, ext = os.path.splitext(file_name)
counter = 1
new_file_name = f"{base}_{counter}{ext}"
while os.path.exists(os.path.join(directory, new_file_name)):
counter += 1
new_file_name = f"{base}_{counter}{ext}"
return new_file_name
Questo garantisce che ogni versione di un file venga salvata con un nome univoco, evitando che venga sovrascritto.
4. Ricerca di Parole Chiave nei Nomi dei File
La funzione contains_exact_keyword()
è fondamentale per filtrare i file che devono essere scaricati. Controlla che una parola chiave specifica sia presente esattamente nel nome del file (ignorando maiuscole/minuscole e evitando match parziali). Questo ti permette di scaricare solo i file rilevanti in base alle parole chiave che hai definito.
def contains_exact_keyword(word, keyword):
return re.search(r'\b' + re.escape(keyword) + r'\b', word, re.IGNORECASE) is not None
In questo modo, puoi cercare file specifici legati a parole chiave, senza preoccuparti di estrarre file che non sono pertinenti.
5. La Funzione Principale: Download e Organizzazione dei File
La funzione più importante è search_and_download_files()
, che gestisce l’intero processo di ricerca e download. Si occupa di iterare attraverso i canali Telegram, cercando i messaggi che contengono file e verificando se il nome del file corrisponde a uno dei termini di ricerca definiti. Se un file non è già presente o ha una dimensione diversa, viene scaricato nella cartella corretta.
async def search_and_download_files():
main_folder = "combolists" # Cartella principale per i file scaricati
for channel_name, channel_id in channels.items():
print(f"Searching messages in channel {channel_name} ({channel_id}) with keywords {', '.join(keywords)}...")
try:
# Pulisce il nome della cartella per il canale
clean_channel_name = clean_folder_name(channel_name, channel_id, main_folder)
# Crea la cartella se non esiste
if not os.path.exists(clean_channel_name):
os.makedirs(clean_channel_name)
channel = await client.get_entity(PeerChannel(channel_id))
offset_id = 0
files_found = 0
total_files = 0
while True:
history = await client(GetHistoryRequest(
peer=channel,
limit=100, # Limite API di Telegram
offset_date=None,
offset_id=offset_id,
max_id=0,
min_id=0,
add_offset=0,
hash=0
))
if not history.messages:
break
for message in history.messages:
if message.media and hasattr(message.media, 'document'):
for attribute in message.media.document.attributes:
if hasattr(attribute, 'file_name'):
file_name = attribute.file_name
if any(contains_exact_keyword(file_name, keyword) for keyword in keywords):
total_files += 1
if not file_exists_with_same_size(clean_channel_name, file_name, message.media.document.size):
files_found += 1
break
offset_id = history.messages[-1].id
print(f"Found {files_found} new files in channel {channel_name} ({channel_id}).")
except Exception as e:
print(f"[!] Error in channel {channel_name} ({channel_id}): {e}.")
print("Download complete.")
Questa funzione è l’anima del processo: ricerca file nei canali Telegram, applica i filtri di ricerca e li scarica nel formato giusto.
Demo
Wordlist Generator Add-On: Come Gestire le Combolist per Creare Wordlist di Username e Password
Dopo aver utilizzato CombolistScraper, è il momento di estrarre username e password dai file di output per creare delle wordlist pronte all’uso. Il Wordlist Generator prende appunto in input le combolist già estratte, le elabora e crea due wordlist separate per username e password.
![]() |
---|
Diagramma del flusso di lavoro dello script Wordlist Generator |
1. Gestione dei File Pre-esistenti
Se esistono già file chiamati new_users.txt
o new_passwords.txt
, lo script esegue una rinomina per archiviarli come old_users.txt
e old_passwords.txt
. Questo passaggio è utile per non sovrascrivere accidentalmente i file esistenti durante il nuovo processo di creazione delle wordlist.
if os.path.isfile("wordlists/new_users.txt"):
if os.path.isfile("wordlists/old_users.txt"):
os.remove("wordlists/old_users.txt") # Delete the destination file if it already exists
os.rename("wordlists/new_users.txt", "wordlists/old_users.txt")
if os.path.isfile("wordlists/new_passwords.txt"):
if os.path.isfile("wordlists/old_passwords.txt"):
os.remove("wordlists/old_passwords.txt") # Delete the destination file if it already exists
os.rename("wordlists/new_passwords.txt", "wordlists/old_passwords.txt")
Questa gestione consente di tenere traccia delle versioni precedenti delle wordlist e di evitare la perdita di dati.
3. Riepilogo dei File Pre-esistenti
Lo script verifica se esistono i file old_users.txt
e old_passwords.txt
, e se sì, stampa un riepilogo del numero di username e password già memorizzati. Questo fornisce all’utente un’idea chiara della quantità di dati pre-esistenti e delle dimensioni dei file.
if os.path.isfile("wordlists/old_users.txt") and os.path.isfile("wordlists/old_passwords.txt"):
with open("wordlists/old_users.txt", "r", encoding="utf-8") as old_users_file:
total_old_users = sum(1 for line in old_users_file)
size_old_users = os.path.getsize("wordlists/old_users.txt")
with open("wordlists/old_passwords.txt", "r", encoding="utf-8") as old_passwords_file:
total_old_passwords = sum(1 for line in old_passwords_file)
size_old_passwords = os.path.getsize("wordlists/old_passwords.txt")
print(f"You already have a total of {total_old_users} usernames ({size_old_users} bytes) and {total_old_passwords} passwords ({size_old_passwords} bytes).")
Questa sezione permette di monitorare la quantità di dati che sono stati già elaborati, per evitare duplicazioni nei passaggi successivi.
4. Estrazione degli Username e delle Password Uniche
Il cuore dello script è l’estrazione delle username e password dalle combolist. Lo script esegue una scansione dei file contenuti nella cartella combolists
, e per ogni file trovato, estrae la parte dell’username (prima dei due punti :
) e la parte della password (dopo i due punti).
Per prima cosa, gli username vengono estratti e scritti nel file new_users.txt
:
with open("wordlists/new_users.txt", "w", encoding="utf-8") as new_users_file:
for root, dirs, files in os.walk("combolists"):
for file_name in files:
with codecs.open(os.path.join(root, file_name), "r", encoding="utf-8", errors="ignore") as combo_file:
for line in combo_file:
try:
username = line.split(":")[0]
new_users_file.write(username + "\n")
except IndexError:
pass
Successivamente, vengono estratte le password uniche e scritte nel file new_passwords.txt
.
Questa parte dello script gestisce in modo automatico l’estrazione delle credenziali dai file, separando correttamente username e password e garantendo che vengano memorizzati solo i dati validi.
5. Riepilogo delle Nuove Wordlist
Una volta completato il processo di estrazione, lo script stampa un riepilogo dei file appena creati, indicando il numero di username e password contenuti nei file new_users.txt
e new_passwords.txt
, nonché la dimensione totale di questi file.
with open("wordlists/new_users.txt", "r", encoding="utf-8") as new_users_file:
total_users = sum(1 for line in new_users_file)
size_users = os.path.getsize("wordlists/new_users.txt")
with open("wordlists/new_passwords.txt", "r", encoding="utf-8") as new_passwords_file:
total_passwords = sum(1 for line in new_passwords_file)
size_passwords = os.path.getsize("wordlists/new_passwords.txt")
print(f"Now you have a total of {total_users} usernames ({size_users} bytes) and {total_passwords} passwords ({size_passwords} bytes).")
Questa sezione è utile per monitorare l’andamento del processo e per avere un controllo su quante informazioni sono state estratte dalle combolist.
Demo
Conclusioni
Gli script fanno il loro lavoro ma sicuramente si potrebbero applicare delle migliorie come: migliore gestione errori, logging, parallellizzazione, ottimizzazione lettura file, verifica credenziali duplicate e, perché no, anche una piccola GUI. Anche l’efficienza del processo di scansione dei file combinati è migliorabile, si potrebbe ridurre il numero di letture e cercare di eseguire operazioni in un’unica passata.
Questi script non sono altro che semplici automazioni realizzate per rispondere a una mia esigenza personale. Se siete pentester, red teamer o vi occupate di ethical hacking e cercate wordlist più pertinenti al contesto italiano, spero che possano esservi utili.
Ad ogni modo, il progetto è open-source e disponibile su GitHub. Ogni contributo è benvenuto.