Categories: ⚔️, Hacking8 min read

Scanners

Prendendo ancora una volta spunto dal manifesto/libro The Fallen Dreams che ci ha accompagnati nei meandri dell’Hacking, il seguente articolo si presenta come uno spin-off della serie, in cui vestiremo il ruolo di uno degli avversari del protagonista.

Cambiare prospettiva è difatti uno dei punti chiave per trovare la soluzione a problemi che sembrano insormontabili, non solo nella vita quotidiana, ma anche in ambito Hacking. Mettersi nei panni di qualcun altro ci dà modo di affrontare il problema da un’angolazione differente e prendendo in analisi nuovi punti di vista, ci permette di pensare in maniera trasversale, fuori dai “nostri” schemi mentali. Questa è una caratteristica fondamentale per un Hacker. Lo è anche per un Cracker, ma essi rimangono quasi sempre soggetti ad uno dei fondamentali assunti del sistema: potere/profitto.

Quindi, nelle vesti di uno degli avversari del manifesto, vediamo come scrivere uno scanner utilizzando il linguaggio Go, impiegandolo per scoprire quali porte e servizi sono esposti e magari vulnerabili su un dato target.

Perché utilizziamo Go?

  • Cross-compilation: una delle features native più interessanti del linguaggio Go, è la possibilità di compilare eseguibili per più piattaforme. Ad esempio, possiamo facilmente scrivere codici da lanciare su sistemi Linux o Mac, compilandoli in ambiente Windows.
  • Ricchezza della libreria standard: Go include molteplici funzioni e librerie all’interno dei pacchetti standard, differenziandosi da molti linguaggi di programmazione che nella loro libreria standard, non offrono la possibilità di lanciare task di data encoding, crypto, network communications, connettività, database, etc…
  • Concorrenziale: il linguaggio Go nasce nel periodo in cui si iniziava a produrre tecnologia basata su processi multicore. Il linguaggio è orientato al threading concorrenziale ed è sviluppato per l’ottimizzazione delle performance e dei processi basati su questa tecnologia.
  • Package management: È possibile scaricare, compilare ed installare pacchetti e dipendenze, con estrema facilità.

Il TCP Handshake

Prima di passare a scrivere uno Scanner TCP, dobbiamo capire come funziona il TCP Handshake.

Il Three-way- handshake è la procedura con cui gli host instaurano una connessione TCP, in 3 passaggi.

Vediamo cosa succede nei panni del nostro attaccante, nel caso in cui la porta si presenti: aperta, chiusa o filtrata.

Open Port

  1. Come prima cosa l’attaccante/client invia pacchetti SYN (synchronize) che indicano l’inizio della comunicazione.
  2. Quando il server riceve il pacchetto SYN, risponde con una “ricevuta di conferma”: Acknowledge (ACK) – SYN/ACK.
  3. L’attaccante/client, una volta ricevuta la conferma, risponde a sua volta con un pacchetto ACK (acknowledege).

Closed Port

  1. Come prima cosa l’attaccante/client invia pacchetti SYN (synchronize) che indicano l’inizio della comunicazione.
  2. Se la porta a cui si sta tentando la connessione è chiusa, il server risponde con un pacchetto RST che indica il reset immediato della connessione.

Filtered Port

Qualora fra attaccante/client e Server ci fosse di mezzo un Firewall, la richiesta di connessione (SYN) andrebbe in time-out (non ricevendo alcuna risposta).

  1. L’attaccante/client invia pacchetti SYN (synchronize) che indicano l’inizio della comunicazione.
  2. Nessuna riposta dal server.

Integrated Development Enviroment

Possiamo ora passare all’installazione dell’ambiente dove sviluppare il nostro tool. Esistono molteplici Integrated Development Enviroment (IDE) dove poter scrivere il nostro codice, ad esempio:

  • L’editor Vim: https://github.com/fatih/vim-go
  • GitHub Atom: https://atom.io/
  • Microsoft Visual Studio Code: https://code.visualstudio.com/docs/languages/go

Ipotizziamo che il nostro attaccante decida di voler sviluppare il tool scegliendo l’IDE di Microsoft Visual Studio Code, sulla sua versione di Kali Linux.

  1. Aggiorniamo il nostro repository e installiamo il package:

sudo apt-get update

sudo apt install curl gpg software-properties-common apt-transport-https

  1. Importiamo la GPG Key di Microsoft nella nostra Kali Linux utilizzando il comando:

curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add –

  1. Aggiungiamo il repository per Visual Studio Code alla nostra Kali Linux:

echo “deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main” | sudo tee /etc/apt/sources.list.d/vscode.list

  1. Installiamo Visual Studio Code:

sudo apt update

sudo apt install code

  1. Lanciamo l’ambiente IDE appena installato:

code

AMBIENTE DI LABORATORIO

Il nostro ambiente di laboratorio sarà così configurato:

  • MACCHINA ATTACCANTE: Kali Linux – IP: 192.168.178.3
  • MACCHINA VITTIMA: Windows Server 2019 x64bit – IP: 192.168.178.12 con installato il servizio IIS in esecuzione sulla porta 80

Per attivare il servizio IIS sulla nostra macchina vittima, Windows Server 2019, ci basterà:

  1. Aprire il Server Managere cliccare su Manage Add Roles and Features. Cliccare su Next
  2. Selezionare Role-based o feature-based installation. Cliccare su Next
  3. Selezionare il server (la nostra macchina dovrebbe essere selezionata di default). Cliccare su Next
  4. Abilitare Web Server (IIS)e cliccare su Next
  5. Ancora
  6. Sulla finestra Web Server Role (IIS)dialog box, cliccare su Next
  7. Nella finestra Select role services, i componenti relativi al web server, devono essere enable Cliccare su Next
  8. Cliccare su Install

PORT SCANNER – TARGET IP&PORT

  • Stabilire una connessione:

Per stabilire la connessione dalla nostra macchina attaccante alla macchina vittima utilizzeremo il pacchetto net di Go:

net.Dial(network, address string)

network: è il primo argomento che specifica il tipo di connessione da stabilire.

address string: il secondo argomento che identifica l’host, la macchina vittima. La stringa da passare è costituita da host:port

In questo esempio stabiliremo la connessione verso il nostro server vittima, all’IP: 192.168.178.12 porta:80 . Il codice conterrà quindi: net.Dial(“tcp”, “192.168.178.12:80”)

  • Stato della connessione:

Adesso sappiamo come stabilire una connessione, ma come facciamo a sapere se è avvenuta con successo?

net.Dial ritorna come risultati Conn e error. Nello specifico, error sarà nil se la connessione è avvenuta con successo. Quindi, per verificare se la porta al dato indirizzo IP sia aperta, ci basterà verificare che error sia uguale a nil.

  • Stampiamo a video il risultato:

Analogamente alle funzioni printf e scanf di C, il pacchetto fmt implementa le funzioni di I/O. Lo utilizzeremo per stampare a video il testo.

Una volta compresa la logica del nostro primo scanner, possiamo lanciare il nostro IDE e procedere con la sua creazione.

Il Codice in Go:

package main

 

import (

      “fmt” // Input/Output

      “net” // Libreria per le connessioni

)

 

func main() { //main

      _, err := net.Dial(“tcp”, “192.168.178.12:80”) // Stabiliamo la connessione

      if err == nil {                                // Se l’errore è “nil”…

             fmt.Println(“Porta Aperta”) //…Allora la connessione è possibile alla data porta

      }

} // close main

 

  • Testiamo il Codice:

Non ci resta che lanciare il nostro codice. Per farlo spostiamoci in basso all’interno dell’IDE e selezioniamo il tab TERMINAL. Digitiamo il comando go run [nomedelnostroprogramma].

In questo caso, se tutto sarà andato come previsto, il nostro programma restituirà a video: Porta Aperta

PORT SCANNER – TARGET IP e PORTE DA 1 a 1024

Ora dobbiamo ottimizzare il nostro programma al fine di scansionare più porte in maniera automatica.

  • Ciclo For:

Per scansionare le prime 1024 porte implementeremo un ciclo for:

for i := 1; i <= 1024; i++ {

}

Per i minore o uguale a 1024, ripeti il ciclo ed incrementa di 1 il valore di i.

  • Sprintf

Ora si presenterà un piccolo inconveniente: il valore contenuto all’interno della variabile i (del cliclo for) è un interno, ma il secondo argomento di Dial (address string) deve essere di tipo stringa. Per convertire un intero in una stringa utilizziamo Sprintf.

Passiamo quindi alla variabile address (o il nome che sceglieremo) la stringa di testo composta dall’indirizzo IP della vittima ed il numero della porta assegnatagli dal ciclo for.

for i := 1; i <= 1024; i++ { // Il nostro ciclo for

             address := fmt.Sprintf(“192.168.178.12:%d”, i)

      }

Riscriviamo il nostro codice Go e rianalizziamolo:

package main

 

import (

      “fmt”

      “net”

)

 

func main() { //main

      for i := 1; i <= 1024; i++ {

             address := fmt.Sprintf(“192.168.178.12:%d”, i)

             connessione, err := net.Dial(“tcp”, address)

             if err != nil {

                  

                   continue

             }

             connessione.Close()

             fmt.Printf(“Porta Aperta %d\n”, i)

      }

}

Il ciclo for viene eseguito 1024 volte, incrementando la variabile i ad ogni loop.

Per ogni singolo ciclo:

– All’interno di address viene passato come stringa l’indirizzo IP della vittima ed il numero della porta, contenuto nella variabile i (ed incrementato di 1 ad ogni round del ciclo).

– All’interno della variabile err, passiamo il risultato di net.Dial

Se il risultato del Dial è diverso da nil , allora la porta sarà chiusa; viceversa chiudiamo la connessione e stampiamo a video “Porta Aperta” con valore attuale del contatore i.

È giunta l’ora di testare il nostro script.

Spostiamoci sulla nostra macchina vittima e apriamo il command prompt di windows utilizzando la combinazione di tasti win+R, digitiamo cmd e premiamo invio.

Diamo il comando netstat –aon e vedremo le porte aperte sulla macchina vittima in stato di LISTENING ed il PID identificativo del processo associato. Il nostro script tenterà la connessione dalla porta 1 alla 1024:

  • netstat: visualizza lo stato delle connessioni
  • -a : anche i socket non attivi
  • -o : visualizza il PID associato al processo
  • -n : mostra gli indirizzi degli host e relative porte

Se i nostri “calcoli” sono esatti, una volta lanciato, il nostro scanner dovrebbe comunicarci 8 porte aperte sulla macchina vittima.

Non ci rimane che verificarlo. Spostiamoci quindi sulla nostra macchina attaccante ed all’interno dell’interfaccia IDE nel menù TERMINAL diamo il comando go run [nomedelnostroprogramma].

Una volta lanciato appariranno 8 porte aperte, esattamente quelle che abbiamo sulla macchina vittima.

Ci siamo! Il nostro scanner in Go è pronto.

Lancio a voi la sfida di migliorare lo scanner come meglio credete (ad esempio implementando la scansione di porte multiple in maniera concorrenziale, per migliorarne la velocità)!

 

Go to Top