1# This file is part of Scapy 2## See http://www.secdev.org/projects/scapy for more informations 3## Copyright (C) Philippe Biondi <phil@secdev.org> 4## This program is published under a GPLv2 license 5 6""" 7Classes and functions for layer 2 protocols. 8""" 9 10from __future__ import absolute_import 11from __future__ import print_function 12import os, struct, time, socket 13 14import scapy 15from scapy.base_classes import Net 16from scapy.config import conf 17from scapy.data import * 18from scapy.compat import * 19from scapy.packet import * 20from scapy.ansmachine import * 21from scapy.plist import SndRcvList 22from scapy.fields import * 23from scapy.sendrecv import srp, srp1, srpflood 24from scapy.arch import get_if_hwaddr 25from scapy.utils import inet_ntoa, inet_aton 26from scapy.error import warning 27if conf.route is None: 28 # unused import, only to initialize conf.route 29 import scapy.route 30 31 32 33 34################# 35## Tools ## 36################# 37 38 39class Neighbor: 40 def __init__(self): 41 self.resolvers = {} 42 43 def register_l3(self, l2, l3, resolve_method): 44 self.resolvers[l2,l3]=resolve_method 45 46 def resolve(self, l2inst, l3inst): 47 k = l2inst.__class__,l3inst.__class__ 48 if k in self.resolvers: 49 return self.resolvers[k](l2inst,l3inst) 50 51 def __repr__(self): 52 return "\n".join("%-15s -> %-15s" % (l2.__name__, l3.__name__) for l2,l3 in self.resolvers) 53 54conf.neighbor = Neighbor() 55 56conf.netcache.new_cache("arp_cache", 120) # cache entries expire after 120s 57 58 59@conf.commands.register 60def getmacbyip(ip, chainCC=0): 61 """Return MAC address corresponding to a given IP address""" 62 if isinstance(ip, Net): 63 ip = next(iter(ip)) 64 ip = inet_ntoa(inet_aton(ip)) 65 tmp = [orb(e) for e in inet_aton(ip)] 66 if (tmp[0] & 0xf0) == 0xe0: # mcast @ 67 return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) 68 iff,a,gw = conf.route.route(ip) 69 if ( (iff == scapy.consts.LOOPBACK_INTERFACE) or (ip == conf.route.get_if_bcast(iff)) ): 70 return "ff:ff:ff:ff:ff:ff" 71 if gw != "0.0.0.0": 72 ip = gw 73 74 mac = conf.netcache.arp_cache.get(ip) 75 if mac: 76 return mac 77 78 res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip), 79 type=ETH_P_ARP, 80 iface = iff, 81 timeout=2, 82 verbose=0, 83 chainCC=chainCC, 84 nofilter=1) 85 if res is not None: 86 mac = res.payload.hwsrc 87 conf.netcache.arp_cache[ip] = mac 88 return mac 89 return None 90 91 92 93### Fields 94 95class DestMACField(MACField): 96 def __init__(self, name): 97 MACField.__init__(self, name, None) 98 def i2h(self, pkt, x): 99 if x is None: 100 try: 101 x = conf.neighbor.resolve(pkt,pkt.payload) 102 except socket.error: 103 pass 104 if x is None: 105 x = "ff:ff:ff:ff:ff:ff" 106 warning("Mac address to reach destination not found. Using broadcast.") 107 return MACField.i2h(self, pkt, x) 108 def i2m(self, pkt, x): 109 return MACField.i2m(self, pkt, self.i2h(pkt, x)) 110 111 112class SourceMACField(MACField): 113 __slots__ = ["getif"] 114 def __init__(self, name, getif=None): 115 MACField.__init__(self, name, None) 116 self.getif = ((lambda pkt: pkt.payload.route()[0]) 117 if getif is None else getif) 118 def i2h(self, pkt, x): 119 if x is None: 120 iff = self.getif(pkt) 121 if iff is None: 122 iff = conf.iface 123 if iff: 124 try: 125 x = get_if_hwaddr(iff) 126 except: 127 pass 128 if x is None: 129 x = "00:00:00:00:00:00" 130 return MACField.i2h(self, pkt, x) 131 def i2m(self, pkt, x): 132 return MACField.i2m(self, pkt, self.i2h(pkt, x)) 133 134 135class ARPSourceMACField(SourceMACField): 136 def __init__(self, name): 137 super(ARPSourceMACField, self).__init__( 138 name, 139 getif=lambda pkt: pkt.route()[0], 140 ) 141 142 143### Layers 144 145ETHER_TYPES['802_AD'] = 0x88a8 146ETHER_TYPES['802_1AE'] = ETH_P_MACSEC 147 148class Ether(Packet): 149 name = "Ethernet" 150 fields_desc = [ DestMACField("dst"), 151 SourceMACField("src"), 152 XShortEnumField("type", 0x9000, ETHER_TYPES) ] 153 __slots__ = ["_defrag_pos"] 154 def hashret(self): 155 return struct.pack("H",self.type)+self.payload.hashret() 156 def answers(self, other): 157 if isinstance(other,Ether): 158 if self.type == other.type: 159 return self.payload.answers(other.payload) 160 return 0 161 def mysummary(self): 162 return self.sprintf("%src% > %dst% (%type%)") 163 @classmethod 164 def dispatch_hook(cls, _pkt=None, *args, **kargs): 165 if _pkt and len(_pkt) >= 14: 166 if struct.unpack("!H", _pkt[12:14])[0] <= 1500: 167 return Dot3 168 return cls 169 170 171class Dot3(Packet): 172 name = "802.3" 173 fields_desc = [ DestMACField("dst"), 174 MACField("src", ETHER_ANY), 175 LenField("len", None, "H") ] 176 def extract_padding(self,s): 177 l = self.len 178 return s[:l],s[l:] 179 def answers(self, other): 180 if isinstance(other,Dot3): 181 return self.payload.answers(other.payload) 182 return 0 183 def mysummary(self): 184 return "802.3 %s > %s" % (self.src, self.dst) 185 @classmethod 186 def dispatch_hook(cls, _pkt=None, *args, **kargs): 187 if _pkt and len(_pkt) >= 14: 188 if struct.unpack("!H", _pkt[12:14])[0] > 1500: 189 return Ether 190 return cls 191 192 193class LLC(Packet): 194 name = "LLC" 195 fields_desc = [ XByteField("dsap", 0x00), 196 XByteField("ssap", 0x00), 197 ByteField("ctrl", 0) ] 198 199def l2_register_l3(l2, l3): 200 return conf.neighbor.resolve(l2, l3.payload) 201conf.neighbor.register_l3(Ether, LLC, l2_register_l3) 202conf.neighbor.register_l3(Dot3, LLC, l2_register_l3) 203 204 205class CookedLinux(Packet): 206 # Documentation: http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html 207 name = "cooked linux" 208 # from wireshark's database 209 fields_desc = [ ShortEnumField("pkttype",0, {0: "unicast", 210 1: "broadcast", 211 2: "multicast", 212 3: "unicast-to-another-host", 213 4:"sent-by-us"}), 214 XShortField("lladdrtype",512), 215 ShortField("lladdrlen",0), 216 StrFixedLenField("src","",8), 217 XShortEnumField("proto",0x800,ETHER_TYPES) ] 218 219 220 221class SNAP(Packet): 222 name = "SNAP" 223 fields_desc = [ X3BytesField("OUI",0x000000), 224 XShortEnumField("code", 0x000, ETHER_TYPES) ] 225 226conf.neighbor.register_l3(Dot3, SNAP, l2_register_l3) 227 228 229class Dot1Q(Packet): 230 name = "802.1Q" 231 aliastypes = [ Ether ] 232 fields_desc = [ BitField("prio", 0, 3), 233 BitField("id", 0, 1), 234 BitField("vlan", 1, 12), 235 XShortEnumField("type", 0x0000, ETHER_TYPES) ] 236 def answers(self, other): 237 if isinstance(other,Dot1Q): 238 if ( (self.type == other.type) and 239 (self.vlan == other.vlan) ): 240 return self.payload.answers(other.payload) 241 else: 242 return self.payload.answers(other) 243 return 0 244 def default_payload_class(self, pay): 245 if self.type <= 1500: 246 return LLC 247 return conf.raw_layer 248 def extract_padding(self,s): 249 if self.type <= 1500: 250 return s[:self.type],s[self.type:] 251 return s,None 252 def mysummary(self): 253 if isinstance(self.underlayer, Ether): 254 return self.underlayer.sprintf("802.1q %Ether.src% > %Ether.dst% (%Dot1Q.type%) vlan %Dot1Q.vlan%") 255 else: 256 return self.sprintf("802.1q (%Dot1Q.type%) vlan %Dot1Q.vlan%") 257 258 259conf.neighbor.register_l3(Ether, Dot1Q, l2_register_l3) 260 261class STP(Packet): 262 name = "Spanning Tree Protocol" 263 fields_desc = [ ShortField("proto", 0), 264 ByteField("version", 0), 265 ByteField("bpdutype", 0), 266 ByteField("bpduflags", 0), 267 ShortField("rootid", 0), 268 MACField("rootmac", ETHER_ANY), 269 IntField("pathcost", 0), 270 ShortField("bridgeid", 0), 271 MACField("bridgemac", ETHER_ANY), 272 ShortField("portid", 0), 273 BCDFloatField("age", 1), 274 BCDFloatField("maxage", 20), 275 BCDFloatField("hellotime", 2), 276 BCDFloatField("fwddelay", 15) ] 277 278 279class ARP(Packet): 280 name = "ARP" 281 fields_desc = [ XShortField("hwtype", 0x0001), 282 XShortEnumField("ptype", 0x0800, ETHER_TYPES), 283 ByteField("hwlen", 6), 284 ByteField("plen", 4), 285 ShortEnumField("op", 1, {"who-has":1, "is-at":2, "RARP-req":3, "RARP-rep":4, "Dyn-RARP-req":5, "Dyn-RAR-rep":6, "Dyn-RARP-err":7, "InARP-req":8, "InARP-rep":9}), 286 ARPSourceMACField("hwsrc"), 287 SourceIPField("psrc","pdst"), 288 MACField("hwdst", ETHER_ANY), 289 IPField("pdst", "0.0.0.0") ] 290 who_has = 1 291 is_at = 2 292 def answers(self, other): 293 if isinstance(other,ARP): 294 if ( (self.op == self.is_at) and 295 (other.op == self.who_has) and 296 (self.psrc == other.pdst) ): 297 return 1 298 return 0 299 def route(self): 300 dst = self.pdst 301 if isinstance(dst,Gen): 302 dst = next(iter(dst)) 303 return conf.route.route(dst) 304 def extract_padding(self, s): 305 return "",s 306 def mysummary(self): 307 if self.op == self.is_at: 308 return self.sprintf("ARP is at %hwsrc% says %psrc%") 309 elif self.op == self.who_has: 310 return self.sprintf("ARP who has %pdst% says %psrc%") 311 else: 312 return self.sprintf("ARP %op% %psrc% > %pdst%") 313 314def l2_register_l3_arp(l2, l3): 315 return getmacbyip(l3.pdst) 316conf.neighbor.register_l3(Ether, ARP, l2_register_l3_arp) 317 318class GRErouting(Packet): 319 name = "GRE routing informations" 320 fields_desc = [ ShortField("address_family",0), 321 ByteField("SRE_offset", 0), 322 FieldLenField("SRE_len", None, "routing_info", "B"), 323 StrLenField("routing_info", "", "SRE_len"), 324 ] 325 326 327class GRE(Packet): 328 name = "GRE" 329 fields_desc = [ BitField("chksum_present",0,1), 330 BitField("routing_present",0,1), 331 BitField("key_present",0,1), 332 BitField("seqnum_present",0,1), 333 BitField("strict_route_source",0,1), 334 BitField("recursion_control",0,3), 335 BitField("flags",0,5), 336 BitField("version",0,3), 337 XShortEnumField("proto", 0x0000, ETHER_TYPES), 338 ConditionalField(XShortField("chksum",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1), 339 ConditionalField(XShortField("offset",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1), 340 ConditionalField(XIntField("key",None), lambda pkt:pkt.key_present==1), 341 ConditionalField(XIntField("seqence_number",None), lambda pkt:pkt.seqnum_present==1), 342 ] 343 344 @classmethod 345 def dispatch_hook(cls, _pkt=None, *args, **kargs): 346 if _pkt and struct.unpack("!H", _pkt[2:4])[0] == 0x880b: 347 return GRE_PPTP 348 return cls 349 350 def post_build(self, p, pay): 351 p += pay 352 if self.chksum_present and self.chksum is None: 353 c = checksum(p) 354 p = p[:4]+chb((c>>8)&0xff)+chb(c&0xff)+p[6:] 355 return p 356 357 358class GRE_PPTP(GRE): 359 360 """ 361 Enhanced GRE header used with PPTP 362 RFC 2637 363 """ 364 365 name = "GRE PPTP" 366 fields_desc = [BitField("chksum_present", 0, 1), 367 BitField("routing_present", 0, 1), 368 BitField("key_present", 1, 1), 369 BitField("seqnum_present", 0, 1), 370 BitField("strict_route_source", 0, 1), 371 BitField("recursion_control", 0, 3), 372 BitField("acknum_present", 0, 1), 373 BitField("flags", 0, 4), 374 BitField("version", 1, 3), 375 XShortEnumField("proto", 0x880b, ETHER_TYPES), 376 ShortField("payload_len", None), 377 ShortField("call_id", None), 378 ConditionalField(XIntField("seqence_number", None), lambda pkt: pkt.seqnum_present == 1), 379 ConditionalField(XIntField("ack_number", None), lambda pkt: pkt.acknum_present == 1)] 380 381 def post_build(self, p, pay): 382 p += pay 383 if self.payload_len is None: 384 pay_len = len(pay) 385 p = p[:4] + chb((pay_len >> 8) & 0xff) + chb(pay_len & 0xff) + p[6:] 386 return p 387 388 389### *BSD loopback layer 390 391class LoIntEnumField(EnumField): 392 def __init__(self, name, default, enum): 393 EnumField.__init__(self, name, default, enum, "!I") 394 395 def m2i(self, pkt, x): 396 return x >> 24 397 398 def i2m(self, pkt, x): 399 return x << 24 400 401LOOPBACK_TYPES = { 0x2: "IPv4", 0x1c: "IPv6" } 402 403class Loopback(Packet): 404 """*BSD loopback layer""" 405 406 name = "Loopback" 407 fields_desc = [ LoIntEnumField("type", 0x2, LOOPBACK_TYPES) ] 408 __slots__ = ["_defrag_pos"] 409 410 411class Dot1AD(Dot1Q): 412 name = '802_1AD' 413 414 415bind_layers( Dot3, LLC, ) 416bind_layers( Ether, LLC, type=122) 417bind_layers( Ether, LLC, type=34928) 418bind_layers( Ether, Dot1Q, type=33024) 419bind_layers( Ether, Dot1AD, type=0x88a8) 420bind_layers( Dot1AD, Dot1AD, type=0x88a8) 421bind_layers( Dot1AD, Dot1Q, type=0x8100) 422bind_layers( Dot1Q, Dot1AD, type=0x88a8) 423bind_layers( Ether, Ether, type=1) 424bind_layers( Ether, ARP, type=2054) 425bind_layers( CookedLinux, LLC, proto=122) 426bind_layers( CookedLinux, Dot1Q, proto=33024) 427bind_layers( CookedLinux, Dot1AD, type=0x88a8) 428bind_layers( CookedLinux, Ether, proto=1) 429bind_layers( CookedLinux, ARP, proto=2054) 430bind_layers( GRE, LLC, proto=122) 431bind_layers( GRE, Dot1Q, proto=33024) 432bind_layers( GRE, Dot1AD, type=0x88a8) 433bind_layers( GRE, Ether, proto=0x6558) 434bind_layers( GRE, ARP, proto=2054) 435bind_layers( GRE, GRErouting, { "routing_present" : 1 } ) 436bind_layers( GRErouting, conf.raw_layer,{ "address_family" : 0, "SRE_len" : 0 }) 437bind_layers( GRErouting, GRErouting, { } ) 438bind_layers( LLC, STP, dsap=66, ssap=66, ctrl=3) 439bind_layers( LLC, SNAP, dsap=170, ssap=170, ctrl=3) 440bind_layers( SNAP, Dot1Q, code=33024) 441bind_layers( SNAP, Dot1AD, type=0x88a8) 442bind_layers( SNAP, Ether, code=1) 443bind_layers( SNAP, ARP, code=2054) 444bind_layers( SNAP, STP, code=267) 445 446conf.l2types.register(ARPHDR_ETHER, Ether) 447conf.l2types.register_num2layer(ARPHDR_METRICOM, Ether) 448conf.l2types.register_num2layer(ARPHDR_LOOPBACK, Ether) 449conf.l2types.register_layer2num(ARPHDR_ETHER, Dot3) 450conf.l2types.register(DLT_LINUX_SLL, CookedLinux) 451conf.l2types.register_num2layer(DLT_LINUX_IRDA, CookedLinux) 452conf.l2types.register(DLT_LOOP, Loopback) 453conf.l2types.register_num2layer(DLT_NULL, Loopback) 454 455conf.l3types.register(ETH_P_ARP, ARP) 456 457 458 459 460### Technics 461 462 463 464@conf.commands.register 465def arpcachepoison(target, victim, interval=60): 466 """Poison target's cache with (your MAC,victim's IP) couple 467arpcachepoison(target, victim, [interval=60]) -> None 468""" 469 tmac = getmacbyip(target) 470 p = Ether(dst=tmac)/ARP(op="who-has", psrc=victim, pdst=target) 471 try: 472 while True: 473 sendp(p, iface_hint=target) 474 if conf.verb > 1: 475 os.write(1, b".") 476 time.sleep(interval) 477 except KeyboardInterrupt: 478 pass 479 480 481class ARPingResult(SndRcvList): 482 def __init__(self, res=None, name="ARPing", stats=None): 483 SndRcvList.__init__(self, res, name, stats) 484 485 def show(self): 486 for s,r in self.res: 487 print(r.sprintf("%19s,Ether.src% %ARP.psrc%")) 488 489 490 491@conf.commands.register 492def arping(net, timeout=2, cache=0, verbose=None, **kargs): 493 """Send ARP who-has requests to determine which hosts are up 494arping(net, [cache=0,] [iface=conf.iface,] [verbose=conf.verb]) -> None 495Set cache=True if you want arping to modify internal ARP-Cache""" 496 if verbose is None: 497 verbose = conf.verb 498 ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=net), verbose=verbose, 499 filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) 500 ans = ARPingResult(ans.res) 501 502 if cache and ans is not None: 503 for pair in ans: 504 conf.netcache.arp_cache[pair[1].psrc] = (pair[1].hwsrc, time.time()) 505 if verbose: 506 ans.show() 507 return ans,unans 508 509@conf.commands.register 510def is_promisc(ip, fake_bcast="ff:ff:00:00:00:00",**kargs): 511 """Try to guess if target is in Promisc mode. The target is provided by its ip.""" 512 513 responses = srp1(Ether(dst=fake_bcast) / ARP(op="who-has", pdst=ip),type=ETH_P_ARP, iface_hint=ip, timeout=1, verbose=0,**kargs) 514 515 return responses is not None 516 517@conf.commands.register 518def promiscping(net, timeout=2, fake_bcast="ff:ff:ff:ff:ff:fe", **kargs): 519 """Send ARP who-has requests to determine which hosts are in promiscuous mode 520 promiscping(net, iface=conf.iface)""" 521 ans,unans = srp(Ether(dst=fake_bcast)/ARP(pdst=net), 522 filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) 523 ans = ARPingResult(ans.res, name="PROMISCPing") 524 525 ans.display() 526 return ans,unans 527 528 529class ARP_am(AnsweringMachine): 530 """Fake ARP Relay Daemon (farpd) 531 532 example: 533 To respond to an ARP request for 192.168.100 replying on the 534 ingress interface; 535 farpd(IP_addr='192.168.1.100',ARP_addr='00:01:02:03:04:05') 536 To respond on a different interface add the interface parameter 537 farpd(IP_addr='192.168.1.100',ARP_addr='00:01:02:03:04:05',iface='eth0') 538 To respond on ANY arp request on an interface with mac address ARP_addr 539 farpd(ARP_addr='00:01:02:03:04:05',iface='eth1') 540 To respond on ANY arp request with my mac addr on the given interface 541 farpd(iface='eth1') 542 543 Optional Args 544 inter=<n> Interval in seconds between ARP replies being sent 545 546 """ 547 548 function_name="farpd" 549 filter = "arp" 550 send_function = staticmethod(sendp) 551 552 def parse_options(self, IP_addr=None, ARP_addr=None): 553 self.IP_addr=IP_addr 554 self.ARP_addr=ARP_addr 555 556 def is_request(self, req): 557 return (req.haslayer(ARP) and 558 req.getlayer(ARP).op == 1 and 559 (self.IP_addr == None or self.IP_addr == req.getlayer(ARP).pdst)) 560 561 def make_reply(self, req): 562 ether = req.getlayer(Ether) 563 arp = req.getlayer(ARP) 564 565 if 'iface' in self.optsend: 566 iff = self.optsend.get('iface') 567 else: 568 iff,a,gw = conf.route.route(arp.psrc) 569 self.iff = iff 570 if self.ARP_addr is None: 571 try: 572 ARP_addr = get_if_hwaddr(iff) 573 except: 574 ARP_addr = "00:00:00:00:00:00" 575 pass 576 else: 577 ARP_addr = self.ARP_addr 578 resp = Ether(dst=ether.src, 579 src=ARP_addr)/ARP(op="is-at", 580 hwsrc=ARP_addr, 581 psrc=arp.pdst, 582 hwdst=arp.hwsrc, 583 pdst=arp.psrc) 584 return resp 585 586 def send_reply(self, reply): 587 if 'iface' in self.optsend: 588 self.send_function(reply, **self.optsend) 589 else: 590 self.send_function(reply, iface=self.iff, **self.optsend) 591 592 def print_reply(self, req, reply): 593 print("%s ==> %s on %s" % (req.summary(),reply.summary(),self.iff)) 594 595 596@conf.commands.register 597def etherleak(target, **kargs): 598 """Exploit Etherleak flaw""" 599 return srpflood(Ether()/ARP(pdst=target), 600 prn=lambda s_r: conf.padding_layer in s_r[1] and hexstr(s_r[1][conf.padding_layer].load), 601 filter="arp", **kargs) 602 603 604