Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save luftreich/a5c970da8b486ec8c089fa25f9e0e729 to your computer and use it in GitHub Desktop.
Save luftreich/a5c970da8b486ec8c089fa25f9e0e729 to your computer and use it in GitHub Desktop.
A Tool for Tracerouting the GC and GFW
#!/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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment