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""" 7IPv4 (Internet Protocol v4). 8""" 9 10from __future__ import absolute_import 11from __future__ import print_function 12import os, time, struct, re, socket, types 13from select import select 14from collections import defaultdict 15 16from scapy.utils import checksum,inet_aton,inet_ntoa 17from scapy.base_classes import Gen 18from scapy.data import * 19from scapy.layers.l2 import * 20from scapy.compat import * 21from scapy.config import conf 22from scapy.consts import WINDOWS 23from scapy.fields import * 24from scapy.packet import * 25from scapy.volatile import * 26from scapy.sendrecv import sr,sr1,srp1 27from scapy.plist import PacketList,SndRcvList 28from scapy.automaton import Automaton,ATMT 29from scapy.error import warning 30from scapy.utils import whois 31 32import scapy.as_resolvers 33 34from scapy.arch import plt, MATPLOTLIB_INLINED, MATPLOTLIB_DEFAULT_PLOT_KARGS 35import scapy.modules.six as six 36from scapy.modules.six.moves import range 37 38#################### 39## IP Tools class ## 40#################### 41 42class IPTools(object): 43 """Add more powers to a class with an "src" attribute.""" 44 __slots__ = [] 45 def whois(self): 46 """whois the source and print the output""" 47 if WINDOWS: 48 print(whois(self.src)) 49 else: 50 os.system("whois %s" % self.src) 51 def _ttl(self): 52 """Returns ttl or hlim, depending on the IP version""" 53 return self.hlim if isinstance(self, scapy.layers.inet6.IPv6) else self.ttl 54 def ottl(self): 55 t = sorted([32,64,128,255]+[self._ttl()]) 56 return t[t.index(self._ttl())+1] 57 def hops(self): 58 return self.ottl() - self._ttl() 59 60 61_ip_options_names = { 0: "end_of_list", 62 1: "nop", 63 2: "security", 64 3: "loose_source_route", 65 4: "timestamp", 66 5: "extended_security", 67 6: "commercial_security", 68 7: "record_route", 69 8: "stream_id", 70 9: "strict_source_route", 71 10: "experimental_measurement", 72 11: "mtu_probe", 73 12: "mtu_reply", 74 13: "flow_control", 75 14: "access_control", 76 15: "encode", 77 16: "imi_traffic_descriptor", 78 17: "extended_IP", 79 18: "traceroute", 80 19: "address_extension", 81 20: "router_alert", 82 21: "selective_directed_broadcast_mode", 83 23: "dynamic_packet_state", 84 24: "upstream_multicast_packet", 85 25: "quick_start", 86 30: "rfc4727_experiment", 87 } 88 89 90class _IPOption_HDR(Packet): 91 fields_desc = [ BitField("copy_flag",0, 1), 92 BitEnumField("optclass",0,2,{0:"control",2:"debug"}), 93 BitEnumField("option",0,5, _ip_options_names) ] 94 95class IPOption(Packet): 96 name = "IP Option" 97 fields_desc = [ _IPOption_HDR, 98 FieldLenField("length", None, fmt="B", # Only option 0 and 1 have no length and value 99 length_of="value", adjust=lambda pkt,l:l+2), 100 StrLenField("value", "",length_from=lambda pkt:pkt.length-2) ] 101 102 def extract_padding(self, p): 103 return b"",p 104 105 registered_ip_options = {} 106 @classmethod 107 def register_variant(cls): 108 cls.registered_ip_options[cls.option.default] = cls 109 @classmethod 110 def dispatch_hook(cls, pkt=None, *args, **kargs): 111 if pkt: 112 opt = orb(pkt[0])&0x1f 113 if opt in cls.registered_ip_options: 114 return cls.registered_ip_options[opt] 115 return cls 116 117class IPOption_EOL(IPOption): 118 name = "IP Option End of Options List" 119 option = 0 120 fields_desc = [ _IPOption_HDR ] 121 122 123class IPOption_NOP(IPOption): 124 name = "IP Option No Operation" 125 option=1 126 fields_desc = [ _IPOption_HDR ] 127 128class IPOption_Security(IPOption): 129 name = "IP Option Security" 130 copy_flag = 1 131 option = 2 132 fields_desc = [ _IPOption_HDR, 133 ByteField("length", 11), 134 ShortField("security",0), 135 ShortField("compartment",0), 136 ShortField("handling_restrictions",0), 137 StrFixedLenField("transmission_control_code","xxx",3), 138 ] 139 140class IPOption_RR(IPOption): 141 name = "IP Option Record Route" 142 option = 7 143 fields_desc = [ _IPOption_HDR, 144 FieldLenField("length", None, fmt="B", 145 length_of="routers", adjust=lambda pkt,l:l+3), 146 ByteField("pointer",4), # 4 is first IP 147 FieldListField("routers",[],IPField("","0.0.0.0"), 148 length_from=lambda pkt:pkt.length-3) 149 ] 150 def get_current_router(self): 151 return self.routers[self.pointer//4-1] 152 153class IPOption_LSRR(IPOption_RR): 154 name = "IP Option Loose Source and Record Route" 155 copy_flag = 1 156 option = 3 157 158class IPOption_SSRR(IPOption_RR): 159 name = "IP Option Strict Source and Record Route" 160 copy_flag = 1 161 option = 9 162 163class IPOption_Stream_Id(IPOption): 164 name = "IP Option Stream ID" 165 copy_flag = 1 166 option = 8 167 fields_desc = [ _IPOption_HDR, 168 ByteField("length", 4), 169 ShortField("security",0), ] 170 171class IPOption_MTU_Probe(IPOption): 172 name = "IP Option MTU Probe" 173 option = 11 174 fields_desc = [ _IPOption_HDR, 175 ByteField("length", 4), 176 ShortField("mtu",0), ] 177 178class IPOption_MTU_Reply(IPOption_MTU_Probe): 179 name = "IP Option MTU Reply" 180 option = 12 181 182class IPOption_Traceroute(IPOption): 183 name = "IP Option Traceroute" 184 option = 18 185 fields_desc = [ _IPOption_HDR, 186 ByteField("length", 12), 187 ShortField("id",0), 188 ShortField("outbound_hops",0), 189 ShortField("return_hops",0), 190 IPField("originator_ip","0.0.0.0") ] 191 192class IPOption_Address_Extension(IPOption): 193 name = "IP Option Address Extension" 194 copy_flag = 1 195 option = 19 196 fields_desc = [ _IPOption_HDR, 197 ByteField("length", 10), 198 IPField("src_ext","0.0.0.0"), 199 IPField("dst_ext","0.0.0.0") ] 200 201class IPOption_Router_Alert(IPOption): 202 name = "IP Option Router Alert" 203 copy_flag = 1 204 option = 20 205 fields_desc = [ _IPOption_HDR, 206 ByteField("length", 4), 207 ShortEnumField("alert",0, {0:"router_shall_examine_packet"}), ] 208 209 210class IPOption_SDBM(IPOption): 211 name = "IP Option Selective Directed Broadcast Mode" 212 copy_flag = 1 213 option = 21 214 fields_desc = [ _IPOption_HDR, 215 FieldLenField("length", None, fmt="B", 216 length_of="addresses", adjust=lambda pkt,l:l+2), 217 FieldListField("addresses",[],IPField("","0.0.0.0"), 218 length_from=lambda pkt:pkt.length-2) 219 ] 220 221 222 223TCPOptions = ( 224 { 0 : ("EOL",None), 225 1 : ("NOP",None), 226 2 : ("MSS","!H"), 227 3 : ("WScale","!B"), 228 4 : ("SAckOK",None), 229 5 : ("SAck","!"), 230 8 : ("Timestamp","!II"), 231 14 : ("AltChkSum","!BH"), 232 15 : ("AltChkSumOpt",None), 233 25 : ("Mood","!p"), 234 28 : ("UTO", "!H"), 235 34 : ("TFO", "!II"), 236 # RFC 3692 237 253 : ("Experiment","!HHHH"), 238 254 : ("Experiment","!HHHH"), 239 }, 240 { "EOL":0, 241 "NOP":1, 242 "MSS":2, 243 "WScale":3, 244 "SAckOK":4, 245 "SAck":5, 246 "Timestamp":8, 247 "AltChkSum":14, 248 "AltChkSumOpt":15, 249 "Mood":25, 250 "UTO":28, 251 "TFO":34, 252 } ) 253 254class TCPOptionsField(StrField): 255 islist=1 256 def getfield(self, pkt, s): 257 opsz = (pkt.dataofs-5)*4 258 if opsz < 0: 259 warning("bad dataofs (%i). Assuming dataofs=5"%pkt.dataofs) 260 opsz = 0 261 return s[opsz:],self.m2i(pkt,s[:opsz]) 262 def m2i(self, pkt, x): 263 opt = [] 264 while x: 265 onum = orb(x[0]) 266 if onum == 0: 267 opt.append(("EOL",None)) 268 x=x[1:] 269 break 270 if onum == 1: 271 opt.append(("NOP",None)) 272 x=x[1:] 273 continue 274 olen = orb(x[1]) 275 if olen < 2: 276 warning("Malformed TCP option (announced length is %i)" % olen) 277 olen = 2 278 oval = x[2:olen] 279 if onum in TCPOptions[0]: 280 oname, ofmt = TCPOptions[0][onum] 281 if onum == 5: #SAck 282 ofmt += "%iI" % (len(oval)//4) 283 if ofmt and struct.calcsize(ofmt) == len(oval): 284 oval = struct.unpack(ofmt, oval) 285 if len(oval) == 1: 286 oval = oval[0] 287 opt.append((oname, oval)) 288 else: 289 opt.append((onum, oval)) 290 x = x[olen:] 291 return opt 292 293 def i2m(self, pkt, x): 294 opt = b"" 295 for oname, oval in x: 296 if isinstance(oname, str): 297 if oname == "NOP": 298 opt += b"\x01" 299 continue 300 elif oname == "EOL": 301 opt += b"\x00" 302 continue 303 elif oname in TCPOptions[1]: 304 onum = TCPOptions[1][oname] 305 ofmt = TCPOptions[0][onum][1] 306 if onum == 5: #SAck 307 ofmt += "%iI" % len(oval) 308 if ofmt is not None and (not isinstance(oval, str) or "s" in ofmt): 309 if not isinstance(oval, tuple): 310 oval = (oval,) 311 oval = struct.pack(ofmt, *oval) 312 else: 313 warning("option [%s] unknown. Skipped.", oname) 314 continue 315 else: 316 onum = oname 317 if not isinstance(oval, str): 318 warning("option [%i] is not string."%onum) 319 continue 320 opt += chb(onum) + chb(2+len(oval))+ raw(oval) 321 return opt+b"\x00"*(3-((len(opt)+3)%4)) 322 def randval(self): 323 return [] # XXX 324 325 326class ICMPTimeStampField(IntField): 327 re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$") 328 def i2repr(self, pkt, val): 329 if val is None: 330 return "--" 331 else: 332 sec, milli = divmod(val, 1000) 333 min, sec = divmod(sec, 60) 334 hour, min = divmod(min, 60) 335 return "%d:%d:%d.%d" %(hour, min, sec, int(milli)) 336 def any2i(self, pkt, val): 337 if isinstance(val, str): 338 hmsms = self.re_hmsm.match(val) 339 if hmsms: 340 h,_,m,_,s,_,ms = hmsms = hmsms.groups() 341 ms = int(((ms or "")+"000")[:3]) 342 val = ((int(h)*60+int(m or 0))*60+int(s or 0))*1000+ms 343 else: 344 val = 0 345 elif val is None: 346 val = int((time.time()%(24*60*60))*1000) 347 return val 348 349 350class DestIPField(IPField, DestField): 351 bindings = {} 352 def __init__(self, name, default): 353 IPField.__init__(self, name, None) 354 DestField.__init__(self, name, default) 355 def i2m(self, pkt, x): 356 if x is None: 357 x = self.dst_from_pkt(pkt) 358 return IPField.i2m(self, pkt, x) 359 def i2h(self, pkt, x): 360 if x is None: 361 x = self.dst_from_pkt(pkt) 362 return IPField.i2h(self, pkt, x) 363 364 365class IP(Packet, IPTools): 366 __slots__ = ["_defrag_pos"] 367 name = "IP" 368 fields_desc = [ BitField("version" , 4 , 4), 369 BitField("ihl", None, 4), 370 XByteField("tos", 0), 371 ShortField("len", None), 372 ShortField("id", 1), 373 FlagsField("flags", 0, 3, ["MF","DF","evil"]), 374 BitField("frag", 0, 13), 375 ByteField("ttl", 64), 376 ByteEnumField("proto", 0, IP_PROTOS), 377 XShortField("chksum", None), 378 #IPField("src", "127.0.0.1"), 379 Emph(SourceIPField("src","dst")), 380 Emph(DestIPField("dst", "127.0.0.1")), 381 PacketListField("options", [], IPOption, length_from=lambda p:p.ihl*4-20) ] 382 def post_build(self, p, pay): 383 ihl = self.ihl 384 p += b"\0"*((-len(p))%4) # pad IP options if needed 385 if ihl is None: 386 ihl = len(p)//4 387 p = chb(((self.version&0xf)<<4) | ihl&0x0f)+p[1:] 388 if self.len is None: 389 l = len(p)+len(pay) 390 p = p[:2]+struct.pack("!H", l)+p[4:] 391 if self.chksum is None: 392 ck = checksum(p) 393 p = p[:10]+chb(ck>>8)+chb(ck&0xff)+p[12:] 394 return p+pay 395 396 def extract_padding(self, s): 397 l = self.len - (self.ihl << 2) 398 return s[:l],s[l:] 399 400 def route(self): 401 dst = self.dst 402 if isinstance(dst, Gen): 403 dst = next(iter(dst)) 404 if conf.route is None: 405 # unused import, only to initialize conf.route 406 import scapy.route 407 return conf.route.route(dst) 408 def hashret(self): 409 if ( (self.proto == socket.IPPROTO_ICMP) 410 and (isinstance(self.payload, ICMP)) 411 and (self.payload.type in [3,4,5,11,12]) ): 412 return self.payload.payload.hashret() 413 if not conf.checkIPinIP and self.proto in [4, 41]: # IP, IPv6 414 return self.payload.hashret() 415 if self.dst == "224.0.0.251": # mDNS 416 return struct.pack("B", self.proto) + self.payload.hashret() 417 if conf.checkIPsrc and conf.checkIPaddr: 418 return (strxor(inet_aton(self.src), inet_aton(self.dst)) 419 + struct.pack("B",self.proto) + self.payload.hashret()) 420 return struct.pack("B", self.proto) + self.payload.hashret() 421 def answers(self, other): 422 if not conf.checkIPinIP: # skip IP in IP and IPv6 in IP 423 if self.proto in [4, 41]: 424 return self.payload.answers(other) 425 if isinstance(other, IP) and other.proto in [4, 41]: 426 return self.answers(other.payload) 427 if conf.ipv6_enabled \ 428 and isinstance(other, scapy.layers.inet6.IPv6) \ 429 and other.nh in [4, 41]: 430 return self.answers(other.payload) 431 if not isinstance(other,IP): 432 return 0 433 if conf.checkIPaddr: 434 if other.dst == "224.0.0.251" and self.dst == "224.0.0.251": # mDNS 435 return self.payload.answers(other.payload) 436 elif (self.dst != other.src): 437 return 0 438 if ( (self.proto == socket.IPPROTO_ICMP) and 439 (isinstance(self.payload, ICMP)) and 440 (self.payload.type in [3,4,5,11,12]) ): 441 # ICMP error message 442 return self.payload.payload.answers(other) 443 444 else: 445 if ( (conf.checkIPaddr and (self.src != other.dst)) or 446 (self.proto != other.proto) ): 447 return 0 448 return self.payload.answers(other.payload) 449 def mysummary(self): 450 s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%") 451 if self.frag: 452 s += " frag:%i" % self.frag 453 return s 454 455 def fragment(self, fragsize=1480): 456 """Fragment IP datagrams""" 457 fragsize = (fragsize+7)//8*8 458 lst = [] 459 fnb = 0 460 fl = self 461 while fl.underlayer is not None: 462 fnb += 1 463 fl = fl.underlayer 464 465 for p in fl: 466 s = raw(p[fnb].payload) 467 nb = (len(s)+fragsize-1)//fragsize 468 for i in range(nb): 469 q = p.copy() 470 del(q[fnb].payload) 471 del(q[fnb].chksum) 472 del(q[fnb].len) 473 if i != nb - 1: 474 q[fnb].flags |= 1 475 q[fnb].frag += i * fragsize // 8 476 r = conf.raw_layer(load=s[i*fragsize:(i+1)*fragsize]) 477 r.overload_fields = p[fnb].payload.overload_fields.copy() 478 q.add_payload(r) 479 lst.append(q) 480 return lst 481 482def in4_chksum(proto, u, p): 483 """ 484 As Specified in RFC 2460 - 8.1 Upper-Layer Checksums 485 486 Performs IPv4 Upper Layer checksum computation. Provided parameters are: 487 - 'proto' : value of upper layer protocol 488 - 'u' : IP upper layer instance 489 - 'p' : the payload of the upper layer provided as a string 490 """ 491 if not isinstance(u, IP): 492 warning("No IP underlayer to compute checksum. Leaving null.") 493 return 0 494 if u.len is not None: 495 if u.ihl is None: 496 olen = sum(len(x) for x in u.options) 497 ihl = 5 + olen // 4 + (1 if olen % 4 else 0) 498 else: 499 ihl = u.ihl 500 ln = u.len - 4 * ihl 501 else: 502 ln = len(p) 503 psdhdr = struct.pack("!4s4sHH", 504 inet_aton(u.src), 505 inet_aton(u.dst), 506 proto, 507 ln) 508 return checksum(psdhdr+p) 509 510class TCP(Packet): 511 name = "TCP" 512 fields_desc = [ ShortEnumField("sport", 20, TCP_SERVICES), 513 ShortEnumField("dport", 80, TCP_SERVICES), 514 IntField("seq", 0), 515 IntField("ack", 0), 516 BitField("dataofs", None, 4), 517 BitField("reserved", 0, 3), 518 FlagsField("flags", 0x2, 9, "FSRPAUECN"), 519 ShortField("window", 8192), 520 XShortField("chksum", None), 521 ShortField("urgptr", 0), 522 TCPOptionsField("options", []) ] 523 def post_build(self, p, pay): 524 p += pay 525 dataofs = self.dataofs 526 if dataofs is None: 527 dataofs = 5+((len(self.get_field("options").i2m(self,self.options))+3)//4) 528 p = p[:12]+chb((dataofs << 4) | orb(p[12])&0x0f)+p[13:] 529 if self.chksum is None: 530 if isinstance(self.underlayer, IP): 531 ck = in4_chksum(socket.IPPROTO_TCP, self.underlayer, p) 532 p = p[:16]+struct.pack("!H", ck)+p[18:] 533 elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): 534 ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p) 535 p = p[:16]+struct.pack("!H", ck)+p[18:] 536 else: 537 warning("No IP underlayer to compute checksum. Leaving null.") 538 return p 539 def hashret(self): 540 if conf.checkIPsrc: 541 return struct.pack("H",self.sport ^ self.dport)+self.payload.hashret() 542 else: 543 return self.payload.hashret() 544 def answers(self, other): 545 if not isinstance(other, TCP): 546 return 0 547 # RST packets don't get answers 548 if other.flags.R: 549 return 0 550 # We do not support the four-way handshakes with the SYN+ACK 551 # answer split in two packets (one ACK and one SYN): in that 552 # case the ACK will be seen as an answer, but not the SYN. 553 if self.flags.S: 554 # SYN packets without ACK are not answers 555 if not self.flags.A: 556 return 0 557 # SYN+ACK packets answer SYN packets 558 if not other.flags.S: 559 return 0 560 if conf.checkIPsrc: 561 if not ((self.sport == other.dport) and 562 (self.dport == other.sport)): 563 return 0 564 # Do not check ack value for SYN packets without ACK 565 if not (other.flags.S and not other.flags.A) \ 566 and abs(other.ack - self.seq) > 2: 567 return 0 568 # Do not check ack value for RST packets without ACK 569 if self.flags.R and not self.flags.A: 570 return 1 571 if abs(other.seq - self.ack) > 2 + len(other.payload): 572 return 0 573 return 1 574 def mysummary(self): 575 if isinstance(self.underlayer, IP): 576 return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%") 577 elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6): 578 return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%") 579 else: 580 return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%") 581 582class UDP(Packet): 583 name = "UDP" 584 fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES), 585 ShortEnumField("dport", 53, UDP_SERVICES), 586 ShortField("len", None), 587 XShortField("chksum", None), ] 588 def post_build(self, p, pay): 589 p += pay 590 l = self.len 591 if l is None: 592 l = len(p) 593 p = p[:4]+struct.pack("!H",l)+p[6:] 594 if self.chksum is None: 595 if isinstance(self.underlayer, IP): 596 ck = in4_chksum(socket.IPPROTO_UDP, self.underlayer, p) 597 # According to RFC768 if the result checksum is 0, it should be set to 0xFFFF 598 if ck == 0: 599 ck = 0xFFFF 600 p = p[:6]+struct.pack("!H", ck)+p[8:] 601 elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): 602 ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p) 603 # According to RFC2460 if the result checksum is 0, it should be set to 0xFFFF 604 if ck == 0: 605 ck = 0xFFFF 606 p = p[:6]+struct.pack("!H", ck)+p[8:] 607 else: 608 warning("No IP underlayer to compute checksum. Leaving null.") 609 return p 610 def extract_padding(self, s): 611 l = self.len - 8 612 return s[:l],s[l:] 613 def hashret(self): 614 return self.payload.hashret() 615 def answers(self, other): 616 if not isinstance(other, UDP): 617 return 0 618 if conf.checkIPsrc: 619 if self.dport != other.sport: 620 return 0 621 return self.payload.answers(other.payload) 622 def mysummary(self): 623 if isinstance(self.underlayer, IP): 624 return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%") 625 elif isinstance(self.underlayer, scapy.layers.inet6.IPv6): 626 return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%") 627 else: 628 return self.sprintf("UDP %UDP.sport% > %UDP.dport%") 629 630icmptypes = { 0 : "echo-reply", 631 3 : "dest-unreach", 632 4 : "source-quench", 633 5 : "redirect", 634 8 : "echo-request", 635 9 : "router-advertisement", 636 10 : "router-solicitation", 637 11 : "time-exceeded", 638 12 : "parameter-problem", 639 13 : "timestamp-request", 640 14 : "timestamp-reply", 641 15 : "information-request", 642 16 : "information-response", 643 17 : "address-mask-request", 644 18 : "address-mask-reply" } 645 646icmpcodes = { 3 : { 0 : "network-unreachable", 647 1 : "host-unreachable", 648 2 : "protocol-unreachable", 649 3 : "port-unreachable", 650 4 : "fragmentation-needed", 651 5 : "source-route-failed", 652 6 : "network-unknown", 653 7 : "host-unknown", 654 9 : "network-prohibited", 655 10 : "host-prohibited", 656 11 : "TOS-network-unreachable", 657 12 : "TOS-host-unreachable", 658 13 : "communication-prohibited", 659 14 : "host-precedence-violation", 660 15 : "precedence-cutoff", }, 661 5 : { 0 : "network-redirect", 662 1 : "host-redirect", 663 2 : "TOS-network-redirect", 664 3 : "TOS-host-redirect", }, 665 11 : { 0 : "ttl-zero-during-transit", 666 1 : "ttl-zero-during-reassembly", }, 667 12 : { 0 : "ip-header-bad", 668 1 : "required-option-missing", }, } 669 670 671 672 673class ICMP(Packet): 674 name = "ICMP" 675 fields_desc = [ ByteEnumField("type",8, icmptypes), 676 MultiEnumField("code",0, icmpcodes, depends_on=lambda pkt:pkt.type,fmt="B"), 677 XShortField("chksum", None), 678 ConditionalField(XShortField("id",0), lambda pkt:pkt.type in [0,8,13,14,15,16,17,18]), 679 ConditionalField(XShortField("seq",0), lambda pkt:pkt.type in [0,8,13,14,15,16,17,18]), 680 ConditionalField(ICMPTimeStampField("ts_ori", None), lambda pkt:pkt.type in [13,14]), 681 ConditionalField(ICMPTimeStampField("ts_rx", None), lambda pkt:pkt.type in [13,14]), 682 ConditionalField(ICMPTimeStampField("ts_tx", None), lambda pkt:pkt.type in [13,14]), 683 ConditionalField(IPField("gw","0.0.0.0"), lambda pkt:pkt.type==5), 684 ConditionalField(ByteField("ptr",0), lambda pkt:pkt.type==12), 685 ConditionalField(ByteField("reserved",0), lambda pkt:pkt.type in [3,11]), 686 ConditionalField(ByteField("length",0), lambda pkt:pkt.type in [3,11,12]), 687 ConditionalField(IPField("addr_mask","0.0.0.0"), lambda pkt:pkt.type in [17,18]), 688 ConditionalField(ShortField("nexthopmtu",0), lambda pkt:pkt.type==3), 689 ConditionalField(ShortField("unused",0), lambda pkt:pkt.type in [11,12]), 690 ConditionalField(IntField("unused",0), lambda pkt:pkt.type not in [0,3,5,8,11,12,13,14,15,16,17,18]) 691 ] 692 def post_build(self, p, pay): 693 p += pay 694 if self.chksum is None: 695 ck = checksum(p) 696 p = p[:2] + chb(ck>>8) + chb(ck&0xff) + p[4:] 697 return p 698 699 def hashret(self): 700 if self.type in [0,8,13,14,15,16,17,18]: 701 return struct.pack("HH",self.id,self.seq)+self.payload.hashret() 702 return self.payload.hashret() 703 def answers(self, other): 704 if not isinstance(other,ICMP): 705 return 0 706 if ( (other.type,self.type) in [(8,0),(13,14),(15,16),(17,18)] and 707 self.id == other.id and 708 self.seq == other.seq ): 709 return 1 710 return 0 711 712 def guess_payload_class(self, payload): 713 if self.type in [3,4,5,11,12]: 714 return IPerror 715 else: 716 return None 717 def mysummary(self): 718 if isinstance(self.underlayer, IP): 719 return self.underlayer.sprintf("ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%") 720 else: 721 return self.sprintf("ICMP %ICMP.type% %ICMP.code%") 722 723 724 725 726 727class IPerror(IP): 728 name = "IP in ICMP" 729 def answers(self, other): 730 if not isinstance(other, IP): 731 return 0 732 if not ( ((conf.checkIPsrc == 0) or (self.dst == other.dst)) and 733 (self.src == other.src) and 734 ( ((conf.checkIPID == 0) 735 or (self.id == other.id) 736 or (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and 737 (self.proto == other.proto) ): 738 return 0 739 return self.payload.answers(other.payload) 740 def mysummary(self): 741 return Packet.mysummary(self) 742 743 744class TCPerror(TCP): 745 name = "TCP in ICMP" 746 def answers(self, other): 747 if not isinstance(other, TCP): 748 return 0 749 if conf.checkIPsrc: 750 if not ((self.sport == other.sport) and 751 (self.dport == other.dport)): 752 return 0 753 if conf.check_TCPerror_seqack: 754 if self.seq is not None: 755 if self.seq != other.seq: 756 return 0 757 if self.ack is not None: 758 if self.ack != other.ack: 759 return 0 760 return 1 761 def mysummary(self): 762 return Packet.mysummary(self) 763 764 765class UDPerror(UDP): 766 name = "UDP in ICMP" 767 def answers(self, other): 768 if not isinstance(other, UDP): 769 return 0 770 if conf.checkIPsrc: 771 if not ((self.sport == other.sport) and 772 (self.dport == other.dport)): 773 return 0 774 return 1 775 def mysummary(self): 776 return Packet.mysummary(self) 777 778 779 780class ICMPerror(ICMP): 781 name = "ICMP in ICMP" 782 def answers(self, other): 783 if not isinstance(other,ICMP): 784 return 0 785 if not ((self.type == other.type) and 786 (self.code == other.code)): 787 return 0 788 if self.code in [0,8,13,14,17,18]: 789 if (self.id == other.id and 790 self.seq == other.seq): 791 return 1 792 else: 793 return 0 794 else: 795 return 1 796 def mysummary(self): 797 return Packet.mysummary(self) 798 799bind_layers( Ether, IP, type=2048) 800bind_layers( CookedLinux, IP, proto=2048) 801bind_layers( GRE, IP, proto=2048) 802bind_layers( SNAP, IP, code=2048) 803bind_layers( Loopback, IP, type=0) 804bind_layers( Loopback, IP, type=2) 805bind_layers( IPerror, IPerror, frag=0, proto=4) 806bind_layers( IPerror, ICMPerror, frag=0, proto=1) 807bind_layers( IPerror, TCPerror, frag=0, proto=6) 808bind_layers( IPerror, UDPerror, frag=0, proto=17) 809bind_layers( IP, IP, frag=0, proto=4) 810bind_layers( IP, ICMP, frag=0, proto=1) 811bind_layers( IP, TCP, frag=0, proto=6) 812bind_layers( IP, UDP, frag=0, proto=17) 813bind_layers( IP, GRE, frag=0, proto=47) 814 815conf.l2types.register(DLT_RAW, IP) 816conf.l2types.register_num2layer(DLT_RAW_ALT, IP) 817conf.l2types.register(DLT_IPV4, IP) 818 819conf.l3types.register(ETH_P_IP, IP) 820conf.l3types.register_num2layer(ETH_P_ALL, IP) 821 822 823def inet_register_l3(l2, l3): 824 return getmacbyip(l3.dst) 825conf.neighbor.register_l3(Ether, IP, inet_register_l3) 826conf.neighbor.register_l3(Dot3, IP, inet_register_l3) 827 828 829################### 830## Fragmentation ## 831################### 832 833@conf.commands.register 834def fragment(pkt, fragsize=1480): 835 """Fragment a big IP datagram""" 836 fragsize = (fragsize+7)//8*8 837 lst = [] 838 for p in pkt: 839 s = raw(p[IP].payload) 840 nb = (len(s)+fragsize-1)//fragsize 841 for i in range(nb): 842 q = p.copy() 843 del(q[IP].payload) 844 del(q[IP].chksum) 845 del(q[IP].len) 846 if i != nb - 1: 847 q[IP].flags |= 1 848 q[IP].frag += i * fragsize // 8 849 r = conf.raw_layer(load=s[i*fragsize:(i+1)*fragsize]) 850 r.overload_fields = p[IP].payload.overload_fields.copy() 851 q.add_payload(r) 852 lst.append(q) 853 return lst 854 855@conf.commands.register 856def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None): 857 """Build overlapping fragments to bypass NIPS 858 859p: the original packet 860overlap: the overlapping data 861fragsize: the fragment size of the packet 862overlap_fragsize: the fragment size of the overlapping packet""" 863 864 if overlap_fragsize is None: 865 overlap_fragsize = fragsize 866 q = p.copy() 867 del(q[IP].payload) 868 q[IP].add_payload(overlap) 869 870 qfrag = fragment(q, overlap_fragsize) 871 qfrag[-1][IP].flags |= 1 872 return qfrag+fragment(p, fragsize) 873 874@conf.commands.register 875def defrag(plist): 876 """defrag(plist) -> ([not fragmented], [defragmented], 877 [ [bad fragments], [bad fragments], ... ])""" 878 frags = defaultdict(PacketList) 879 nofrag = PacketList() 880 for p in plist: 881 if IP not in p: 882 nofrag.append(p) 883 continue 884 ip = p[IP] 885 if ip.frag == 0 and ip.flags & 1 == 0: 886 nofrag.append(p) 887 continue 888 uniq = (ip.id,ip.src,ip.dst,ip.proto) 889 frags[uniq].append(p) 890 defrag = [] 891 missfrag = [] 892 for lst in six.itervalues(frags): 893 lst.sort(key=lambda x: x.frag) 894 p = lst[0] 895 lastp = lst[-1] 896 if p.frag > 0 or lastp.flags & 1 != 0: # first or last fragment missing 897 missfrag.append(lst) 898 continue 899 p = p.copy() 900 if conf.padding_layer in p: 901 del(p[conf.padding_layer].underlayer.payload) 902 ip = p[IP] 903 if ip.len is None or ip.ihl is None: 904 clen = len(ip.payload) 905 else: 906 clen = ip.len - (ip.ihl<<2) 907 txt = conf.raw_layer() 908 for q in lst[1:]: 909 if clen != q.frag<<3: # Wrong fragmentation offset 910 if clen > q.frag<<3: 911 warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag<<3, p,txt,q)) 912 missfrag.append(lst) 913 break 914 if q[IP].len is None or q[IP].ihl is None: 915 clen += len(q[IP].payload) 916 else: 917 clen += q[IP].len - (q[IP].ihl<<2) 918 if conf.padding_layer in q: 919 del(q[conf.padding_layer].underlayer.payload) 920 txt.add_payload(q[IP].payload.copy()) 921 else: 922 ip.flags &= ~1 # !MF 923 del(ip.chksum) 924 del(ip.len) 925 p = p/txt 926 defrag.append(p) 927 defrag2=PacketList() 928 for p in defrag: 929 defrag2.append(p.__class__(raw(p))) 930 return nofrag,defrag2,missfrag 931 932@conf.commands.register 933def defragment(plist): 934 """defrag(plist) -> plist defragmented as much as possible """ 935 frags = defaultdict(lambda:[]) 936 final = [] 937 938 pos = 0 939 for p in plist: 940 p._defrag_pos = pos 941 pos += 1 942 if IP in p: 943 ip = p[IP] 944 if ip.frag != 0 or ip.flags & 1: 945 ip = p[IP] 946 uniq = (ip.id,ip.src,ip.dst,ip.proto) 947 frags[uniq].append(p) 948 continue 949 final.append(p) 950 951 defrag = [] 952 missfrag = [] 953 for lst in six.itervalues(frags): 954 lst.sort(key=lambda x: x.frag) 955 p = lst[0] 956 lastp = lst[-1] 957 if p.frag > 0 or lastp.flags & 1 != 0: # first or last fragment missing 958 missfrag += lst 959 continue 960 p = p.copy() 961 if conf.padding_layer in p: 962 del(p[conf.padding_layer].underlayer.payload) 963 ip = p[IP] 964 if ip.len is None or ip.ihl is None: 965 clen = len(ip.payload) 966 else: 967 clen = ip.len - (ip.ihl<<2) 968 txt = conf.raw_layer() 969 for q in lst[1:]: 970 if clen != q.frag<<3: # Wrong fragmentation offset 971 if clen > q.frag<<3: 972 warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag<<3, p,txt,q)) 973 missfrag += lst 974 break 975 if q[IP].len is None or q[IP].ihl is None: 976 clen += len(q[IP].payload) 977 else: 978 clen += q[IP].len - (q[IP].ihl<<2) 979 if conf.padding_layer in q: 980 del(q[conf.padding_layer].underlayer.payload) 981 txt.add_payload(q[IP].payload.copy()) 982 else: 983 ip.flags &= ~1 # !MF 984 del(ip.chksum) 985 del(ip.len) 986 p = p/txt 987 p._defrag_pos = max(x._defrag_pos for x in lst) 988 defrag.append(p) 989 defrag2=[] 990 for p in defrag: 991 q = p.__class__(raw(p)) 992 q._defrag_pos = p._defrag_pos 993 defrag2.append(q) 994 final += defrag2 995 final += missfrag 996 final.sort(key=lambda x: x._defrag_pos) 997 for p in final: 998 del(p._defrag_pos) 999 1000 if hasattr(plist, "listname"): 1001 name = "Defragmented %s" % plist.listname 1002 else: 1003 name = "Defragmented" 1004 1005 return PacketList(final, name=name) 1006 1007 1008 1009### Add timeskew_graph() method to PacketList 1010def _packetlist_timeskew_graph(self, ip, **kargs): 1011 """Tries to graph the timeskew between the timestamps and real time for a given ip""" 1012 1013 # Filter TCP segments which source address is 'ip' 1014 tmp = (self._elt2pkt(x) for x in self.res) 1015 b = (x for x in tmp if IP in x and x[IP].src == ip and TCP in x) 1016 1017 # Build a list of tuples (creation_time, replied_timestamp) 1018 c = [] 1019 tsf = ICMPTimeStampField("", None) 1020 for p in b: 1021 opts = p.getlayer(TCP).options 1022 for o in opts: 1023 if o[0] == "Timestamp": 1024 c.append((p.time, tsf.any2i("", o[1][0]))) 1025 1026 # Stop if the list is empty 1027 if not c: 1028 warning("No timestamps found in packet list") 1029 return [] 1030 1031 # Prepare the data that will be plotted 1032 first_creation_time = c[0][0] 1033 first_replied_timestamp = c[0][1] 1034 1035 def _wrap_data(ts_tuple, wrap_seconds=2000): 1036 """Wrap the list of tuples.""" 1037 1038 ct,rt = ts_tuple # (creation_time, replied_timestamp) 1039 X = ct % wrap_seconds 1040 Y = ((ct-first_creation_time) - ((rt-first_replied_timestamp)/1000.0)) 1041 1042 return X, Y 1043 1044 data = [_wrap_data(e) for e in c] 1045 1046 # Mimic the default gnuplot output 1047 if kargs == {}: 1048 kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS 1049 lines = plt.plot(data, **kargs) 1050 1051 # Call show() if matplotlib is not inlined 1052 if not MATPLOTLIB_INLINED: 1053 plt.show() 1054 1055 return lines 1056 1057PacketList.timeskew_graph = _packetlist_timeskew_graph 1058 1059 1060### Create a new packet list 1061class TracerouteResult(SndRcvList): 1062 __slots__ = ["graphdef", "graphpadding", "graphASres", "padding", "hloc", 1063 "nloc"] 1064 def __init__(self, res=None, name="Traceroute", stats=None): 1065 PacketList.__init__(self, res, name, stats) 1066 self.graphdef = None 1067 self.graphASres = 0 1068 self.padding = 0 1069 self.hloc = None 1070 self.nloc = None 1071 1072 def show(self): 1073 return self.make_table(lambda s_r: (s_r[0].sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), 1074 s_r[0].ttl, 1075 s_r[1].sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) 1076 1077 1078 def get_trace(self): 1079 trace = {} 1080 for s,r in self.res: 1081 if IP not in s: 1082 continue 1083 d = s[IP].dst 1084 if d not in trace: 1085 trace[d] = {} 1086 trace[d][s[IP].ttl] = r[IP].src, ICMP not in r 1087 for k in six.itervalues(trace): 1088 try: 1089 m = min(x for x, y in six.itervalues(k) if y) 1090 except ValueError: 1091 continue 1092 for l in list(k): # use list(): k is modified in the loop 1093 if l > m: 1094 del k[l] 1095 return trace 1096 1097 def trace3D(self): 1098 """Give a 3D representation of the traceroute. 1099 right button: rotate the scene 1100 middle button: zoom 1101 left button: move the scene 1102 left button on a ball: toggle IP displaying 1103 ctrl-left button on a ball: scan ports 21,22,23,25,80 and 443 and display the result""" 1104 trace = self.get_trace() 1105 import visual 1106 1107 class IPsphere(visual.sphere): 1108 def __init__(self, ip, **kargs): 1109 visual.sphere.__init__(self, **kargs) 1110 self.ip=ip 1111 self.label=None 1112 self.setlabel(self.ip) 1113 def setlabel(self, txt,visible=None): 1114 if self.label is not None: 1115 if visible is None: 1116 visible = self.label.visible 1117 self.label.visible = 0 1118 elif visible is None: 1119 visible=0 1120 self.label=visual.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible) 1121 def action(self): 1122 self.label.visible ^= 1 1123 1124 visual.scene = visual.display() 1125 visual.scene.exit = True 1126 start = visual.box() 1127 rings={} 1128 tr3d = {} 1129 for i in trace: 1130 tr = trace[i] 1131 tr3d[i] = [] 1132 for t in range(1, max(tr) + 1): 1133 if t not in rings: 1134 rings[t] = [] 1135 if t in tr: 1136 if tr[t] not in rings[t]: 1137 rings[t].append(tr[t]) 1138 tr3d[i].append(rings[t].index(tr[t])) 1139 else: 1140 rings[t].append(("unk",-1)) 1141 tr3d[i].append(len(rings[t])-1) 1142 for t in rings: 1143 r = rings[t] 1144 l = len(r) 1145 for i in range(l): 1146 if r[i][1] == -1: 1147 col = (0.75,0.75,0.75) 1148 elif r[i][1]: 1149 col = visual.color.green 1150 else: 1151 col = visual.color.blue 1152 1153 s = IPsphere(pos=((l-1)*visual.cos(2*i*visual.pi/l),(l-1)*visual.sin(2*i*visual.pi/l),2*t), 1154 ip = r[i][0], 1155 color = col) 1156 for trlst in six.itervalues(tr3d): 1157 if t <= len(trlst): 1158 if trlst[t-1] == i: 1159 trlst[t-1] = s 1160 forecol = colgen(0.625, 0.4375, 0.25, 0.125) 1161 for trlst in six.itervalues(tr3d): 1162 col = next(forecol) 1163 start = (0,0,0) 1164 for ip in trlst: 1165 visual.cylinder(pos=start,axis=ip.pos-start,color=col,radius=0.2) 1166 start = ip.pos 1167 1168 movcenter=None 1169 while True: 1170 visual.rate(50) 1171 if visual.scene.kb.keys: 1172 k = visual.scene.kb.getkey() 1173 if k == "esc" or k == "q": 1174 break 1175 if visual.scene.mouse.events: 1176 ev = visual.scene.mouse.getevent() 1177 if ev.press == "left": 1178 o = ev.pick 1179 if o: 1180 if ev.ctrl: 1181 if o.ip == "unk": 1182 continue 1183 savcolor = o.color 1184 o.color = (1,0,0) 1185 a,b=sr(IP(dst=o.ip)/TCP(dport=[21,22,23,25,80,443]),timeout=2) 1186 o.color = savcolor 1187 if len(a) == 0: 1188 txt = "%s:\nno results" % o.ip 1189 else: 1190 txt = "%s:\n" % o.ip 1191 for s,r in a: 1192 txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n") 1193 o.setlabel(txt, visible=1) 1194 else: 1195 if hasattr(o, "action"): 1196 o.action() 1197 elif ev.drag == "left": 1198 movcenter = ev.pos 1199 elif ev.drop == "left": 1200 movcenter = None 1201 if movcenter: 1202 visual.scene.center -= visual.scene.mouse.pos-movcenter 1203 movcenter = visual.scene.mouse.pos 1204 1205 1206 def world_trace(self, **kargs): 1207 """Display traceroute results on a world map.""" 1208 1209 # Check that the GeoIP module can be imported 1210 try: 1211 import GeoIP 1212 except ImportError: 1213 message = "Can't import GeoIP. Won't be able to plot the world." 1214 scapy.utils.log_loading.info(message) 1215 return list() 1216 1217 # Check if this is an IPv6 traceroute and load the correct file 1218 if isinstance(self, scapy.layers.inet6.TracerouteResult6): 1219 geoip_city_filename = conf.geoip_city_ipv6 1220 else: 1221 geoip_city_filename = conf.geoip_city 1222 1223 # Check that the GeoIP database can be opened 1224 try: 1225 db = GeoIP.open(conf.geoip_city, 0) 1226 except: 1227 message = "Can't open GeoIP database at %s" % conf.geoip_city 1228 scapy.utils.log_loading.info(message) 1229 return list() 1230 1231 # Regroup results per trace 1232 ips = {} 1233 rt = {} 1234 ports_done = {} 1235 for s,r in self.res: 1236 ips[r.src] = None 1237 if s.haslayer(TCP) or s.haslayer(UDP): 1238 trace_id = (s.src,s.dst,s.proto,s.dport) 1239 elif s.haslayer(ICMP): 1240 trace_id = (s.src,s.dst,s.proto,s.type) 1241 else: 1242 trace_id = (s.src,s.dst,s.proto,0) 1243 trace = rt.get(trace_id,{}) 1244 if not r.haslayer(ICMP) or r.type != 11: 1245 if trace_id in ports_done: 1246 continue 1247 ports_done[trace_id] = None 1248 trace[s.ttl] = r.src 1249 rt[trace_id] = trace 1250 1251 # Get the addresses locations 1252 trt = {} 1253 for trace_id in rt: 1254 trace = rt[trace_id] 1255 loctrace = [] 1256 for i in range(max(trace)): 1257 ip = trace.get(i,None) 1258 if ip is None: 1259 continue 1260 loc = db.record_by_addr(ip) 1261 if loc is None: 1262 continue 1263 loc = loc.get('longitude'), loc.get('latitude') 1264 if loc == (None, None): 1265 continue 1266 loctrace.append(loc) 1267 if loctrace: 1268 trt[trace_id] = loctrace 1269 1270 # Load the map renderer 1271 from mpl_toolkits.basemap import Basemap 1272 bmap = Basemap() 1273 1274 # Split latitudes and longitudes per traceroute measurement 1275 locations = [zip(*tr) for tr in six.itervalues(trt)] 1276 1277 # Plot the traceroute measurement as lines in the map 1278 lines = [bmap.plot(*bmap(lons, lats)) for lons, lats in locations] 1279 1280 # Draw countries 1281 bmap.drawcoastlines() 1282 1283 # Call show() if matplotlib is not inlined 1284 if not MATPLOTLIB_INLINED: 1285 plt.show() 1286 1287 # Return the drawn lines 1288 return lines 1289 1290 def make_graph(self,ASres=None,padding=0): 1291 if ASres is None: 1292 ASres = conf.AS_resolver 1293 self.graphASres = ASres 1294 self.graphpadding = padding 1295 ips = {} 1296 rt = {} 1297 ports = {} 1298 ports_done = {} 1299 for s,r in self.res: 1300 r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r 1301 s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s 1302 ips[r.src] = None 1303 if TCP in s: 1304 trace_id = (s.src,s.dst,6,s.dport) 1305 elif UDP in s: 1306 trace_id = (s.src,s.dst,17,s.dport) 1307 elif ICMP in s: 1308 trace_id = (s.src,s.dst,1,s.type) 1309 else: 1310 trace_id = (s.src,s.dst,s.proto,0) 1311 trace = rt.get(trace_id,{}) 1312 ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl 1313 if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and scapy.layers.inet6.IPv6 in r and scapy.layers.inet6.ICMPv6TimeExceeded in r): 1314 if trace_id in ports_done: 1315 continue 1316 ports_done[trace_id] = None 1317 p = ports.get(r.src,[]) 1318 if TCP in r: 1319 p.append(r.sprintf("<T%ir,TCP.sport%> %TCP.sport% %TCP.flags%")) 1320 trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%') 1321 elif UDP in r: 1322 p.append(r.sprintf("<U%ir,UDP.sport%> %UDP.sport%")) 1323 trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%') 1324 elif ICMP in r: 1325 p.append(r.sprintf("<I%ir,ICMP.type%> ICMP %ICMP.type%")) 1326 trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') 1327 else: 1328 p.append(r.sprintf("{IP:<P%ir,proto%> IP %proto%}{IPv6:<P%ir,nh%> IPv6 %nh%}")) 1329 trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}') 1330 ports[r.src] = p 1331 else: 1332 trace[ttl] = r.sprintf('"%r,src%"') 1333 rt[trace_id] = trace 1334 1335 # Fill holes with unk%i nodes 1336 unknown_label = incremental_label("unk%i") 1337 blackholes = [] 1338 bhip = {} 1339 for rtk in rt: 1340 trace = rt[rtk] 1341 max_trace = max(trace) 1342 for n in range(min(trace), max_trace): 1343 if n not in trace: 1344 trace[n] = next(unknown_label) 1345 if rtk not in ports_done: 1346 if rtk[2] == 1: #ICMP 1347 bh = "%s %i/icmp" % (rtk[1],rtk[3]) 1348 elif rtk[2] == 6: #TCP 1349 bh = "%s %i/tcp" % (rtk[1],rtk[3]) 1350 elif rtk[2] == 17: #UDP 1351 bh = '%s %i/udp' % (rtk[1],rtk[3]) 1352 else: 1353 bh = '%s %i/proto' % (rtk[1],rtk[2]) 1354 ips[bh] = None 1355 bhip[rtk[1]] = bh 1356 bh = '"%s"' % bh 1357 trace[max_trace + 1] = bh 1358 blackholes.append(bh) 1359 1360 # Find AS numbers 1361 ASN_query_list = set(x.rsplit(" ",1)[0] for x in ips) 1362 if ASres is None: 1363 ASNlist = [] 1364 else: 1365 ASNlist = ASres.resolve(*ASN_query_list) 1366 1367 ASNs = {} 1368 ASDs = {} 1369 for ip,asn,desc, in ASNlist: 1370 if asn is None: 1371 continue 1372 iplist = ASNs.get(asn,[]) 1373 if ip in bhip: 1374 if ip in ports: 1375 iplist.append(ip) 1376 iplist.append(bhip[ip]) 1377 else: 1378 iplist.append(ip) 1379 ASNs[asn] = iplist 1380 ASDs[asn] = desc 1381 1382 1383 backcolorlist=colgen("60","86","ba","ff") 1384 forecolorlist=colgen("a0","70","40","20") 1385 1386 s = "digraph trace {\n" 1387 1388 s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" 1389 1390 s += "\n#ASN clustering\n" 1391 for asn in ASNs: 1392 s += '\tsubgraph cluster_%s {\n' % asn 1393 col = next(backcolorlist) 1394 s += '\t\tcolor="#%s%s%s";' % col 1395 s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col 1396 s += '\t\tfontsize = 10;' 1397 s += '\t\tlabel = "%s\\n[%s]"\n' % (asn,ASDs[asn]) 1398 for ip in ASNs[asn]: 1399 1400 s += '\t\t"%s";\n'%ip 1401 s += "\t}\n" 1402 1403 1404 1405 1406 s += "#endpoints\n" 1407 for p in ports: 1408 s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p,p,"|".join(ports[p])) 1409 1410 s += "\n#Blackholes\n" 1411 for bh in blackholes: 1412 s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh 1413 1414 if padding: 1415 s += "\n#Padding\n" 1416 pad={} 1417 for snd,rcv in self.res: 1418 if rcv.src not in ports and rcv.haslayer(conf.padding_layer): 1419 p = rcv.getlayer(conf.padding_layer).load 1420 if p != b"\x00"*len(p): 1421 pad[rcv.src]=None 1422 for rcv in pad: 1423 s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv 1424 1425 1426 1427 s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" 1428 1429 1430 for rtk in rt: 1431 s += "#---[%s\n" % repr(rtk) 1432 s += '\t\tedge [color="#%s%s%s"];\n' % next(forecolorlist) 1433 trace = rt[rtk] 1434 maxtrace = max(trace) 1435 for n in range(min(trace), maxtrace): 1436 s += '\t%s ->\n' % trace[n] 1437 s += '\t%s;\n' % trace[maxtrace] 1438 1439 s += "}\n"; 1440 self.graphdef = s 1441 1442 def graph(self, ASres=None, padding=0, **kargs): 1443 """x.graph(ASres=conf.AS_resolver, other args): 1444 ASres=None : no AS resolver => no clustering 1445 ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net) 1446 ASres=AS_resolver_cymru(): use whois.cymru.com whois database 1447 ASres=AS_resolver(server="whois.ra.net") 1448 type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option 1449 target: filename or redirect. Defaults pipe to Imagemagick's display program 1450 prog: which graphviz program to use""" 1451 if ASres is None: 1452 ASres = conf.AS_resolver 1453 if (self.graphdef is None or 1454 self.graphASres != ASres or 1455 self.graphpadding != padding): 1456 self.make_graph(ASres,padding) 1457 1458 return do_graph(self.graphdef, **kargs) 1459 1460 1461 1462@conf.commands.register 1463def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4 = None, filter=None, timeout=2, verbose=None, **kargs): 1464 """Instant TCP traceroute 1465traceroute(target, [maxttl=30,] [dport=80,] [sport=80,] [verbose=conf.verb]) -> None 1466""" 1467 if verbose is None: 1468 verbose = conf.verb 1469 if filter is None: 1470 # we only consider ICMP error packets and TCP packets with at 1471 # least the ACK flag set *and* either the SYN or the RST flag 1472 # set 1473 filter="(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))" 1474 if l4 is None: 1475 a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/TCP(seq=RandInt(),sport=sport, dport=dport), 1476 timeout=timeout, filter=filter, verbose=verbose, **kargs) 1477 else: 1478 # this should always work 1479 filter="ip" 1480 a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/l4, 1481 timeout=timeout, filter=filter, verbose=verbose, **kargs) 1482 1483 a = TracerouteResult(a.res) 1484 if verbose: 1485 a.show() 1486 return a,b 1487 1488 1489 1490############################# 1491## Simple TCP client stack ## 1492############################# 1493 1494class TCP_client(Automaton): 1495 1496 def parse_args(self, ip, port, *args, **kargs): 1497 self.dst = str(Net(ip)) 1498 self.dport = port 1499 self.sport = random.randrange(0,2**16) 1500 self.l4 = IP(dst=ip)/TCP(sport=self.sport, dport=self.dport, flags=0, 1501 seq=random.randrange(0,2**32)) 1502 self.src = self.l4.src 1503 self.swin=self.l4[TCP].window 1504 self.dwin=1 1505 self.rcvbuf = b"" 1506 bpf = "host %s and host %s and port %i and port %i" % (self.src, 1507 self.dst, 1508 self.sport, 1509 self.dport) 1510 1511# bpf=None 1512 Automaton.parse_args(self, filter=bpf, **kargs) 1513 1514 1515 def master_filter(self, pkt): 1516 return (IP in pkt and 1517 pkt[IP].src == self.dst and 1518 pkt[IP].dst == self.src and 1519 TCP in pkt and 1520 pkt[TCP].sport == self.dport and 1521 pkt[TCP].dport == self.sport and 1522 self.l4[TCP].seq >= pkt[TCP].ack and # XXX: seq/ack 2^32 wrap up 1523 ((self.l4[TCP].ack == 0) or (self.l4[TCP].ack <= pkt[TCP].seq <= self.l4[TCP].ack+self.swin)) ) 1524 1525 1526 @ATMT.state(initial=1) 1527 def START(self): 1528 pass 1529 1530 @ATMT.state() 1531 def SYN_SENT(self): 1532 pass 1533 1534 @ATMT.state() 1535 def ESTABLISHED(self): 1536 pass 1537 1538 @ATMT.state() 1539 def LAST_ACK(self): 1540 pass 1541 1542 @ATMT.state(final=1) 1543 def CLOSED(self): 1544 pass 1545 1546 1547 @ATMT.condition(START) 1548 def connect(self): 1549 raise self.SYN_SENT() 1550 @ATMT.action(connect) 1551 def send_syn(self): 1552 self.l4[TCP].flags = "S" 1553 self.send(self.l4) 1554 self.l4[TCP].seq += 1 1555 1556 1557 @ATMT.receive_condition(SYN_SENT) 1558 def synack_received(self, pkt): 1559 if pkt[TCP].flags & 0x3f == 0x12: 1560 raise self.ESTABLISHED().action_parameters(pkt) 1561 @ATMT.action(synack_received) 1562 def send_ack_of_synack(self, pkt): 1563 self.l4[TCP].ack = pkt[TCP].seq+1 1564 self.l4[TCP].flags = "A" 1565 self.send(self.l4) 1566 1567 @ATMT.receive_condition(ESTABLISHED) 1568 def incoming_data_received(self, pkt): 1569 if not isinstance(pkt[TCP].payload, NoPayload) and not isinstance(pkt[TCP].payload, conf.padding_layer): 1570 raise self.ESTABLISHED().action_parameters(pkt) 1571 @ATMT.action(incoming_data_received) 1572 def receive_data(self,pkt): 1573 data = raw(pkt[TCP].payload) 1574 if data and self.l4[TCP].ack == pkt[TCP].seq: 1575 self.l4[TCP].ack += len(data) 1576 self.l4[TCP].flags = "A" 1577 self.send(self.l4) 1578 self.rcvbuf += data 1579 if pkt[TCP].flags.P: 1580 self.oi.tcp.send(self.rcvbuf) 1581 self.rcvbuf = b"" 1582 1583 @ATMT.ioevent(ESTABLISHED,name="tcp", as_supersocket="tcplink") 1584 def outgoing_data_received(self, fd): 1585 raise self.ESTABLISHED().action_parameters(fd.recv()) 1586 @ATMT.action(outgoing_data_received) 1587 def send_data(self, d): 1588 self.l4[TCP].flags = "PA" 1589 self.send(self.l4/d) 1590 self.l4[TCP].seq += len(d) 1591 1592 1593 @ATMT.receive_condition(ESTABLISHED) 1594 def reset_received(self, pkt): 1595 if pkt[TCP].flags & 4 != 0: 1596 raise self.CLOSED() 1597 1598 @ATMT.receive_condition(ESTABLISHED) 1599 def fin_received(self, pkt): 1600 if pkt[TCP].flags & 0x1 == 1: 1601 raise self.LAST_ACK().action_parameters(pkt) 1602 @ATMT.action(fin_received) 1603 def send_finack(self, pkt): 1604 self.l4[TCP].flags = "FA" 1605 self.l4[TCP].ack = pkt[TCP].seq+1 1606 self.send(self.l4) 1607 self.l4[TCP].seq += 1 1608 1609 @ATMT.receive_condition(LAST_ACK) 1610 def ack_of_fin_received(self, pkt): 1611 if pkt[TCP].flags & 0x3f == 0x10: 1612 raise self.CLOSED() 1613 1614 1615 1616 1617##################### 1618## Reporting stuff ## 1619##################### 1620 1621 1622@conf.commands.register 1623def report_ports(target, ports): 1624 """portscan a target and output a LaTeX table 1625report_ports(target, ports) -> string""" 1626 ans,unans = sr(IP(dst=target)/TCP(dport=ports),timeout=5) 1627 rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n" 1628 for s,r in ans: 1629 if not r.haslayer(ICMP): 1630 if r.payload.flags == 0x12: 1631 rep += r.sprintf("%TCP.sport% & open & SA \\\\\n") 1632 rep += "\\hline\n" 1633 for s,r in ans: 1634 if r.haslayer(ICMP): 1635 rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n") 1636 elif r.payload.flags != 0x12: 1637 rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n") 1638 rep += "\\hline\n" 1639 for i in unans: 1640 rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n") 1641 rep += "\\hline\n\\end{tabular}\n" 1642 return rep 1643 1644 1645@conf.commands.register 1646def IPID_count(lst, funcID=lambda x:x[1].id, funcpres=lambda x:x[1].summary()): 1647 """Identify IP id values classes in a list of packets 1648 1649lst: a list of packets 1650funcID: a function that returns IP id values 1651funcpres: a function used to summarize packets""" 1652 idlst = [funcID(e) for e in lst] 1653 idlst.sort() 1654 classes = [idlst[0]] 1655 classes += [t[1] for t in zip(idlst[:-1], idlst[1:]) if abs(t[0]-t[1]) > 50] 1656 lst = [(funcID(x), funcpres(x)) for x in lst] 1657 lst.sort() 1658 print("Probably %i classes:" % len(classes), classes) 1659 for id,pr in lst: 1660 print("%5i" % id, pr) 1661 1662 1663@conf.commands.register 1664def fragleak(target,sport=123, dport=123, timeout=0.2, onlyasc=0): 1665 load = "XXXXYYYYYYYYYY" 1666# getmacbyip(target) 1667# pkt = IP(dst=target, id=RandShort(), options=b"\x22"*40)/UDP()/load 1668 pkt = IP(dst=target, id=RandShort(), options=b"\x00"*40, flags=1)/UDP(sport=sport, dport=sport)/load 1669 s=conf.L3socket() 1670 intr=0 1671 found={} 1672 try: 1673 while True: 1674 try: 1675 if not intr: 1676 s.send(pkt) 1677 sin,sout,serr = select([s],[],[],timeout) 1678 if not sin: 1679 continue 1680 ans=s.recv(1600) 1681 if not isinstance(ans, IP): #TODO: IPv6 1682 continue 1683 if not isinstance(ans.payload, ICMP): 1684 continue 1685 if not isinstance(ans.payload.payload, IPerror): 1686 continue 1687 if ans.payload.payload.dst != target: 1688 continue 1689 if ans.src != target: 1690 print("leak from", ans.src, end=' ') 1691 1692 1693# print repr(ans) 1694 if not ans.haslayer(conf.padding_layer): 1695 continue 1696 1697 1698# print repr(ans.payload.payload.payload.payload) 1699 1700# if not isinstance(ans.payload.payload.payload.payload, conf.raw_layer): 1701# continue 1702# leak = ans.payload.payload.payload.payload.load[len(load):] 1703 leak = ans.getlayer(conf.padding_layer).load 1704 if leak not in found: 1705 found[leak]=None 1706 linehexdump(leak, onlyasc=onlyasc) 1707 except KeyboardInterrupt: 1708 if intr: 1709 raise 1710 intr=1 1711 except KeyboardInterrupt: 1712 pass 1713 1714 1715@conf.commands.register 1716def fragleak2(target, timeout=0.4, onlyasc=0): 1717 found={} 1718 try: 1719 while True: 1720 p = sr1(IP(dst=target, options=b"\x00"*40, proto=200)/"XXXXYYYYYYYYYYYY",timeout=timeout,verbose=0) 1721 if not p: 1722 continue 1723 if conf.padding_layer in p: 1724 leak = p[conf.padding_layer].load 1725 if leak not in found: 1726 found[leak]=None 1727 linehexdump(leak,onlyasc=onlyasc) 1728 except: 1729 pass 1730 1731 1732conf.stats_classic_protocols += [TCP,UDP,ICMP] 1733conf.stats_dot11_protocols += [TCP,UDP,ICMP] 1734 1735if conf.ipv6_enabled: 1736 import scapy.layers.inet6 1737