-
-
Save luftreich/a5c970da8b486ec8c089fa25f9e0e729 to your computer and use it in GitHub Desktop.
A Tool for Tracerouting the GC and GFW
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 "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 "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