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