2. Import Elenco Porte da File JSON
In questa lezione utilizzeremo un file JSON per poter mostrare ai nostri utenti il servizio associato ad ogni porta che è stata trovata aperta sul nostro server, e quindi evitare che l'utente debba scrivere ogni volta la porta che vuole analizzare in un ciclo infinito.
Ho ricavato il file JSON che utilizzeremo partendo da un file CSV messo a disposizione dallo IANA (Internet Assigned Numbers Authority) e nello specifico da questo link. Scorrendo il file, vediamo che si tratta di un elenco di coppie chiave valore dove per ogni chiave (porta) è associato un valore che rappresenta il servizio comunemente associato alla stessa porta:
{
"1": "tcpmux",
"3": "compressnet",
"7": "echo",
"9": "discard",
"13": "daytime",
"17": "qotd",
"19": "chargen",
"20": "ftp-data",
"21": "ftp",
"22": "ssh"
(...)
}
Come utilizzare file JSON con Python?
Si tratta di un'estensione di file particolarmente comune, e per questo motivo nella Standard Library di Python è presente un modulo json che contiene tutta una serie di funzionalità che ci rendono la vita semplice quando ci troviamo a dover lavorare con questi file.
Importiamo quindi subito il modulo e creiamo una funzione extract_json_data() che si occupi di restituirci il contenuto del file. Per rendere la funzione utilizzabile con più file e non solo col file specifico utilizzato dal nostro scanner, le passiamo come parametro filename. Definiamo quindi una variabile globale PORTS_DATA_FILE alla quale assegniamo il nome del nostro file, in questo caso “common_ports.json” (che potete scaricare dal repository dedicato nel mio profilo GitHub)
import json
PORTS_DATA_FILE = "./common_ports.json"
def extract_json_data(filename):
with open(filename, "r") as file:
data = json.load(file)
return data
Stiamo facendo dei progressi: ma prima di utilizzare questi dati, dobbiamo ancora elaborarli in modo da poterli utilizzare col resto delle funzioni scritte finora.
Il nostro codice attuale è il seguente, e come potete notare da sotto ad if __name__ == “__main__”, stiamo anzitutto trasformando l’input passato dall’utente per la porta, in intero necessario al corretto funzionamento con la funzione scan_port():
import json
import socket
OPEN_PORTS = []
PORTS_DATA_FILE = "./common_ports.json"
def extract_json_data(filename):
with open(filename, "r") as file:
data = json.load(file)
return data
def get_host_ip_addr(target):
try:
ip_addr = socket.gethostbyname(target)
except socket.gaierror as e:
print(f"C'è stato un errore... {e}")
else:
return ip_addr
def scan_port(ip, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1.0)
conn_status = sock.connect_ex((ip, port))
if conn_status == 0:
OPEN_PORTS.append(port)
sock.close()
if __name__ == "__main__":
print("Programma scritto per solo scopo educativo!!!")
target = input("Inserire Target: ")
ip_addr = get_host_ip_addr(target)
while True:
try:
port = int(input("Inserire Porta: "))
scan_port(ip_addr, port)
print(OPEN_PORTS)
except KeyboardInterrupt:
print("\nExiting...")
break
Possiamo quindi definire un'altra funzione, get_ports_info(), che sarà responsabile di restituirci le informazioni sulle porte di cui avremo bisogno per il nostro scanner nei formati corretti, partendo dai dati estratti dal file JSON.
def get_ports_info():
data = extract_json_data(PORTS_DATA_FILE)
ports_info = {int(k): v for (k, v) in data.items()}
return ports_info
Per molti, la riga {int(k): v for (k, v) in data.items()} potrebbe sembrare criptica e confusionaria: si tratta di una Dict Comprehension di Python, simile alle List Comprehension di cui abbiamo parlato nel tutorial dedicato.
Dict Comprehension in Python
Stiamo creando un nuovo dizionario a partire dai dati ottenuti da data.items, che restituisce a sua volta un elenco di tuple di tipo (k, v), dove k rappresenta la chiave e v il valore del dizionario restituito da extract_json_data(). Per ciascuna tupla vogliamo creare una nuova coppia chiave valore per il nostro dizionario ports_info, che abbia come chiave la versione int della stessa chiave, e come valore lo stesso valore. Quindi restituiamo il dizionario con return, in modo che possa essere utilizzato.
Modifichiamo il codice sotto a if __name__ == "__main__":
if __name__ == "__main__":
print("Programma scritto per solo scopo educativo!!!")
target = input("Inserire Target: ")
ip_addr = get_host_ip_addr(target)
ports_info = get_ports_info()
for port in ports_info.keys():
try:
print(f"Scanning: {ip_addr}:{port}")
scan_port(ip_addr, port)
except KeyboardInterrupt:
print("\nExiting...")
break
print("Open Ports:")
for port in OPEN_PORTS:
print(str(port), ports_info[port])
Creiamo anzitutto il dizionario ports_info utilizzando la funzione get_ports_info(). Quindi, per ogni porta in esso contenuta (per ogni chiave del dizionario) effettuiamo la scansione sull'IP del target scelto dall'utente e per ogni porta aperta individuata mandiamo in print sia il numero che il servizio associato!
Di seguito il codice completo per questa lezione:
import json
import socket
OPEN_PORTS = []
PORTS_DATA_FILE = "./common_ports.json"
def extract_json_data(filename):
with open(filename, "r") as file:
data = json.load(file)
return data
def get_ports_info():
data = extract_json_data(PORTS_DATA_FILE)
ports_info = {int(k): v for (k, v) in data.items()}
return ports_info
def get_host_ip_addr(target):
try:
ip_addr = socket.gethostbyname(target)
except socket.gaierror as e:
print(f"C'è stato un errore... {e}")
else:
return ip_addr
def scan_port(ip, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1.0)
conn_status = sock.connect_ex((ip, port))
if conn_status == 0:
OPEN_PORTS.append(port)
sock.close()
if __name__ == "__main__":
print("Programma scritto per solo scopo educativo!!!")
target = input("Inserire Target: ")
ip_addr = get_host_ip_addr(target)
ports_info = get_ports_info()
for port in ports_info.keys():
try:
print(f"Scanning: {ip_addr}:{port}")
scan_port(ip_addr, port)
except KeyboardInterrupt:
print("\nExiting...")
break
print("Open Ports:")
for port in OPEN_PORTS:
print(str(port), ports_info[port])