commit 5d18904645b4521b512c4b5feca3eb02dc73257d Author: Dominik Roth Date: Sun Apr 4 17:52:01 2021 +0200 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f9f56b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.key +*.crt +certs/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..a58b77a --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# CloudOnFire + +This script accepts PROXY-Calls on 9097 and tunnels them into the Edge of the Cloudflare Infrastructure. +This way you can access the Internet using a huge range of IPs, that change on every request and are considered clean. +In order for this to work, we MITM all SSL-Trafic using our own certs. This means all websites with HSTS won't work. +And browsers and other software might refuse to connect to a server, because the cert it gets looks rather suspicious... diff --git a/main.py b/main.py new file mode 100644 index 0000000..c5e84c5 --- /dev/null +++ b/main.py @@ -0,0 +1,166 @@ +import os +import re +import time +import requests +from urllib.parse import urlparse, urlsplit + +from http.server import HTTPServer, BaseHTTPRequestHandler +from subprocess import Popen, PIPE +import ssl +import threading + +class CFProxy(BaseHTTPRequestHandler): + TOKEN_HEADER = 'H-Token' + TOKEN_VALUE = 'agfjkewjkfvasfhgkzuc' + HOST_HEADER = 'H-Host' + IP_HEADER = 'H-IP' + + PROXY_HOST = "blue-wind-f1c2.grogu.workers.dev" # "proxy.grogu.ml" + UA = "User Agent" + FAKE_IP = "1.1.1.1" + PROXY = None + + cakey = 'ca.key' + cacert = 'ca.crt' + certkey = 'cert.key' + certdir = 'certs/' + + lock = threading.Lock() + + def __init__(self, *args): + self.session = requests.Session() + self.session.proxies.update({'https': self.PROXY, + 'http': self.PROXY}) + self.session.headers.update({'User-Agent': self.UA, + self.TOKEN_HEADER: self.TOKEN_VALUE}) + self.proxy_host = self.PROXY_HOST + self.fake_ip = self.FAKE_IP + BaseHTTPRequestHandler.__init__(self, *args) + + def do_GET_old(self): + url = self.path + print("[GET] "+url) + req = self.get(url) + self.send_response(req.status_code) + self.end_headers() + self.wfile.write(req.text.encode()) + + def do_CONNECT(self): + hostname = self.path.split(':')[0] + certpath = "%s/%s.crt" % (self.certdir.rstrip('/'), hostname) + + with self.lock: + if not os.path.isfile(certpath): + epoch = "%d" % (time.time() * 1000) + p1 = Popen(["openssl", "req", "-new", "-key", self.certkey, "-subj", "/CN=%s" % hostname], stdout=PIPE) + p2 = Popen(["openssl", "x509", "-req", "-days", "3650", "-CA", self.cacert, "-CAkey", self.cakey, "-set_serial", epoch, "-out", certpath], stdin=p1.stdout, stderr=PIPE) + p2.communicate() + + #self.wfile.write(("%s %d %s\r\n" % (self.protocol_version, 200, 'Connection Established')).encode()) + self.send_response(200, 'Connection Established') + self.end_headers() + + self.connection = ssl.wrap_socket(self.connection, keyfile=self.certkey, certfile=certpath, server_side=True) + self.rfile = self.connection.makefile("rb", self.rbufsize) + self.wfile = self.connection.makefile("wb", self.wbufsize) + + conntype = self.headers.get('Proxy-Connection', '') + if self.protocol_version == "HTTP/1.1" and conntype.lower() != 'close': + self.close_connection = 0 + else: + self.close_connection = 1 + + print("[CON OK]") + + def request_handler(self, req, req_body): + pass + + def response_handler(self, req, req_body, res, res_body): + pass + + def filter_headers(self, headers): + # http://tools.ietf.org/html/rfc2616#section-13.5.1 + hop_by_hop = ('connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailers', 'transfer-encoding', 'upgrade') + for k in hop_by_hop: + del headers[k] + + # accept only supported encodings + if 'Accept-Encoding' in headers: + ae = headers['Accept-Encoding'] + filtered_encodings = [x for x in re.split(r',\s*', ae) if x in ('identity', 'gzip', 'x-gzip', 'deflate')] + headers['Accept-Encoding'] = ', '.join(filtered_encodings) + + return headers + + def do_ALL(self): + if self.path == 'http://127.0.0.1/': + self.send_cacert() + return + + req = self + content_length = int(req.headers.get('Content-Length', 0)) + req_body = self.rfile.read(content_length) if content_length else None + + if req.path[0] == '/': + if isinstance(self.connection, ssl.SSLSocket): + req.path = "https://%s%s" % (req.headers['Host'], req.path) + else: + req.path = "http://%s%s" % (req.headers['Host'], req.path) + + req_body_modified = self.request_handler(req, req_body) + if req_body_modified is False: + self.send_error(403) + return + elif req_body_modified is not None: + req_body = req_body_modified + req.headers['Content-length'] = str(len(req_body)) + + u = urlsplit(req.path) + scheme, netloc, path = u.scheme, u.netloc, (u.path + '?' + u.query if u.query else u.path) + assert scheme in ('http', 'https') + if netloc: + req.headers['Host'] = netloc + setattr(req, 'headers', self.filter_headers(req.headers)) + + res = self.get(req.path) + self.send_response(res.status_code) + self.end_headers() + self.wfile.write(res.text.encode()) + + do_GET = do_ALL + do_POST = do_ALL # wont work, because we drop the body... + + def send_cacert(self): + with open(self.cacert, 'rb') as f: + data = f.read() + + self.wfile.write("%s %d %s\r\n" % (self.protocol_version, 200, 'OK')) + self.send_header('Content-Type', 'application/x-x509-ca-cert') + self.send_header('Content-Length', len(data)) + self.send_header('Connection', 'close') + self.end_headers() + self.wfile.write(data) + + def get(self, url, **kwargs): + return self.handleReq('GET', url, **kwargs) + + def post(self, url, **kwargs): + return self.handleReq('POST', url, **kwargs) + + def handleReq(self, method, url, **kwargs): + # Gestion des headers + if 'headers' in kwargs: + for k, v in kwargs.get('headers'): + self.session.headers.update({k: v}) + kwargs.pop('headers') + parsed_uri = urlparse(url) + self.session.headers.update({self.HOST_HEADER: parsed_uri.hostname}) + self.session.headers.update({self.IP_HEADER: self.fake_ip}) + proxyfied_url = '{0}://{1}{2}'.format(parsed_uri.scheme, self.proxy_host, parsed_uri.path) + return self.session.request(method, proxyfied_url, **kwargs) + +PORT = 9097 +httpd = HTTPServer(('', PORT), CFProxy) +print ("[i] Now serving at " + str(PORT)) +httpd.serve_forever() + diff --git a/setup_https_intercept.sh b/setup_https_intercept.sh new file mode 100644 index 0000000..e630abc --- /dev/null +++ b/setup_https_intercept.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +openssl genrsa -out ca.key 2048 +openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/CN=proxy2 CA" +openssl genrsa -out cert.key 2048 +mkdir certs/