diff --git a/README.md b/README.md index bf2449d..86f0307 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ By [@thetechr0mancer](https://twitter.com/thetechr0mancer) ## Installation ~~~bash -pip install git+https://github.com/blacklanternsecurity/trevorproxy +pip install git+https://github.com/c3l3si4n/TREVORproxy-tor ~~~ See the accompanying [**Blog Post**](https://github.com/blacklanternsecurity/TREVORspray/blob/trevorspray-v2/blogpost.md) for a fun rant and some cool demos! @@ -64,7 +64,6 @@ $ cat /etc/proxychains.conf ... socks5 127.0.0.1 1080 ... - # Start TREVORproxy $ trevorproxy ssh root@1.2.3.4 root@4.3.2.1 [DEBUG] Opening SSH connection to root@1.2.3.4 @@ -77,7 +76,6 @@ $ trevorproxy ssh root@1.2.3.4 root@4.3.2.1 [DEBUG] iptables -A OUTPUT -t nat -d 127.0.0.1 -o lo -p tcp --dport 1080 -j DNAT --to-destination 127.0.0.1:32482 -m statistic --mode nth --every 2 --packet 0 [DEBUG] iptables -A OUTPUT -t nat -d 127.0.0.1 -o lo -p tcp --dport 1080 -j DNAT --to-destination 127.0.0.1:32483 [INFO] Listening on socks5://127.0.0.1:1080 - # Test SOCKS proxy $ proxychains curl ifconfig.me 1.2.3.4 @@ -89,6 +87,42 @@ $ proxychains curl ifconfig.me 4.3.2.1 ~~~ +## Example #3 - Send traffic through Tor Stream Isolation tunnels +~~~bash +# Configure proxychains +$ cat /etc/proxychains.conf +... +socks4 127.0.0.1 1080 +... + + +# Start TREVORproxy +$ trevorproxy tor +[INFO] Sleeping 5 seconds to make sure Tor is properly initialized +[DEBUG] Creating iptables rules +[DEBUG] sudo iptables -A OUTPUT -t nat -d 127.0.0.1 -o lo -p tcp --dport 1080 -j DNAT --to-destination 127.0.0.1:32482 -m statistic --mode nth --every 5 --packet 0 +[DEBUG] sudo iptables -A OUTPUT -t nat -d 127.0.0.1 -o lo -p tcp --dport 1080 -j DNAT --to-destination 127.0.0.1:32483 -m statistic --mode nth --every 4 --packet 0 +[DEBUG] sudo iptables -A OUTPUT -t nat -d 127.0.0.1 -o lo -p tcp --dport 1080 -j DNAT --to-destination 127.0.0.1:32484 -m statistic --mode nth --every 3 --packet 0 +[DEBUG] sudo iptables -A OUTPUT -t nat -d 127.0.0.1 -o lo -p tcp --dport 1080 -j DNAT --to-destination 127.0.0.1:32485 -m statistic --mode nth --every 2 --packet 0 +[DEBUG] sudo iptables -A OUTPUT -t nat -d 127.0.0.1 -o lo -p tcp --dport 1080 -j DNAT --to-destination 127.0.0.1:32486 +[INFO] Listening on socks5://127.0.0.1:1080 + +# Test SOCKS proxy +$ proxychains -q curl ifconfig.me +103.251.167.21 + +$ proxychains -q curl ifconfig.me +185.220.101.14 + +$ proxychains -q curl ifconfig.me +185.220.100.241 + +$ proxychains -q curl ifconfig.me +5.45.98.12 + +$ +~~~ + ## CLI Usage ~~~ $ trevorproxy --help @@ -141,4 +175,4 @@ optional arguments: ![trevor](https://user-images.githubusercontent.com/20261699/92336575-27071380-f070-11ea-8dd4-5ba42c7d04b7.jpeg) -`#trevorforget` \ No newline at end of file +`#trevorforget` diff --git a/trevorproxy/cli.py b/trevorproxy/cli.py index 3399082..8b97838 100755 --- a/trevorproxy/cli.py +++ b/trevorproxy/cli.py @@ -40,6 +40,10 @@ def main(): ssh.add_argument('-kp', '--key-pass', action='store_true', help=argparse.SUPPRESS) ssh.add_argument('--base-port', default=32482, type=int, help='Base listening port to use for SOCKS proxies (default: 32482)') + tor = subparsers.add_parser('tor', help='round-robin traffic through TOR nodes') + tor.add_argument('--base-port', default=32482, type=int, help='Base listening port to use for TOR proxies (default: 32482)') + tor.add_argument('--num-instances', default=8, type=int, help='Number of tor chains to spawn (default: 8)') + try: options = parser.parse_args() @@ -107,7 +111,35 @@ def main(): server.serve_forever() finally: subnet_proxy.stop() + elif options.proxytype == 'tor': + + from lib.tor import TorLoadBalancer + + for binary in TorLoadBalancer.dependencies: + if not which(binary): + log.error(f'Please install {binary}') + sys.exit(1) + + + load_balancer = TorLoadBalancer( + base_port=options.base_port, + num_instances=options.num_instances + ) + + try: + load_balancer.start() + log.info(f'Listening on socks5://{options.listen_address}:{options.port}') + + # serve forever + while 1: + # rebuild proxy if it goes down + for proxy in load_balancer.proxies.values(): + pass + time.sleep(1) + + finally: + load_balancer.stop() ''' from ipaddress import ip_network, ip_address blacklist = [ip_address('192.168.0.1'), ip_address('192.168.0.250'), ip_address('192.168.0.133')] @@ -147,4 +179,4 @@ def main(): if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/trevorproxy/lib/__pycache__/errors.cpython-310.pyc b/trevorproxy/lib/__pycache__/errors.cpython-310.pyc new file mode 100644 index 0000000..438dd48 Binary files /dev/null and b/trevorproxy/lib/__pycache__/errors.cpython-310.pyc differ diff --git a/trevorproxy/lib/__pycache__/logger.cpython-310.pyc b/trevorproxy/lib/__pycache__/logger.cpython-310.pyc new file mode 100644 index 0000000..fa5f178 Binary files /dev/null and b/trevorproxy/lib/__pycache__/logger.cpython-310.pyc differ diff --git a/trevorproxy/lib/__pycache__/tor.cpython-310.pyc b/trevorproxy/lib/__pycache__/tor.cpython-310.pyc new file mode 100644 index 0000000..2252a8a Binary files /dev/null and b/trevorproxy/lib/__pycache__/tor.cpython-310.pyc differ diff --git a/trevorproxy/lib/__pycache__/util.cpython-310.pyc b/trevorproxy/lib/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..f735388 Binary files /dev/null and b/trevorproxy/lib/__pycache__/util.cpython-310.pyc differ diff --git a/trevorproxy/lib/errors.py b/trevorproxy/lib/errors.py index 3f1e142..14da3e4 100644 --- a/trevorproxy/lib/errors.py +++ b/trevorproxy/lib/errors.py @@ -4,5 +4,8 @@ class TrevorProxyError(Exception): class SSHProxyError(TrevorProxyError): pass +class TorProxyError(TrevorProxyError): + pass + class InterfaceProxyError(TrevorProxyError): pass \ No newline at end of file diff --git a/trevorproxy/lib/ssh.py b/trevorproxy/lib/ssh.py index 077284b..41c47fd 100644 --- a/trevorproxy/lib/ssh.py +++ b/trevorproxy/lib/ssh.py @@ -118,13 +118,8 @@ def __repr__(self): -class IPTables: +class IPTables: for proxy_address,proxy_port in proxy - def __init__(self, proxies, address=None, proxy_port=None): - - if address is None: - self.address = '127.0.0.1' - else: self.address = str(address) if proxy_port is None: self.proxy_port = 1080 diff --git a/trevorproxy/lib/tor.py b/trevorproxy/lib/tor.py new file mode 100644 index 0000000..7eee0c1 --- /dev/null +++ b/trevorproxy/lib/tor.py @@ -0,0 +1,218 @@ +import sh +import logging +from time import sleep +import subprocess as sp +from pathlib import Path +from .util import sudo_run + +log = logging.getLogger('trevorproxy.tor') + + +class IndividualTorProxy: + def __init__(self, host='127.0.0.1', proxy_port=None, sh=None): + self.host = host + self.proxy_port = proxy_port + self.sh = sh + self.command = '' + + def stop(self): + + try: + self.sh.process.terminate() + except: + try: + self.sh.process.kill() + except: + pass + + + def __hash__(self): + + return hash(str(self)) + + + def __str__(self): + + return f'socks5://127.0.0.1:{self.proxy_port}' + + + def __repr__(self): + + return str(self) +class TorProxy: + + def __init__(self, ports): + + self.ports = ports + + + self.sh = None + self.command = '' + self.proxies = dict() + for port in ports: + individual_proxy = IndividualTorProxy(proxy_port=port) + self.proxies[str(individual_proxy)] = individual_proxy + + + + def start(self, wait=True, timeout=30): + + self.stop() + with sh.sudo: + arguments = [] + + for port in self.ports: + arguments.append("--SocksPort") + arguments.append(f"127.0.0.1:{port}") + self.sh = sh.tor(*arguments, _bg=True, _bg_exc=False,) + + + + + + def stop(self): + + try: + self.sh.process.terminate() + except: + try: + self.sh.process.kill() + except: + pass + + + + def __hash__(self): + + return hash(str(self)) + + + def __str__(self): + + return str([f'socks4://127.0.0.1:{proxy_port}' for proxy_port in self.ports]) + + def get_iptables_input(self): + + return [(f'socks4://127.0.0.1:{proxy_port}', proxy_port) for proxy_port in self.ports] + + def __repr__(self): + + return str(self) + + + +class IPTables: + + def __init__(self, proxies, address=None, proxy_port=None): + + if address is None: + self.address = '127.0.0.1' + else: + self.address = str(address) + if proxy_port is None: + self.proxy_port = 1080 + else: + self.proxy_port = int(proxy_port) + + self.proxies = [p for p in proxies if p is not None] + + self.iptables_rules = [] + + + def start(self): + + log.debug('Creating iptables rules') + + current_ip = False + for i,proxy in enumerate(self.proxies): + if proxy is not None: + iptables_add = ['iptables', '-A'] + iptables_main = ['OUTPUT', '-t', 'nat', '-d', f'{self.address}', '-o', 'lo', '-p', \ + 'tcp', '--dport', f'{self.proxy_port}', '-j', 'DNAT', '--to-destination', f'127.0.0.1:{proxy.proxy_port}'] + + # if this isn't the last proxy + if not i == len(self.proxies) - 1: + iptables_main += ['-m', 'statistic', '--mode', 'nth', '--every', f'{len(self.proxies)-i}', '--packet', '0'] + self.iptables_rules.append(iptables_main) + cmd = iptables_add + iptables_main + sudo_run(cmd) + def stop(self): + + log.debug('Cleaning up iptables rules') + + for rule in self.iptables_rules: + iptables_del = ['iptables', '-D'] + cmd = iptables_del + rule + sudo_run(cmd) + + + +class TorLoadBalancer: + + dependencies = ['ss', 'iptables', 'sudo', 'tor'] + + def __init__(self, base_port=33482, num_instances=5, current_ip=False, socks_server=True): + + self.args = dict() + self.base_port = base_port + self.current_ip = current_ip + self.proxies = dict() + self.socks_server = socks_server + self.num_instances = num_instances + proxy_ports = [] + for i in range(num_instances): + proxy_ports.append(self.base_port + i) + + #self.proxies[str(proxy)] = proxy + + self.main_proxy_class = TorProxy(proxy_ports) + self.proxies = self.main_proxy_class.proxies + + + + + self.proxy_round_robin = list(self.proxies.values()) + self.round_robin_counter = 0 + + self.iptables = IPTables(list(self.proxies.values())) + + + def start(self, timeout=30): + + self.main_proxy_class.start(wait=False) + + # wait for them all to start + left = int(timeout) + log.info("Sleeping 5 seconds to make sure Tor is properly initialized") + sleep(5) + + if self.socks_server: + self.iptables.start() + + + def stop(self): + + self.main_proxy_class.stop() + if self.socks_server: + self.iptables.stop() + def __next__(self): + ''' + Yields proxies in round-robin fashion forever + Note that a proxy can be "None" if current_ip is specified + ''' + + proxy_num = self.round_robin_counter % len(self.proxies) + proxy = self.proxy_round_robin[proxy_num] + self.round_robin_counter += 1 + return proxy + + + def __enter__(self): + + return self + + + def __exit__(self, exc_type, exc_value, exc_traceback): + + log.info('Shutting down proxies') + self.stop()