Youtube Rickroll Proxy

From Sidvind
Jump to: navigation, search

Inspired by Upside-Down-Ternet I created a similar setup which replaces all youtube videos with Rick Astley's “Never Gonna Give You Up.” aka rickroll. It won't work for HTTPS, at least not without getting certificate failures. It works by first hijacking connections to the content delivery networks, discards your requests and inserts rickrolls instead. This setups also hijacks youtube.com and ytimg.com requests but only proxies to a real server, in retrospect I realized it wasn't necessary but I present the solution as it was when I made it.

DNS config[edit]

Code: named.conf

  1. # youtube hijack proxy
  2. zone "youtube.com" IN {
  3.      type master;
  4.      file "pri/youtube.zone";
  5. };
  6. zone "ytimg.com" {
  7.      type master;
  8.      file "pri/ytimg.zone";
  9. };

Code: pri/youtube.zone

  1. $ORIGIN youtube.com.
  2. @     IN      A       10.13.37.254
  3. www   IN      A       10.13.37.254
  4. *.c   IN      A       10.13.37.253

Code: pri/ytimg.zone

  1. $ORIGIN ytimg.com.
  2. @     IN      A       10.13.37.254
  3. s     IN      A       10.13.37.254
  4. i1    IN      A       10.13.37.254
  5. i2    IN      A       10.13.37.254
  6. i3    IN      A       10.13.37.254
  7. i4    IN      A       10.13.37.254

Please note that *.c.youtube.com is redirected to a different IP!

iptables[edit]

Code:

  1. iptables -A PREROUTING -t nat -d 10.13.37.254/32 -i eth1 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 13254
  2. iptables -A PREROUTING -t nat -d 10.13.37.253/32 -i eth1 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 13253

You can redirect to another host if you want, I chose to only redirect port as I was already running apache on port 80.

script[edit]

Code: fulhack.py (view, download)

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8; -*-
  3.  
  4. import socket
  5. import sys
  6. import random
  7. import urllib
  8. from copy import copy
  9. from signal import signal, SIGINT
  10. from threading import Thread
  11. from select import select
  12. from httplib import HTTPResponse
  13. from BaseHTTPServer import BaseHTTPRequestHandler
  14.  
  15. youtube = [
  16.     '173.194.32.0',
  17.     '173.194.32.1',
  18.     '173.194.32.2',
  19.     '173.194.32.3',
  20.     '173.194.32.4',
  21.     '173.194.32.5',
  22.     '173.194.32.6',
  23.     '173.194.32.7',
  24.     '173.194.32.8',
  25.     '173.194.32.9',
  26.     '173.194.32.10',
  27.     '173.194.32.11',
  28.     '173.194.32.12',
  29.     '173.194.32.13',
  30.     '173.194.32.14',
  31.     '173.194.32.15']
  32.  
  33. cdn = []
  34.  
  35. rickroll = open('./rickroll').read()
  36. running = True
  37.  
  38. class HTTPRequest(BaseHTTPRequestHandler):
  39.     def __init__(self, sock):
  40.         self.rfile = sock.makefile("rb")
  41.         self.raw_requestline = self.rfile.readline()
  42.         self.error_code = self.error_message = None
  43.         self.parse_request()
  44.        
  45.     def send_error(self, code, message):
  46.         self.error_code = code
  47.         self.error_message = message
  48.  
  49. class CDNProxy(Thread):
  50.     def __init__(self, sock, addr, ip):
  51.         global read_ip
  52.         Thread.__init__(self)
  53.         self.client = sock
  54.         self.addr = addr
  55.        
  56.         print 'Connection from', addr, 'to CDN'
  57.  
  58.     def run(self):
  59.         global running
  60.         cli = self.client
  61.  
  62.         req = HTTPRequest(cli)
  63.         req.path = urllib.quote("/videoplayback?sparams=id%2Cexpire%2Cip%2Cipbits%2Citag%2Csource%2Calgorithm%2Cburst%2Cfactor%2Ccp&fexp=900081&algorithm=throttle-factor&itag=34&ip=90.0.0.0&burst=40&sver=3&signature=CF95846B1987992E985924FB529D8B22AEB43FC8.AA4E2CD9FD6331D2F952F9C695E16D6B3CD166E9&source=youtube&expire=1328078746&key=yt1&ipbits=8&factor=1.25&cp=U0hRTVBSVl9JUENOMV9ITFpIOk5walczM1pvOTFB&id=a078394896111c0d&redirect_counter=1")
  64.  
  65.         st = '%s %s %s\r\n%s\r\n\r\n' % (req.command, req.path, req.request_version, str(req.headers))
  66.         #print [st]
  67.         #srv.send(st)
  68.  
  69.         # ignore request, acquire rickroll
  70.         global rickroll
  71.         data = copy(rickroll)
  72.         while len(data) > 0 and running:
  73.             rd, wd, xd = select([], [cli], [], 1)
  74.             if cli not in wd: continue
  75.            
  76.             n = cli.send(data)
  77.             data = data[n:]
  78.  
  79.         print 'Connection terminated'
  80.         cli.close()
  81.            
  82. class WWWProxy(Thread):
  83.     def __init__(self, sock, addr, ip):
  84.         global read_ip
  85.         Thread.__init__(self)
  86.         self.client = sock
  87.         self.addr = addr
  88.        
  89.         print 'Connection from', addr, 'to youtube.com/ytimg.com'
  90.  
  91.         # connect to real server
  92.         ip = random.choice(ip)
  93.         self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  94.         self.server.connect((ip, 80))
  95.  
  96.     def proxy(self, src, dst):
  97.         data = src.recv(4096)
  98.         size = len(data)
  99.         if size == 0:
  100.             return False
  101.         while len(data) > 0:
  102.             n = dst.send(data)
  103.             data = data[n:]
  104.         return True
  105.  
  106.     def run(self):
  107.         global running
  108.         cli = self.client
  109.         srv = self.server
  110.         while running:
  111.             rd, wd, xd = select([cli, srv], [], [], 1)
  112.            
  113.             if cli in rd and not self.proxy(cli, srv):
  114.                 break
  115.            
  116.             if srv in rd and not self.proxy(srv, cli):
  117.                 break
  118.  
  119.         print 'Connection terminated'
  120.         cli.close()
  121.         srv.close()
  122.  
  123. def sigint_handler(*args):
  124.     global running
  125.     running = False
  126.  
  127. signal(SIGINT, sigint_handler)
  128.  
  129. pairs = [
  130.     ('0.0.0.0', 13254, youtube, WWWProxy),
  131.     ('0.0.0.0', 13253, cdn, CDNProxy)]
  132.  
  133. class derp(object):
  134.     pass
  135.  
  136. sock = []
  137. for p in pairs:
  138.     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  139.     s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  140.     s.bind(p[:2])
  141.     s.listen(5)
  142.     x = derp()
  143.     x.sock = s
  144.     x.fileno = s.fileno
  145.     x.proxy_ip = p[2]
  146.     x.factory = p[3]
  147.     sock.append(x)
  148.  
  149. while True:
  150.     rd,wd,xd = select(sock, [], [], 1)
  151.     for s in rd:
  152.         x = s.factory(ip=s.proxy_ip, *s.sock.accept())
  153.         x.start()

Instead of changing the request I downloaded the rickroll response (including headers,e.g. curl -i URL) to a file called rickroll which is sent to the client.

This script could be improved in may ways but as a proof-of-concept it is enough.