像偶等长期在外流浪一簇,靠租个小房间的过日子,与人共享一条宽带上网。本来嘛,两三兆的带宽供四五个人上网肯定是没啥问题。可是这年头,一说下载就是P2P,像Bit Torrent(这东西叫“变态”,还真是传神,就因为太变态,有些国家的ISP都封P2P了),迅雷都是比较出了名的耗带宽的。迅雷虽然可以限速,可是会有多少人会去设呢?而且还带了bt功能。bt那东西嘛,设了限速基本没啥用,特别是对于ADSL的网络,有人下bt,别人基本就别想好好上网了,比起56k的猫也要慢上不少。于是呼,像P2P终结者等软件就开始大行其道(当然也有不少人也是为了独占带宽用的)。最近,这个网络里面除了个别人,其他就都别想上网了,严重时大家都没法上网了。这个时候,大家可能会想到用ARP防火墙来防止ARP攻击,这个时候运气好的话,网速变快了,如果ARP防火墙功能比较弱的话,就变得没无效或者网速变得比原来还慢,甚至彻底不能上网了。这个时候,我的土ARP防火墙就该出马了,再加上一些限制别人网速的功能,就能解决别人下P2P时候网速太慢的问题了。为什么说土呢,就一Console的程序,总长度还没超过100行。
与ARP相关的软件很多,有像p2p终结者那样的流量控制的,还有像Cain那样的嗅探用的,还有像各种ARP病毒等。这些都利用了一个原理就是ARP欺骗,利用ARP欺骗来实现会话劫持。下面介绍一下ARP欺骗的原理。
在以太网内,机器间的交互是通过mac地址来进行的,通常机器间的交互只要知道对方机器的IP地址即可,那么,通讯的双方是如何把IP地址转化成mac地址的呢?正常情况下,如果机器A需要与机器B进行通讯,在机器A不知道机器B的mac地址(MACB)的情况下,机器A会向网络广播一个ARP请求数据包(会带上机器A的IP地址IPA与mac地址MACA),询问谁是IPB。这个时候IPB接收到这个数据包后会向机器A发一个ARP响应数据包告诉机器A,IPB对应的mac地址是MACB。这样机器A与机器B都知道了对方的MAC地址了,就能互相通讯了。用简单的语言来描述的话,就是:A喊一声,IPB的MAC地址是什么,然后B告诉A,B的MAC地址是MACB。
那么ARP欺骗是怎么回事呢?这个时候机器C来了,C一直声称IPB对应的MAC地址是MACC,因为A不知道谁说的是正确的,它会以第一个接收到的数据包为准,所以,只要C一直在说,把B的回应淹没掉就可以了。这样A一直会认为IPB的MAC地址就是MACC了。这个时候C就可以伪装成B与A通讯了。这只是做到了ARP欺骗的第一步,并没有太大的用处。那么接下去继续利用ARP欺骗来做到会话劫持。正常情况下,A与B之前会直接进行通讯,这个时候,C又来捣乱了,C用上面的方式欺骗了A,让A认为C就是B,同时C也用上面的方式欺骗了B,让B认为了C就是A。此时A本来想发给B的数据包以及B本来想发给C的数据包都发给了C,C只要做一下转发,转发给本来就应该接收的那方。从A与B的角度来说,两者都是正常通讯的,只是大家都不知道,中间还有个C。此时会话劫持已经完成,C可以做很多事情了,如:控制流量,禁止某些数据包通过,篡改某些数据包,获取数据包里的信息等等。
那么如何防范会话劫持呢。根据上述原理,只要有一方不被欺骗,劫持就不成立了。假如我用的机器就是A机器,那么只对A有控制权,自然只能在A上做手脚了。我们知道操作系统都维护有一个ARP Cache,Cache里存的就是IP与MAC的映射。要防止有人仿冒B,只要Cache里关于B的映射是正确的就行了。操作系统给我们提供了一种方法,那就是可以设置静态的映射,除非手工重新设置,那么在重启系统之前是不会变的,那么我们只要拿到正确的B的MAC地址就可以。好在B在绝大部分情况下就是网关,所以很容易拿到正确的MAC地址。剩下的就很简单了。
在Windows下可以用如下的命令,前面是IP地址,后面是MAC地址linux下也用类似的命令,形式稍有不同,具体可以参见linux下arp命令的帮助。
arp -s 192.168.1.1 00-aa-00-62-c6-09
这样再也不可能有机器来来仿冒成B了。很多ARP防火墙其实只实现到了这一层。这个时候,很多同学都会发现:怎么用了ARP防火墙之后,网速反而慢了啊!这个时候其实还没完,因为C还可以欺骗B啊。但是C对B的欺骗也不完全,有时候B能认对A有时候不能,所以B发出的数据,有时候会发给A,有时候发给C,而通过C再转发给A的数据,对于类似于TCP这样的协议,由于C没有与A建立会话,所以,绝大部分的数据包都会丢弃,TCP的连接也有可能因为丢包而断掉,于是为了丢包重发,以及重新建立连接等,消耗大量时间与带宽,直接的感觉就是网速很慢。所以这个时候还需要做一个事情,就是把C对B的欺骗的校正过来。C发给B的是大量错误的ARP响应包,把原来正确的给淹没掉了,那么要纠正过来也很简单,那就是发送比C更多的正确的ARP响应包,把C的错误的包给淹没掉就行了。现在有不少ARP防火墙开始支持到第二个层次了。
根据以前原理,用java写了个简单的ARP防火墙,利用的是jpcap(包装了winpcap,libpcap),有兴趣的同学可以了解一下下面的核心代码。
public class IpMacMap {
private String ip;
private byte[] mac;
public IpMacMap() {
}
public IpMacMap(String ip, byte[] mac) {
this.ip = ip;
this.mac = mac;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public byte[] getMac() {
return mac;
}
public void setMac(byte[] mac) {
this.mac = mac;
}
}
private static EthernetPacket getEthernetPacket(byte[] senderMac, byte[] targetMac) {
EthernetPacket packet = new EthernetPacket(); // 创建一个以太网头
packet.frametype = EthernetPacket.ETHERTYPE_ARP; // 选择以太包类型
packet.dst_mac = targetMac;
packet.src_mac = senderMac;
return packet;
}
private static ARPPacket getArpPacket(IpMacMap sender, IpMacMap target, short arpType) throws UnknownHostException {
ARPPacket packet = new ARPPacket();
packet.hardtype = ARPPacket.HARDTYPE_ETHER; // 选择以太网类型(Ethernet)
packet.prototype = ARPPacket.PROTOTYPE_IP; // 选择IP网络协议类型
packet.operation = arpType;
packet.hlen = 6; // MAC地址长度固定6个字节
packet.plen = 4; // IP地址长度固定4个字节
packet.target_hardaddr = target.getMac();
packet.target_protoaddr = InetAddress.getByName(target.getIp()).getAddress();
packet.sender_hardaddr = sender.getMac();
packet.sender_protoaddr = InetAddress.getByName(sender.getIp()).getAddress();
return packet;
}
private static ARPPacket createAntiARPPacket() throws UnknownHostException {
IpMacMap source = new IpMacMap();
source.setIp(device.addresses[0].address.getHostAddress());//本机IP地址
source.setMac(device.mac_address);//本机mac地址
IpMacMap target = new IpMacMap();
target.setIp(getwayIPAddress);//网关的IP
target.setMac(getewayMacAddress.getMacAddress());//网关的mac地址
ARPPacket antiSpoofPacket = getArpPacket(source, target, ARPPacket.ARP_REPLY);
antiSpoofPacket.datalink = getEthernetPacket(source.getMac(), target.getMac());
return antiSpoofPacket;
}