交互式数据包处理程序 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 地址,以及使用的网络协议。

[root@localhost 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\x00@\x00@\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

  • http://www.cnblogs.com/xiaowuyi/p/3337189.html
  • http://www.freebuf.com/sectool/94507.html
  • http://www.osedu.net/article/network/2014-01-02/445.html
  • http://blog.csdn.net/wang_walfred/article/details/50475912
  • https://github.com/interference-security/Multiport
  • http://www.secdev.org/projects/scapy/

征集

如果你也需要使用ProcessOn这款在线绘图工具,可以使用如下邀请链接进行注册:
https://www.processon.com/i/56205c2ee4b0f6ed10838a6d

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值