Youtube Rickroll Proxy

_O6o5e-PpRs 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
 zone "youtube.com" IN { type master; file "pri/youtube.zone"; }; zone "ytimg.com" { type master; file "pri/ytimg.zone"; };
 * 1) youtube hijack proxy

 $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
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

 * 1) !/usr/bin/env python
 * 2) -*- 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 which is sent to the client.

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