#!/usr/bin/env python import time import threading from scapy.all import * import sys import socket maxhop = 25 falunfetch = """GET /?falun HTTP/1.1\r\nUser-Agent: Wget/1.15 (linux-gnu)\r\nAccept: */*\r\nHost: www.google.com\r\nConnection: Keep-Alive\r\n\r\n""" inject = """GET /h.js HTTP/1.1\r\nHost: hm.baidu.com\r\nUser-Agent: Mozilla/5.0\r\nAccept: */*\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n""" class CannonTraceroute: def __init__(self, src, dst): self.src = src self.dst = dst self.ports = {} self.idcount = 0 self.portlock = threading.Lock() t = threading.Thread(target=self.run_sniffer) t.daemon = True t.start() def run(self): for x in range(1, maxhop): # sending the SYN isn't necessary for # the cannon, but it IS necessary for # some NATS/firewalls! self.send_msg([inject], ttl=x, attempts=300, syn=True) for x in range(1, maxhop): self.send_msg([falunfetch], ttl=x, attempts=6, syn=True) # This sleep is not necessary, but # it makes pcaps easier to distinguish # time.sleep(3) time.sleep(10) # Msg should be list def send_msg(self, payload, ttl=32, syn=False, attempts=3, dport=80): blaster = [] self.portlock.acquire() for x in range(attempts): sport = random.randint(1024, 32000) while sport in self.ports: sport = random.randint(1024, 32000) self.ports[sport] = (payload, ttl, []) seq = random.randint(1, 31313131) ack = random.randint(1, 31313131) if syn: syn = TCP(sport=sport, dport=80, flags="S", seq=seq) ip = IP(src=self.src, dst=self.dst, id=self.idcount, ttl=ttl) self.idcount += 1 seq += 1 blaster.append(ip/syn) ackp = TCP(sport=sport, dport=dport, flags="A", seq=seq, ack=ack) ip = IP(src=self.src, dst=self.dst, id=self.idcount, ttl=ttl) self.idcount += 1 blaster.append(ip/ackp) for i in payload: ip = IP(src=self.src, dst=self.dst, id=self.idcount, ttl=ttl) self.idcount += 1 psh = TCP(sport=sport, dport=80, flags="PA", seq=seq, ack=ack) seq += len(i) blaster.append(ip/psh/i) self.portlock.release() sys.stderr.write('.') sys.stderr.flush() send(blaster, verbose=0) def sniffer(self, packet): if ICMP in packet and packet[IP][ICMP].type == 11: sport = packet[IP][ICMP].payload.sport src = packet[IP].src if sport in self.ports: self.portlock.acquire() self.ports[sport][2].append(('ICMP', src)) self.portlock.release() else: print "Unknown port" + sport if TCP in packet: dport = packet[IP][TCP].dport status = "" if packet[IP][TCP].flags & 0x4 != 0: status = "Reset" elif len(packet[IP][TCP].payload) > 100: status = "Payload" # RST flagged if dport in self.ports and status != "": self.portlock.acquire() self.ports[dport][2].append((status,)) self.portlock.release() def run_sniffer(self): print "Sniffer started" sniff(prn=self.sniffer, filter="src %s or icmp" % self.dst, store=0 ) def maxhop(self): hopdata = {} maxhop = 1 for item in self.ports: data = self.ports[item] fetch = data[0] hop = data[1] responses = data[2] if data[0] == [falunfetch]: for i in responses: if i[0] == 'ICMP' and hop > maxhop: maxhop = hop # The maximum hop that gets an ICMP back as well. return maxhop def prettyprint(self): hopdata = {} cannon = {} for item in self.ports: data = self.ports[item] fetch = data[0] hop = data[1] responses = data[2] if data[0] == [falunfetch]: if hop not in hopdata: hopdata[hop] = [] for i in responses: hopdata[hop].append(i) if data[0] == [inject]: if hop not in cannon: cannon[hop] = [] for i in responses: cannon[hop].append(i) print "Traceroute for the Great Firewall" for x in range(1, self.maxhop() + 1): if hopdata[x] == []: print "Hop %2i: *" % x else: icmp = {} status = " " for item in hopdata[x]: if item[0] == 'ICMP': icmp[item[1]] = True elif item[0] == 'Reset': status = "Firewall" lst = [] for item in icmp: lst.append(item) lst.sort() icmp_info = "" for item in lst: icmp_info += item + " " icmp_info.strip() print "Hop %2i: %s ICMPs: %s" % (x, status, icmp_info) print print print "Traceroute for the Great Cannon" for x in range(1, self.maxhop() + 1): if x not in cannon: print "Hop %2i: Not Evaluated" % x elif cannon[x] == []: print "Hop %2i: *" % x else: icmp = {} status = " " for item in cannon[x]: if item[0] == 'ICMP': icmp[item[1]] = True elif item[0] == 'Reset' and status == " ": status = "Firewall" elif item[0] == 'Payload': status = " Cannnon" lst = [] for item in icmp: lst.append(item) lst.sort() icmp_info = "" for item in lst: icmp_info += item + " " icmp_info.strip() print "Hop %2i: %s ICMPs: %s" % (x, status, icmp_info) if __name__ == '__main__': # myip = "192.150.187.17" target = "123.125.65.120" myip = None if len(sys.argv) <= 2: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("www.google.com",80)) myip = s.getsockname()[0] s.close() else: myip = sys.argv[2] if len(sys.argv) > 1: target = sys.argv[1] tr = CannonTraceroute(myip, target) tr.run() print print print "Great Firewall and Great Cannon traceroute from %s to %s" % \ (myip, target) tr.prettyprint()