Scapy
scapy是python用来构建数据包的一个模块,可以根据协议构件各种数据包。
scapy依赖scapy库需要安装
1 |
pip install scapy |
使用文本编写需要导入模块from scapy.all import *,其他操作类似交互模式
scapy可以直接使用交互模式,再终端里直接输入scapy就进入了交互模式。
1 |
C:\Users\Administrator>scapy |
上面是进入信息,里面有警告信息,这些没有什么影响。
lsc()获取帮助信息
更改配置信息
交互模式下有原色设置,conf.color_theme = BrightTheme()还有几种模式设置,DefaultTheme, BrightTheme, RastaTheme, ColorOnBlackTheme, BlackAndWhite, HTMLTheme, LatexTheme
1 |
#设置颜色模式 conf.color_theme = BrightTheme() |
conf有很多配置直接输入conf可以看到
1 |
conf |
这些一般都不用设置
输入explore()可以设置一些信息
用ip数据包来举例
查看详细的信息
1 |
ls(IP()) |
建立数据包使用普通的赋值方式
1 |
ip=IP() |
直接输入ip查看已经设置的参数,IP()也可以但是他们不是一个数据包
1 |
ip |
.show()
上面的数据包没有设置任何参数,使用.show()查看所有的参数
1 |
IP().show() |
.command()
生成数据包的命令,a是之前已经设置好的数据包
1 |
a.command() |
.summary()
显示数据包摘要,谁对谁发数据
1 |
>>> a.summary() |
构建包时赋值
1 |
128) ip_1=IP(ttl= |
构建包后赋值
1 |
6 ip_1.version= |
如果数据包里有同样的参数使用下面方法
IP和TCP都有flags参数,取出单个再赋值
1 |
>>>tcp = IP()/TCP() |
tcp[1]仅代表TCP,对他进行赋值,就算IP有相同的参数也不会搞混
1 |
1].flags=2 tcp[ |
查看特定参数
1 |
ip_1.src |
删除指定参数
1 |
>>> del(ip_1.version) |
可以看到删除后就恢复了默认值
数据包堆叠
网络数据包都是一层一层的堆叠起来的,使用scapy也能构建堆叠数据包
1 |
pakage |
proto=tcp这个已经和之前只有IP的数据包不一样了。而且各种种类的数据包随意堆叠,从简单变复杂。
查看数据包
有很多中查看数据包的方式,除了前面提到的查看简洁的方式还有更复杂的方式,下面从简单到复杂介绍查看方式
直接使用变量名
使用show()
raw()
1 |
raw(a) |
查看原始数据,列出16进制格式的2进制信息。
str()
1 |
str(IP()) |
列出16进制格式的2进制信息。
hexdump
1 |
hexdump(IP()) |
十六进制信息和对应的字符串
隐藏默认字段
设置数据包有很多默认的字段,如果不需要可以直接删除。
1 |
a=IP(_) |
a是一个默认的IP数据包,有12个参数,其实有很多都没有用
1 |
a.hide_defaults() |
隐藏了很多参数。
Ether
以太网协议,最广泛的局域网技术,实现链路层的数据传输和地址封装
与电话铜缆上的 VDSL 相结合,形成EoVDSL技术;与 无源光网络 相结合,产生EPON技术;在无线环境中,发展为WLAN技术。
1 |
Ether().show() |
Address Resolution Protocol,地址解析协议。主要作用是实现通过IP地址获得对应主机的MAC地址.
1 |
ARP().show() |
动态主机配置协议,由服务器控制一段IP地址范围,客户端自动获得服务器分配的IP地址和子网掩码。
1 |
DHCP().show() |
使用拓展性小。
三层协议数据包都需要用到IP协议,用来指定目标地址和源地址
查看IP的所有参数
1 |
IP().show() |
IHL: (Internet Header Length 报头长度)是计算机名词,位于IP报文的第二个字段,4位,表示IP报文头部按32位字长(32位,4字节)计数的长度,也即报文头的长度等于IHL的值乘以4。
**tos:**1000 – minimize delay
#最小延迟
0100 – maximize throughput
#最大吞吐量
0010 – maximize reliability
#最高可靠性
0001 – minimize monetary cost
#最小费用
0000 – normal service
#一般服务
ICMP协议通常是IP协议的一个组成部分,主要传递差错校验和其他需要主要的信息,ping就是使用了这个协议。
1 |
ICMP().show() |
查看TCP的全部参数,有三次握手和四次挥手的流程,网络可靠。
1 |
TCP().show() |
**flags:**有UAPRSF六种标志,分别对应
scapy中的组合
对目标进行发包,不做可靠验证。
1 |
UDP().show() |
1 |
DNS().show() |
DNSQR
1 |
DNSQR().show() |
DNSRR
1 |
DNSRR().show() |
Trivial File Transfer Protocol,简单文件传输协议基于 UDP
1 |
###[ TFTP opcode ]### |
简单网络管理协议,监视并控制被管理的设备
1 |
SNMP().show() |
SNMPget
1 |
###[ SNMP ]### |
SNMPset
1 |
###[ SNMP ]### |
这是一个树状的查询列表。绑定对应的值查询对应的结果
1 |
>>>s=SNMP() |
查看PySNMP模块
Network Time Protocol,网络时间协议,它可以使计算机对其 服务器 或 时钟源 (如石英钟,GPS等等)做同步化可介由加密确认的方式来防止恶毒的 协议 攻击。
1 |
NTP().show() |
参考NTPlib模块
Syslog
系统日志,syslog通常被用于信息系统管理及信息安全审核。虽然它有不少缺陷
参照logging,不列于网络通讯范畴
参照paramiko
Telent
简单的请求-响应协议基于TCP,HTTP是万维网(WWW)的支撑协议
1 |
Referer: #来源url,从这个url连接来的 |
1 |
date: #日期 |
客户端传递的参数在url中,例如?name=Blosslom&age=20
客户端传递参数在请求数据中
1 |
username: |
参照http模块
电子邮件传输的协议,建立在FTP文件传输服务上
发送数据包
send()
发送三层数据包
一个点代表发送一次,使用ctrl+C停止
1 |
send(a) |
发送了上面生产的20个数据包send(a)和send(b)一样,因为b是a生成的
loop=1循环发送
1 |
1) send(a,loop= |
一个点代表发送一次,使用ctrl+C停止
sendp()
发送两层的数据包,两层数据包都需要会用Ether协议
下列的参数都是通用的
sr1()
仅发送接收一个三层数据包
srp1()
仅发送接收一个二层数据包
发送接收多个三层数据包
用于发送三层数据包和接收回复
1 |
"www.baidu.com")/ICMP()/"you are just a pice" a=IP(dst= |
Received 1 packets, got 1 answers, remaining 0 packets表示发送一个包接收一个包,剩余没收到的零个包
对比两组数据
1 |
a.show() |
发现src和dst位置是调换的
srp()
发送接收多个两层数据包
fuzz()
构建模糊模板,随机设置指定协议数据包
1 |
"www.baidu.com")/fuzz(TCP()) a=IP(dst= |
详细的参数设置如下
1 |
a.show() |
很多参数使用了rand随机
构建数据包
在windows下要想达到完整的收发数据包必须要安装winpcap或者Npcap,推荐使用Npcap
使用python编写程序的时候如果报错,则可能需要修改源文件。直接将错误部分注释。
数据包集合
1 |
a=IP() |
对a的dst参数设置一个域名,域名后面有个/30表示后缀指定生成的范围,使用生成列表生成赋值给b
**后缀解释:**后缀最大值是从32开始的,表示生成一个关联的地址,如果超过32则还是按照32的生成
1 |
"www.baidu.com/32" a.dst= |
如果换成31则在32数量基础上进行乘2
1 |
"www.baidu.com/31" a.dst= |
30则在31数量基础上进行乘2
以此类推所以一共可以生产的相关IP有2的32次方4294967296个IP接近256的4次方这和IP规范有关。这样可以生产全球所有的IP地址。
多参数生成
1 |
2,3,(3,5)] a.ttl=[ |
ttl=[2, 3, (3, 5)]的意思是将ttl值设置成2和3,(3,5)是3到5的意思
其他参数都是如此,会自动进行组合,生产全部情况数据包
ipaddress
这是一个模块产生IP列表,原理和上面的一样。
1 |
import ipaddress |
产生256个ip地址
RandIP()
RandShort()
二层数据包
注意二层数据包只能使用srp(),srp1(),sendp()发送方法
Ether()/ARP()
arp包,可以构件arp广播包等其他类型的数据包
1 |
pkt=Ether(dst="FF:FF:FF:FF:FF:FF")/ARP(op=1,hwdst="00:00:00:00:00:00",pdst="192.168.8.1") |
**Ethernet:**将目标MAC地址设置成广播地址,源地址默认是本地地址。
**ARP:**op=1表示who-has。hwdst对应目标MAC不知道设置成00:00… ,pdst对应IP
1 |
0) srp(pkt,verbose= |
发送数据包,verbose=0表示不显示正在发送的信息。可以看到返回的式两个对象,第一个对象是返回的结果,第二个是未响应的数据。使用字典接收看看对象的类型
1 |
0) r = srp(pkt,verbose= |
一个scapy的对象,有对象就应该有对应的方法
res方法
如果直接使用r.show()会报错,使用r[0].show()返回的信息是summary()方法的摘要信息。
1 |
0].res r[ |
取出了两个详细的信息,是一个列表,去掉列表需要r[0].res[0],使用r[0][0]也是同样的效果,这样就只返回 了一个元组,第一个对象是我们发出去的包,第二个是返回的数据包。
获得ARP数据层的内容
1 |
0].res[0][1].getlayer(ARP) r[ |
返回信息已经过滤了Ether,如果要取出一个值就需要把他转化为列表,然后使用列表key取出
getlayer()
1 |
0].res[0][1].getlayer(ARP).fields r[ |
这个包里hwsrc就变成对方的MAC了,其他参数也是同理。
haslayer()
同getlayer一样,不过他是判断是否有某种数据包
三层数据包
IP/ICMP数据包
通常ping就是使用这种数据包
1 |
b"are you baidu" ping = IP()/ICMP()/ |
发送了5个包,收到了一个包
1 |
response |
返回的信息也是传递的字符串。不过IP源地址是百度的。
1 |
response.getlayer(ICMP) |
提取ICMP包
1 |
response.getlayer(ICMP).fields |
转化为字典
DNS数据包
因为DNS查询时UDP连接方式,使用TCP则无法查询
IP/UDP/DNS类型数据包
构造数据包
1 |
dns=IP()/UDP()/DNS() |
设置目标地址
1 |
"8.8.8.8" dns.dst= |
设置需要查询的目标
1 |
"qq.com") dns.qd=DNSQR(qname= |
发送数据包
1 |
sr1(dns) |
查询到的结果显示了出来
IP/TCP数据包
构造数据包的同时进行赋值
1 |
pkt=IP(dst="www.baidu.com",src="192.168.248.144")/TCP() |
1 |
r=sr1(pkt) |
查看回包的摘要信息
1 |
r.summary() |
我们发送的数据保重flags=‘S’,收到的回包是SA,百度已经正确回应。
完整的回包是
1 |
r |
ping程序扫描存活主机
使用scapy作为引擎编写一个ping扫描工具,ping扫描主要是探测网络上的一些存活主机。在渗透测试中第一步往往都是信息收集,而信息收集的第一步是确认主机是否存活。通常很多小黑们都是使用系统自带的扫描工具,下面我们亲手制造一个扫描工具,了解其中的扫描原理。
程序编写需要使用python的scapy,logging,random,threading,ipaddress,sys,time模块
1 |
from scapy.all import * |
编写程序首先需要有一个思路,使用scapy的ping扫描的核心就是,给对方发送一个IP/ICMP数据包,如果对方回应就可以 判断存活。下面分步骤来编写扫描器。
清除模块导入时的提示信息
scapy模块导入时会出现依赖未安装的提示,其实这些依赖都无关紧要,都是一些图像或者和IPv6有关的信息。
1 |
logging.getLogger("scapy.runtime").setLevel(logging.ERROR) |
日志清除报错信息
IP段生成
扫描主机基本上都是扫描一个IP段,手动输入特别麻烦,一般都是IP后面跟上/24之类的掩码。比如192.168.0.0/24代表192.168.0.0-255,使用ipaddress生产 非常方便
1 |
all_ip = ipaddress.ip_network('192.168.0.0/24') |
注意这里的所有IP都是ipaddress.IPv4Address对象,使用的时候转成str类型
填写报文信息
查看IP报文信息
1 |
IP().show() |
上面的报文有些是必填的信息
查看ICMP报文信息
1 |
ICMP().show() |
1 |
ip_id = random.randint(0,65535) |
使用随机函数生三个随机数,都是0到65535,为什是65535,因为他们都是16位。
将ip和上面的随机数填入报文构造一个数据包
1 |
ping = IP(dst=ip,id=ip_id)/ICMP(id=icmp_id,seq=seq)/b"who are you" |
发送数据包
1 |
response = sr1(ping,verbose=0,timeout=2) |
收到的数据包传递给了response
为了确保可行性,先给百度发一个包看看
1 |
###[ IP ]### |
上面这个是我们发送的数据包
下面这个是百度给我们回应的包
1 |
###[ IP ]### |
ping的特色就是发什么回什么,我们发了一个who are you百度也回了一个
如果目标是死的会回什么呢?做一下实验将数据包发送给一个不存在的主机
1 |
ping = IP(dst="193.168.0.0",id=ip_id)/ICMP(id=icmp_id,seq=seq)/b"who are you" |
没有回应打印了None,利用这个特性可以很容易判断主机是否存活
加入条件,如果主机存活打印出来IP存活,不存活则不打印
1 |
if response != None: |
多线程扫描
将上面的整理封装成ping函数,使用线程调用函数。
1 |
all_ip = ipaddress.ip_network(get_ip) |
接收外部参数,主函数如下
1 |
if __name__=="__main__": |
优化一下界面,得到完整代码如下
1 |
from scapy.all import * |
使用结果如下
1 |
F:\桌面>ping.py 183.232.231.0/24 |
扫描了百度所在的网段,几乎全部是存活的。
扫描程序的局限性
我使用该程序扫描我本地路由的ip有些存活的无法识别,是因为防火墙阻挡了ICMP数据包,对方不接受ping的数据包,所以无法识别这些设备。
ARP局域网发现
使用arp扫描局域网的主机,准确率高达99%以上,因为只要是连接在局域网里的主机都会相应arp数据包。
编写过程和ping扫描差不多,只需要更改发送的数据包。由于arp是二层协议,所以它无法跨路由进行扫描,只能扫描同一网段的主机。
准备好上面ping扫描的源代码,对其发包代码进行修改,测试。
查看Ether和ARP的报文信息
1 |
###[ Ethernet ]### |
需要将Ether的dst改成广播地址,ARP的pdst改成目标IP,只需设置这两个参数即可。
除之外还需要判断用户输入的是不是局域网的IP段,逻辑代码如下,需要使用socket
1 |
import socket |
编写完整代码如下
1 |
from scapy.all import * |
这种ARP扫描无法跨路由进行扫描,虽然很精确但是非常受限。
TCP端口开放扫描
使用TCP三次握手的特性,我们发送S字段的flags标志位,如果目标端口返回SA字段的flags标志位则可以说明目标的端口是开放的。TCP端口扫描的程序设计是比前面两个复杂的。假设需要对一个网段的所有端口进行扫描,则需要进行1.7千万次的发包,所以扫描是一个非常缓慢的过程。
需要导入模块scapy,logging,random,threading,ipaddress,sys,time模块
1 |
from scapy.all import * |
清除提示信息
1 |
logging.getLogger("scapy.runtime").setLevel(logging.ERROR) |
IP/TCP数据包如下
1 |
###[ IP ]### |
生产随机参数
1 |
ip_id = random.randint(0,65535) |
构建数据包
1 |
tcp = IP(id=ip_id,dst="193.112.63.1")/TCP(dport=(1,100),seq=tcp_seq) |
IP段生成
1 |
all_ip = ipaddress.ip_network('192.168.0.0/24') |
设计数据包集合
1 |
tcp=[i for i in tcp] |
利用生成列表的原理,将每个不同的部分组合。
发送数据包
1 |
response = [i for i in [sr1(j) for j in tcp]] |
将列表里的每一个数据包发送再生成一个新列表
返回值判断
1 |
if response[1].getlayer(TCP).fields['flags']==18: |
设计数据包,发送数据包,返回值判断这三个可以合并成一行代码,只需要设计好算法,代码就可以正常运行.
编写算法的时候有很多坑,会出现TypeError: ‘NoneType’ object is not subscriptable的情况,因为数据包时以列表的形式发出去的,返回的结果也是被生成列表,所以列表中有的返回值时None,
1 |
... |
在后面的处理中会报错,所以需要过滤掉None,使用filter方法,完整合并算法如下:
1 |
response = [i if i[1].haslayer(TCP) and i[1].getlayer(TCP).fields['flags']==18 \ |
样一句代码就可以将回应SA的数据包保存列表其他以None保存列表,再经过filter进行过滤None
1 |
response = list(filter(None,response)) |
其实这样句完全可以合并到上面,但是语法太复杂了,导致很难理解。
1 |
for i in response: |
输出形式根据自己的设定样式输出
对每一个IP开启一个线程进行扫描,这样使用的时间就可以大大缩短
1 |
get_ip = sys.argv[1] |
完成扫描程序如下:
1 |
from scapy.all import * |
1 |
F:\桌面>tcp.py 193.112.63.0/30 1 100 |
UDP端口扫描
UDP端口扫描类似TCP端口扫描的编写方法,只不过返回数据包判断不同,开放的UDP端口不对包进行回应,未开放的UDP端口会回应一个ICMP的端口不可达的差错报文。通过判断是否回应数据包来判断是否开放UDP端口。
1 |
from scapy.all import * |
扫描结果如下
1 |
F:\桌面>udp.py 193.112.63.0/29 1 100 |
结果非常不准确,有很大的误差。因为如果主机没有存活也是不会回包的。
绕过防火墙扫描
如果数据包抵达防火墙时TTL值为0时,而且这个端口确实被防火墙放过的,防火墙会返回一个ICMP超时差错数据包
Ping攻击
单纯的ping即使使用全部带宽都不一定能ping死服务器,但是借助别人的服务器来攻击目标服务器是有希望攻击到目标的,铸造ping包广播服务器组攻击目标的方法叫Smurf攻击。广播包走路由需要使三层包结构,目标地址是255。不过现在很少有路由器允许这种包转发了(ip directed-broadcast),有远程唤起的服务器组可能开启
1 |
from scapy.all import * |
SYN DDoS
基于TCP通讯协议,TCP建立连接需要进行三次握手,客户端发送SYN,服务器回SYN+ACK,客户端回ACK,完成上述才能进行正常TCP通讯。所谓SYN DDoS就是只发送SYN数据包,对于服务器返回的SYN+ACK不处理,占用服务器的处理内存。SYN+ACK数据包发送到收到客户端的ACK回包有一个时间叫RTT时间,时间一到服务器就会取消等待,攻击需要在RTT时间内占满服务器的内存。
达到拒绝服务的原理是占用了合法的数据,其他的连接状态被占用,服务器返回RST数据包造成拒绝服务。
Handshake DDoS
和上面原理类似,不过这个是真正的建立连接。一个主机建立握手是有限的,还是必须使用随机的IP地址。
DHCP DDoS
使用discover寻求IP地址,耗尽资源,使用二层包发送方法
1 |
Ether(dst="FF:FF:FF:FF:FF:FF",src=RandMAC,type=0x0800)/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(dport=67,sport=68)/BOOTP(op=1,chaddr=RandMAC/DHCP(option=[("message-type","discover"),("param_req_list",b"\x01\x06\x0f,\x03!\x96+"),("end")]) |
ARP毒化
需要安装依赖包,pip install pyx
如果想要可视化一个数据包只需要一行命令即可
1 |
a=IP()/TCP()/UDP() |
可视化了堆叠的三个数据包,生成了pdf文件
还可以生产eps文件用于photoshop打开
1 |
a.psdump() |
Socket
socket又叫套接字,通过网络连接硬件之间的通讯。他是python的一个标准库,直接导入可以使用。
1 |
import scoket |
服务端流程
python的HTTP使用很少,因为他只有组基本的安全保障,一般都是使用框架例如django,flask。因为http是内置库用起来很方便,如果不是什么大项目还是可以使用的。
server
使用这个可以自己编写web服务器。具体操作如下
1 |
from http.server import HTTPServer,SimpleHTTPRequestHandler |
导入文件的格式,不能直接导入http因为http和http.server不是一个文件。HTTPServer用来创建对象的一个server类,SimpleHTTPRequestHandler是一个用来处理request的Handler类,所有请求被HTTPServer接收到都交给SimpleHTTPRequestHandler处理。
1 |
try: |
这段代码和上面的联合起来就是一个简单的web服务,在通国际目录下放入index.html文件就可以形成一个网站。
使用这个搭建的网址危险系数是很高的!
minumumTFTP
这是一个用来TFTP传输文件的一个模块,首先需要安装模块pip install minumumTFTP
进入python
1 |
>>>import minimumTFTP |
启动的path是用来存放文件的
不进入python
1 |
python-m minimumtftp-s[目录] |
客户端可以收发文件
进入python
1 |
>>>Client = minimumTFTP.Client(server_IP,client_directory, filename) |
1 |
Client.get() |
1 |
Client.put() |
不进入python
1 |
python-m minimumtftp-g[serverip][client_directory][filename] |
1 |
python-m minimumtftp-p[serverip][client_directory][filename] |
PySNMP
需要安装pip install pysnmp
在python文件中导入模块
1 |
from pysnmp.hlapi import * |
创建SNMP引擎
1 |
SnmpEngine() |
0x80004fb80532e05b88这个是引擎唯一标识
dir()
使用dir可以列出很多
1 |
>> dir() |
每一个都是一个对象
取出名字有Cmd的
1 |
for i in dir() if 'Cmd' in i] [i |
打印getCmd
1 |
getCmd |
NTPlib
需要安装模块pip install ntplib
1 |
>>>import ntplib |
打印的是一个对象
1 |
>>>response = c.request('pool.ntp.org') #访问国家时间网,获取时间对象 |
同样时一个对象
1 |
>>>print(response.tx_time) |
1 |
>>>now = time.localtime(response.tx_time) #转化时间 |
将时间戳转化成日常时间
1 |
>>>get_now = time.strftime("%H:%M:%S",now) #格式化时间 |
格式化输出时间
安照秒数电脑本身计时少了一秒,影响不大。应用到加密中很有用。
paramiko
需要安装pip install paramiko,他是python的一个库,可以使用ssh协议对远程服务器进行操作,分为两大模块
SSHClient
远程终端连接
1 |
>>>import paramiko |
执行系统命令
1 |
client.exec_command('df -h ') |
返回了元组,里面有三个对象,分开接收
1 |
>>>put,out,err=client.exec_command('df -h ') #只看输出out |
SFTPClient
实现远程文件操作,文件上传、下载、修改文件操作。