Basic Sniffer Tutorial

Transcript

Basic Sniffer Tutorial
Basic Sniffer Tutorial
LnZ <[email protected] >Aspinall <[email protected] >
S.P.I.N.E. Research Group
April 2, 2003
Abstract
Con questo semplice esempio cerchero’ di mostrarvi l’uso di base delle
socket raw e delle ”ioctl” necessarie a scrivere uno sniffer nel modo piu’
semplice possibile. E’ necessaria una conoscenza di base dei protocolli di
rete prima di cominciare a scrivere anche solo una linea di codice di uno
sniffer, se non ne avete vi consiglio di darvi un’occhiata ai vari Stevens
(se non sapete cosa siano vergognatevi ;) ). Il nostro amico Google vi dar
una mano, provate con una semplice ricerca di ”TCP/IP Illustrated”1 .
Il codice non e’ assolutamente ottimizzato, e’ studiato per essere letto
facilmente.
Contents
1 Descrizione Codice
1
2 Codice Sniffer in C
6
3 Esempio di utilizzo Raw Sockets <Aspinall >
7
4 Ringraziamenti
1
11
Descrizione Codice
Iniziamo subito con la definizione delle strutture che il nostro sniffer utilizzera’
qui di seguito vi riporto le strutture per una macchina ”Little Endian”:
typedef struct {
unsigned int h_len:4; //Lunghezza Header ip
unsigned int ip_version:4; //Versione protocollo IP
//Se avete una macchina "Big Endian" invertite i due campi precedenti
unsigned char tos; //Tipo del servizio
unsigned short t_len; //Lunghezza totale
unsigned short ident; //IP Ident
unsigned short frag_and_flags; //Frammenti e Flags
unsigned char ttl; //Time To Live
unsigned char proto; //Protocollo di Trasporto
unsigned short checksum; //Checksum pacchetto IP
1 Guardate
la bibliografia per i riferimneti
1
unsigned int source_ip;//IP sorgente
unsigned int dest_ip;//IP destinazione
}IP_Header;
typedef struct {
unsigned short source;//Porta sorgente
unsigned short dest;//Porta destinazione
unsigned int seq;//Numero della sequenza
unsigned int ack_seq;//Numero di Acnowledge
unsigned char unused;//Non utilizzato
unsigned char doff:4;Offset dei dati ;)
//Se avete una macchina "Big Endian" invertite i due campi precedenti
unsigned char flags;//Flags TCP
unsigned short checksum;//Checksum pacchetto TCP
unsigned short urg_ptr;//Urgent Pointer
}TCP_Header;
Queste due strutture, le inseriremo in un file header che poi importeremo all’interno
del nostro codice. Chiamatelo inet structures.h.
Ed ora iniziamo a dare un’occhiata al codice e a commentarlo (non spaventatevi!
;) ) riga per riga, il codice completo lo trovate alla fine:
#include <stdio.h>
Questa include e’ per le funzioni di i/o
#include <sys/socket.h>
Senza questa le socket ve le scordate ;)
#include <sys/ioctl.h>
Questa ci serve per poter utilizzare la funzione ioctl().
#include <net/if.h>
Funzioni per accedere alle interfacce.
#include <netinet/in.h>
Strutture necessarie all’utilizzo delle socket
#include <arpa/inet.h>
Contiene funzioni per fare conversioni da network a host byte order.Vi ricordate
little/Big Endian? no? leggetevi lo Stevens!!
#include <unistd.h>
#include "inet_structures.h"
Questa e la nostra libreria contenente le nostre strutture.
#define IFACE "eth0"
Interfaccia sulla quale vogliamo effettuare lo sniffing.
2
int sock, bytes_received, fromlen;
In sock verra’ salvato il file descriptor della connessione (ricordatevi che in Unix
le socket vengono viste come file particolari), mentre Bytes received, verra’ utilizzata come buffer per il salvataggio dei bytes ricevuti(ho detto ricevuti e non
letti!).
fromlen viene utilizzata come variabile d’appoggio per salvare la dimensione
della struttura sockaddr in.
char buffer[65535];
Buffer utilizzato per salvare i byte letti dalla socket.
struct sockaddr from;
struct IP_Header *ip;
struct TCP_Header *tcp;
Queste sono le strutture che utiliziamo.
Inet_SetPromisc(IFACE);
Questa e’ la funzione che utiliziamo per aprire la socket e per settare l’interfaccia
in modalita’ promiscua.
La modalita’ promiscua ci serve per poter analizzare tutti i pacchetti che viaggiano sulla nostra Lan locale, in pratica la nostra scheda di rete ”cattura” anche
i pacchetti con indirizzo MAC diverso dal proprio.
Ovviamente saranno visibili solo i pacchetti che viaggiano sul nostro stesso segmento di rete, unico modo per vedere anche pacchetti su altri segmenti e’ utilizzare l’arp poisoning, in questo modo la nostra scheda si identifichera’ come
destinatario dei paccheti degli altri host nella loro arp cache.
sock = Inet_SetPromisc(IFACE);
Serve a creare la socket e a mettere in promiscuo la scheda, la funzione ve la
spiego in dettaglio piu’ avanti.
while(1)
{
In questo modo lo sniffer analizza in modo continuo i pacchetti.
fromlen = sizeof(from);
dimensione della strutura sockaddr in.
bytes_recieved = recvfrom(sock, buffer, sizeof buffer,
0, (struct sockaddr *)&from, &fromlen);
int recvfrom(int sockfd, void *buf, int len, unsigned int flags
struct sockaddr *from, int *fromlen);
Con questa chiamata, ci ritroveremo ad avere la struttura from riempita con
tutti i dati della macchina da cui il pacchetto proviene.
printf("\nBytes received ::: %5d\n",bytes_recieved);
3
recvfrom() restituisce il numero di byte che riesce a leggere dalla socket o -1 in
caso di errore
printf("Source address ::: %s\n",inet_ntoa(from.sin_addr));
Stampo l’IP sorgente del pacchetto.
ip = (struct ip *)buffer;
//Guardo se e’ un paccheto TCP
if(ip->proto == 6) {
printf("Lunghezza Header = %d\n",ip->h_len);
printf("Protocollo = %d\n",ip->proto);
tcp = (struct tcp *)(buffer + (4*ip->h_len));
printf("Porta Sorgente = %d\n",ntohs(tcp->source));
printf("Porta Destinazione = %d\n",ntohs(tcp->dest));
}
Analizzo l’header IP e stampo le informazioni a video.
int Inet_OpenRawSock() {
int sock;
if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
perror("Errore creazione socket");
exit(0);
};
return(sock);
}
Con questa funzione creiamo la nostra socket in modalita’ raw e con protocollo
TCP.
int socket(int domain, int type, int protocol);
Alla funzione socket() che si occupa di definire il protocollo vengono passati tre
parametri :
domain : Definisce un dominio di comunicazione,ovvero la famiglia del protocollo che sara’ usato per la comunicazione.
PF_INET : ipv4 protocollo internet
PF_INET6 : ipv6 protocollo internet
PF_UNIX : Comunicazione locale
PF_NS : Protocolli di Xerox NS
PF_IMPLINK : Livello di collegamento IMP
*PF (protocol family) e AF (address family) sono equivalenti.
type : Indica il tipo di socket
SOCK_STREAM : streaming connection (TCP)
SOCK_DGRAM : datagram communication (UDP)
SOCK_RAW : Accesso "grezzo" al protocollo di rete (IP)
protocol : Indica il particolare protocollo utilizzato dal socket0 : il sistema usa
il protocollo piu’ adatto al tipo di socket
IPPROTO_UDP : udp
IPPROTO_TCP : tcp
IPPROTO_ICMP : icmp
IPPROTO_RAW : ip
4
int Inet_SetPromisc(char *iface, int sock ) {
int sock;
struct ifreq ifr;
sock = Inet_OpenRawSock(iface);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
if((ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)) {
perror("Errore Interfaccia\n");
exit(0);
}
printf("Interfaccia: %s\n", iface);
//Setto l’interfaccia in modalita’ promiscua
if (!(ifr.ifr_flags & IFF_PROMISC))
{
ifr.ifr_flags |= IFF_PROMISC;
if ( ioctl(sock, SIOCSIFFLAGS, &ifr) < 0 ){
// promisc mode
perror("Errore non riesco a settare la modalita’ promiscua");
exit(0);
}
}
printf("Interfaccia %s settata in modo promiscuo", iface);
return(sock);
}
Questa funzione e’ il cuore del nostro programma, prende la nostra interfaccia di
rete
e
la
”setta”
in
modalita’
promiscua.
ifr e’ la struttura tramite utilizzata dalla funzione ioctl per accedere ai parametri
della nostra interfaccia.
ioctl(sock, SIOCGIFFLAGS, &ifr)
Con questa chiamata, andiamo a leggere quali sono tutti i parametri della nostra
interfaccia,
per
poi
poterli
modificare.
Il primo parametro e’ la socket su qui vogliamo lavorare, mentre il secondo
e’ la richiesta che vogliamo efettuare, ifr ci serve per salvare i valori della ioctl.
ifr.ifr_flags |= IFF_PROMISC;
Qui modifico i parametri dell’interfaccia per poi renderli effettivi tramite la ioctl
successiva.
if (ioctl (sock, SIOCSIFFLAGS, &ifr) == -1 )
Questa seconda chiamata alla ioctl rende effettivi i parametri che ho modificato
nella struttura ifr.
5
2
Codice Sniffer in C
#include
#include
#include
#include
#include
#include
#include
#include
<stdio.h>
<sys/socket.h>
<sys/ioctl.h>
<net/if.h>
<netinet/in.h>
<arpa/inet.h>
<unistd.h>
"inet_structures.h"
#define IFACE "eth0"
//prototipi
int Inet_OpenRawSock(void);
int Inet_SetPromisc(char *iface);
int main() {
int sock, bytes_recieved, fromlen;
char buffer[65535];
struct sockaddr from;
IP_Header *ip;
TCP_Header *tcp;
//Creiamo la socket e mettiamola in promiscuo
sock=Inet_SetPromisc(IFACE);
while(1)
{
fromlen = sizeof from;
bytes_recieved = recvfrom(sock, buffer, sizeof(buffer),
0, (struct sockaddr *)&from, &fromlen);
if (bytes_recieved < 0){
printf("sock=%d",sock);
perror("Errore recv");
exit(1);
}
printf("\nBytes received ::: %5d\n",bytes_recieved);
//printf("Source address ::: %s\n",inet_ntoa(from.sin_addr));
ip = (IP_Header *)buffer;
//Guardo se e’ un paccheto TCP
if(ip->proto == 6) {
printf("Lunghezza Header = %d\n",ip->h_len);
printf("Protocollo = %d\n",ip->proto);
tcp = (TCP_Header *)(buffer + (4*ip->h_len));
printf("Porta Sorgente = %d\n",ntohs(tcp->source));
printf("Porta Destinazione = %d\n",ntohs(tcp->dest));
}
}
}
6
int Inet_OpenRawSock() {
int sock;
if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
perror("Errore creazione socket");
exit(0);
};
return(sock);
}
int Inet_SetPromisc(char *iface) {
int sock;
struct ifreq ifr;
sock = Inet_OpenRawSock();
printf("sock promisc=%d",sock);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
if((ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)) {
perror("Errore Interfaccia\n");
exit(0);
}
printf("Interfaccia: %s\n", iface);
//Setto l’interfaccia in modalita’ promiscua
if (!(ifr.ifr_flags & IFF_PROMISC))
{
ifr.ifr_flags |= IFF_PROMISC;
if ( ioctl(sock, SIOCSIFFLAGS, &ifr) < 0 ){
// promisc mode
perror("Errore non riesco a settare la modalita’ promiscua");
exit(0);
}
}
printf("Interfaccia %s settata in modo promiscuo", iface);
return(sock);
}
3
Esempio di utilizzo Raw Sockets <Aspinall >
/****************************************************************
* Copyright (c) 2003
*
* author : <[email protected]> or <[email protected]>*
* send to host a rst flag with ip spoofed*
* compile gcc spoof_tcp.c -o spoof_tcp
*
* Use : #./spoof_tcp 1.1.1.1 212.4.13.231
*
7
* License : This source file is under GPL
*
* Only for Linux kernel
*
*
*
*
*
* Special thanks to : #networking@azzurranet
*
*
mydecay <[email protected]>
*
*
sviat <[email protected]>
*
* This code is derived from my knowledge of raw sockets,
*
* due to lack of well-done documentation on the web. you
*
* can use it to forge your own tcp packets,all you’ve got
*
* to do is change header to suit your needs.
*
*
*
* Disclaimer:
*
* Use of this information constitutes acceptance for use in
*
* an AS IS condition.There are NO warranties with regard to
*
* this information. In no event shall the author be liable for *
* any damages whatsoever arising out of or in connection with
*
* the use or spread of this information. Any use of this
*
* information is at the user’s own risk.
*
****************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<stdlib.h>
<stdio.h>
<sys/types.h>
<sys/socket.h>
<netinet/in.h>
<arpa/inet.h>
<netinet/in_systm.h>
<netinet/ip.h>
<netinet/tcp.h>
<string.h>
<unistd.h>
<netdb.h>
<time.h>
void send_rst(void);
/* algoritmo del checksum */
unsigned short in_cksum(unsigned short *addr,int len)
{
int sum = 0;
u_short answer = 0;
u_short *w = addr;
int nleft = len;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1 ) {
8
*( u_char* ) ( &answer ) = *( u_char * )w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return(answer);
}
// main()
main(int argc, char *argv[])
{
struct pseudo_header {
u_long saddr;
u_long daddr;
char useless;
char protocol;
u_short length;
};
char buf[256], buf2[256];
struct
struct
struct
struct
struct
hostent *host;
hostent *host2;
iphdr *ip = (struct iphdr *) buf;
tcphdr *tcp = (struct tcphdr *) (buf + sizeof(struct iphdr));
pseudo_header *pseudo = (struct pseudo_header *)buf2;
struct sockaddr_in addr;
struct sockaddr_in addr2;
int fd, on=1;
if (geteuid ()) {
fprintf (stderr, "You should be root\n");
exit (1);
}
if (argc < 3) {
printf("Usage : <spoof_sorg> <host_dest> <port>\n");
exit(1);
}
if ((host = gethostbyname(argv[1])) != NULL)
memcpy (&addr.sin_addr, host->h_addr, host->h_length);
else if((addr.sin_addr.s_addr = inet_addr(argv[1])) == INADDR_NONE) {
herror("gethostbyname");
9
exit(1);
}
if ((host2 = gethostbyname(argv[2])) != NULL)
memcpy (&addr2.sin_addr, host->h_addr, host->h_length);
else if((addr2.sin_addr.s_addr = inet_addr(argv[2])) == INADDR_NONE) {
herror("gethostbyname");
exit(1);
}
if((fd = socket(PF_INET,SOCK_RAW,IPPROTO_TCP)) < 0) {
perror("socket");
exit(1);
}
if(setsockopt(fd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)) < 0) {
perror("setsockopt");
exit(1);
}
/* memoria a 0 */
memset(ip, 0, sizeof(struct iphdr));
memset(tcp, 0, sizeof(struct tcphdr));
memset(pseudo, 0, sizeof(struct pseudo_header));
/* HEADER */
pseudo->saddr = inet_addr(argv[1]); /* sorgente */
pseudo->daddr = inet_addr(argv[2]); /* destinazione */
pseudo->useless = 0; /* inutile :P */
pseudo->protocol = IPPROTO_TCP; /* tcp */
pseudo->length = htons(sizeof(struct tcphdr)); /* lunghezza header */
ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
ip->id = htons((u_short)random());
ip->frag_off = htons(IP_DF);
ip->ttl = 255;
ip->protocol=IPPROTO_TCP;
ip->saddr = addr.sin_addr.s_addr;
ip->daddr = addr2.sin_addr.s_addr;
ip->check = 0;
tcp->source = htons(rand()); /* guardare sopra*/
tcp->dest = htons(atoi(argv[3])); /* guarda sopra */
tcp->seq = htonl(random()); /* dopo */
tcp->ack_seq = 0; /* dopo */
10
tcp->doff = 5; /* offset */
tcp->fin = 0;
tcp->syn = 0;
tcp->rst = 1;
tcp->psh = 0;
tcp->ack = 0;
tcp->urg = 0;
tcp->window = htons(4000);
tcp->urg_ptr = 0;
tcp->check = 0;
/* checksum */
memcpy(buf2, pseudo, sizeof(struct pseudo_header));
memcpy(buf2 + sizeof(struct pseudo_header), tcp, sizeof(struct tcphdr));
memset(buf2 + sizeof(struct pseudo_header) + sizeof(struct tcphdr), 0, 12);
tcp->check = in_cksum((unsigned short *)buf2,(sizeof(struct pseudo_header) + sizeof(struct tcphdr) + 12
ip->check = in_cksum((unsigned short *)buf2,(sizeof(struct iphdr) + sizeof(struct tcphdr) + 12) & ~1);
/* send */
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = addr2.sin_addr.s_addr;
addr.sin_port = htons(7);
if(sendto(fd,buf, ip->tot_len, 0,(struct sockaddr *)&addr, sizeof(addr)) < 0 )
perror("sendto");
exit(1);
}
printf("the ip header is %d bytes long.\n", sizeof(struct iphdr));
printf("the tcp header is %d bytes long.\n", sizeof(struct tcphdr));
printf("ip checksum correct\ntcp checksum correct\n");
printf("done\n");
return 0;
}
4
Ringraziamenti
Eccovi i soliti noiosi ringraziamenti (mi costringono con la forza a farli!!)
Un saluto a tutto il gruppo degli spine e ovviamente anche a tutti i ragazzi di
#phrack.it;)
...dimenticavo di salutare anche tutte le rosse con gli occhi verdi!! che vi credevate?che vivessi di sola informatica?:)
Ovviamente saluto anche il miglior chan di Azzurra #networking.
11
{
References
[1] W. Richard Stevens. The Protocols (TCP/IP Illustrated, Volume 1)
Edition: Hardcover Stevens1
[2] W. Richard Stevens. T The Implementation (TCP/IP Illustrated,
Volume 2) Edition: Hardcover Stevens1
12