Youtube Rickroll Proxy
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]
- # youtube hijack proxy
- zone "youtube.com" IN {
- type master;
- file "pri/youtube.zone";
- };
- zone "ytimg.com" {
- type master;
- file "pri/ytimg.zone";
- };
- $ORIGIN youtube.com.
- @ IN A 10.13.37.254
- www IN A 10.13.37.254
- *.c IN A 10.13.37.253
- $ORIGIN ytimg.com.
- @ IN A 10.13.37.254
- s IN A 10.13.37.254
- i1 IN A 10.13.37.254
- i2 IN A 10.13.37.254
- i3 IN A 10.13.37.254
- i4 IN A 10.13.37.254
Please note that *.c.youtube.com is redirected to a different IP!
iptables[edit]
- iptables -A PREROUTING -t nat -d 10.13.37.254/32 -i eth1 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 13254
- 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]
- #!/usr/bin/env python
- # -*- coding: utf-8; -*-
- import socket
- import sys
- import random
- import urllib
- from copy import copy
- from signal import signal, SIGINT
- from threading import Thread
- from select import select
- from httplib import HTTPResponse
- from BaseHTTPServer import BaseHTTPRequestHandler
- youtube = [
- '173.194.32.0',
- '173.194.32.1',
- '173.194.32.2',
- '173.194.32.3',
- '173.194.32.4',
- '173.194.32.5',
- '173.194.32.6',
- '173.194.32.7',
- '173.194.32.8',
- '173.194.32.9',
- '173.194.32.10',
- '173.194.32.11',
- '173.194.32.12',
- '173.194.32.13',
- '173.194.32.14',
- '173.194.32.15']
- cdn = []
- rickroll = open('./rickroll').read()
- running = True
- class HTTPRequest(BaseHTTPRequestHandler):
- def __init__(self, sock):
- self.rfile = sock.makefile("rb")
- self.raw_requestline = self.rfile.readline()
- self.error_code = self.error_message = None
- self.parse_request()
- def send_error(self, code, message):
- self.error_code = code
- self.error_message = message
- class CDNProxy(Thread):
- def __init__(self, sock, addr, ip):
- global read_ip
- Thread.__init__(self)
- self.client = sock
- self.addr = addr
- print 'Connection from', addr, 'to CDN'
- def run(self):
- global running
- cli = self.client
- req = HTTPRequest(cli)
- 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")
- st = '%s %s %s\r\n%s\r\n\r\n' % (req.command, req.path, req.request_version, str(req.headers))
- #print [st]
- #srv.send(st)
- # ignore request, acquire rickroll
- global rickroll
- data = copy(rickroll)
- while len(data) > 0 and running:
- rd, wd, xd = select([], [cli], [], 1)
- if cli not in wd: continue
- n = cli.send(data)
- data = data[n:]
- print 'Connection terminated'
- cli.close()
- class WWWProxy(Thread):
- def __init__(self, sock, addr, ip):
- global read_ip
- Thread.__init__(self)
- self.client = sock
- self.addr = addr
- print 'Connection from', addr, 'to youtube.com/ytimg.com'
- # connect to real server
- ip = random.choice(ip)
- self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.server.connect((ip, 80))
- def proxy(self, src, dst):
- data = src.recv(4096)
- size = len(data)
- if size == 0:
- return False
- while len(data) > 0:
- n = dst.send(data)
- data = data[n:]
- return True
- def run(self):
- global running
- cli = self.client
- srv = self.server
- while running:
- rd, wd, xd = select([cli, srv], [], [], 1)
- if cli in rd and not self.proxy(cli, srv):
- break
- if srv in rd and not self.proxy(srv, cli):
- break
- print 'Connection terminated'
- cli.close()
- srv.close()
- def sigint_handler(*args):
- global running
- running = False
- signal(SIGINT, sigint_handler)
- pairs = [
- ('0.0.0.0', 13254, youtube, WWWProxy),
- ('0.0.0.0', 13253, cdn, CDNProxy)]
- class derp(object):
- pass
- sock = []
- for p in pairs:
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.bind(p[:2])
- s.listen(5)
- x = derp()
- x.sock = s
- x.fileno = s.fileno
- x.proxy_ip = p[2]
- x.factory = p[3]
- sock.append(x)
- while True:
- rd,wd,xd = select(sock, [], [], 1)
- for s in rd:
- x = s.factory(ip=s.proxy_ip, *s.sock.accept())
- 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.