• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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