Search code examples
pythonproxytor

How to set up a Tor-Server (Hidden Service) as a proxy?


The goal is, being able to access the proxy anonymously, such that the host (proxy) doesn't know, where the request came from (of course with credentials).

The client should be able to acess www.example.com over the hosts ip, without the host knowing the clients ip.

Here's a example request route to www.example.com: ![Network-route

  • How would I hookup a browser to it?
  • How would I connect using Python? (something proxy-chain like?)

Note: OS doesn't depend, programming language preferably Python

EDIT:

  • The Client in my case should be able to specify:
    • Headers
    • request-method
    • site-url
  • For the request which the hidden-service makes (so basically a proxy)

Solution

  • first you need to create a hidden service on tor from host to be able to communicate over tor network

    basic flask proxy example (for more advanced proxy you can follow this code) i didnt test this code but you can fix errors:

    """
    A simple proxy server, based on original by gear11:
    https://gist.github.com/gear11/8006132
    Modified from original to support both GET and POST, status code passthrough, header and form data passthrough.
    Usage: http://hostname:port/p/(URL to be proxied, minus protocol)
    For example: http://localhost:5000/p/www.google.com
    """
    from stem.control import Controller
    import re
    from urllib.parse import urlparse, urlunparse
    from flask import Flask, render_template, request, abort, Response, redirect
    import requests
    import logging
    
    app = Flask("example")
    port = 5000
    host = "127.0.0.1"
    hidden_svc_dir = "c:/temp/"
    logging.basicConfig(level=logging.INFO)
    CHUNK_SIZE = 1024
    LOG = logging.getLogger("app.py")
    
    
    @app.route('/<path:url>', methods=["GET", "POST"])
    def root(url):
        # If referred from a proxy request, then redirect to a URL with the proxy prefix.
        # This allows server-relative and protocol-relative URLs to work.
        referer = request.headers.get('referer')
        if not referer:
            return Response("Relative URL sent without a a proxying request referal. Please specify a valid proxy host (/p/url)", 400)
        proxy_ref = proxied_request_info(referer)
        host = proxy_ref[0]
        redirect_url = "/p/%s/%s%s" % (host, url, ("?" + request.query_string.decode('utf-8') if request.query_string else ""))
        LOG.debug("Redirecting relative path to one under proxy: %s", redirect_url)
        return redirect(redirect_url)
    
    
    @app.route('/p/<path:url>', methods=["GET", "POST"])
    def proxy(url):
        """Fetches the specified URL and streams it out to the client.
        If the request was referred by the proxy itself (e.g. this is an image fetch
        for a previously proxied HTML page), then the original Referer is passed."""
        # Check if url to proxy has host only, and redirect with trailing slash
        # (path component) to avoid breakage for downstream apps attempting base
        # path detection
        url_parts = urlparse('%s://%s' % (request.scheme, url))
        if url_parts.path == "":
            parts = urlparse(request.url)
            LOG.warning("Proxy request without a path was sent, redirecting assuming '/': %s -> %s/" % (url, url))
            return redirect(urlunparse(parts._replace(path=parts.path+'/')))
    
        LOG.debug("%s %s with headers: %s", request.method, url, request.headers)
        r = make_request(url, request.method, dict(request.headers), request.form)
        LOG.debug("Got %s response from %s",r.status_code, url)
        headers = dict(r.raw.headers)
        def generate():
            for chunk in r.raw.stream(decode_content=False):
                yield chunk
        out = Response(generate(), headers=headers)
        out.status_code = r.status_code
        return out
        
    def make_request(url, method, headers={}, data=None):
        url = 'http://%s' % url
    
        # Pass original Referer for subsequent resource requests
        referer = request.headers.get('referer')
        if referer:
            proxy_ref = proxied_request_info(referer)
            headers.update({ "referer" : "http://%s/%s" % (proxy_ref[0], proxy_ref[1])})
    
        # Fetch the URL, and stream it back
        LOG.debug("Sending %s %s with headers: %s and data %s", method, url, headers, data)
        return requests.request(method, url, params=request.args, stream=True, headers=headers, allow_redirects=False, data=data)
    
    def proxied_request_info(proxy_url):
        """Returns information about the target (proxied) URL given a URL sent to
        the proxy itself. For example, if given:
            http://localhost:5000/p/google.com/search?q=foo
        then the result is:
            ("google.com", "search?q=foo")"""
        parts = urlparse(proxy_url)
        if not parts.path:
            return None
        elif not parts.path.startswith('/p/'):
            return None
        matches = re.match('^/p/([^/]+)/?(.*)', parts.path)
        proxied_host = matches.group(1)
        proxied_path = matches.group(2) or '/'
        proxied_tail = urlunparse(parts._replace(scheme="", netloc="", path=proxied_path))
        LOG.debug("Referred by proxy host, uri: %s, %s", proxied_host, proxied_tail)
        return [proxied_host, proxied_tail]
    
    controller = Controller.from_port(address="127.0.0.1", port=9151)
    try:
        controller.authenticate(password="")
        controller.set_options([
            ("HiddenServiceDir", hidden_svc_dir),
            ("HiddenServicePort", "80 %s:%s" % (host, str(port)))
            ])
        svc_name = open(hidden_svc_dir + "/hostname", "r").read().strip()
        print "onion link: %s" % svc_name
    except Exception as e:
        print e
    
    app.run()
    

    after runing this you will get a onion link like: "somelongstringwithnumber123.onion" with that onion link you can connect to host from client over tor network

    Than you need to make request over tor network from host:

    import requests
    
    session = requests.session()
    session.proxies = {}
    session.proxies['http'] = 'socks5h://localhost:9050'
    session.proxies['https'] = 'socks5h://localhost:9050'
    
    r = session.get("http://somelongstringwithnumber123.onion/p/alpwebtasarim.com")
    print(r.text)
    

    im not gonna test that codes but i hope you understand the main idea.