Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add experimental support for Tor Stream Isolation #6

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down Expand Up @@ -64,7 +64,6 @@ $ cat /etc/proxychains.conf
...
socks5 127.0.0.1 1080
...

# Start TREVORproxy
$ trevorproxy ssh [email protected] [email protected]
[DEBUG] Opening SSH connection to [email protected]
Expand All @@ -77,7 +76,6 @@ $ trevorproxy ssh [email protected] [email protected]
[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
Expand All @@ -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
Expand Down Expand Up @@ -141,4 +175,4 @@ optional arguments:

![trevor](https://user-images.githubusercontent.com/20261699/92336575-27071380-f070-11ea-8dd4-5ba42c7d04b7.jpeg)

`#trevorforget`
`#trevorforget`
34 changes: 33 additions & 1 deletion trevorproxy/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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')]
Expand Down Expand Up @@ -147,4 +179,4 @@ def main():


if __name__ == '__main__':
main()
main()
Binary file added trevorproxy/lib/__pycache__/errors.cpython-310.pyc
Binary file not shown.
Binary file added trevorproxy/lib/__pycache__/logger.cpython-310.pyc
Binary file not shown.
Binary file added trevorproxy/lib/__pycache__/tor.cpython-310.pyc
Binary file not shown.
Binary file added trevorproxy/lib/__pycache__/util.cpython-310.pyc
Binary file not shown.
3 changes: 3 additions & 0 deletions trevorproxy/lib/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ class TrevorProxyError(Exception):
class SSHProxyError(TrevorProxyError):
pass

class TorProxyError(TrevorProxyError):
pass

class InterfaceProxyError(TrevorProxyError):
pass
7 changes: 1 addition & 6 deletions trevorproxy/lib/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
218 changes: 218 additions & 0 deletions trevorproxy/lib/tor.py
Original file line number Diff line number Diff line change
@@ -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()