Initial Commit

This commit is contained in:
Dominik Moritz Roth 2021-04-04 17:52:01 +02:00
commit 5d18904645
4 changed files with 181 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.key
*.crt
certs/

6
README.md Normal file
View File

@ -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...

166
main.py Normal file
View File

@ -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()

6
setup_https_intercept.sh Normal file
View File

@ -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/