#!/usr/bin/env python2.7 # # clockskewer.py -- skewers http servers in onionland to an ip address # # This script takes advantage of the fact that no one # in onionland configures their http server correctly # by having it send datetime stamps in every response # # calculates the clockskew and then finds a corrilating # tor relay with an open http server with the same skew # # catches people with their pants down, sadly lots of # onionlanders have no pants and don't realize it :3 # # WTFPL 2012 Jeff Becker # # @ampernand <--- follow my ass on twitter if you care # import socks from pytorctl import TorCtl as ctl from datetime import datetime import logging,socket     localhost='127.0.0.1' # uncomment for using special ssh tunnel located at dash # localhost='dash'   socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5,localhost,port=9050) logging.basicConfig(format='>> %(message)s',level=logging.INFO) reg_socket = socket.socket debug = logging.debug info = logging.info warn = logging.warning error = logging.error   def torcontrol():     """    open control session    """     s = reg_socket()     s.connect((localhost,9051))     c = ctl.Connection(s)     c.authenticate()     return c   def get_relays():     """    Get all relays that your tor relay knows of    """     info('Get List Of Relays')     c = torcontrol()     if c is None:         error('Could not connect to tor control port')         return None     ret = []     re = c.get_network_status(get_iterator=True)     c.close()     if re is None:         error('Failed to get relay list')         return none     for r in re:         ret.append(r.ip)     info('Got %d relays'%len(ret))     return ret   now = datetime.utcnow     def skew(dstamp,timeformat='%a, %d %b %Y %H:%M:%S GMT'):     """    most common type of timestamp i've seen on onionland is    %a, %d %b %Y %H:%M:%S GMT      default to use that    """     return now() - datetime.strptime(dstamp,timeformat)   def http(host,ua='Onionland Clockskewer 0.1'):     """    do an http request and get the "Date" header and the "Server" header      returns          skew_in_ms, server_header      or in the event of an error or both headers not found:        None, None    """     debug('REQUEST http://%s/'%host)     try:         dstamp = None         server = None         s = socks.socksocket()         debug('CONNECT')         s.connect((host,80))         debug('REQUEST')         s.send('GET / HTTP/1.1\r\n')         s.send('Host: %s\r\n'%host)         s.send('User-Agent: %s\r\n'%ua)         s.send('\r\n')         resp = ''         count = 0         r = 0         # begin masive hack         while True:             r += 1             if r > 9000:                 break             c = s.recv(1)             if c is None or c == '':                 debug('break')                 break             resp += c             if resp.count('\r\n') > 0:                 count += 1                 if resp == '\r\n':                     break                 line = resp.strip()                 if count == 1:                     if line.count('HTTP') == 0:                         raise Exception()                     resp = ''                     continue                 if len(line) == 0:                     break                 i = line.index(':')                 k = line[:i].strip()                 v = line[i+1:].strip()                 if k == 'Date':                     dstamp = v                 elif k == 'Server':                     server = v                 resp = ''           #end massive hack         s.close()         return skew(dstamp), server     except Exception as e:         return None, None   def check(onion,ip,delta):     """    check to see if an onion is similar to a clearnet machine      1) request onion    2) request clearnet    3) compute difference of skews    4) if the difference of the skews is <= delta we have a potential candidate    """     t1,s1 = http(onion)     if t1 is None:         return False     t2,s2 = http(ip)     if t2 is None:         return False     try:           d = t1 - t2         d = abs( d.microseconds )  + abs (d.seconds ) * 1000         return d <= delta     except:         warn('Failed to check %s vs %s'%(onion,ip))     return False             def clockskewer(onions):     possible = []     relays = get_relays()     if relays  is None:         return     for onion in onions:         info('Locating %s'%onion)         res = 0         # iterate through all the tor relays in the public list         # this can take days if not weeks for each iteration         for ip in relays:             if check(onion,ip,7000):                 info('!!!!')                 info('POSSIBLE MATCH: %s ->  %s'%(onion,ip))                 possible.append((onion,ip))                 res += 1         info('Possible Matches for %s : %d'% (onion,res))     info('Found %d results'%len(possible))     for p in possible:         info('%s -> %s'%p)   if __name__ == '__main__':     import sys     clockskewer(sys.argv[1:])