Last active
September 19, 2023 23:15
-
-
Save mgeeky/3f678d385984ba0377299a844fb793fa to your computer and use it in GitHub Desktop.
DTP Scanner - simple script trying to determine type of configured switchport and DTP negotation mode in order to assist in VLAN Hopping attacks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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. | |
# | |
# Mariusz B. / mgeeky, '18 | |
# | |
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment