RAW SOKET PROGRAMLAMA murat at enderunix.org 0x1 [GIRIS] Sun(TM), "socket" kavramini, "agda calisan iki program arasindaki iki yonlu haberlesme baglantisinin bir taraftaki sonlanici noktasi" olarak tanim- lar. Her soket bir portla iliskilidir. Boylece TCP katmani verinin yollanaca- gi ve alinacagi uygulamayi bulabilir. Network programcilari bilirler; bir network baglantisi acmak istedigimizde once, o baglantiya ozel bir "soket" olusturur ve bundan sonraki TCP yada UDP alis verisimizi bu soket descriptor'u uzerinden yapariz. Bu konu konu hakkinda bkz: http://www.enderunix.org/docs/socket.html Fakat "normal" soketler bizleri tamamen ozgur kilmiyor. Soketleri normal modunda kullandigimiz zaman yapabilecegimiz cogu seyi yapamiyoruz. Ne gibi? 1. ICMPv4, IGMPv6 gibi IP paketlerini yazamiyor ve okuyamiyoruz. Eger sadece "normal" soketlerle yasiyor olsaydik ping programini nasil yazacaktik? 2. Bazi isletim sistemi cekirdekleri, IPv4 protokol field'i ICMP, IGMP, TCP veya UDP olmayan paketleri islemiyorlar. Ya bizim propriatery bir protoko - lumuz varsa? Bunu nasil okuyup yazacagiz? Iste bunun gibi sorularimizin cevabi RAW socket'ler oluyorlar. Raw socket'ler ile normal TCP ve UDP soketlerle yapamadigimiz cogu seyi yapabili - yoruz. Raw socket'ler "privileged" kullanicilara, kullanici verisinin iletisimi icin kullanilan normal soketlere nazaran, protokole direk erisim saglamamiza olanak sagliyor. Bunlar, mevcut protokollerin ustune kurulan protokolleri gelistirmemizi kolaylastiriyor veya normal arabirim ile erisemedigimiz bircok module erismemizi sagliyorlar. Bu dokumanin en son versiyonlarina http://www.enderunix.org/docs/rawsocket.txt adresinden ulasabilirsiniz. 0x2 [ONERILER] Bu yaziyi okuyup anlayabilmeniz icin temel C bilgisine sahip olmaniz onerilir. Gdb ve gcc ile daha onceden calismis olmaniz teknik olarak isinizi kolaylastiracaktir. Ama daha once TCP/IP hakkinda detayli bir calisma yapmali - siniz. W. Richard Stevens'in TCP/IP Illustrated I kitabini siddetle oneriyorum. Referans'lar kisminda bu kitabi satin alabileceginiz bir link de veriyorum. 0x3 [TEMEL TCP/IP BILGILERI] Evet, sadece hatirlamak icin tekrar edelim. Yalniz bu bilgiler sizin burada neler dondugunu tam olarak anlamaniz icin yeterli olmayacaktir. Onun icin WRS'nin kitabinda ilgili bolumleri mutlaka okuyup anlamaniz gerekli. Butun ag protokolleri gibi, TCP/IP de katmanli bir yapidadir. Her katman iletisimin baska bir yanini yurutmekle yukumludur. TCP/IP'de dort katman vardir. -------------------------------------------------------- | 4. Application |telnet, ftp, dns etc. | -------------------------------------------------------- | 3. Transport | TCP UDP | -------------------------------------------------------- | 2. Network | IP ICMP IGMP | -------------------------------------------------------- | 1. Link | device driver, network card | -------------------------------------------------------- Sekil 1. TCP/IP protokolu katmanlari. Socket kullanan uygulamaniz biraz veri yolladiginda, bu paket TCP katmanina verilir. TCP katmani uygulamanin soket'ine ait port numarasi ve karsi makinadaki uygulamanin port numarasini ve bunun gibi kendi katmanina ait bilgileri yazdiktan sonra IP katmanina verilir. IP katmani da paketin onune kendi basligini ekler, ve paketin hangi IP adresinden geldi ve hangi IP adresine gidecegi gibi bilgileri paket'in icine yazar, paketi network'e birakilmak uzere 1. layer olan Link Layer'a verir. Link katmani da mac adresi ve protokol bilgilerini yazdiktan sonra paketi network'e birakir. Ayni sekilde, her gelen paket de, her layer de "demultiplex" edile edile paketin asil sahibi uygulamaya teslim edilir. 1. LINK KATMANI TCP/IP katmanlarindan ilkidir, paket ag'dan makinaya kabul edildiginde ilk bu katmana ugrar. Link katmaninin gorevi sunlardir: a) IP katmani icin datagramlarini almak/yollamak b) ARP katmani icin ARP request ve reply'larini almak/yollamak c) RARP modulu icin RARP requestleri Link katmanininda kullanilan aygita gore bu katmanin uzunlugu boyutu degisir. Bazi cok kullanilan Link katmani protokolleri arasinda (Ethernet, PPP, ATM ve SLIP'i sayabiliriz). 2. NETWORK KATMANI TCP/IP katmanlarinin ikincisidir ve ag baglantilarini handle eder. Uzunlugu genelde 20 byte'dir. Asagida genel kullanimda bir IP basligi field'lari verilmistir. IP Header field'lari /usr/include/ip.h 'dan struct ip { u_int ip_hl:4, /* header length */ ip_v:4; u_char ip_tos; /* type of service */ u_short ip_len; /* total length */ u_short ip_id; /* identification */ u_short ip_off; u_char ip_ttl; /* time to live */ u_char ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ }; Field Uzunluk Ornek ------------------------------ --------------- ------------------- Versiyon 4 bit 4 Baslik uzunlugu 4 bit 5 Type of Service 8 bit 0 Toplam uzunluk 16 bit 40 Identification 16 bit 8123013 Flags 3 bit 0 Fragment Offset 13 bit 0 Time to Live (a.k.a TTL) 8 bit 64 Protokol 8 bit 6 [TCP] Checksum 16 bit 0x3a43 Kaynak IP adresi 32 bit Hedef IP adresi 32 bit 3. TRANSPORT katmani Bu katman da ag verisinin programlardan alinip, programlara ulastirila bilmesi icin gerekli fonksiyoneliteye sahiptir. TCP ve UDP protokolleri bu katmanda yer almaktadirlar. UDP, basit ve guvenilirligi olmayan bir datagram protokoludur. TCP ise tam tersine, daha kompleks ve guvenilirligi yuksek bir Transport Katmani protokoludur. Transport katmaninda hangi protokolun calisacagi, Network katmaninda IP paketinde protokol field'i icinde belirtil- mektedir. Asagidaki tablolarda UDP ve TCP protokollerinin protokol header'leri verilmistir, inceleyiniz. UDP protokolu field'lari: /usr/include/netinet/udp.h 'dan /* * Udp protocol header. * Per RFC 768, September, 1981. */ struct udphdr { u_short uh_sport; /* source port */ u_short uh_dport; /* destination port */ u_short uh_ulen; /* udp length */ u_short uh_sum; /* udp checksum */ }; Bu durumda; Field Uzunluk Ornek ------------------------------ --------------- ------------------- Kaynak Port 16 bit 12831 Hedef Port 16 bit 53 UDP datagram uzunlugu 16 bit 321 UDP checksum 16 bit 0xeb8a TCP protokolu field'lari /usr/include/netinet/tcp.h 'dan /* * TCP header. * Per RFC 793, September, 1981. */ struct tcphdr { u_short th_sport; /* source port */ u_short th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ u_int th_x2:4, /* (unused) */ th_off:4; /* data offset */ u_char th_flags; u_short th_win; /* window */ u_short th_sum; /* checksum */ u_short th_urp; /* urgent pointer */ }; Field Uzunluk Ornek ------------------------------ --------------- ------------------- Kaynak Port 16 bit 12783 Hedef Port 16 bit 80 Sequnce Number 32 bit Ack Number 32 bit x2 4 bit Data Offset 4 bit Flags 8 bit Window Buyuklugu 16 bit TCP Checksum 16 bit 0xebcd Urgent Pointer 16 bit Evet kisaca protokoller boyle. 4. katmanda da uygulamalarin verileri bulunuyor, ve direk uygulamadan alinip uygulamaya teslim ediliyor. Siz normal soket kullandiginiz zaman iste bu 4. katmani kontrol edebiliyoruz. RAW soket kullanarak 2. katmana, hatta patch'ler kullanarak 1. katmana kadar kontrol edebiliyoruz. [0x4] RAW SOKET OLUSTURMA Raw soketlerimizi de aynen normak soketlerde oldugu gibi socket(2) system call'i ile olusturuyoruz. int socket(int domain, int type, int protocol); Fakat, type ve protokol parametleri sirasiyla SOCK_RAW ve protokol ismi oluyor: if ((sd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) < 0) { .. } Bu durumda hemen UDP paketleri yazmaya baslayabiliriz. Ama 2. katman header'i (IP) gene kernel tarafindan doldurulup yollanacaktir. 2. katmani da kendimiz olusturmak icin IP_HDRINCL socket opsiyonunu socket'e uygulamaliyiz: const int on = 1; if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on) < 0) { .. } Bazi durumlarda bu raw soket'le bind ve connect'le islem yapabilirsiniz ama eger raw soket yaziyorsaniz, fleksibilite'den hicbirsey kaybetmek istemeye- ceksiniz. Raw soket'imizi olusturduktan sonra sendto(2) ve sendmsg(2) system call'lari ile raw socket'e yazabiliriz. Eger IP spoofing yapmiyorsaniz ve yolladiginiz mesajin cevabini bekli- yorsaniz bilmelisiniz ki , TCP ve UDP paketleri hicbir zaman raw soket'lere dagitilmazlar. Bu paketler icin match edecek bir uygulama ararlar. Raw socket'e TCP veya UDP yaziyorsaniz, geri donen veriyi okumak icin isletim sisteminizin packet filtering arabirimini kullanmaniz gerekecek e.g. Linux icin SOCK_PACKET, BSD icin BPF, Solaris icin NIT gibi... Bundan sonra, 2. layer'dan baslayarak paketi bit-by-bit kendimiz olus- turuyoruz, ve sendto() / sendmsg() system call'lari ile paketi network'e inject ediyoruz. Bundan sonraki bolumde ornek 3 program uzerinde, ICMP, UDP ve TCP paketi gonderecegiz. Bu programlari http://www.enderunix.org/docs/raws-sample.tar.gz adresinden download edebilirsiniz. Programlarin olusturdugu etkiyi gormek icin tcpdump programi ile agi izlemelisiniz... [0x5 ICMP RAW PAKET] Asagidakiler program icin gerekli olacak include dosyalar: #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include unsigned short in_cksum(unsigned short *addr, int len) { int nleft = len; int sum = 0; unsigned short *w = addr; unsigned short answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(unsigned char *) (&answer) = *(unsigned char *) w; sum += answer; } sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); answer = ~sum; return (answer); } Bu internet checksum fonksiyonu. Butun katmanlarin checksum'umunu hesaplamak icin kullanabiliriz. ICMP protokolunde checksum zorunlu, hesaplanmasi gerek. void *run(void *arg) { struct ip ip; struct udphdr udp; struct icmp icmp; int sd; const int on = 1; struct sockaddr_in sin; u_char *packet; Burada, hafizada paket'imiz icin yer aciyoruz: packet = (u_char *)malloc(60); Ilk olarak Layer II, IP basligini'nin icini dolduruyoruz. Baslik uzunlugu. Gercekte dikkate alinmaz, genel kullanim degeri 5. ip.ip_hl = 0x5; Kullandigimiz Ip protokolu IPv4: ip.ip_v = 0x4; Type of Service. ip.ip_tos = 0x0; Paketimizin toplam uzunlugu: bkz yukari malloc paket. ip.ip_len = 60; Paketin id'si. kafadan bir rakam veriyoruz. ip.ip_id = htons(12830); Paketimiz fragmented ise fragment offseti. Fragmented olmadigi icin 0: ip.ip_off = 0x0; Time to live. Packet'in network'de varacagi yere varmadan once maksimum dolasabilecegi hop sayisi: 64 genel kullanim: ip.ip_ttl = 64; Bir ust layerdeki protokol: ICMP ip.ip_p = IPPROTO_ICMP; Checksum'u hesaplamadan once ip_sum degerini 0 yapmak durumundayiz: ip.ip_sum = 0x0; Kaynak IP adresi, bu pekala makinanizdaki interface'lerin IP'lerinden birisi olmayabilir :) ip.ip_src.s_addr = inet_addr("172.17.14.174"); Hedef IP adresi: ip.ip_dst.s_addr = inet_addr("172.17.14.169"); Checksum'u hesaplarken, Ip basligini ve uzunlugunu internet checksum algoritmasini uyarlayan fonksiyona yolluyoruz. Fonksiyon bize 16 bit unsigned int checksum degeri donuyor: ip.ip_sum = in_cksum((unsigned short *)&ip, sizeof(ip)); Sonra Ip basligimizi hazirlamayi bitirmis oluyoruz. Ip header'imizi paketimizin basina kopyaliyoruz: memcpy(packet, &ip, sizeof(ip)); Simdi geldik Layer III header'a:ICMP: Icmp tipi: icmp.icmp_type = ICMP_ECHO; Kodu 0. Echo Request. icmp.icmp_code = 0; ID. random bir deger: icmp.icmp_id = 1000; Icmp paketlerinden kacincisi? icmp.icmp_seq = 0; IP checksum'unda oldugu gibi, gene once cheksum field'ini 0 yapip fonksiyona ICMP basligini ve uzunlugunu gonderiyoruz. Gelen degeri icmp checksum kismina yaziyoruz. icmp.icmp_cksum = 0; icmp.icmp_cksum = in_cksum((unsigned short *)&icmp, 8); Sonra da packet'e IP header'dan sonraki kisma kopyaliyoruz: memcpy(packet + 20, &icmp, 8); Evet paket'imizi byte byte olusturduk. Simdi de bunu network'e inject edecegiz. Once RAW modda bir soket olusturalim: if ((sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("raw socket"); exit(1); } IP paketini de kendimiz hazirladigimiz icin kernel'a bunu belirtmemiz lazim. Yoksa kernel Ip header'i kendi tekrar yazar. Bunun icin soket'e IP_HDRINCL opsiyonunu veriyoruz: if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) { perror("setsockopt"); exit(1); } Fakat hala Layer I basligini biz hazirlamadigimiz icin kernel'a I. Layer'daki MAC adresini hangi IP icin olusturacagini belirlememiz gerekiyor. Onun icin sendto() ve sendmsg() fonksiyonuna hedef IP adresini struct in_addr yapisi icinde veriyoruz: memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = ip.ip_dst.s_addr; Simdi gelelim paketi aga birakmaya. Bunun icin send() kullanamiyoruz, cunku soket connected bir soket degil. Connected olmayan soketlerimizde data yollamak icin sendto veya sendmsg sistem cagrilarini kullanabiliriz. if (sendto(sd, packet, 60, 0, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) { perror("sendto"); exit(1); } } int main(int argc, char **argv) { run(NULL); return 0; } Bu kadar. Simdi programi derleyip, calistiralim. Programin olusturdugu etkiyi tcpdump programini kullanarak gorebilirsiniz. Baska bir terminalde # tcpdump icmp host 172.16.17.160 Ve, # make icmp # ./icmp Tcpdump programinda icmp echo request'in gittigini ve buna karsi makinadan cevaplar geldigini gorebilirsiniz. Fakat paketlerin sizi tekrar bulabile- ceginden emin olunuz. IP spoofed bir IP ise kendi network'unuzde olmasina dikkat edin, yoksa geri donen paketleri goremezsiniz. tcpdump ciktisi: x-wing# tcpdump icmp tcpdump: listening on fxp0 11:50:43.789297 172.17.14.90 > 172.17.14.169: icmp: echo request 11:50:43.789439 172.17.14.90 > 172.17.14.174: icmp: echo reply x-wing# [0x6 UDP RAW PAKET] Evet, simdi biraz daha farkli bir ornege bakalim. IP ve UDP protokol basliklarini kendimiz olusturup, raw socket'imizle yollayalim. UDP'de checksum hesaplamak zorunlu degil, ama modern isletim sistemlerinin hemen hemen hepsinde bu yapiliyor. Eger sizin isletim sisteminiz checksum hesap- lamadan UDP paketi yolluyorsa, karsi tarafin size reply'lari da checksum'suz oluyor, tersi durum icin checksum hesaplanip paket yollaniyor. Simdiki ornegimizde UDP basligi icin de checksum'u hesaplayacagiz. Yalniz UDP ve TCP protokollerinde checksum'u hesaplayabilmek icin datagram'in uzunlugu hedef ve kaynak ip adreslerini de iceren bir "pseudo header" kullaniyoruz. Bunun nasil yapildigini simdiki UDP orneginde goreceksiniz. Normal include dosyalari: #include #include #include #include #include #include #include #include #include #include #include #include #include Checksum'u hesaplamada kullanacagimiz pseudo header. Normal UDP header'i enkapsule eder. struct psd_udp { struct in_addr src; struct in_addr dst; unsigned char pad; unsigned char proto; unsigned short udp_len; struct udphdr udp; }; Internet Checksum algoritmasini implemente eden fonksiyon: unsigned short in_cksum(unsigned short *addr, int len) { int nleft = len; int sum = 0; unsigned short *w = addr; unsigned short answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(unsigned char *) (&answer) = *(unsigned char *) w; sum += answer; } sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); answer = ~sum; return (answer); } Bu fonksiyon udp header'i ve uzunlugunu, hedef ve kaynak adresleri alir, Pseudo header'la beraber in_cksum'a yollar. unsigned short in_cksum_udp(int src, int dst, unsigned short *addr, int len) { struct psd_udp buf; memset(&buf, 0, sizeof(buf)); buf.src.s_addr = src; buf.dst.s_addr = dst; buf.pad = 0; buf.proto = IPPROTO_UDP; buf.udp_len = htons(len); memcpy(&(buf.udp), addr, len); return in_cksum((unsigned short *)&buf, 12 + len); } void *run(void *arg) { struct ip ip; struct udphdr udp; int sd; const int on = 1; struct sockaddr_in sin; u_char *packet; Paketimizi olusturuyoruz: packet = (u_char *)malloc(60); Ip basligimizi ICMP orneginde oldugu gibi yaziyoruz: ip.ip_hl = 0x5; ip.ip_v = 0x4; ip.ip_tos = 0x0; ip.ip_len = 60; ip.ip_id = htons(12830); ip.ip_off = 0x0; ip.ip_ttl = 64; ip.ip_p = IPPROTO_UDP; ip.ip_sum = 0x0; ip.ip_src.s_addr = inet_addr("172.17.14.174"); ip.ip_dst.s_addr = inet_addr("172.17.14.169"); ip.ip_sum = in_cksum((unsigned short *)&ip, sizeof(ip)); memcpy(packet, &ip, sizeof(ip)); UDP basligimizi yaziyoruz, ve paketimize IP basligindan sonraki kisma yerles- tiriyoruz. Kaynak UDP portu: udp.uh_sport = htons(45512); Hedef UDP portu: udp.uh_dport = htons(53); UDP datagraminin uzunlugu: udp.uh_ulen = htons(8); Gene ayni sekilde, cksum field'ini sifir yapip, udp_cksum'a yolluyoruz: udp.uh_sum = 0; udp.uh_sum = in_cksum_udp(ip.ip_src.s_addr, ip.ip_dst.s_addr, (unsigned short *)&udp, sizeof(udp)); memcpy(packet + 20, &udp, sizeof(udp)); Diger kisimlar icmp ornegindeki gibi ayni. if ((sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("raw socket"); exit(1); } if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) { perror("setsockopt"); exit(1); } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = ip.ip_dst.s_addr; if (sendto(sd, packet, 60, 0, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) { perror("sendto"); exit(1); } } [0x7 TCP RAW PAKET, SPOOF EDILMIS BIR TCP BAGLANTISI] Su ana kadar ki bolumu ve ornekleri yapmis iseniz, raw socket mantigini anlamissinizdir. Simdi daha da gelismis bir ornek uzerinde TCP paket*ler*ini raw socket'le yollama ve almaya bakacagiz. Bu ornegimizde programimiz kendi uzerinde tanimli olmayan bir IP adresinden server'a spoof edilmis TCP SYN ve server'in cevabina ACK yollayacak. Once kisaca TCP baglantisinin nasil olustu- guna bakalim. Iki TCP soket arasinda baglanti olusabilmesi icin 3-yollu-el SIKISMA surecinin tamamlanmis olmasi gerekmektedir (ing. 3-way-handshake). Bu uc-yollu el SIKISMA, su sekilde ozetlenebilir: 1. Istemci, bir paket sira numarasi belirler. (ing. Initial Sequence Number). Bu numara ile beraber, sunucuya SYN paketi yollar. Istemci SYN_SENT durumuna girer. 2. SYN paketi geldiginde sunucu SYN_RECEIVED durumuna gecer. Sunucu da Yalniz, dikkatinizi cektiyse o source'da kullandigimiz hedef IP ayni sekilde bir paket sira numarasi belirler. Baglantiyi kabul etmek istiyorsa, TCP acknowledgement number kismina istemciden gelen seq_no'yu bir artirip yazar ve SYN ve ACK bitlerinin set o edilmis oldugu acknowledgment paketi istemciye yollar. 3. Istemci, sunucudan gelen ISN + 1'i ACK Number kismina yazar ve ACK paketini sunucuya yollar. Bu durumda her iki tarafta ESTABLISHED durumuna gecmistir artik. Asagida boyle olusmus bir baglantinin tcpdump ciktisi var: 1. SYN: (Client -> Server) 11:10:02.999329 172.17.14.174.1030 > 172.16.1.204.33334: S 375294043:375294043(0) win 65535 (DF) 2. SYN_ACK: (Server -> Client) 11:10:02.999609 172.16.1.204.33334 > 172.17.14.174.1030: S 2827812752:2827812752(0) ack 375294044 win 32768 (DF) 3. ACK: (Client -> Server) 11:10:02.999639 172.17.14.174.1030 > 172.16.1.204.33334: . ack 1 win 65535 (DF) Evet iste simdi bu islemlerin *basitce* simule edildigi bir program yazacagiz. Programin tamamini ve ornek olarak kullandigimiz server.c'yi raws-sample.tar.gz icinde tcp dizini altinda bulabilirsiniz. Ben burada sadece kodun uzerinden genel olarak gidecegim. Programin iki thread halinde calisiyor. Birinci thread, SYN paketini yolluyor, ikinci thread'de packet filter device'dan makinaya ulasan butun paketleri dinliyor. SYN paketi- nin cevabini gordugu anda, server'in ISN'i arti bir ile syn_ack'i yolluyor. Yalniz, dikkatinizi cektiyse o source'da kullandigimiz hedef IP adresi bizim agimizda degil. Kaynak IP adresi bizim agimizdadir, ama spoofer makinamizin uzerindeki interface'lerin hicbirinde bu IP tanimli degildir. Paketler geri donerken router'a kadar gelecek, router'da bu IP icin ARP request broadcast edecek, ama hicbir zaman cevap alamayacagi icin paketi route edemeyektir. Onun icin, biz de spoof ettigimiz IP adresi icin bir *published* arp entry giriyoruz. x-wing# arp -s 172.17.14.90 00:0c:76:0f:9b:5a permanent pub x-wing# arp -an ? (172.17.14.1) at 00:05:5e:07:dc:c2 on fxp0 [ethernet] ? (172.17.14.90) at 00:0c:76:0f:9b:5a on fxp0 permanent published [ethernet] ? (172.17.14.160) at 00:10:5a:af:3e:b2 on fxp0 [ethernet] x-wing# Boylece makinamiz, broadcast domaininde 172.17.14.90 icin arp request'lere cevap verecek. #include #include #include TCP Baslik dosyasi: #include int sd; UDP 'den asina oldugunuz uzere checksum'da kullanacagimiz TCP pseudo basligi. struct psd_tcp { struct in_addr src; struct in_addr dst; unsigned char pad; unsigned char proto; unsigned short tcp_len; struct tcphdr tcp; }; unsigned short in_cksum(unsigned short *addr, int len) { int nleft = len; int sum = 0; unsigned short *w = addr; unsigned short answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(unsigned char *) (&answer) = *(unsigned char *) w; sum += answer; } sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); answer = ~sum; return (answer); } unsigned short in_cksum_tcp(int src, int dst, unsigned short *addr, int len) { struct psd_tcp buf; u_short ans; memset(&buf, 0, sizeof(buf)); buf.src.s_addr = src; buf.dst.s_addr = dst; buf.pad = 0; buf.proto = IPPROTO_TCP; buf.tcp_len = htons(len); memcpy(&(buf.tcp), addr, len); ans = in_cksum((unsigned short *)&buf, 12 + len); return (ans); } SYN + ACK paketini yollayan rutin. Server'in ISN'ini aliyor. Acknowledment Number kismina bunu 1 artirip yaziyor. Cunku baglanti bir sequence number tuketir. void send_syn_ack(int s_seq) { struct ip ip; struct tcphdr tcp; const int on = 1; struct sockaddr_in sin; u_char *packet; packet = (u_char *)malloc(60); ip.ip_hl = 0x5; ip.ip_v = 0x4; ip.ip_tos = 0x0; ip.ip_len = sizeof(struct ip) + sizeof(struct tcphdr); ip.ip_id = htons(12831); ip.ip_off = 0x0; ip.ip_ttl = 64; ip.ip_p = IPPROTO_TCP; ip.ip_sum = 0x0; ip.ip_src.s_addr = inet_addr("172.17.14.90"); ip.ip_dst.s_addr = inet_addr("172.16.1.204"); ip.ip_sum = in_cksum((unsigned short *)&ip, sizeof(ip)); memcpy(packet, &ip, sizeof(ip)); tcp.th_sport = htons(3333); tcp.th_dport = htons(33334); tcp.th_seq = htonl(0x131123 + 1); Server'in ack numarasini 1 artirip yaziyoruz: tcp.th_ack = htonl(s_seq + 1); tcp.th_off = sizeof(struct tcphdr) / 4; tcp.th_flags = TH_ACK; tcp.th_win = htons(32768); tcp.th_sum = 0; tcp.th_sum = in_cksum_tcp(ip.ip_src.s_addr, ip.ip_dst.s_addr, (unsigned short *)&tcp, sizeof(tcp)); memcpy((packet + sizeof(ip)), &tcp, sizeof(tcp)); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = ip.ip_dst.s_addr; if (sendto(sd, packet, 60, 0, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) { perror("sendto"); exit(1); } } Ilk SYN paketini yollayan rutin: void send_syn() { struct ip ip; struct tcphdr tcp; const int on = 1; struct sockaddr_in sin; u_char *packet; packet = (u_char *)malloc(60); ip.ip_hl = 0x5; ip.ip_v = 0x4; ip.ip_tos = 0x0; ip.ip_len = sizeof(struct ip) + sizeof(struct tcphdr); ip.ip_id = htons(12830); ip.ip_off = 0x0; ip.ip_ttl = 64; ip.ip_p = IPPROTO_TCP; ip.ip_sum = 0x0; ip.ip_src.s_addr = inet_addr("172.17.14.90"); ip.ip_dst.s_addr = inet_addr("172.16.1.204"); ip.ip_sum = in_cksum((unsigned short *)&ip, sizeof(ip)); memcpy(packet, &ip, sizeof(ip)); tcp.th_sport = htons(3333); tcp.th_dport = htons(33334); tcp.th_seq = htonl(0x131123); tcp.th_off = sizeof(struct tcphdr) / 4; tcp.th_flags = TH_SYN; tcp.th_win = htons(32768); tcp.th_sum = 0; tcp.th_sum = in_cksum_tcp(ip.ip_src.s_addr, ip.ip_dst.s_addr, (unsigned short *)&tcp, sizeof(tcp)); memcpy((packet + sizeof(ip)), &tcp, sizeof(tcp)); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = ip.ip_dst.s_addr; if (sendto(sd, packet, 60, 0, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) { perror("sendto"); exit(1); } } void *run(void *arg) { struct ip ip; struct tcphdr tcp; const int on = 1; struct sockaddr_in sin; if ((sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("raw socket"); exit(1); } if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) { perror("setsockopt"); exit(1); } send_syn(sd); } Bu rutin, diger thread'de calisan packet'lerin pcap_loop tarafindan dagiltildigi rutin. 14.90 icin gelen paketleri inceler. Bunu yapmamizdaki amac, server'in yolladigi Initial Sequnce Number'i yakalamak. Cunku bunu yollayacagimiz ACK paketinde 1 artirip yollamamiz lazim. void raw_packet_receiver(u_char *udata, const struct pcap_pkthdr *pkthdr, const u_char *packet) { struct ip *ip; struct tcphdr *tcp; u_char *ptr; int l1_len = (int)udata; int s_seq; ip = (struct ip *)(packet + l1_len); tcp = (struct tcphdr *)(packet + l1_len + sizeof(struct ip)); printf("%d\n", l1_len); printf("a packet came, ack is: %d\n", ntohl(tcp->th_ack)); printf("a packet came, seq is: %u\n", ntohl(tcp->th_seq)); s_seq = ntohl(tcp->th_seq); send_syn_ack(s_seq); sleep(100); } Bu kisimlar self-explanatory, Diger thread, calismaya buradan basliyor. void *pth_capture_run(void *arg) { pcap_t *pd; char *filter = "dst host 172.17.14.90 and ip"; char *dev = "fxp0"; char errbuf[PCAP_ERRBUF_SIZE]; bpf_u_int32 netp; bpf_u_int32 maskp; struct bpf_program fprog; /* Filter Program */ int dl = 0, dl_len = 0; if ((pd = pcap_open_live(dev, 1514, 1, 500, errbuf)) == NULL) { fprintf(stderr, "cannot open device %s: %s\n", dev, errbuf); exit(1); } pcap_lookupnet(dev, &netp, &maskp, errbuf); pcap_compile(pd, &fprog, filter, 0, netp); if (pcap_setfilter(pd, &fprog) == -1) { fprintf(stderr, "cannot set pcap filter %s: %s\n", filter, errbuf); exit(1); } pcap_freecode(&fprog); dl = pcap_datalink(pd); switch(dl) { case 1: dl_len = 14; break; default: dl_len = 14; break; } if (pcap_loop(pd, -1, raw_packet_receiver, (u_char *)dl_len) < 0) { fprintf(stderr, "cannot get raw packet: %s\n", pcap_geterr(pd)); exit(1); } } Programin ana rutini. Packet capture icin bir thread olusturur. Ve syn paketini yollar. int main(int argc, char **argv) { pthread_t tid_pr; if (pthread_create(&tid_pr, NULL, pth_capture_run, NULL) != 0) { fprintf(stderr, "cannot create raw packet reader: %s\n", strerror(errno)); exit(1); } printf("raw packet reader created, waiting 1 seconds for packet reader thread to settle down...\n"); sleep(1); run(NULL); pthread_join(tid_pr, NULL); return 0; } Programi calistiriyoruz: x-wing# ./tcp raw packet reader created, waiting 1 seconds for packet reader thread to settle down... a packet came, ack is: 1249572 a packet came, seq is: 3431236214 x-wing# Simdi olusturdugumuz etkiyi tcpdump ciktisinda gorelim: x-wing# tcpdump host 172.16.1.204 tcpdump: listening on fxp0 Spoof ederek yolladigimiz ilk paket: (SYN) 11:35:42.839916 172.17.14.90.3333 > 172.16.1.204.33334: S 1249571:1249571(0) win 32768 Router, spoof ettigimiz IP olan 172.14.17.90 icin arp sorgusu yapiyor. 11:35:42.840290 arp who-has 172.17.14.90 tell 172.17.14.1 Biz daha once bunun icin published arp bilgisi girdigimiz icin, bizim makina- miz buna bizim MAC adresimiz ile cevap veriyor: 11:35:42.840299 arp reply 172.17.14.90 is-at 0:c:76:f:9b:5a Server SYN'e SYN + ACK (Client ISN + 1) ile cevap veriyor: 11:35:45.910287 172.16.1.204.33334 > 172.17.14.90.3333: S 3431236214:3431236214(0) ack 1249572 win 32768 (DF) Biz de Server'in SYN + ACK'ini ACK'liyoruz (Server ISN + 1): 11:35:45.910467 172.17.14.90.3333 > 172.16.1.204.33334: . ack 1 win 32768 Evet bu durumda baglanti olusmus oluyor. Sunucuda netstat -an ciktisina bakalim: $ netstat -an | grep 33334 tcp 0 0 172.16.1.204.33334 172.17.14.90.3333 ESTABLISHED tcp 0 0 *.33334 *.* LISTEN $ Ama kendi makinanizda baktiginiz zaman boyle bir baglantinin gercekte var olmadigini goreceksiniz... [0x8 SON SOZLER] Bu defaki dokumanimizda TCP, UDP veya IP katmaninda cig olarak nasil paket yollanip alinabilecegini ogretmeyi amacladik. RAW soketler aslinda hemen hemen pratik hayatta hic kullanilmamakla beraber, kisiye ag isleyis detaylarini ogretmesi acisindan son derece faydali. Raw socket programlamanin gercek hayat ornekleri arasinda ping, traceroute sayilabilir. Standart TCP/UDP soketlerinin yetmedigi durumlarda her zaman raw soket'leri bir kurtarici olarak yaninizda bulacaksiniz. Raw soketleri en cok kullanan programcilar(!) aslinda yer altinda yasi- yorlar. Underground'da Ip Spoofing denen metod, aslinda raw soket kullanarak basit paketler olusturmaktan baska bir sey degildir!. Bu yazi, kaynak gosterilmek ve onceden yazarindan izin alinmak kaydiyla tekrar yayinlanabilir. [0x9 GREETINGS] a, staff@EnderUNIX. [0x10 REFERANSLAR] 1. TCP/IP Illustrated: The Protocols. Stevens, W.R. http://www.amazon.co.uk/exec/obidos/ASIN/0201633469/202-4722604-8065457 2. UNIX Network Programming Vol 1: Networking APIs - Sockets and XTI. Stevens, W.R. http://www.amazon.co.uk/exec/obidos/ASIN/013490012X/qid=1061545513/sr=2-1/ref=sr_2_3_1/202-4722604-8065457 3. The Design and Implementation of the 4.4 BSD Operating System. Mc Kuisick et al. http://www.amazon.co.uk/exec/obidos/ASIN/0201549794/qid=1061545390/sr=2-2/ref=sr_2_3_2/202-4722604-8065457 4. Modern Operating Systems. Tanenbaum, A.S. http://www.amazon.co.uk/exec/obidos/ASIN/0130926418/qid=1061545435/sr=2-1/ref=sr_2_3_1/202-4722604-8065457