文章目录
Python是一个非常强大的网络安全渗透语言。
Python中与网络相关的模块:
-
Socket模块
-
python-nmap模块
-
Scapy模块
Socket模块
Socket并不是TCP/IP协议族中的协议,而是一个编程接口。Socket正是TCP/IP提供的外部接口。
Socket是对TCP/IP的封装和应用。
Socket被称作“套接字”,用于描述IP地址和端口,是一个通信、链的句柄,可以实现不同虚拟机 或不同计算机之间的通信。网络上的两个程序通过一个双向的通信连接实现数据的交换,应用程序通过“套接字”向网络发出请求或者应答网络请求。
简介
Socket模块的主要目的是帮助在网络上的两个程序之间建立信息通道。在Python中提供了两个基本的Socket模块:
-
服务端Socket
-
客户端Socket
当创建一个服务端Socket之后,这个Socket就会在本机的一个端口上等待连接,客户端Socket会访问这个端口,当两者完成连接之后,就可以进行交互了。
基本用法
1、Socket实例化
Socket实例化格式
socket(family,type[,protocal])
family:地址族,常用地址族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX、UNIX域Socket)、AF_ROUTE等。默认为AF_INET,通常使用默认即可。
type:Socket类型
SOCK_STREAM,TCP类型(默认)
SOCK_DGRAM,UDP类型
SOCK_RAM,原始类型,允许对底层协议如IP或ICMP进行直接访问,基本用不到。
protocal:使用的协议,通常赋值“0”,由系统自动选择。
#初始化一个TCP类型的Socket
s=socket.socket()
#初始化一个UDP类型的Socket
s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
2、Socket常用函数
bind()
由服务端Socket()调用,会将之前创建Socket与指定的IP地址和端口进行绑定。
#将创建的Socket套接字绑定到本机的2345端口
s.bind(("127.0.0.1",2345))
listen()
用于在使用TCP的服务端开启监听模式。
#服务端开启一个监听,最大连接数为5
s.listen(5)
accept()
用于在使用TCP的服务端接收连接,一般是阻塞态。接受TCP连接并返回(conn, adress),其中conn为新的套接字对象,可以用来接收和发送数据,address为连接客户端的地址。
connect()
用于在使用TCP的客户端去连接服务端时使用
# 连接本机的2345端口
s.connect(("127.0.0.1",2345))
send()
用于在使用TCP时发送数据,可能未将指定的内容全部发送。
send(string[,flag]) #返回值是发送字节数量
sendall()
用于在使用TCP时发送数据,完整发送TCP数据。
# 发送一段字符到Socket
s.sendall(bytes("Hello!", encoding="utf-8"))
# 成功返回None,失败抛出异常
recv()
用于使用TCP接收数据
recv(bufsize[,flag])
# bufsize指定最多可以接收的数量
#接收一段长度为1024 的字符Socket
obj.recv(1024)
sendto()
用于使用UDP时发送数据
recvfrom()
UDP专用,接收数据,返回远端的IP地址和端口
close():关闭Socket。
3、使用Socket编写一个简单的服务端和客户端
服务端程序
import socket
s1 = socket.socket()
s1.bind(("127.0.0.1",2345))
s1.listen(5)
while 1:
conn,address = s1.accept()
print("a new connect from",address)
conn.sendall("Hello world")
conn.close()
客户端程序
import socket
s2 = socket.socket()
s2.connect(("127.0.0.1",2345))
message = input("请输入信息:")
s2.send(message.encode())
data = s2.recv(1024)
s2.close()
print("Received",repr(data))
python-nmap模块
Nmap是网络安全审计工具,可以通过对设备的探测来审计它的安全性。
Nmap的功能
-
主机发现功能。向目标主机发送信息,然后根据目标的反应来确定它是否处于开机并联网状态。
-
端口扫描。向目标计算机的制定端口发送信息,然后根据目标端口的反应来判断它是否开放。
-
服务及版本检测。向目标计算机的目标端口发送特制的信息,然后根据目标的反应来检测它运行服务的服务类型和版本。
-
操作系统检测。
-
其他高级的审计技术。如:伪造发起扫描端的身份,进行隐蔽的扫描,规避目标的防御设备(例如防火墙),对系统进行安全漏洞检测,并提供完善的报告选项。
简介
python-nmap是一个可以帮助使用Nmap功能的Python模块文件。
如果想要在Python中正常使用python-nmap模块,必须现在系统中安装Namp。
基本用法
python-nmap模块的核心是PortScanner
、PortScannerAsync
、PortScannerError
、PortScannerHostDict
、PortScannerYield
等5个类。
1、python-nmap模块类的实例化
nmap.PortScanner()
2、pyhon-nmap模块中的函数
Portscanner类中的函数
**scan()**函数:用来对指定目标进行扫描
scan(self,hosts="127.0.0.1",ports=None,arguments="-sV",sudo=False)
# **host****:**要扫描的主机
# **ports**:要扫描的端口
## 单一端口:“80”
## 多个端口:“80,443,8080”
## 端口范围:“1-1000”
# **arguments**:Nmap扫描时所使用的的参数
## -sP:对目标进行Ping主机在线扫描
## -PR:对目标进行一个ARP的主机在线扫描
## -sS:对目标主机进行一个TCP半开(SYN)类型的端口扫描
## -sT:对目标进行一个TCP全开类型的端口扫描
## -O:扫描目标的操作系统类型
## -sV:扫描目标上锁安装网络服务软件的版本
# 对192.168.1.101的1~500端口进行一次TCP半开扫描
import nmap
nm = nmap.PortScanner()
nm.scan("192.168.1.101","1-500","-sS")
**all_hosts()函数:**返回一个被扫描的所有主机列表
>>> nm.all_hosts()
["192.168.1.101"]
**command_line()**函数:返回在当前扫描中使用的命令行
>>> nm.command_line()
'nmap -oX - -p 1-500 -sS 192.168.1.101'
**csv()**函数:返回一个CSV(逗号分隔值文件格式)的输出
>>> nm.csv()
>>> print(nm.csv())
**has_host(self,host)**函数:检查是否有host的扫描结果,如果有则返回True,否则返回False
>>> nm.has_host("192.168.1.101")
True
**scaninfo()**函数:列出一个扫描信息的结构
>>> nm.scaninfo()od
{"tcp":{"servers":"1-500","method":"syn"}}
这个类还支持如下的操作:
nm["192.168.1.101"].hostname() # 获取主机名,通常为用户记录
nm["192.168.1.101"].state() # 获取主机的状态
nm["192.168.1.101"].all_protocols() # 获取执行的协议['tcp','udp']包含(IP|TCP|UDP|SCTP)
nm["192.168.1.101"].["tcp"].keys() #获取TCP所有的端口号
nm["192.168.1.101"].all_tcp() # 获取TCP所有端口号(按端口号大小进行排序)
nm["192.168.1.101"].all_udp() # 获取UDP所有端口号
nm["192.168.1.101"].all_sctp() # 获取所有SCTP所有端口号
nm["192.168.1.101"].has_tcp(22) # 主机是否有关于22端口的信息
nm["192.168.1.101"]["tcp"][22] # 获取主机关于22端口的信息
nm["192.168.1.101"].tcp(22) # 获取主机关于22端口的信息
nm["192.168.1.101"]["tcp"][22]["state"] # 获取主机22端口的状态
nm["192.168.1.101"]
nm["192.168.1.101"]
nm["192.168.1.101"]
PortScannerAsync类中的函数
PortScannerAsync类中的函数scan()与PortScanner类中的scan()基本一样,但是多了一个回调函数。
scan(self,host="127.0.0.1",ports=None,arguments="-sV",callback=None,sudo=False)
# callback是以(host,scan_data)为参数的函数
import namp
nma = nmap.PortScannerAsync()
nma.scan(hosts="192.168.1.0/24",arguments="-sP")
still_scanning():如果扫描正在进行,则返回True,否则返回False
wait(self,timeout=None):函数表示等待时间
stop():停止当前的扫描
3、使用python-nmap模块来编写一个扫描器
扫描192.168.1.101开放的从1~1000的哪些端口
import nmap
nm = nmap.PortScanner()
nm.scan("192.168.1.101","1-1000")
for host in nm.all_hosts():
print("*"*30)
print("Host:%s(%s)"%(host,nm[host].honstname()))
print("State:%s"%nm[host].state())
for proto in nm[host].all_protocols():
print("*"*30)
print('Protocol:%s'%proto)
lport = nm[host][proto].keys()
lport.sort()
for port in lport:
print('port:%s\state:%s'%(port,nm[host][proto][port]['state']))
nma = nmap.PortScannerAsync()
def callback_result(host,scan_result):
print()
print(host,scan_result)
nma.scan(hosts="192.168.1.0/24",arguments="-sP",callback_result)
在使用scan函数扫描的过程中会执行callback_result函数,可以一边扫描一边输出扫描结果。
Scapy模块
简介
Scapy的内部实现了大量的网络协议(DNS、ARP、IP、TCP、UDP等),可以实现对网络数据包的发送、监听和解析。这个模块相比与Nmap来说,更加底层。可以更为直观的了解到网络中的各种扫描和攻击行为。
基本用法
Scapy本身就是一个可以运行的工具,它自己具备一个独立的运行环境,因而可以不在Python环境下运行。
1、Scapy的基本操作
在Scapy中,每一个协议就是一个类。只需要实例化一个协议类,就可以创建一个该协议的数据包。
例如,如果要创建一个IP类型的数据包,就可以使用如下命令:
>>> ip = IP()
>>> ip
<IP >
构造一个发往“192.168.1.101”的IP数据包
>>> ip = IP(dst="192.168.1.101")
>>> ip
<ip dst=192.168.1.101 >
这个dst的值可以是一个IP地址,也可以是一个IP范围,例如192.168.1.0/24,这时产生的不是一个数据包,而是256个数据包。
>>> target = "192.168.1.0/24"
>>> ip = IP(dst=target)
>>> ip
<ip dst=Net("192.168.1.0/24") >
# 查看其中每一个数据包,可以使用[p for p in ip]
>>> [p for p in ip]
Scapy采用分层的形式来构造数据包,通常最下面的一个协议为Ether,然后是IP,再之是TCP或者是UDP。IP()函数无法构造ARP请求和应答数据包,但可以使用Ether()
# 构造一个广播数据包
>>> Ether(dst="ff:ff:ff:ff:ff:ff")
<Ether dst=ff:ff:ff:ff:ff:ff>
Scapy中的分层通过符号/
实现,按照协议由底而上的顺序从左向右排列,例如:
>>> Ether()/IP()/TCP()
构造一个HTTP数据包
>>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n"
使用ls()函数来查看一个类所拥有的属性
# 查看Ether类的属性
>>> ls(Ether())
2、Scapy模块中的函数
Scapy中提供了许多用来完成发送数据包的函数。
✨ send(),sendp()
**send()**工作在第三层,用来发送IP数据包;
**sendp()**工作在的二层,用来发送Enter数据包。
例如:构造一个目的地址为“192.168.1.101”的ICMP数据包,并将其发送出去。
>>> send(IP(dst="192.168.1.101")/ICMP())
Sent 1 packets.
>>> sendp(Ether(dst="ff:ff:ff:ff:ff:ff"))
Sent 1 packets.
这两个函数的特点是只发不收。
✨ fuzz()
如果希望发送一个内容是随机填充的数据包,而且又要保证这个数据包的正确性,可以使用fuzz()
函数。
如果不使用fuzz()
来填充数据包,这样有可能会导致目标端口不响应,从而无法对目标端口状态进行判断。
>>> IP(dst="192.168.1.101")/fuzz(TCP())
✨ sr(),sr1(),srp()
Scapy中提供了三个用来发送和接收的数据包的函数,分别是sr()
、sr1()
和srp()
,其中sr()
和sr1()
主要用于第三层,而srp()
用于第二层。
# 向127.0.0.1发送一个ICMP数据包
>>> sr(IP(dst="127.0.0.1")/ICMP())
Begin emission:
.Finished sending 1 packets.
^C
Received 1 packets, got 0 answers, remaining 1 packets
(<Results: TCP:0 UDP:0 ICMP:0 Other:0>,
<Unanswered: TCP:0 UDP:0 ICMP:1 Other:0>)
当产生的数据包发送出去之后,Scapy就会监听接收到的数据包,并将其中对应的数据包筛选出来,显示在下面。Reveived表示收到的数据包个数,answers表示对应的应答数据包。
sr()的返回值是两个列表,第一个列表是收到了应答的包和对应的应答,第二个列表是未收到应答的包。
>>> ans,unans = sr(IP(dst="192.168.120.3")/ICMP())
Begin emission:
Finished sending 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
>>> ans.summary()
IP / ICMP 192.168.120.2 > 192.168.120.3 echo-request 0 ==>
IP / ICMP 192.168.120.3 > 192.168.120.2 echo-reply 0 / Padding
sr1()函数跟sr()函数作用基本一样,但是只返回一个应答包。所以只需要使用一个列表就可以保存这个函数的返回值。
>>> p = sr1(IP(dst="192.168.120.3")/ICMP())
Begin emission:
Finished sending 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
>>> p
<IP version=4 ihl=5 tos=0x0 len=28 id=692
flags= frag=0 ttl=128 proto=icmp chksum=0xc6d6
src=192.168.120.3 dst=192.168.120.2
|<ICMP type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0
|<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>>
>>> p.summary()
'IP / ICMP 192.168.120.3 > 192.168.120.2 echo-reply 0 / Padding'
>>>
可以利用sr1()函数来测试目标的某个端口号是否开放,采用半开扫描(SYN)是办法。
>>> p1 = sr1(IP(dst="192.168.120.3")/TCP(dport=443,flags="S"))
Begin emission:
Finished sending 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
>>> p1
<IP version=4 ihl=5 tos=0x0 len=44 id=1692 flags=DF
frag=0 ttl=128 proto=tcp chksum=0x82d9 src=192.168.120.3 dst=192.168.120.2 |
<TCP sport=https dport=ftp_data seq=3559562169 ack=1
dataofs=6 reserved=0 flags=SA window=65392
chksum=0xb59a urgptr=0 options=[('MSS', 1460)] |
<Padding load='\x00\x00' |>>>
从上面p的值可以看来,192.168.120.3回应了发送的设置了SYN标志位的TCP数据包,这表明这台主机的443端口是开放的。
✨ sniff()
sniff()函数,可以在自己的程序中捕获经过本机网卡的数据包。
>>> sniff()
^C<Sniffed: TCP:0 UDP:0 ICMP:8 Other:4>
使用sniff()开始监听,但是捕获的数据包不会即时显示,需要使用组合键Ctrl+C停止监听时,才能显示捕获的数据包。
此函数可以使用参数filter对数据包进行过滤。
# 针对IP进行过滤
>>> sniff(filter=" host 192.168.120.3")
# 针对指定协议进行过滤
>>> sniff(filter="icmp")
# 同时满足多个条件使用“and”“or”等关系运算符来表达
>>> sniff(filter=" host 192.168.120.3 and icmp")
参数iface可以用来指定所要进行监听的网卡
>>> sniff(iface="eth1")
参数count用来指定监听到数据包的数量,达到指定的数量就会停止监听
>>> sniff(count=3)
现在设计一个综合性的监听器,它会在网卡eth0上监听源地址或者目标地址为192.168.120.3的icmp数据包,当收到3个数据包之后,就会停止监听。
>>> sniff(filter="icmp and host 192.168.120.3", count=3, iface="eth0")
<Sniffed: TCP:0 UDP:0 ICMP:3 Other:0>
>>> _
<Sniffed: TCP:0 UDP:0 ICMP:3 Other:0>
>>> a=_
>>> a
<Sniffed: TCP:0 UDP:0 ICMP:3 Other:0>
>>> a.summary()
Ether / IP / ICMP 192.168.120.3 > 192.168.120.2 echo-request 0 / Raw
Ether / IP / ICMP 192.168.120.2 > 192.168.120.3 echo-reply 0 / Raw
Ether / IP / ICMP 192.168.120.3 > 192.168.120.2 echo-request 0 / Raw
>>> a.nsummary()
0000 Ether / IP / ICMP 192.168.120.3 > 192.168.120.2 echo-request 0 / Raw
0001 Ether / IP / ICMP 192.168.120.2 > 192.168.120.3 echo-reply 0 / Raw
0002 Ether / IP / ICMP 192.168.120.3 > 192.168.120.2 echo-request 0 / Raw
如果需要查看这三个数据包的内容,可以使用_
,在Scapy中这个符号表示的是上一条语句执行的结果。
函数pkt.nsummary()和pkt.summary()作用相同,只是要操作的对象是多个数据包。
3、Scapy模块的常用简单实例
检测端口是否被屏蔽
正常的时候,如果一个开放的端口会回应ack数据包,而关闭的端口会回应rst数据包。在网络中,一些网络安全设备会过滤掉一部分端口,这些端口不会响应来自外界的数据包,一切发往这些端口的数据包都如同石沉大海。注意这些端口的状态并非开放或者关闭,而是被屏蔽。这是一种网络安全管理经常用到的方法。
使用Scapy来实现一次ACK类型的端口扫描,例如:对192.168.120.3的21、23、135、443、445这5个端口是否被屏蔽进行扫描,采用ACK的扫描方式。
>>> ans,unans = sr(IP(dst="192.168.120.3")/TCP(dport=[21,23,135,443,445],flags="A"))
Begin emission:
****Finished sending 5 packets.
*
Received 5 packets, got 5 answers, remaining 0 packets
查看未被过滤的端口:
>>> for s,r in ans:
...: if s[TCP].dport == r[TCP].sport:
...: print(str(s[TCP].dport)+"is unfiltered")
...:
21is unfiltered
23is unfiltered
135is unfiltered
443is unfiltered
445is unfiltered
查看被过滤的端口:
>>> for s in unans:
...: print(str(s[TCP].dport)+"is filtered")
...:
在Python中编写程序来实现查看端口是否被屏蔽
from scapy.all import IP,TCP,sr
ans,unans = sr(IP(dst="192.168.120.3")/TCP(dport=[21,23,135,443,445],flags="A"))
for s,r in ans:
if s[TCP].dport == r[TCP].sport:
print("The port" + str(s[TCP].dport) + "is unfiltered")
使用IP(),TCP()来创建数据包,使用fuzz()来填充数据包,使用sr()来发送数据包。
from scapy.all import fuzz,TCP,IP,sr
ans,unans = sr(IP(dst="192.168.120.3")/fuzz(TCP(dport=80,flags="S")))
接下来使用循环来查看,如果r[TCP].flags == 18,则表示反会数据包flags的值为0x012(SYN,ACK),目标端口为开放状态。如果r[TCP].flags == 20,则表示反会数据包flags的值为0x014(RST,ACK),目标端口为关闭状态。
for s,r in ans:
if r[TCP].flags == 18:
print("This port is Open")
if r[TCP].flags == 20:
print("This port is Closed")