交互式数据包处理程序 Scapy 入门指南

概述

Scapy 是一个强大的交互式数据包处理程序(使用python编写)。它能够伪造或者解码大量的网络协议数据包,能够发送、捕捉、匹配请求和回复包等等。它可以很容易地处理一些典型操作,比如端口扫描,tracerouting,探测,单元测试,攻击或网络发现(可替代hping,NMAP,arpspoof,ARP-SK,arping,tcpdump,tethereal,P0F等)。最重要的他还有很多更优秀的特性——发送无效数据帧、注入修改的802.11数据帧、在WEP上解码加密通道(VOIP)、ARP缓存攻击(VLAN)等,这也是其他工具无法处理完成的。下面就先通过一些实例来学习一下 Scapy 这个强大的工具吧。


版权说明

著作权归作者所有。

商业转载请联系作者获得授权,非商业转载请注明出处。

本文作者:Coding-Naga

发表日期: 2016年4月13日

本文链接:http://blog.csdn.net/lemon_tree12138/article/details/51141440

来源:CSDN

更多内容:分类 >> 黑客的隐形衣


实验环境

  • Centos 6.5
  • Python 2.6.6
  • setuptools-20.7.0
  • prettytable-0.7.2
  • Scapy (2.3.2)

功能列表

基本测试

登入 Scapy 环境

$ scapy

框架包的引入

>>> from scapy.all import *

这里要注意的是不要使用右边的这种方式导入包:from scapy import *

查看配置信息

conf 变量保存了配置信息

>>> conf
ASN1_default_codec = <ASN1Codec BER[1]>
AS_resolver = <scapy.as_resolvers.AS_resolver_multi instance at 0x13c1dd0>
( 此处省略 N 行 )
verb       = 2
version    = ‘2.3.2‘
warning_threshold = 5
wepkey     = ‘‘

查看 scapy 支持的指令集

>>> lsc()
arpcachepoison      : Poison target‘s cache with (your MAC,victim‘s IP) couple
arping              : Send ARP who-has requests to determine which hosts are up
bind_layers         : Bind 2 layers on some specific fields‘ values
corrupt_bits        : Flip a given percentage or number of bits from a string
( 此处省略 N 行 )
wireshark           : Run wireshark on a list of packets
wrpcap              : Write a list of packets to a pcap file

比如这里的 arping ,我们就可以这样来使用:

arping

得到所在局域网内所有可用的ip与mac的对应关系

>>> arping("172.16.2.79/80")
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
  30:5a:3a:45:1a:28 172.16.2.79
(<ARPing: TCP:0 UDP:0 ICMP:0 Other:1>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)

查看 scapy 中已实现的网络协议

缺省参数模式

>>> ls()
AH         : AH
ARP        : ARP
ASN1_Packet : None
ATT_Error_Response : Error Response
ATT_Exchange_MTU_Request : Exchange MTU Request
( 此处省略 N 行 )
_IPv6ExtHdr : Abstract IPV6 Option Header
_MobilityHeader : Dummy IPv6 Mobility Header

携带参数模式

>>> ls(UDP)
sport      : ShortEnumField            = (53)
dport      : ShortEnumField            = (53)
len        : ShortField                = (None)
chksum     : XShortField               = (None)

ls() 中携带的参数可以是任何的一个具体的包,常用的有ARP、Ether、ICMP、IP、UDP、TCP,也支持SNMP、DHCP、STP等。

IP 模块的使用

我们可以像在 python 中一样实例化一个 IP 对象。

>>> data = IP()
>>> data
<IP  |>

也可以传入需要自定义的参数

>>> data = IP(dst="172.16.2.79")
>>> data
<IP  dst=172.16.2.79 |>

查看 IP 模块对象的所有信息

>>> data = IP()
>>> data.show()
###[ IP ]###
  version= 4
  ihl= None
  tos= 0x0
  len= None
  id= 1
  flags=
  frag= 0
  ttl= 64
  proto= ip
  chksum= None
  src= 127.0.0.1
  dst= 127.0.0.1
  \options\
>>> data = IP(dst="172.16.2.79")
>>> data.show()
###[ IP ]###
  version= 4
  ihl= None
  tos= 0x0
  len= None
  id= 1
  flags=
  frag= 0
  ttl= 64
  proto= ip
  chksum= None
  src= 172.16.2.91
  dst= 172.16.2.79
  \options\

发送报文

可以将上面的 IP 对象封装成一个数据包发送出去。

>>> send(data, iface="eth0")
.
Sent 1 packets.

接收报文

这边我所做的测试是,在本地开启两个 Putty 客户端,远程连接 172.16.2.91 虚拟机。 putty-1 用于 scapy 监听接收数据包, putty-2 用于向 172.16.2.91 发送指令。这里我发送的测试指令是 ls.

测试的结果如下:

>>> receive = sniff(filter="tcp and host 172.16.2.79", count=2)
>>> receive
<Sniffed: TCP:2 UDP:0 ICMP:0 Other:0>
>>> receive.show()
0000 Ether / IP / TCP 172.16.2.79:58081 > 172.16.2.91:ssh A / Padding
0001 Ether / IP / TCP 172.16.2.79:61186 > 172.16.2.91:ssh PA / Raw
>>> receive[0]
<Ether  dst=08:00:27:24:b8:a3 src=30:5a:3a:45:1a:28 type=0x800 |<IP  version=4L ihl=5L tos=0x0 len=40 id=20835 flags=DF frag=0L ttl=64 proto=tcp chksum=0x8ca2 src=172.16.2.79 dst=172.16.2.91 options=[] |<TCP  sport=58081 dport=ssh seq=2977133173 ack=3389913828 dataofs=5L reserved=0L flags=A window=16249 chksum=0x47bd urgptr=0 |<Padding  load=‘\x00\x00\x00\x00\x00\x00‘ |>>>>
>>> receive[1]
<Ether  dst=08:00:27:24:b8:a3 src=30:5a:3a:45:1a:28 type=0x800 |<IP  version=4L ihl=5L tos=0x0 len=104 id=20838 flags=DF frag=0L ttl=64 proto=tcp chksum=0x8c5f src=172.16.2.79 dst=172.16.2.91 options=[] |<TCP  sport=61186 dport=ssh seq=3311241534 ack=3819002209 dataofs=5L reserved=0L flags=PA window=16277 chksum=0x4540 urgptr=0 options=[] |<Raw  load=‘\xc0\xday\xa2X\xe5\‘=QS\xf9\x1e\xe2|\xa0\xb4\xb5Y\r\xb0e\x86\x02\x13x\x19E[\x94\x0c\xff\xec#\x1c?;W\xab\x18\xf6"\x90\‘\xd9\x94\x01G\xb0\xc6\x07\x08\xc3\‘\r\x7f\xa9jo\xa1\x04\xc1\\\x13y‘ |>>>>

更多详细过程,请参见下面的数据嗅探及过滤章节。


细说数据发送

在上面的基本测试阶段,对发送数据进行一些常规测试。本节会是对发送数据的一个全面解析。

send

在第三层发送数据包,但没有接收功能。

>>> send(IP(dst="www.baidu.com",ttl=1)/ICMP())
.
Sent 1 packets.
>>> send(IP(dst="192.168.115.188")/ICMP())
.
Sent 1 packets.
>>> send(IP(dst="www.baidu.com")/UDP()/NTP(version=4), loop=2)
>>> send(IP(dst="www.baidu.com")/fuzz(UDP()/NTP(version=4)), loop=2)

fuzz函数的作用:可以更改一些默认的不可以被计算的值(比如校验和checksums),更改的值是随机的,但是类型是符合字段的值的。

向某一个 IP 发送一个数据包

data_sender.py

import struct
from scapy.all import *

# data = struct.pack(‘=BHI‘, 0x12, 20, 1000)
data = struct.pack(‘=BHI‘, 0x12, 0x4EF3, 0x76)
pkt = IP(src=‘172.16.2.91‘, dst=‘172.16.2.79‘)/UDP(sport=12345,dport=5555)/data
send(pkt, inter=1, count=5)

上面的 python 代码的功能是从 172.16.2.91/12345 每隔 1 秒就向 172.16.2.79/5555 发送 5 个相同的数据包,这个数据包中包含了三个数据,分别为:0x12, 0x4EF3, 0x76。如果在 172.16.2.79 上使用抓包工具,也是可以捕获这个数据包的。

sendp

在第二层发送数据包,同样没有接收功能。

>>> sendp(Ether()/IP(dst="www.baidu.com",ttl=1)/ICMP())
.
Sent 1 packets.
>>> sendp(Ether()/IP(dst="192.168.115.188",ttl=(1,4)),iface="eth0")
....
Sent 4 packets.
>>> sendp("hello ,i am walfred ",iface="eth0",loop=1,inter=0.2)

sr

在第三层发送数据包,有接收功能。

>>> sr(IP(dst="www.baidu.com")/TCP(dport=[21, 22, 23]))
Begin emission:
.Finished to send 3 packets.
.......^C
Received 8 packets, got 0 answers, remaining 3 packets
(<Results: TCP:0 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:3 UDP:0 ICMP:0 Other:0>)
>>> p = sr(IP(dst="www.baidu.com",ttl=1)/ICMP())
Begin emission:
....Finished to send 1 packets.
...*
Received 8 packets, got 1 answers, remaining 0 packets
>>> p
(<Results: TCP:0 UDP:0 ICMP:1 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
>>> p[0]
<Results: TCP:0 UDP:0 ICMP:1 Other:0>
>>> p[0].show()
0000 IP / ICMP 172.16.2.91 > 180.97.33.108 echo-request 0 ==> IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror

连续发送ttl=1,2,3,4四个包的情况

>>> p=sr(IP(dst="www.baidu.com",ttl=(1,4))/ICMP())
Begin emission:
....*.*Finished to send 4 packets.
.*.*
Received 11 packets, got 4 answers, remaining 0 packets
>>> p
(<Results: TCP:0 UDP:0 ICMP:4 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
>>> p[0].show()
0000 IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0 ==> IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
0001 IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0 ==> IP / ICMP 121.225.2.1 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
0002 IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0 ==> IP / ICMP 218.2.151.29 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror / Padding
0003 IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0 ==> IP / ICMP 202.102.69.70 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror / Padding

sr1

在第三层发送数据包,有接收功能,但只接收第一个包。以上面的发送四个包为例

>>> q=sr1(IP(dst="www.baidu.com",ttl=(1,4))/ICMP())
Begin emission:
.....*.**Finished to send 4 packets.
..*
Received 12 packets, got 4 answers, remaining 0 packets
>>> q
<IP  version=4L ihl=5L tos=0xc0 len=56 id=30311 flags= frag=0L ttl=128 proto=icmp chksum=0x670e src=172.16.2.20 dst=172.16.2.91 options=[] |<ICMP  type=time-exceeded code=ttl-zero-during-transit chksum=0xf4ff reserved=0 length=0 unused=None |<IPerror  version=4L ihl=5L tos=0x0 len=28 id=1 flags= frag=0L ttl=1 proto=icmp chksum=0x35a8 src=172.16.2.91 dst=180.97.33.108 options=[] |<ICMPerror  type=echo-request code=0 chksum=0xf7ff id=0x0 seq=0x0 |>>>>
>>> q.show();
###[ IP ]###
  version= 4L
  ihl= 5L
  tos= 0xc0
  len= 56
  id= 30311
  flags=
  frag= 0L
  ttl= 128
  proto= icmp
  chksum= 0x670e
  src= 172.16.2.20
  dst= 172.16.2.91
  \options###[ ICMP ]###
     type= time-exceeded
     code= ttl-zero-during-transit
     chksum= 0xf4ff
     reserved= 0
     length= 0
     unused= None
###[ IP in ICMP ]###
        version= 4L
        ihl= 5L
        tos= 0x0
        len= 28
        id= 1
        flags=
        frag= 0L
        ttl= 1
        proto= icmp
        chksum= 0x35a8
        src= 172.16.2.91
        dst= 180.97.33.108
        \options###[ ICMP in ICMP ]###
           type= echo-request
           code= 0
           chksum= 0xf7ff
           id= 0x0
           seq= 0x0

srloop

在第三层工作

>>> p=srloop(IP(dst="www.baidu.com",ttl=1)/ICMP())
RECV 1: IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
RECV 1: IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
RECV 1: IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
^C
Sent 3 packets, received 3 packets. 100.0% hits.
>>> p=srloop(IP(dst="www.baidu.com",ttl=1)/ICMP(),inter=3,count=2)
RECV 1: IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
RECV 1: IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror

Sent 2 packets, received 2 packets. 100.0% hits.

这里第一条语句在执行时,将会不停的ping百度,第二条执行时每隔3秒ping一次,一共执行两次。inter表示间隔,count记录次数。

srp

在第二层发送数据包,有接收功能。

>>> srp(IP(dst="192.168.115.1")/TCP(dport=[21,22,23]))
>>> p=srp(IP(dst="www.baidu.com",ttl=1)/ICMP())

srp1

在第二层发送数据包,有接收功能,但只接收第一个包。以上面的发送四个包为例

>>> q=srp1(IP(dst="www.baidu.com",ttl=(1,4))/ICMP())

srploop

在第三层工作

>>> p = srploop(IP(dst="172.16.2.79", ttl=4)/TCP())
fail 1: IP / TCP 172.16.2.91:ftp_data > 172.16.2.79:http S
fail 1: IP / TCP 172.16.2.91:ftp_data > 172.16.2.79:http S
fail 1: IP / TCP 172.16.2.91:ftp_data > 172.16.2.79:http S
send...
Sent 3 packets, received 0 packets. 0.0% hits.
>>> p = srploop(IP(dst="www.baidu.com", ttl=4)/ICMP())
fail 1: IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0
fail 1: IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0
send...
Sent 2 packets, received 0 packets. 0.0% hits.

端口扫描

TCP 连接扫描

客户端与服务器建立 TCP 连接要进行一次三次握手,如果进行了一次成功的三次握手,则说明端口开放。

# encoding=utf-8

import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *

dst_ip = "180.97.33.107"  # 百度 IP
src_port = RandShort()
dst_port = 80

tcp_connect_scan_resp = sr1(IP(dst=dst_ip)/TCP(sport=src_port, dport=dst_port, flags="S"), timeout=10)
if str(type(tcp_connect_scan_resp)) == "":
    print("Closed")
elif tcp_connect_scan_resp.haslayer(TCP):
    if tcp_connect_scan_resp.getlayer(TCP).flags == 0x12:
        send_rst = sr(IP(dst=dst_ip)/TCP(sport=src_port, dport=dst_port, flags="AR"), timeout=10)
        print("Open")
elif tcp_connect_scan_resp.getlayer(TCP).flags == 0x14:
    print("Closed")
Begin emission:
...Finished to send 1 packets.
.*
Received 5 packets, got 1 answers, remaining 0 packets
Begin emission:
.Finished to send 1 packets.
.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Received 498 packets, got 0 answers, remaining 1 packets
Open

TCP SYN 扫描(也称为半开放扫描或stealth扫描)

同 TCP 连接扫描非常相似。同样是客户端向服务器发送一个带有 SYN 标识和端口号的数据包,如果目标端口开发,则会返回带有 SYN 和 ACK 标识的 TCP 数据包。但是,这时客户端不会返回 RST+ACK 而是返回一个只带有 RST 标识的数据包。这种技术主要用于躲避防火墙的检测。

# encoding=utf-8

import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *

dst_ip = "180.97.33.107"  # 百度 IP
src_port = RandShort()
dst_port = 80

stealth_scan_resp = sr1(IP(dst=dst_ip)/TCP(sport=src_port, dport=dst_port, flags="S"), timeout=10)
if str(type(stealth_scan_resp)) == "":
    print("Filtered")
elif stealth_scan_resp.haslayer(TCP):
    if stealth_scan_resp.getlayer(TCP).flags == 0x12:
        send_rst = sr(IP(dst=dst_ip)/TCP(sport=src_port, dport=dst_port, flags="R"), timeout=10)
        print("Open")
    elif stealth_scan_resp.getlayer(TCP).flags == 0x14:
        print("Closed")
elif stealth_scan_resp.haslayer(ICMP):
    if int(stealth_scan_resp.getlayer(ICMP).type) == 3 and int(stealth_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]:
        print("Filtered")
Begin emission:
...Finished to send 1 packets.
..*
Received 6 packets, got 1 answers, remaining 0 packets
Begin emission:
Finished to send 1 packets.
..........................................................................................................................................................................................................................................
Received 234 packets, got 0 answers, remaining 1 packets
Open

TCP 圣诞树(Xmas Tree)扫描

在圣诞树扫描中,客户端会向服务器发送带有 PSH,FIN,URG 标识和端口号的数据包给服务器。如果目标端口是开放的,那么不会有任何来自服务器的回应。如果服务器返回了一个带有 RST 标识的 TCP 数据包,那么说明端口处于关闭状态。但如果服务器返回了一个 ICMP 数据包,其中包含 ICMP 目标不可达错误类型3以及 ICMP 状态码为1,2,3,9,10或13,则说明目标端口被过滤了无法确定是否处于开放状态。

# encoding=utf-8

import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *

dst_ip = "180.97.33.107"  # 百度 IP
src_port = RandShort()
dst_port = 80

xmas_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port, flags="FPU"), timeout=10)
if str(type(xmas_scan_resp)) == "":
    print("Open|Filtered")
elif xmas_scan_resp.haslayer(TCP):
    if xmas_scan_resp.getlayer(TCP).flags == 0x14:
        print("Closed")
elif xmas_scan_resp.haslayer(ICMP):
    if int(xmas_scan_resp.getlayer(ICMP).type) == 3 and int(xmas_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]:
        print("Filtered")
Begin emission:
...Finished to send 1 packets.
.....................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Received 456 packets, got 0 answers, remaining 1 packets
Traceback (most recent call last):
  File "scan.py", line 12, in <module>
    elif(xmas_scan_resp.haslayer(TCP)):
AttributeError: ‘NoneType‘ object has no attribute ‘haslayer‘

这里扫描百度(外网)出现异常状况,于是将百度的 IP 地址替换成了本地的主机 IP 地址。再次执行结果如下:

Begin emission:
...Finished to send 1 packets.
.*
Received 5 packets, got 1 answers, remaining 0 packets
Closed

TCP FIN 扫描

FIN 扫描会向服务器发送带有 FIN 标识和端口号的 TCP 数据包。如果没有服务器端回应则说明端口开放。如果服务器返回一个 RST 数据包,则说明目标端口是关闭的。如果服务器返回了一个 ICMP 数据包,其中包含 ICMP 目标不可达错误类型3以及 ICMP 代码为1,2,3,9,10或13,则说明目标端口被过滤了无法确定端口状态。

# encoding=utf-8

import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *

dst_ip = "180.97.33.107"  # 百度 IP
src_port = RandShort()
dst_port = 80

fin_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port, flags="F"), timeout=10)
if str(type(fin_scan_resp)) == "":
    print("Open|Filtered")
elif fin_scan_resp.haslayer(TCP):
    if fin_scan_resp.getlayer(TCP).flags == 0x14:
        print("Closed")
elif fin_scan_resp.haslayer(ICMP):
    if int(fin_scan_resp.getlayer(ICMP).type) == 3 and int(fin_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]:
        print("Filtered")
Begin emission:
...Finished to send 1 packets.
..........................................................................................................................................................................................................
Received 205 packets, got 0 answers, remaining 1 packets
Traceback (most recent call last):
  File "scan.py", line 12, in <module>
    elif(fin_scan_resp.haslayer(TCP)):
AttributeError: ‘NoneType‘ object has no attribute ‘haslayer‘

与 Xmas Tree 扫描类似,这里同样切换成 172.16.2.79 的本地地址。再次测试,结果如下:

Begin emission:
...Finished to send 1 packets.
.*
Received 5 packets, got 1 answers, remaining 0 packets
Closed

TCP 空扫描(Null)

# encoding=utf-8

import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *

dst_ip = "180.97.33.107"  # 百度 IP
src_port = RandShort()
dst_port = 80

null_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port, flags=""), timeout=10)
if str(type(null_scan_resp)) == "":
    print("Open|Filtered")
elif null_scan_resp.haslayer(TCP):
    if null_scan_resp.getlayer(TCP).flags == 0x14:
        print("Closed")
elif null_scan_resp.haslayer(ICMP):
    if int(null_scan_resp.getlayer(ICMP).type) == 3 and int(null_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]:
        print("Filtered")
Begin emission:
...Finished to send 1 packets.
.............................................................................................................................................................................................................
Received 208 packets, got 0 answers, remaining 1 packets
Traceback (most recent call last):
  File "scan.py", line 12, in <module>
    elif(null_scan_resp.haslayer(TCP)):
AttributeError: ‘NoneType‘ object has no attribute ‘haslayer‘

TCP ACK 扫描

import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *

dst_ip = "180.97.33.107"
src_port = RandShort()
dst_port=80

ack_flag_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags="A"),timeout=10)
if (str(type(ack_flag_scan_resp))==""):
    print "Stateful firewall presentn(Filtered)"
elif(ack_flag_scan_resp.haslayer(TCP)):
    if(ack_flag_scan_resp.getlayer(TCP).flags == 0x4):
        print "No firewalln(Unfiltered)"
elif(ack_flag_scan_resp.haslayer(ICMP)):
    if(int(ack_flag_scan_resp.getlayer(ICMP).type)==3 and int(ack_flag_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
        print "Stateful firewall presentn(Filtered)"
Begin emission:
...Finished to send 1 packets.
..................................................................................................................................................................................................
Received 197 packets, got 0 answers, remaining 1 packets
Traceback (most recent call last):
  File "scan.py", line 12, in <module>
    elif(ack_flag_scan_resp.haslayer(TCP)):
AttributeError: ‘NoneType‘ object has no attribute ‘haslayer‘

TCP 窗口扫描

import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *

dst_ip = "180.97.33.107"
src_port = RandShort()
dst_port=80

window_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags="A"),timeout=10)
if (str(type(window_scan_resp))==""):
    print "No response"
elif(window_scan_resp.haslayer(TCP)):
    if(window_scan_resp.getlayer(TCP).window == 0):
        print "Closed"
    elif(window_scan_resp.getlayer(TCP).window > 0):
        print "Open"
Begin emission:
...Finished to send 1 packets.
......................................................................................................................................................................................................................
Received 217 packets, got 0 answers, remaining 1 packets
Traceback (most recent call last):
  File "scan.py", line 12, in <module>
    elif(window_scan_resp.haslayer(TCP)):
AttributeError: ‘NoneType‘ object has no attribute ‘haslayer‘

UDP 扫描

import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *

dst_ip = "180.97.33.107"
src_port = RandShort()
dst_port=53
dst_timeout=10

def udp_scan(dst_ip,dst_port,dst_timeout):
    udp_scan_resp = sr1(IP(dst=dst_ip)/UDP(dport=dst_port),timeout=dst_timeout)
    if (str(type(udp_scan_resp))==""):
        retrans = []
        for count in range(0,3):
            retrans.append(sr1(IP(dst=dst_ip)/UDP(dport=dst_port),timeout=dst_timeout))
        for item in retrans:
            if (str(type(item))!=""):
                udp_scan(dst_ip,dst_port,dst_timeout)
        return "Open|Filtered"
    elif (udp_scan_resp.haslayer(UDP)):
        return "Open"
    elif(udp_scan_resp.haslayer(ICMP)):
        if(int(udp_scan_resp.getlayer(ICMP).type)==3 and int(udp_scan_resp.getlayer(ICMP).code)==3):
            return "Closed"
        elif(int(udp_scan_resp.getlayer(ICMP).type)==3 and int(udp_scan_resp.getlayer(ICMP).code) in [1,2,9,10,13]):
            return "Filtered"

print udp_scan(dst_ip,dst_port,dst_timeout)
Begin emission:
...Finished to send 1 packets.
....................................................................................................................................................................................................................................................................................................................
Received 311 packets, got 0 answers, remaining 1 packets
Traceback (most recent call last):
  File "scan.py", line 28, in <module>
    print udp_scan(dst_ip,dst_port,dst_timeout)
  File "scan.py", line 20, in udp_scan
    elif (udp_scan_resp.haslayer(UDP)):
AttributeError: ‘NoneType‘ object has no attribute ‘haslayer‘

多路扫描

这里说的多路扫描是指整合了上面所有的扫描,将这些扫描集成到一个模块之中。使用的是网上提供的源码 multiport.py

这里的源码已经给出。可以直接复制使用,使用的命令行格式如下:

$ python multiport.py 180.97.33.107 -p 80

结果

+----------+------------------+--------------+---------------+---------------+---------------+---------------------------+-------------+---------------+
| Port No. | TCP Connect Scan | Stealth Scan |   XMAS Scan   |    FIN Scan   |   NULL Scan   |       ACK Flag Scan       | Window Scan |    UDP Scan   |
+----------+------------------+--------------+---------------+---------------+---------------+---------------------------+-------------+---------------+
| 80       |       Open       |     Open     | Open|Filtered | Open|Filtered | Open|Filtered | Stateful firewall present | No response | Open|Filtered |
|          |                  |              |               |               |               |         (Filtered)        |             |               |
+----------+------------------+--------------+---------------+---------------+---------------+---------------------------+-------------+---------------+

TCP 路由跟踪

route_tracking.py

# encoding=utf-8

from scapy.all import *

ans, unans = sr(IP(dst="www.baidu.com", ttl=(2, 25), id=RandShort())/TCP(flags=0x2))
for snd, rcv in ans:
    print("{0}\t{1}\t{2}".format(snd.ttl, rcv.src, isinstance(rcv.payload, TCP)))

此 Demo 的功能是捕获数据包在网络中跳转时的 IP 地址,以及使用的网络协议。

[[email protected] scapy]# python route_tracking.py
WARNING: No route found for IPv6 destination :: (no default route?)
Begin emission:
...*.**..***.***..**.*..**..**.**.*.*.Finished to send 24 packets.
*.*..*..................^C
Received 62 packets, got 23 answers, remaining 1 packets
2       121.225.2.1     False
3       218.2.151.29    False
4       221.231.206.209 False
5       202.102.69.10   False
6       180.97.32.10    False
7       10.203.195.6    False
8       180.97.33.107   True
9       180.97.33.107   True
10      180.97.33.107   True
11      180.97.33.107   True
12      180.97.33.107   True
13      180.97.33.107   True
14      180.97.33.107   True
15      180.97.33.107   True
16      180.97.33.107   True
17      180.97.33.107   True
18      180.97.33.107   True
19      180.97.33.107   True
20      180.97.33.107   True
21      180.97.33.107   True
22      180.97.33.107   True
23      180.97.33.107   True
24      180.97.33.107   True

数据嗅探及过滤

数据嗅探

可以使用 sniffer 函数进行嗅探流量,iface 表示使用的网卡接口,count 是嗅探包的个数。结果显示嗅探到了 1 个 TCP 包和 2 个 UDP 包。可以输入 pkts[i] 查看包的具体内容。

>>> receive = sniff(count=3, iface="eth0")
>>> receive.show()
0000 Ether / IP / TCP 172.16.2.91:ssh > 172.16.2.79:58081 PA / Raw
0001 Ether / IP / UDP 172.16.2.52:netbios_ns > 172.16.255.255:netbios_ns / NBNSQueryRequest
0002 Ether / IP / UDP 172.16.2.60:netbios_ns > 172.16.255.255:netbios_ns / NBNSQueryRequest
>>> receive[0]
<Ether  dst=30:5a:3a:45:1a:28 src=08:00:27:24:b8:a3 type=0x800 |<IP  version=4L ihl=5L tos=0x10 len=104 id=30775 flags=DF frag=0L ttl=64 proto=tcp chksum=0x657e src=172.16.2.91 dst=172.16.2.79 options=[] |<TCP  sport=ssh dport=58081 seq=3396365828 ack=2977251685 dataofs=5L reserved=0L flags=PA window=1515 chksum=0x5d25 urgptr=0 options=[] |<Raw  load=‘$\xecruC4)\xdc\x1fY\xc1\x80\xdd\xd2\x9d\xa2"\x95\x88T\x00\x7f\xe8]\xd8\x11\xd8\x0ck,\xa2H\xd6]\xd3\xccs\x84\xef\x99p\xecl\xb2\xbft(R\xa3^\x82rS\xb7\xe0\xc8j\xad+Qh\xfc^Y‘ |>>>>

如果这里我们不指定 count 的值,那么 scapy 将会一直处于监听状态,需要使用 ctrl + C 进行中断监听。

>>> pkts = sniff(iface="eth0")
^C>>> pkts
<Sniffed: TCP:3 UDP:128 ICMP:0 Other:106>

将上面嗅探到的内容写入到一个 .pcap 数据包文件中。

>>> wrpcap("demo.pcap",pkts)

现在我们做一个实验,目的在于解析这个 demo.pcap 文件。

>>> pkts = sniff(iface="eth0",count=3)
>>> pkts
<Sniffed: TCP:2 UDP:0 ICMP:0 Other:1>
>>> wrpcap("demo.pcap", pkts)

使用 WinSCP 将 demo.pcap 文件下载到本地,再使用 WireShark 打开。如下图:

从图中可以看出,这与上面 sniff 嗅探到的信息一致。

这里也可以使用 scapy 内置的 pcap 文件解析工具来读取并解析结果。

>>> read_pkts = rdpcap("demo.pcap")
>>> read_pkts[0]
<Ether  dst=30:5a:3a:45:1a:28 src=08:00:27:24:b8:a3 type=0x800 |<IP  version=4L ihl=5L tos=0x10 len=104 id=18563 flags=DF frag=0L ttl=64 proto=tcp chksum=0x9532 src=172.16.2.91 dst=172.16.2.79 options=[] |<TCP  sport=ssh dport=58081 seq=3389821892 ack=2977085813 dataofs=5L reserved=0L flags=PA window=1002 chksum=0x5d25 urgptr=0 options=[] |<Raw  load=‘\xca\xa6D\xf6w\xfdc\x0c\x814\xe9\xf1\x9e~;E\x0bm\xed\x99\x1b\xc5\xaf\xb5S\xfe\x8f\xdc\xf1\xe7Bu\x88dVM\x13\xb4Y\x0eT\xc6\xc6\x87\xc1E#\x124}\xad\xe2H\xbf\xbe\xe4\xf6\x96,\x0b\xf5\x050c‘ |>>>>

数据过滤

然而在实际应用中,我们还需要在数据嗅探的时候进行过滤操作。就在抓包工具 WireShark 中一样。

>>> receive = sniff(iface="eth0", filter="icmp", count=3, prn=lambda x:x.sprintf("{IP:%IP.src% -> %IP.dst%\n}{Raw:%Raw.load%\n}"))
>>> receive = sniff(filter="icmp", count=3, prn=lambda x:x.sprintf("{IP:%IP.src% -> %IP.dst%\n}{Raw:%Raw.load%\n}"))
172.16.2.91 -> 180.97.33.107
‘d\x9a\x0cW\x00\x00\x00\x00\tF\x07\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\‘()*+,-./01234567‘

180.97.33.107 -> 172.16.2.91
‘d\x9a\x0cW\x00\x00\x00\x00\tF\x07\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\‘()*+,-./01234567‘

172.16.2.91 -> 180.97.33.107
‘e\x9a\x0cW\x00\x00\x00\x00\x97N\x07\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\‘()*+,-./01234567‘

>>> receive
<Sniffed: TCP:0 UDP:0 ICMP:3 Other:0>

这里可以很明显地看出的确是捕获到了 3 个数据包 。而且这 3 个数据包都是基于 ICMP 协议的。因为这里对数据进行了过滤操作。

现在我们来对每个数据包进行详细解析。

>>> receive[0]
<Ether  dst=5c:dd:70:97:4a:a8 src=08:00:27:24:b8:a3 type=0x800 |<IP  version=4L ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0xb671 src=172.16.2.91 dst=180.97.33.107 options=[] |<ICMP  type=echo-request code=0 chksum=0x69d9 id=0x4e1b seq=0x1 |<Raw  load=‘d\x9a\x0cW\x00\x00\x00\x00\tF\x07\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\‘()*+,-./01234567‘ |>>>>

在第 0 个数据包中,我们可以分析获得以下几点信息:

1. 数据链路层:源主机的 MAC 地址为 08:00:27:24:b8:a3,而 目标主机的 MAC 地址为 5c:dd:70:97:4a:a8。且类型为 0x800(IPv4);

2. 网络层:网络协议的版本为 4L、IP报文头部长度 = IHL * 4、IP数据包在计算机网络中可以转发的最大跳数为64、使用的网络协议为 ICMP等等;

3. 传输的数据为: d\x9a\x0cW\x00\x00\x00\x00\tF\x07\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !”#$%&\’()*+,-./01234567

其他两个数据包可以做相同的分析。

现在进行一个实时处理捕获的数据信息。实验的过程是开启两个窗口,一个是用来显示捕获的数据包,一个是用来处理 ping 命令。如下:

>>> pkts = sniff(iface="eth0", filter="icmp", count=30, prn=lambda x: x.summary())

帧与字符串的互相转换

>>> icmp_str = str(pkts[0])
>>> icmp_str
‘\\\xddp\x97J\xa8\x08\x00\‘$\xb8\xa3\x08\x00E\x00\x00T\x00\x[email protected]\x[email protected]\x01\xb6p\xac\x10\x02[\xb4a!l\x08\x00\xc4a[\x19\x00Yuo\x0cW\x00\x00\x00\x00\x8d\x92\n\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\‘()*+,-./01234567‘
>>> recombine = Ether(icmp_str)
>>> recombine
<Ether  dst=5c:dd:70:97:4a:a8 src=08:00:27:24:b8:a3 type=0x800 |<IP  version=4L ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0xb670 src=172.16.2.91 dst=180.97.33.108 options=[] |<ICMP  type=echo-request code=0 chksum=0xc461 id=0x5b19 seq=0x59 |<Raw  load=‘uo\x0cW\x00\x00\x00\x00\x8d\x92\n\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\‘()*+,-./01234567‘ |>>>>

伪造网络数据包

Scapy可以对数据包进行伪造处理。下面从网上找到的一些测试方式。

在虚拟机端 172.16.2.91 上使用 Scapy 向本地主机 172.16.79 发送数据包,而本地主机则开启一个抓包工具进行数据包的实时抓取,抓包工具这里选择的是 WireShark。

按照常规的操作,可以使用如下指令向本地主机发送数据包:

>>> send(IP(dst="172.16.2.79")/TCP(dport=80, flags="S"))

如果只是单纯的这样进行发送数据包,WireShark 是可以捕获到这个数据包的。结果如下:

如果将数据包的源地址修改,比如修改为如下这样的:

>>> send(IP(src=‘172.16.2.90‘, dst="172.16.2.79")/TCP(dport=80, flags="S"))

同一时间重新开启 WireShark 捕获,这时 WireShark 无法捕获到与之前类似的信息了。

而如果将源地址重新修改回正确的源地址,则 WireShark 可以正常捕获数据包信息了。

>>> send(IP(src=‘172.16.2.91‘, dst="172.16.2.79")/TCP(dport=80,flags="S"))

对于以上的这个实验,有两个疑问:

1. 这里无法判断数据包在传输的什么位置出现问题,而致使 WireShark 无法捕获到数据包信息。也就是说无法获知,这是被发送端丢弃,还是被接收端拒绝;

2. 如果要说伪造网络数据包的话,测试的结果也应该是在本地主机上捕获到一个条记录,这条记录的内容是被修改之后的内容;


数据包拦截

上面的伪造网络数据包中,需要 Nfqueue 的支持。

由于对 Nfqueue 超出了本文的范围,Nfqueue 会在下次对 Nfqueue 的研究中体现。


Ref


时间: 2024-11-05 16:26:58

交互式数据包处理程序 Scapy 入门指南的相关文章

一小时包教会 —— webpack 入门指南

什么是 webpack? webpack是近期最火的一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX).coffee.样式(含less/sass).图片等都作为模块来使用和处理. 我们可以直接使用 require(XXX) 的形式来引入各模块,即使它们可能需要经过编译(比如JSX和sass),但我们无须在上面花费太多心思,因为 webpack 有着各种健全的加载器(loader)在默默处理这些事情,这块我们后续会提到. 你可以不打算将其用在你的项目上,但没有理由不去掌握它,因为以近期

转载------一小时包教会 —— webpack 入门指南

本文写的蛮好,转载地址:http://www.w2bc.com/Article/50764 其他的地址: http://webpack.github.io/docs/usage-with-gulp.html http://webpack.github.io/docs/usage-with-grunt.html

scapy - 基于python的数据包操作库

简介 地址:https://github.com/secdev/scapy scapy是一个基于python的交互式数据包操作程序和库. 它能够伪造或者解码多种协议的数据包,通过使用pcap文件对他们进行发送.收集.存储和读取,匹配请求和响应等等. 相关的应用 https://www.cnblogs.com/wpqwpq/p/9019516.html 原文地址:https://www.cnblogs.com/Antiver/p/10190516.html

C语言分析数据包程序

#include <pcap.h> #include <stdlib.h> #include <malloc.h> #include <stdio.h> #include <string.h> #include <signal.h> #include <time.h> #include <sys/socket.h> #include <unistd.h> #include <sys/types

AngularJS快速入门指南01:导言

AngularJS使用新的attributes扩展了HTML AngularJS对单页面应用的支持非常好(SPAs) AngularJS非常容易学习 现在就开始学习AngularJS吧! 关于本指南 本指南旨在帮助你尽可能快速而有效地学习AngularJS.通过该指南你会学习到AngularJS的一些基本特性,例如指令.表达式.过滤器.模块和控制器等.以及其它所有你需要知道的有关AngularJS的东西,如事件.DOM节点.表单.用户输入.数据验证.Http对象等. AngularJS快速入门指

打开适配器并捕获数据包

得到适配器的具体信息就可以抓取信息了. 抓取信息之前应该介绍一个核心的函数:pcap_open_live() (在之前的版本用的是pcap_open()这个函数) pcap_t* pcap_open_live(char* device, int snaplen, int promisc, int to_ms, char* ebuf) 原文是这样描述的 pcap_t* pcap_open_live(char* device, int snaplen, int promisc, int to_ms,

Wireshark入门与进阶---数据包捕获与保存的最基本流程

Wireshark入门与进阶系列(一) 本文由CSDN-蚍蜉撼青松 [主页:http://blog.csdn.net/howeverpf]原创,转载请注明出处! 你在百度上输入关键字"Wireshark.使用.教程",可以找到一大堆相关的资料.那么问题来了, 为什么我还要写这个系列的文章? 前面你能搜到的那些资料,大部分可能存在两个小问题: 网上大部分资料引自(or译自)Wireshark官方的用户手册,或使用类似于用户手册的写法,它们很详细的告诉你Wireshark有哪些窗口.菜单.

一起学微软Power BI系列-官方文档-入门指南(5)探索数据奥秘

我们几篇系列文章中,我们介绍了官方入门文档与获取数据等基本知识.今天继续给大家另外一个重点,探索数据奥秘.有了数据源,有了模型,下一步就是如何解析数据了.解析数据的过程需要很多综合技能,不仅仅是需要掌握Power BI的功能要点,熟练的利用一些新功能来提高效率,同时还需要合理的利用仪表盘,以及合理的调整对象,同时作为一个完整的应用,必须掌握数据刷新和同步的技巧,避免做重复工作. 本文原文地址:一起学微软Power BI系列-官方文档-入门指南(5)探索数据奥秘 Power BI系列文章地址:微软

AngularJS快速入门指南14:数据验证

thead>tr>th, table.reference>tbody>tr>th, table.reference>tfoot>tr>th, table.reference>thead>tr>td, table.reference>tbody>tr>td, table.reference>tfoot>tr>td { padding: 8px; line-height: 1.42857143; vertic