Data over ICMP project:

...yet another protocol tunneling

1.1 Intro

Dunque, dunque... era una notte buia e tempestosa... freddo e gelo fuori dalla finestra. Era una di quelle serate in cui...
Uhm... No. A parte il fatto che non sono parole mie, l'intro stile bfi non mi si addice proprio. Ricominciamo. :-)
Lo so. I 'data over protocol' non sono certo una novità. Nel corso degli anni nella comunità cosidetta 'underground' si sono visti applicare stravolgimenti di ogni genere ai protocolli: dall'HTTP tunneling a vere e proprie assurdità quali l''IP over DNS, giusto per citarne una delle tante, e un progetto di questo tipo non rappresenta di certo una novità, ma io che ci posso fare? :-)
Era diverso tempo che l'ideuzza mi frullava nella testa: prendere un protocollo utilizzato, in genere, per scopi ben precisi, modificarlo a basso livello e fargli espletare compiti per i quali non è stato progettato in origine.
Nel dettaglio l'idea iniziale era quella di 'costruire' da zero i vari campi di cui è composto il pacchetto ICMP, editarne il campo DATA posto in coda alla struttura e riempirlo di... dati, appunto.
Certamente non nato come protocollo di trasporto quanto più di controllo o 'diagnostica', ICMP torna assai utile in svariati casi, primo fra tutti la verifica riguardo lo stato online/offline di un host remoto (chi non ha mai usato il ping?).
La domanda 'perchè proprio ICMP?' in questo caso è tanto scontata quanto lecita.
Un protocollo progettato specificatamente per il trasporto come TCP, infatti, garantirebbe sicuramente una serie di vantaggi in più, primo fra tutti la maggiore sicurezza nel recapito dei dati.
La convenzione vuole che se esiste un posto affidabile in cui infilare dei dati per spedirli in modo corretto, ed essere ragionevolmente sicuri che essi arrivino a destinazione, quello sia proprio il campo DATA di un pacchetto TCP.
Tale campo (payload), inoltre, sarebbe editabile direttamente per mezzo dei più classici moduli socket in modo 'nativo' senza andare a scomodare SOCKET_RAW, incappare di conseguenza in problemi di privilegi, editare manualmente ogni singolo campo del pacchetto e via dicendo.
Perchè complicarsi la vita con ICMP allora? Perchè andare a gestire pacchetti grezzi e rischiare la perdita di pacchetti?
Il motivo è facilmente intuibile e presto detto: cercare di far passare il più possibile inosservata la connessione e cercare di evitare di farsi bloccare dal solito, immancabile firewall.
Inizializzare una connessione TCP 'costa' in termini di silenziosità. Decisamente tanto...
Un semplicissimo netstat sarebbe sufficiente a rendere noti i due socket comunicanti e l'immancabile firewall avrebbe non poco da ridire riguardo una nuova connessione che fa capolino cercando di comunicare con qualcuno all'esterno.
A riguardo molti potrebbero obbiettare facendo notare che la quasi totalità dei firewall effettua di default un blocco per i pacchetti TCP quanto per quelli ICMP, bisogna però considerare il fatto che in ambito server si è soliti dare libero accesso a ICMP praticamente quasi sempre (la prima prerogativa di un server è appunto quella di essere raggiungibile). Un amministratore, inoltre, potrebbe configurare il suo firewall affinchè non accetti gli ICMP di tipo ECHO(request) in entrata ma lasci passare quelli di tipo ECHOREPLY per poter usare il ping dalla propria macchina.
La verità è che ICMP è utile, specialmente per i già menzionati motivi di diagnostica. Molto difficilmente un amministratore vorrebbe farne a meno.
Questo è uno degli insoliti casi in cui un applicativo di questo tipo ha più probabilità di funzionare su solide macchine adibite a server anzichè client che possono essere utilizzati sì da un'utenza meno esperta, ma proprio in quanto tale non necessitante di un protocollo di questo tipo e solita a lasciare un applicativo firewall con settaggi di default i quali bloccano ICMP in modo sistematico (o perlomeno così accade nella maggior parte dei casi).
Da qui l'idea di utilizzare un protocollo che proprio per il suo quotidiano utilizzo amministrativo che ne permette solitamente il passaggio a livello firewall e per la sua natura poco adatta al trasporto, potrebbe rivelarsi un buon compromesso per muoversi nell'ombra, dapprima recapitando del semplice testo e dopo... chissà? Magari far eseguire dei comandi (whoops!).
Ma andiamo per ordine cominciando ad illustrare l'impiego più semplice per creare una base di partenza per poi, via via, sperimentare utilizzi più complessi e redditizi da integrare nel progetto padre. Non so se avrò voglia e risorse tecniche sufficienti per fare quello che mi sono prefissato di fare in seguito ma per ora preferisco rimanere ancorato al presente e vedere che succede. Si parte!

1.2 La pratica

Quello che si andrà a sviluppare in questa sede sarà un semplice applicativo client che recapiterà a una macchina remota dei pacchetti ICMP con un payload da noi specificato. Dall'altra parte ci dovrà essere un secondo applicativo ricevente in grado di sniffare il contenuto di tali pacchetti e stamparlo a video.
Per payload (carico) si intende il blocco di dati (header escluso) che il pacchetto si porta dietro, dall'host sorgente all'host di destinazione. In questo caso il payload è rappresentato dalla stringa di testo mandata dal client.
Il primo problema che appare evidente è il come distinguere i pacchetti ICMP derivanti dal nostro applicativo client da tutti gli altri pacchetti transitanti per la rete che possono venire ricevuti dalla macchina remota (tra i piu noti, gli ECHO(request) derivanti dai ping, ad esempio).
Tali pacchetti mandati 'lato client' si potrebbero differenziare dagli ICMP convenzionali inserendo un determinato valore a noi noto all'interno della stringa contenuta nel payload (es: il primo carattere della stringa dovrà essere == '@' ). Una sorta di 'firma', insomma.
Tramite l'utilizzo del modulo Impacket (chi conosce Python? :-)) verrà dapprima forgiata la struttura del pacchetto, e tramite un socket di tipo RAW verrà spedito al mittente.
Per simulare uno stack (molto spartano) al tutto si aggiungerà una funzione di frammentazione che lato client spezzetta i pacchetti con campo DATA > 54 bytes, in singoli pacchetti da 54 ognuno. Avendo una stringa di testo da 130 caratteri, ad esempio, si avranno due pacchetti con campo DATA da 54 bytes e un terzo pacchetto da 22, il tutto ovviamente mandato in sequenza, incrementando di volta in volta il valore del campo ID di ICMP.
Come tipo di ICMP ho optato per l'ECHOREPLY, almeno per ora, di modo da non avere pacchetti di ritorno a sporcare lo sniffing. Un ECHOREPLY ha, inoltre, statisticamente più probabilità di valicare un firewall (leggasi i motivi sopra citati).
Il codice (txt):

All'analisi di uno sniffer appare chiaro il risultato che si ottiene (screenshot).
L'applicativo ricevente sniffer, come detto in precedenza, dovrà essere in grado di ricevere i pacchetti, ignorare quelli convenzionali e stampare a video soltanto quelli aventi un payload firmato '@', dal nostro client, with love.
Il codice (txt):

Anche in questo caso uno screenshot è più esplicativo di mille parole.
Per far funzionare il tutto si avrà bisogno di:

  • Un interprete Python (versione usata 2.4.1)
  • Le librerie libpcap o Winpcap nel caso in cui si usi una piattaforma Windows.
  • La libreria impacket reperibile qui.
  • La libreria pcapy scaricabile da qui.

Se nella vita avete di meglio da fare che installare moduli e interpreti (posso capirlo), per comodità verso i Windows user non 'pitonati' ho compilato due binari, comodamente eseguibili da shell, reperibili qui.
Per eseguirsi non necessitano di alcun modulo o interprete aggiuntivo salvo le librerie Winpcap che devono essere necessariamente installate.

1.5 Si, ma...

So bene quanto voi che un applicativo del genere è fondamentalmente inutile, ma permettetemi di esporre alcune idee aggiuntive che potrebbero rendere il progetto decisamente più interessante.
Prendete quanto detto fino ad ora come una base da cui poter implementare qualcosa di più grande.

A look to the future: Command over ICMP

Anzitutto, al posto di far viaggiare semplici ed inutili stringhe di testo da far stampare allo sniffer perchè non mandare veri e propri comandi da fare interpretare ed eseguire alla macchina remota?
E se oltre a far eseguire un comando ne facessimo anche recapitare l'output alla macchina che lo ha richiesto? Si, direi che il tutto comincia ad acquistare un nuovo significato. :-)
L'idea nella pratica: il client manderà il solito pacchetto con un payload contenente il comando, lo sniffer posto in remoto interpreterà la stringa, processerà il comando in essa contenuto e infine impiegherà l'output del comando in un pacchetto ICMP che farà ritornare al client.
Una specie di "command over ICMP" o "shell over ICMP" a seconda dei gusti... (non che abbia scoperto qualcosa di nuovo, si intende... ma qualcosa nella vita la dovrò pur fare, no?
:-)).

Spoofing

Un'altra possibile 'feature' da aggiungere potrebbe essere sicuramente la possibilità di poter spoofare l'indirizzo ip sorgente. Trattandosi del più classico e rozzo blind spoofing non si avrebbero pacchetti di ritorno con cui permettere di visualizzare l'output dei comandi, certo, ma la silenziosità in questo caso comincerebbe a raggiungere livelli ragguardevoli, senza contare che il comando verrebbe comunque eseguito dal server.

Controllo sul recapito


Una grande debolezza di tutto il meccanismo resta indubbiamente quella intrinseca di ICMP data dall'impossibilità di assicurare la comunicazione robusta e affidabile garantita da TCP, ma a pensarci bene si potrebbe aggiungere una forma di controllo sul recapito dei messaggi tramite l'accoppiata ICMP ECHO(request) e ICMP ECHOREPLY.
Mi spiego meglio: entrambi i componenti non manderanno più dei 'one way packets' di tipo ECHOREPLY ma dei pacchetti di tipo ECHO(request) per mezzo dei quali si potrà effettuare il controllo sul recapito attendendo il pacchetto di ritorno.
Forse può non risultare chiarissimo:

  • A manda un pacchetto di tipo ECHO(request) a B, contenente il comando.
  • A continua a mandare il pacchetto a B fino a quando B non risponderà con un pacchetto ECHO_REPLY.
  • B processa il comando e ne incapsula l'output in un pacchetto di tipo ECHO(request) che rimanderà ad A.
  • B continua a mandare il pacchetto fino a quando A non risponderà con un pacchetto ECHOREPLY stante a indicare l'avvenuto recapito.
  • Solo allora B potrà continuare il ciclo di frammentazione mandando il secondo pacchetto, il terzo, il quarto e cosi via... e ogni nuovo ciclo adotterà ovviamente la stessa tecnica di verifica sul recapito.
client ICMP type

payload (DATA)

server
---> ECHO(request) @netstat --->
<--- ECHO_REPLY @netstat <---
<--- ECHO(request) @Connessioni attive
Proto Indirizzo locale Indirizzo esterno Stato
TCP startdus:1111 irc.daxnet.no:6667 ESTABLISHED
TCP startdus:1124 64.12.28.152:5190 ESTABLISHED 
<---
---> ECHOREPLY @Connessioni attive
Proto Indirizzo locale Indirizzo esterno Stato
TCP startdus:1111 irc.daxnet.no:6667 ESTABLISHED
TCP startdus:1124 64.12.28.152:5190 ESTABLISHED 
--->

Una sorta di TCP un po' spartano, insomma, ma il protocollo dopotutto è quello che è...
Gia che siamo in vena di creatività potremmo anche crittografare il payload sia in uscita che in entrata nel caso in cui un amministratore notasse il traffico insolito (si insospettirebbe comunque e chiunque vedendo del testo crittografato in una discreta quantità di pacchetti ICMP come minimo mette subito mano a iptables o chi per esso. Ma tant'è...)
Chiave privata? Chiave pubblica? Non lo so... non ho mai messo mano a cose di questo tipo ma si può sempre provare, no?
Le possibilità di impiego, come si vede, sono molteplici e l'unico limite è dato dalla fantasia di chi ha un po' di tempo da spendere dietro al monitor. Quello che ho fatto non è stato altro che prendere una manciata di idee, metterle su un piatto e aspettare che mi venisse voglia a sufficienza per provare a metterle in pratica.
Poi... chi sa cosa preserva il futuro?

Stay tuned.

 

Argomenti correlati

Shell over ICMP project


billiejoex
EOF 31/08/2005 - hh 02:43AM