2018-02-02 22:22:43 +01:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
|
|
|
#
|
|
|
|
# Simple script showing configuration of the DTP protocol on
|
|
|
|
# the switch's port. This reconessaince will be helpful for performing
|
|
|
|
# VLAN Hopping attacks.
|
|
|
|
#
|
2021-10-24 23:11:42 +02:00
|
|
|
# Mariusz Banach / mgeeky, '18
|
2018-02-02 22:22:43 +01:00
|
|
|
#
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
from scapy.all import *
|
|
|
|
|
|
|
|
config = {
|
|
|
|
'count' : 10,
|
|
|
|
'timeout' : 90
|
|
|
|
}
|
|
|
|
|
|
|
|
ciscoConfigMaps = {
|
|
|
|
2: '''
|
|
|
|
ACCESS/OFF/ACCESS
|
|
|
|
Administrative Mode: static access
|
|
|
|
Operational Mode: static access
|
|
|
|
Administrative Trunking Encapsulation: dot1q
|
|
|
|
Operational Trunking Encapsulation: native
|
|
|
|
Negotiation of Trunking: Off''',
|
|
|
|
|
|
|
|
3: '''
|
|
|
|
ACCESS/DESIRABLE/ACCESS
|
|
|
|
Administrative Mode: dynamic desirable
|
|
|
|
Operational Mode: static access
|
|
|
|
Administrative Trunking Encapsulation: dot1q
|
|
|
|
Operational Trunking Encapsulation: native
|
|
|
|
Negotiation of Trunking: On''',
|
|
|
|
4: '''
|
|
|
|
ACCESS/AUTO/ACCESS
|
|
|
|
Administrative Mode: dynamic auto
|
|
|
|
Operational Mode: static access
|
|
|
|
Administrative Trunking Encapsulation: dot1q
|
|
|
|
Operational Trunking Encapsulation: native
|
|
|
|
Negotiation of Trunking: On''',
|
|
|
|
|
|
|
|
0x81: '''
|
|
|
|
TRUNK/ON/TRUNK
|
|
|
|
Administrative Mode: trunk
|
|
|
|
Operational Mode: trunk
|
|
|
|
Administrative Trunking Encapsulation: dot1q
|
|
|
|
Operational Trunking Encapsulation: dot1q
|
|
|
|
Negotiation of Trunking: On''',
|
|
|
|
}
|
|
|
|
|
|
|
|
def showConfig(stat):
|
|
|
|
if stat in ciscoConfigMaps.keys():
|
|
|
|
print(ciscoConfigMaps[stat])
|
|
|
|
|
|
|
|
def inspectPacket(dtp):
|
|
|
|
tlvs = dtp['DTP'].tlvlist
|
|
|
|
|
|
|
|
stat = -1
|
|
|
|
for tlv in tlvs:
|
|
|
|
if tlv.type == 2:
|
|
|
|
# TLV: DTPStatus
|
|
|
|
stat = ord(tlv.status)
|
|
|
|
break
|
|
|
|
|
|
|
|
print(' ' + '=' * 60)
|
|
|
|
if stat == -1:
|
|
|
|
print('[!] Something went wrong: Got invalid DTP packet.')
|
|
|
|
print(' ' + '=' * 60)
|
|
|
|
return False
|
|
|
|
|
|
|
|
elif stat == 2:
|
|
|
|
print('[-] DTP disabled, Switchport in Access mode configuration')
|
|
|
|
print('[-] VLAN Hopping via Switch Spoofing/trunking IS NOT possible.')
|
|
|
|
print('\n\tSWITCH(config-if)# switchport mode access')
|
|
|
|
|
|
|
|
elif stat == 3:
|
|
|
|
print('[+] DTP enabled, Switchport in default configuration')
|
|
|
|
print('[+] VLAN Hopping via Switch Spoofing/trunking IS POSSIBLE.')
|
|
|
|
print('\n\tSWITCH(config-if)# switchport dynamic desirable (or none)')
|
|
|
|
|
|
|
|
elif stat == 4 or stat == 0x84:
|
|
|
|
print('[+] DTP enabled, Switchport in Dynamic Auto configuration')
|
|
|
|
print('[+] VLAN Hopping via Switch Spoofing/trunking IS POSSIBLE.')
|
|
|
|
print('\n\tSWITCH(config-if)# switchport mode dynamic auto')
|
|
|
|
|
|
|
|
elif stat == 0x81:
|
|
|
|
print('[+] DTP enabled, Switchport in Trunk configuration')
|
|
|
|
print('[+] VLAN Hopping via Switch Spoofing/trunking IS POSSIBLE.')
|
|
|
|
print('\n\tSWITCH(config-if)# switchport mode trunk')
|
|
|
|
|
|
|
|
elif stat == 0xa5:
|
|
|
|
print('[?] DTP enabled, Switchport in Trunk with 802.1Q encapsulation forced configuration')
|
|
|
|
print('[?] VLAN Hopping via Switch Spoofing/trunking may be possible.')
|
|
|
|
print('\n\tSWITCH(config-if)# switchport mode trunk 802.1Q')
|
|
|
|
|
|
|
|
elif stat == 0x42:
|
|
|
|
print('[?] DTP enabled, Switchport in Trunk with ISL encapsulation forced configuration')
|
|
|
|
print('[?] VLAN Hopping via Switch Spoofing/trunking may be possible.')
|
|
|
|
print('\n\tSWITCH(config-if)# switchport mode trunk ISL')
|
|
|
|
|
|
|
|
showConfig(stat)
|
|
|
|
print(' ' + '=' * 60)
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
def packetCallback(pkt):
|
|
|
|
print('[>] Packet: ' + pkt.summary())
|
|
|
|
|
|
|
|
def main(argv):
|
|
|
|
if os.getuid() != 0:
|
|
|
|
print('[!] This program must be run as root.')
|
|
|
|
return False
|
|
|
|
|
|
|
|
load_contrib('dtp')
|
|
|
|
|
|
|
|
print('[*] Sniffing for DTP frames (Max count: {}, Max timeout: {} seconds)...'.format(
|
|
|
|
config['count'], config['timeout']
|
|
|
|
))
|
|
|
|
|
|
|
|
dtps = sniff(
|
|
|
|
count = config['count'],
|
|
|
|
filter = 'ether[20:2] == 0x2004',
|
|
|
|
timeout = config['timeout'],
|
|
|
|
prn = packetCallback,
|
|
|
|
stop_filter = lambda x: x.haslayer(DTP)
|
|
|
|
)
|
|
|
|
|
|
|
|
if len(dtps) == 0:
|
|
|
|
print('[-] It seems like there was no DTP frames transmitted.')
|
|
|
|
print('[-] VLAN Hopping may not be possible (unless Switch is in Non-negotiate state):')
|
|
|
|
print('\n\tSWITCH(config-if)# switchport nonnegotiate\t/ or / ')
|
|
|
|
print('\tSWITCH(config-if)# switchport mode access')
|
|
|
|
return False
|
|
|
|
|
|
|
|
print('[*] Got {} DTP frames.\n'.format(
|
|
|
|
len(dtps)
|
|
|
|
))
|
|
|
|
|
|
|
|
success = False
|
|
|
|
for dtp in dtps:
|
|
|
|
if dtp.haslayer(DTP):
|
|
|
|
if inspectPacket(dtp):
|
|
|
|
success = True
|
|
|
|
break
|
|
|
|
|
|
|
|
if not success:
|
|
|
|
print('[-] Received possibly corrupted DTP frames! General failure.')
|
|
|
|
|
|
|
|
print('')
|
|
|
|
return success
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main(sys.argv)
|