cdt规约报文用程序解析_用Python运维网络(5):scapy

本文参考了文章:

弈心:网络工程师的Python之路---Scapy基础篇​zhuanlan.zhihu.com
v2-81da1338241259e8962963ebd7d25b78_ipico.jpg
弈心:网络工程师的Python之路---Scapy应用篇​zhuanlan.zhihu.com
v2-81da1338241259e8962963ebd7d25b78_ipico.jpg

什么是Scapy?

根据scapy官方的定义:

Scapy is a Python program that enables the user to send, sniff and dissect and forge network packets. This capability allows construction of tools that can probe, scan or attack networks.
In other words, Scapy is a powerful interactive packet manipulation program. It is able to forge or decode packets of a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more. Scapy can easily handle most classical tasks like scanning, tracerouting, probing, unit tests, attacks or network discovery. It can replace hping, arpspoof, arp-sk, arping, p0f and even some parts of Nmap, tcpdump, and tshark).

大意就是:Scapy是一个强大的,用Python编写的交互式数据包处理程序,它能让用户发送、嗅探、解析,以及伪造网络报文,从而用来侦测、扫描和向网络发动攻击。Scapy可以轻松地处理扫描(scanning)、路由跟踪(tracerouting)、探测(probing)、单元测试(unit tests)、攻击(attacks)和发现网络(network discorvery)之类的传统任务。它可以代替hping,arpspoof,arp-sk,arping,p0f 甚至是部分的Nmap,tcpdumptshark 的功能。

Scapy实验运行环境和拓扑:

网络环境同前几篇:

v2-b8059fd9cc0acdf6686b83fdd15b55b1_b.jpg

几台交换机的管理ip地址为:

  • L3-1 192.168.56.10/24
  • L2-1 192.168.11.253/24
  • L2-2 192.168.12.253/24
  • L2-3 192.168.13.253/24
  • L2-4 192.168.14.253/24
  • L2-5 192.168.15.253/24
用户名: python
密 码: 123

scapy运行在win7或ubuntu上,安装很简单,网上有很多教程。

https://www.cnblogs.com/qingkongwuyun/p/8508733.html

测试环境:

ubuntu下:

v2-25802433903cda505b77185384b5fc95_b.jpg

win7下:

v2-7b7d32091ff24e8832596773a5bb75f9_b.jpg

用ls()函数来查看scapy支持的网络协议, (由于输出内容太长,只截取部分以供参考)。

可以看到网工们耳熟能详的ARP, BOOTP, Dot1Q, DHCP, DNS, GRE, HSRP, ICMP, IP, NTP, RIP, SNMP, STP, PPPoE, TCP, TFTP, UDP等等统统都支持。

v2-58c16a9ffca2ce516b7cf5246ec5fac8_b.jpg

除了ls()外,还可以用lsc()函数来查看scapy的指令集(函数)。比较常用的函数包括arpcachepoison(用于arp毒化攻击,也叫arp欺骗攻击),arping(用于构造一个ARP的who-has包) ,send(用于发3层报文),sendp(用于发2层报文), sniff(用于网络嗅探,类似Wireshark和tcpdump), sr(发送+接收3层报文),srp(发送+接收2层报文)等等

v2-37adf9fe937d09f01ab1ef70d4b17df8_b.jpg

这里还可以用使用ls()的携带参数模式,比如ls(IP)来查看IP包的各种默认参数。

v2-e097248a76145c68b020db7ca7c1eb23_b.jpg

v2-0e89a62f438885f3ff3cac1d00a3560c_b.jpg

实验1

实验目的:使用IP()函数构造一个目的地址为192.168.12.101(即拓扑中的PC2)的IP报文,然后用send()函数将该IP报文发送给PC2,在PC2上开启抓包软件以验证是否收到该报文。

a. 首先用IP()函数构造一个目的地址为192.168.12.101的IP报文,将它实例化给ip这个变量。

ip = IP(dst='192.168.192.168.12.101')

v2-c60ea565a4d9b82b3bf730e9d6592426_b.png

b. 用ls(ip)查看该IP报文的内容,可以发现src已经变为192.168.56.1(本机的IP),dst变为了192.168.12.101。 一个最基本的IP报文就构造好了。

ls(ip)

v2-faacbf07486d6d0b6b7e4c89784c40a4_b.jpg

c. 构造好了IP报文(src=192.168.56.1, dst=192.168.12.101)后,我们就可以用send()这个函数来把它发送给PC2了。

为了验证PC2确实接收到了我们发送的报文,在PC2上开启抓包功能。

v2-671e7c8666db02e53e5a0900e411bf00_b.jpg

在scapy上输入 send(ip,iface='VirtualBox Host-Only Network')将该报文发出去,注意后面的iface参数用来指定端口,该参数可选。

v2-c40eb898474434233a039f38d2f2af20_b.png

抓包结果:

v2-fef807a54841e5dfb79e6c3cdd52018a_b.jpg

这时可以看到我们已经抓到了从192.168.56.1发来的IP报文,注意Protocol: IPv6 Hop-by-Hop Option (0),这是因为该包的proto位为0, 不代表任何协议。


实验2

实验目的:除了send()外,scapy还有个sendp()函数,两者的区别是前者是发送三层报文,后者则是发送二层报文,实验2将演示如何用sendp()来构造二层报文。


a. 用sendp()配合Ether()和arp()函数来构造一个ARP报文,命令如下

sendp(Ether(dst='ff:ff:ff:ff:ff:ff') / ARP(hwsrc = '01:23:45:67:89:01', psrc = '192.168.56.1', hwdst = 'ff:ff:ff:ff:ff:ff', pdst = '192.168.12.101') / 'abc', iface='VirtualBox Host-Only Network') 

这里我们构造了一个源MAC地址为01:23:45:67:89:01, 源IP地址为192.168.56.1, 目标MAC地址为ff:ff:ff:ff:ff:ff,目标IP地址为192.168.12.101,payload为abc的ARP报文。

b. 可以另开一个控制台,启用sniff()来抓包,并将抓包的内容实例化到data这个变量上。

>>> data = sniff()

使用show()命令监控本地的收发包情报

>>> data.show()

注意是arp包,所以不能跨网段,需要在56段监听才行:

v2-126ea8b0ee70bc1d99982e55094fb605_b.jpg

v2-aa9373cea66ede5f0ceecb2903781c1a_b.jpg

可以看到该报文ARP部分的内容和ARP报文的结构完全一致

v2-1e5d1226ba83f655187aa2660eb8c979_b.jpg

hardware type(HTPYE)为0x0001的时候,表示Ethernet

protocol type(PTPYE)为0x0800的时候,表示IPv4

hardware length (HLEN)为0x06的时候,表示MAC地址长度为6byte

protocol length(PLEN)为0x04的时候,表示IP地址长度为4byte

ARP包有request和response之分,request包的OPER(Opcode)位为0x0001 (也就是这里的who has), response包的OPER位为0x0002。

最后的payload位(padding)即为我们自己定制的内容'abc'。


实验3

实验目的:从实验1和实验2的例子可以看出:send()和sendp()函数只能发送报文,不能接收返回的报文。如果要想查看返回的3层报文,需要用到sr()函数,实验3将演示如何使用sr()函数。

a. 用sr()向PC2发一个ICMP包,可以看到返回的结果是一个tuple(元组),该元组里的元素是两个列表,其中一个列表叫Results(响应),另一个叫Unanswered(未响应)。

>>> sr(IP(dst = '192.168.43.1') / ICMP())

v2-818648855c8124a4d4a2291bd56c42f8_b.png

这里可以看到192.168.43.1响应了这个ICMP包,所以在Results后面的ICMP:显示1。

b. 如果向一个不存在的IP,比如192.168.43.2发ICMP包,那么这时会看到scapy在找不到该IP的MAC地址(因为目标IP 192.168.43.2和我们的主机192.168.43.1在同一个网段下,这里要触发ARP寻找目标IP对应的MAC地址)的时候,转用广播。当然广播也找不到目标IP,这里可以Ctrl+C强行终止。

sr(IP(dst = '192.168.43.2') / ICMP())

v2-740ded6c64785278dc126adb47254b97_b.png

c. 我们可以将sr()函数返回的元组里的两个元素分别赋值给两个变量,第一个变量叫ans,对应Results(响应)这个元素,第二个变量叫unans,对应Unanswered(未响应)这个元素。

ans, unans = sr(IP(dst = '192.168.43.2') / ICMP())

v2-4107ae13dc953bfbd3e6467effe4cb7a_b.jpg

d. 这里还可以进一步用show(), summary(), nsummary()等方法来查看ans的内容,这里可以看到192.168.43.114向192.168.43.1发送了echo-request的ICMP包,192.168.43.1向192.168.43.114回了一个echo-reply的ICMP包。

v2-6f79b6bdca4e6aa1adccc1a254da3b4a_b.jpg

e. 如果想要查看该ICMP包更多的信息,还可以用ans[0](ans本身是个列表)来查看,因为这里我们只向192.168.2.11发送了一个echo-request包,所以用[0]来查看列表里的第一个元素。

ans[0]

v2-040dfc3a2a82769923e9806d467692c8_b.png

可以看到ans[0]本身又是一个包含了两个元素的元组,我们可以继续用ans[0][0]和ans[0][1]查看这两个元素。

ans[0][0]
ans[0][1]

v2-44fa20bac749dc38da48dc78edf89b70_b.png

v2-c1204f4975591b274332a2ee495f6201_b.jpg

实验4

实验目的:实验3讲到了sr(),它是用来接收返回的3层报文。实验4将使用srp()来接收返回的2层报文。

a. 用srp()配合Ether()和ARP()构造一个arp报文,二层目的地址为ff:ff:ff:ff:ff:ff,三层目的地址为192.168.56.0/24, 因为我们是向整个/24网络发送arp, 耗时会很长,所以这里用timeout = 5,表示将整个过程限制在5秒钟之内完成,最后的iface参数前面讲过就不解释了。

ans, unans = srp(Ether(dst = "ff:ff:ff:ff:ff:ff") / ARP(pdst = "192.168.56.0/24"), timeout = 5, iface = "VirtualBox Host-Only Network")

v2-e709f8bf9a7f53d19818240f9d1dcb5b_b.png

v2-9534290dd7b9a0c224e7c0263232e30a_b.jpg

b. 实验环境56段有5台设备,从上图可以看到我们收到了5个answers,符合我们的实验环境,下面用ans.summary()来具体看看到底是哪5个IP响应了我们的'who has'类型的arp报文。

ans.summary()

v2-cf3b2e73867fbff4a6ea74f4fdd40ca2_b.jpg

这里可以看到192.168.56.1, 192.168.56.100, 192.168.56.101, 192.168.56.10, 192.168.56.253响应了我们的'who has'类型的arp报文,并且能看到它们各自对应的MAC地址。

c. 用unans.summary()来查看那些没有回复'who has'类型arp报文的IP地址

unans.summary()

v2-cedff1ec878366809ff0e3c8b3097194_b.jpg

实验5

实验目的:使用tcp()函数构造四层报文,理解和应用RandShort(),RandNum()和Fuzz()函数。

a. 实验开始前,首先对外网络接口进行抓包。

v2-388fbf8f156831500709f404e956ecbb_b.jpg

v2-6edf0db9d3787d09e20769f75573cfb9_b.jpg

b. 在scapy上使用ip()和tcp()函数来构造一个目的地IP为http://www.baidu.com,源端口为30,目的端口为80的TCP SYN报文。

>>> ans, unans = sr(IP(dst = "www.baidu.com") / TCP(sport = 30, dport = 80, flags = "S"))

v2-5bedfdab5a3999511600fbab7c7af00e_b.png

c. TCP SYN报文发送后,监听网卡的抓包软件上发现了发出的报文

v2-2fc65c6d0256ea1f991499d575c60871_b.jpg

d. 在scapy上输入ans[0]继续验证从主机发出的包,以及远端收到的包。

ans[0]

v2-87a5f432ccd61b056cbdc3ea90106b36_b.png

e. TCP端口号除了手动指定外,还可以使用RandShort(), RandNum()和Fuzz()这几个函数来让scapy帮你自动生成一个随机的端口号,通常可以用作sport(源端口号)。

首先来看RandShort(),RandShort()会在1-65535的范围内随机生成一个TCP端口号,将上面的sport = 30 替换成 sport = RandShort()即可使用。

>>> ans, unans = sr(IP(dst = "www.baidu.com") / TCP(sport = RandShort(), dport = 80, flags = "S"))

v2-aee676f83bed63921b49528100d1afc1_b.png

这里可以看到RandShort()替我们随机生成了13116这个TCP源端口号

f. 如果你想指定scapy生成端口号的范围,可以使用RandNum(),比如你只想在1000-1500这个范围内生成端口号,可以使用RandNum(1000,1500)来指定,举例如下:

ans, unans = sr(IP(dst = "www.bing.com") / TCP(sport = RandNum(1000,1500), dport = 80, flags = "S"))

v2-670551a893628739131690f2fca2acc8_b.jpg

这里RandNum()帮我们生成了1246这个源端口号

由于我们指定的范围是1000-1500,很有可能和一些知名的端口号重复,这个时候会出现sport显示的不是端口号,而是具体的网络协议名字的情况。

g. 最后来讲下fuzz()函数,前面的RandShort()和RandNum()都是写在sport后面的(当然也可以写在dport后面用来随机生成目的端口号),用fuzz()的话则可以省略sport这部分,fuzz()会帮你检测到你漏写了sport,然后帮你随机生成一个sport也就是源端口号。

使用fuzz()的命令如下:

ans, unans = sr(IP(dst = "www.163.com") / fuzz(TCP(dport = 80, flags = "S")))

v2-a04975725d981243d55390319db0e650_b.jpg

这里看到fuzz()函数已经替我们随机生成了7673这个源端口号

v2-960ce5d1c0949fd9da6013fa4c95b235_b.jpg

此后的内容为Scapy的实际应用,包括怎么使用Scapy进行TCP的SYN扫描、ACK扫描、FIN扫描、Xmas扫描, Null扫描,怎么使用Scapy执行TCP SYN flooding攻击,ARP欺骗攻击,DHCP饥饿攻击,怎么用Scapy探测rogue DHCP服务器等等。

实验6 -- TCP SYN扫描

实验目的:使用TCP SYN扫描交换机L2-1(192.168.11.101)的53(DNS), 80 (HTTP), 23 (FTP)端口,知道如何判断端口是被关闭了(closed)还是被过滤了(filtered),两者各自有什么特征。

实验原理:TCP三次握手的原理和过程相信大家都知道。根据RFC 793,当发送端的TCP SYN包发出后,大致会有下面四种情况发生:

  1. 目的端口在接收端打开(也可以说接收端正在侦听该端口),收到SYN包的接收端回复SYN/ACK包给发送端,收到SYN/ACK包的发送端此时知道目的端口是打开的(open)。
  2. 目的端口在接收端被关闭,收到SYN包的接收端回复RST包给发送端,告知发送端该目的端口已经被关闭了(closed)。
  3. 如果发送端和接收端之间有防火墙或者使用ACL进行包过滤的路由器,那么SYN包在到达接收端之前就被防火墙或路由器拦截下来,此时防火墙或路由器会回复一个类型3(Unreachable,不可达)的ICMP包(注意不再是TCP包)给发送端告知该目的端口已经被过滤了(filtered)。
  4. 如果ICMP在防火墙或路由器上被关闭了,这时SYN包会被防火墙、路由器"静悄悄"地丢弃,路由器和防火墙不会发送类型3的ICMP包告知发送端。此时发送端收不到任何回应(no response),这里我们同样可以判断该目的端口已经被过滤了(filtered)。

知道实验原理后来看代码:

#!/usr/bin/env python
# _*_ coding: utf-8 _*_
"""
 @author: antenna
 @license: (C) Copyright 2019, Antenna.
 @contact: lilyef2000@gmail.com
 @software: 
 @file: synscan.py
 @time: 2019/3/30 3:11
 @desc:
"""
from scapy.all import *

target = 'www.cnblog.com'

ans, unans = sr(IP(dst = target) / TCP(sport = RandShort(), dport = [21, 80, 53], flags = "S"), timeout = 5)

for sent, received in ans:
        if received.haslayer(TCP) and str(received[TCP].flags) == "SA":
                print("Port " + str(sent[TCP].dport) + " of " + target + " is OPEN!")
        elif received.haslayer(TCP) and str(received[TCP].flags) == "RA":
                print("Port " + str(sent[TCP].dport) + " of " + target + " is closed!")
        elif received.haslayer(ICMP) and str(received[ICMP].type) == "3":
                print("Port " + str(sent[TCP].dport) + " of " + target + " is filtered!")

for sent in unans:
        print(str(sent[TCP].dport) + " is filtered!")
  • 这里我们使用sr()函数对'www.cnblog.com'做端口21,80,53的TCP SYN扫描(注意flags = "S"), timeout设为5秒。sr()函数返回的是一个元组,该元组下面有两个元素,一个是Results,一个是Unanswered,我们用ans来表示Results,也就是被响应的包,用unans来表示Unanswered,表示没有被响应的包。
ans, unans = sr(IP(dst = target) / TCP(sport = RandShort(), dport = [21, 80, 123], flags = "S"), timeout = 5)
  • ans和unans各自又含两个包,一个是发出去的包,一个是接收到的包,以ans[0][0]和ans[0][1]为例,第一个[0]表示抓到的第一个包,第二个[0]和[1]分别表示第一个包里发出的包和接收到的包。举例如下:
>>> target = 'www.cnblog.com'
>>> ans, unans = sr(IP(dst = target) / TCP(sport = RandShort(), dport = [21, 80, 53], flags = "S"),
 timeout = 5)
Begin emission:
Finished sending 3 packets.
.....***
Received 8 packets, got 3 answers, remaining 0 packets
>>> ans[0][0]  # (第一个包里发出的包)
<IP  frag=0 proto=tcp dst=150.129.42.39 |<TCP  sport=32179 dport=ftp flags=S |>>
>>> ans[0][1]  # (第一个包里接收到的包)
<IP  version=4 ihl=5 tos=0x0 len=72 id=6115 flags= frag=0 ttl=47 proto=icmp chksum=0xc70f src=150.12
9.42.39 dst=192.168.43.114 |<ICMP  type=dest-unreach code=host-prohibited chksum=0x1dc6 reserved=0 l
ength=0 nexthopmtu=0 |<IPerror  version=4 ihl=5 tos=0x0 len=44 id=1 flags= frag=0 ttl=46 proto=tcp c
hksum=0xe008 src=192.168.43.114 dst=150.129.42.39 |<TCPerror  sport=32179 dport=ftp seq=0 ack=0 data
ofs=6 reserved=0 flags=S window=8192 chksum=0xdd48 urgptr=0 options=[('MSS', 536)] |>>>>
>>>
  • 正因如此,所以下面for loop里的sent, received分别代表的是ans[0][0]、ans[0][1](抓到的第一个端口为22的包),ans[1][0]、ans[1][1](抓到的第二个端口为80的包)以及ans[2][0]、ans[2][1](抓到的第三个端口为53的包)里的内容
for sent, received in ans:
  • haslayer()函数返回的是布尔值,用来判断从接收端返回的包(received)里所含协议的类型,这里用来判断该received包是否包含TCP协议,并且该包里TCP的flag位是否为SA, SA代表SYN/ACK,如果这两个条件都满足,则说明该端口在接收端是打开的(Open),然后将该信息打印出来。
        if received.haslayer(TCP) and str(received[TCP].flags) == "SA":
                print("Port " + str(sent[TCP].dport) + " of " + target + " is OPEN!")
  • 同理,如果返回的包是TCP包,并且该TCP包的flag位为RA(RA表示Reset+),则说明该端口在接收端已经被关闭(closed),将该信息打印出来。
        elif received.haslayer(TCP) and str(received[TCP].flags) == "RA":
                print("Port " + str(sent[TCP].dport) + " of " + target + " is closed!")
  • 如果返回的包是ICMP包,并且该ICMP包的类型为3,则说明该端口被路由器或者防火墙过滤了(filtered),将该信息打印出来。
        elif received.haslayer(ICMP) and str(received[ICMP].type) == "3":
                print("Port " + str(sent[TCP].dport) + " of " + target + " is filtered!")
  • 最后,如果发送端没有收到任何回复(no response),我们同样可以判断该端口被路由器或者防火墙过滤了(filtered),将该信息打印出来。
for sent in unans:
        print(str(sent[TCP].dport) + " is filtered!")

执行代码看效果:

  • 目标网站如何,我也不清楚,查查看:
antenna@pythonmanager:~$ sudo python3 synscan.py 
Begin emission:
.Finished sending 3 packets.
***
Received 4 packets, got 3 answers, remaining 0 packets
Port 21 of www.cnblog.com is OPEN!
Port 80 of www.cnblog.com is OPEN!
Port 53 of www.cnblog.com is OPEN!
  • 执行代码后,会发现所有端口都是打开的

v2-60c566a445745722ee2891abbbc9dc70_b.png

可以尝试搭建环境,添加acl或防火墙来验证更多的效果。

实验7 -- 使用scapy分析路由

不说废话,直接上代码:

import os,sys,time,subprocess
import warnings,logging

warnings.filterwarnings("ignore", category=DeprecationWarning)
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import traceroute
domains = "113.108.238.121 180.96.12.11"  #  raw_input('Please input one or more IP/domain: ')
target =  domains.split(' ')
dport = [80]
if len(target) >= 1 and target[0]!='':
    res,unans = traceroute(target,dport=dport,retry=-2)
    res.graph(target="> test.svg", type="svg")
    time.sleep(1)
    subprocess.Popen(r"C:ImageMagickconvert test.svg test.png", shell=True)
else:
    print("IP/domain number of errors,exit")

代码说明:

需要下载ImageMagick转换生成的svg格式的路由图到png格式。

代码执行结果:

Begin emission:
*********.*******.........*********Finished sending 60 packets.
*******.Begin emission:
Finished sending 28 packets.
Begin emission:
Finished sending 28 packets.

Received 43 packets, got 32 answers, remaining 26 packets
   113.108.238.121:tcp80 180.96.12.11:tcp80 
1  192.168.43.1    11    192.168.43.1    11 
4  114.247.23.185  11    114.247.23.185  11 
5  -                     221.219.202.213 11 
6  202.96.12.21    11    202.96.12.5     11 
7  -                     219.158.4.158   11 
8  219.158.9.38    11    -                  
9  202.97.17.153   11    202.97.88.253   11 
10 202.97.63.213   11    -                  
11 -                     202.102.69.14   11 
12 -                     180.96.35.46    11 
13 -                     180.96.65.162   11 
14 -                     180.96.65.157   11 
15 -                     180.96.65.10    11 
16 -                     180.96.65.9     11 
17 -                     180.96.65.10    11 
18 -                     180.96.65.9     11 
19 -                     180.96.65.10    11 
20 -                     180.96.65.161   11 
21 -                     180.96.65.10    11 
22 -                     180.96.65.157   11 
23 -                     180.96.65.162   11 
24 -                     180.96.65.141   11 
25 -                     180.96.65.162   11 
26 -                     180.96.65.9     11 
27 -                     180.96.65.10    11 
28 -                     180.96.65.157   11 
29 -                     180.96.65.10    11 
30 -                     180.96.65.161   11 

Process finished with exit code 0

v2-216b610d469c1f79b3f803276cdcc484_b.jpg

怎么早没发现scapy还有这功能,太牛了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值