关于ARP的RFC文档在此!

ARP干嘛的

曾经有段时间, 六七年前了吧. 本科的时候, 流行了一阵子ARP病毒攻击, 导致整个局域网都不能上网了. 当时只听说这个东西防不住, 只要有一个人中毒, 就导致所有人上不了网. 现在也终于知道这是怎么回事了, 也能手工让某个同学上不了网了, 咳咳.

大家应该也都知道ARP是干嘛的, 我再啰嗦一下.. 比如我访问了百度, 百度回了包给我, 百度只知道我的IP是什么,不知道我的MAC地址. 这个包到网关的时候, IP这一层再把数据交给下一层的链路层, 链路层不知道IP是什么东西的, 它只认MAC地址. 所以就需要把IP转成MAC地址, ARP请求就是做这个的.

就是说, 我可以通过这个协议广播问一下所有机器 , 谁的IP是XXX.XXX.XXX.XXX, 请把你的MAC地址告诉我. 这个IP是XXX.XXX.XXX.XXX的机器收到请求之后, 就会告诉我, XXX.XXX.XXX.XXX的MAC地址是啥啥啥.

不幸这个是基于互相信任的, 理论上大家都会相信别人说的是正确的. 但是, 我可以撒谎说, XXX.XXX.XXX.XXX是我, 我的MAC是啥啥啥. 然后本来应该到 XXX.XXX.XXX.XXX那里的数据包就到我这里来了. XXX.XXX.XXX.XXX不仅仅是断网了, 我还能窃听他的数据.

协议格式

要想伪造, 啊,不, 发送一个ARP请求或者应答, 一定要了解协议格式. 从RFC文件抄了一份.

To communicate mappings from <protocol, address> pairs to 48.bit
Ethernet addresses, a packet format that embodies the Address
Resolution protocol is needed.  The format of the packet follows.

    Ethernet transmission layer (not necessarily accessible to
         the user):
        48.bit: Ethernet address of destination
        48.bit: Ethernet address of sender
        16.bit: Protocol type = ether_type$ADDRESS_RESOLUTION
    Ethernet packet data:
        16.bit: (ar$hrd) Hardware address space (e.g., Ethernet,
                         Packet Radio Net.)
        16.bit: (ar$pro) Protocol address space.  For Ethernet
                         hardware, this is from the set of type
                         fields ether_typ$<protocol>.
         8.bit: (ar$hln) byte length of each hardware address
         8.bit: (ar$pln) byte length of each protocol address
        16.bit: (ar$op)  opcode (ares_op$REQUEST | ares_op$REPLY)
        nbytes: (ar$sha) Hardware address of sender of this
                         packet, n from the ar$hln field.
        mbytes: (ar$spa) Protocol address of sender of this
                         packet, m from the ar$pln field.
        nbytes: (ar$tha) Hardware address of target of this
                         packet (if known).
        mbytes: (ar$tpa) Protocol address of target.

这个还是TCP/IP协议详解书里面的图好看一些, 且等我截个图来, 呃,截歪了. arp协议,截取自是TCP/IP协议详解

其实里面有些值, 像”硬件地址长度”什么的, 是个变量, 后面的”发送端以太网地址”就是根据这个而变化 .就我们目前的应用和环境来说, 拿这个图就可以啦.
这里的长度单位是字节, 不是上一篇IP协议里面的bit了.

详细解释我也copy一下吧. 也是出自TCP/IP协议详解一书.

以太网报头中的前两个字段是以太网的源地址和目的地址。目的地址为全 1的特殊地址是 广播地址。电缆上的所有以太网接口都要接收广播的数据帧。

两个字节长的以太网帧类型表示后面数据的类型。对于 ARP请求或应答来说,该字段的 值为0x0806。

形容词hardware(硬件)和protocol(协议)用来描述 ARP分组中的各个字段。例如,一个 ARP 请求分组询问协议地址(这里是 IP地址)对应的硬件地址(这里是以太网地址)。

硬件类型字段表示硬件地址的类型。它的值为 1即表示以太网地址。协议类型字段表示要 映射的协议地址类型。它的值为 0x0800即表示 IP地址。它的值与包含 IP数据报的以太网数据 帧中的类型字段的值相同,这是有意设计的(参见图 2-1)。

接下来的两个 1字节的字段,硬件地址长度和协议地址长度分别指出硬件地址和协议地址 的长度,以字节为单位。对于以太网上 IP地址的 ARP请求或应答来说,它们的值分别为 6和4。 操作字段指出四种操作类型,它们是 ARP请求(值

(值为3)和RARP应答(值为4)(我们在第5章讨论RARP)。这个字段必需的,因为 ARP请求 和ARP应答的帧类型字段值是相同的。

接下来的四个字段是发送端的硬件地址(在本例中是以太网地址)、发送端的协议地址 (IP地址)、目的端的硬件地址和目的端的协议地址。注意,这里有一些重复信息:在以太网的数据帧报头中和 ARP请求数据帧中都有发送端的硬件地址。

对于一个 ARP请求来说,除目的端硬件地址外的所有其他的字段都有填充值。当系统收到一份目的端为本机的 ARP请求报文后,它就把硬件地址填进去,然后用两个目的端地址分 别替换两个发送端地址,并把操作字段置为 2,最后把它发送回去。

来段程序吧

其它也没啥好说的了, 先来段程序吧.

广播一个ARP请求, 问一下网关的MAC地址是多少. 运行环境是ubuntu12.04. osx上面是不行的. 据说windows也不行. 搜索了好久, 也不知道mac上面怎么搞.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
广播一个ARP请求, 问一下网关的MAC地址是多少.
运行环境是ubuntu12.04.
osx上面是不行的. 据说windows也不行.  搜索了好久, 也不知道mac上面怎么搞.
'''

import socket
import struct


def main():
    st = struct.Struct('!6s 6s h h h b b h 6s 4s 6s 4s')

    GATEWAY = '192.168.1.1'
    MYIP = '192.168.1.8'
    MYMAC = '20:c9:d0:88:96:3f'

    dst_ethernet_addr = ''.join(
        [chr
         (int(e, 16))
         for e in 'FF:FF:FF:FF:FF:FF'.split(':')])
    protocol_type = 0x0806
    hw_addr_space = 1
    protocol_addr_space = 0x800
    hw_addr_length = 6
    protocol_addr_length = 4
    op = 1
    my_mac = ''.join([chr(int(e, 16)) for e in MYMAC.split(':')])
    my_ip = socket.inet_aton(MYIP)
    target_hw_addr = ''.join(
        [chr
         (int(e, 16))
         for e in '00:00:00:00:00:00'.split(':')])
    des_ip = socket.inet_aton(GATEWAY)
    data = (
        dst_ethernet_addr,
        my_mac,
        protocol_type,
        hw_addr_space,
        protocol_addr_space,
        hw_addr_length,
        protocol_addr_length,
        op,
        my_mac,
        my_ip,
        target_hw_addr,
        des_ip,
        )
    packed_data = st.pack(*data)

    s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.SOCK_RAW)
    s.bind(('eth0', socket.SOCK_RAW))

    # 下面这样也行, 不知道区别.
    #http://sock-raw.org/papers/sock_raw 这个应该可以参考
    #s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
    #s.bind(('eth0',0))

    r = s.send(packed_data)
    print r
    return

if __name__ == '__main__':
    main()

抓包结果, 这里已经忽略了以太网首部的14个字节. 是从上图中的”硬件类型”开始的.

% sudo tcpdump -nn -vvv -x -c1 arp   
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
19:28:43.209235 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 192.168.1.1 tell 192.168.1.8, length 28
	0x0000:  0001 0800 0604 0001 20c9 d088 963f c0a8
	0x0010:  0108 0000 0000 0000 c0a8 0101
1 packet captured
1 packet received by filter
0 packets dropped by kernel

ARP攻击

上面的代码简单改一下就可以做ARP攻击了.

对网关做一个ARP应答.
op改为2, 代表ARP应答. 然后把 “以太网源地址”和”发送端以太网地址”都写自己的. IP地址写要攻击的人. OVER