├── database └── .gitkeep ├── storage └── .gitkeep ├── config └── .gitkeep ├── app ├── default │ ├── config │ │ ├── server-name-indication.txt │ │ ├── proxies.txt │ │ ├── config.json │ │ └── payload.txt │ └── database │ │ ├── authentications.json │ │ ├── servers.json │ │ └── accounts.json ├── data │ └── banners.txt ├── __init__.py ├── ssh_statistic.py ├── server.py ├── http_requests.py ├── app.py ├── default.py ├── ssh_clients.py ├── ssh_create.py └── server_tunnel.py ├── reset-database.py ├── reset.py ├── inject.py ├── create-ssh.py ├── LICENSE ├── app.py ├── export-ssh.py ├── check-serverid.py ├── .gitignore ├── app-check.py ├── sni-scanner.py ├── ssh.py ├── README.md └── check-accounts.py /database/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /storage/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/default/config/server-name-indication.txt: -------------------------------------------------------------------------------- 1 | line-scdn.net 2 | -------------------------------------------------------------------------------- /app/data/banners.txt: -------------------------------------------------------------------------------- 1 | Brainfuck Tunnel [Rabbit Version 1.3.190201] 2 | (c) 2018 Aztec Rabbit. 3 | -------------------------------------------------------------------------------- /reset-database.py: -------------------------------------------------------------------------------- 1 | import app 2 | 3 | 4 | def main(): 5 | app.log('Reseting Database') 6 | app.reset_database() 7 | app.log('Reseting Database Complete \n') 8 | 9 | if __name__ == '__main__': 10 | main() 11 | -------------------------------------------------------------------------------- /reset.py: -------------------------------------------------------------------------------- 1 | import app 2 | 3 | 4 | def main(): 5 | app.log('Reseting to Default Settings') 6 | app.reset_to_default_settings() 7 | app.log('Reseting to Default Settings Complete \n') 8 | 9 | if __name__ == '__main__': 10 | main() 11 | -------------------------------------------------------------------------------- /app/default/config/proxies.txt: -------------------------------------------------------------------------------- 1 | # Axis 2 | # 3 | 4 | 10.8.3.8:80 5 | 10.8.3.8:8080 6 | 7 | 8 | # XL 9 | # 10 | 11 | 202.152.240.50:80 12 | 202.152.240.50:8080 13 | 14 | 15 | # Indosat 16 | # 17 | # 18 | # 10.19.19.19:8080 19 | 20 | 21 | # Smartfren 22 | # 23 | # 24 | # 10.17.27.250:8080 25 | 26 | 27 | # Telkomsel 28 | # 29 | # 30 | # 10.1.89.130:8000 31 | 32 | -------------------------------------------------------------------------------- /inject.py: -------------------------------------------------------------------------------- 1 | import os 2 | import app 3 | import json 4 | 5 | 6 | def real_path(file_name): 7 | return os.path.dirname(os.path.abspath(__file__)) + file_name 8 | 9 | def main(): 10 | try: 11 | config_file = real_path('/config/config.json') 12 | config = json.loads(open(config_file).read()) 13 | inject_host = str(config['inject_host_external']) 14 | inject_port = int(config['inject_port_external']) 15 | except KeyError: app.json_error(config_file); return 16 | 17 | app.server((inject_host, inject_port), external=True, quiet=False).run() 18 | 19 | if __name__ == '__main__': 20 | main() 21 | -------------------------------------------------------------------------------- /create-ssh.py: -------------------------------------------------------------------------------- 1 | import os 2 | import app 3 | import time 4 | import json 5 | 6 | 7 | def real_path(file_name): 8 | return os.path.dirname(os.path.abspath(__file__)) + file_name 9 | 10 | def main(): 11 | try: 12 | ssh_create = app.ssh_create() 13 | ssh_create.accounts = app.generate_accounts(json.loads(open(real_path('/database/accounts.json')).read())['accounts']) 14 | ssh_create.start() 15 | app.log('\nCtrl-C to Exit', time=False, status=None) 16 | time.sleep(60*60*24) 17 | except KeyboardInterrupt: 18 | pass 19 | finally: print() 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /app/default/database/authentications.json: -------------------------------------------------------------------------------- 1 | { 2 | "authentications": [ 3 | { 4 | "username": "aztecrabbit", 5 | "password": "aztecrabbit" 6 | }, 7 | { 8 | "username": "aztec", 9 | "password": "aztec" 10 | }, 11 | { 12 | "username": "rabbit", 13 | "password": "rabbit" 14 | }, 15 | { 16 | "username": "brainfuck", 17 | "password": "brainfuck" 18 | }, 19 | { 20 | "username": "private", 21 | "password": "private" 22 | }, 23 | { 24 | "username": "protected", 25 | "password": "protected" 26 | }, 27 | { 28 | "username": "public", 29 | "password": "public" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | from os import system, name 2 | from sys import version, exit 3 | from subprocess import Popen, PIPE, STDOUT 4 | from .app import * 5 | from .default import * 6 | from .server import * 7 | from .server_tunnel import * 8 | from .ssh_clients import * 9 | from .ssh_create import * 10 | from .ssh_statistic import * 11 | 12 | 13 | Popen('rm *.stackdump', stdout=PIPE, stderr=STDOUT, shell=True) 14 | system('cls' if name == 'nt' else 'clear; clear') 15 | print(colors('[G1]{banners}[CC]'.format(banners=open(real_path('/data/banners.txt')).read()))) 16 | if version[0] == '2': 17 | log('For Python 3 Only!\n', status='[R1]INFO') 18 | exit() 19 | -------------------------------------------------------------------------------- /app/default/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "tunnel_type_external": "0", 3 | "inject_host_external": "127.0.0.1", 4 | "inject_port_external": "3128", 5 | "socks5_port_list_external": [ 6 | "#1080", 7 | "#1081", 8 | "#1082", 9 | "#1083", 10 | "#1084", 11 | "#1085" 12 | ], 13 | 14 | "tunnel_type": "0", 15 | "inject_host": "127.0.0.1", 16 | "inject_port": "8080", 17 | "socks5_port_list": [ 18 | "#1080", 19 | "#1081", 20 | "#1082", 21 | "#1083", 22 | "#1084", 23 | "#1085" 24 | ], 25 | 26 | "proxy_command": "ncat --proxy-type http --proxy {inject_host}:{inject_port} %host %port", 27 | "proxy_command": "nc -X CONNECT -x {inject_host}:{inject_port} %h %p", 28 | "proxy_command": "corkscrew {inject_host} {inject_port} %h %p" 29 | } 30 | -------------------------------------------------------------------------------- /app/default/config/payload.txt: -------------------------------------------------------------------------------- 1 | # Default 2 | # 3 | 4 | --- 5 | 6 | [method] [host_port] [protocol][crlf]Host: fbcdn-static-b-a.akamaihd.net[crlf]Host: [crlf][crlf] 7 | 8 | 9 | # Axis Facebook Unlimited 10 | # 11 | # | fbcdn-static-b-a.akamaihd.net 12 | # 13 | # | 2: HTTP Proxy -> SSH 14 | # 15 | # | 10.8.3.8 : 8080, 80 16 | # | 202.152.240.50 : 8080, 80 17 | # 18 | # 1GB+ > Speed berkurang 19 | # 20 | 21 | --- 22 | 23 | [method] [host_port] [protocol][crlf]Host: axisnet.id[crlf]Host: [crlf][crlf] 24 | 25 | 26 | # Axis 0P0K 27 | # 28 | # | axisnet 29 | # | axisnet.id 30 | # | mmc.xl.net.id 31 | # 32 | # | 2: HTTP Proxy -> SSH 33 | # 34 | # | 10.8.3.8 : 8080, 80 35 | # | 202.152.240.50 : 8080, 80 36 | # 37 | # 20MB+ > DNS diblock 38 | # 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Aztec Rabbit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import app 3 | import json 4 | 5 | 6 | def real_path(file_name): 7 | return os.path.dirname(os.path.abspath(__file__)) + file_name 8 | 9 | def main(): 10 | try: 11 | config_file = real_path('/config/config.json') 12 | config = json.loads(open(config_file).read()) 13 | tunnel_type = str(config['tunnel_type']) 14 | inject_host = str(config['inject_host']) 15 | inject_port = int(config['inject_port']) 16 | socks5_port_list = app.filter_array(config['socks5_port_list']) 17 | except KeyError: app.json_error(config_file); return 18 | 19 | if len(socks5_port_list) == 0: socks5_port_list.append('1080') 20 | 21 | log_connecting = True if len(socks5_port_list) > 1 else False 22 | quiet = True if len(socks5_port_list) > 1 else False 23 | 24 | app.server((inject_host, inject_port), quiet=quiet).start() 25 | 26 | ssh_clients = app.ssh_clients((inject_host, inject_port), socks5_port_list, log_connecting=log_connecting) 27 | ssh_clients.accounts = app.generate_accounts(app.convert_hostnames(real_path('/database/accounts.json'))) 28 | ssh_clients.start() 29 | 30 | if __name__ == '__main__': 31 | main() 32 | -------------------------------------------------------------------------------- /export-ssh.py: -------------------------------------------------------------------------------- 1 | import os 2 | import app 3 | import time 4 | 5 | 6 | def real_path(file_name): 7 | return os.path.dirname(os.path.abspath(__file__)) + file_name 8 | 9 | def main(): 10 | try: 11 | app.log('Exporting SSH Accounts') 12 | 13 | accounts = app.generate_accounts(app.convert_hostnames(real_path('/database/accounts.json'))) 14 | data = open(real_path('/app/data/banners.txt')).read() + '\n' 15 | 16 | for account in accounts: 17 | name = account['name'] 18 | host = account['host'] 19 | hostname = account['hostname'] 20 | username = account['username'] 21 | password = account['password'] 22 | data += 'name : {}\n'.format(name.upper()) + \ 23 | 'host : {}\n'.format(host) + \ 24 | 'hostname : {}\n'.format(hostname) + \ 25 | 'username : {}\n'.format(username) + \ 26 | 'password : {}{}'.format(password, '\n\n') 27 | 28 | open(real_path('/storage/ssh-accounts.txt'), 'w').write(data.strip() + '\n') 29 | 30 | app.log('Exporting SSH Accounts Complete \n') 31 | except KeyboardInterrupt: pass 32 | 33 | if __name__ == '__main__': 34 | main() 35 | -------------------------------------------------------------------------------- /check-serverid.py: -------------------------------------------------------------------------------- 1 | import os 2 | import app 3 | import json 4 | import threading 5 | 6 | 7 | def real_path(file_name): 8 | return os.path.dirname(os.path.abspath(__file__)) + file_name 9 | 10 | def main(): 11 | app.log('Checking serverid', status=None) 12 | data_create_ssh_temp = [ 13 | { 14 | "link": "", 15 | "head": "", 16 | "post": "", 17 | "page": "", 18 | "pattern-class": "", 19 | "pattern-serverid": "", 20 | "pattern-hostname": "", 21 | "replace-username": "", 22 | "replace-password": "" 23 | } 24 | ] 25 | 26 | ssh_create = app.ssh_create(verbose=True) 27 | ssh_create.accounts = app.generate_accounts(json.loads(open(real_path('/database/accounts.json')).read())['accounts']) 28 | # ssh_create.data_create_ssh = data_create_ssh_temp 29 | 30 | threads = [] 31 | 32 | for data in ssh_create.data_create_ssh: 33 | threads.append(threading.Thread(target=ssh_create.update_serverid_thread, args=(data, ))) 34 | 35 | for thread in threads: 36 | thread.daemon = True 37 | thread.start() 38 | 39 | try: 40 | for thread in threads: 41 | thread.join() 42 | except KeyboardInterrupt: pass 43 | 44 | app.log('Checking serverid complete \n', status=None) 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /app/ssh_statistic.py: -------------------------------------------------------------------------------- 1 | import json 2 | import threading 3 | from .app import * 4 | 5 | 6 | lock = threading.RLock() 7 | 8 | class ssh_statistic(threading.Thread): 9 | def __init__(self, command = ''): 10 | super(ssh_statistic, self).__init__() 11 | 12 | self.command = command 13 | self.start() 14 | 15 | def run(self): 16 | file = real_path('/data/statistic.txt') 17 | while True: 18 | try: 19 | with lock: 20 | data = open(file, 'r').read() 21 | download, upload = data.split(' ') if len(data.split(' ')) == 2 else (0, 0) 22 | 23 | if not self.command: 24 | return 25 | elif self.command == 'download': 26 | download = int(download) + 1 27 | elif self.command == 'upload': 28 | upload = int(upload) + 1 29 | elif self.command == 'clear': 30 | open(file, 'w').write('0 0') 31 | return 32 | 33 | open(file, 'w').write(str(download) + ' ' + str(upload)) 34 | log_replace('Down: {download} Up: {upload}'.format(download=download, upload=upload)) 35 | except FileNotFoundError: 36 | open(file, 'w') 37 | continue 38 | except RuntimeError: 39 | pass 40 | except Exception as exception: 41 | log_exception(exception) 42 | break 43 | -------------------------------------------------------------------------------- /app/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import threading 3 | from .app import * 4 | from .server_tunnel import * 5 | 6 | 7 | class server(threading.Thread): 8 | def __init__(self, inject_host_port, force_tunnel_type=None, external=False, quiet=False): 9 | super(server, self).__init__() 10 | 11 | self.inject_host, self.inject_port = self.inject_host_port = inject_host_port 12 | self.socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 13 | self.socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 14 | self.force_tunnel_type = force_tunnel_type 15 | self.external = external 16 | self.quiet = quiet 17 | 18 | self.daemon = True 19 | 20 | def log(self, value, status='INFO', status_color='[G1]'): 21 | log(value, status=status, status_color=status_color) 22 | 23 | def run(self): 24 | try: 25 | self.socket_server.bind(self.inject_host_port) 26 | self.socket_server.listen(True) 27 | if self.quiet != 'full': self.log('Inject running on {inject_host} port {inject_port}'.format(inject_host=self.inject_host, inject_port=self.inject_port)) 28 | while True: 29 | try: 30 | server_tunnel(self.socket_server.accept(), self.force_tunnel_type, self.external, self.quiet).start() 31 | except KeyboardInterrupt: pass 32 | except OSError: 33 | self.log('Inject not running on {inject_host} port {inject_port} because port used by another programs'.format(inject_host=self.inject_host, inject_port=self.inject_port), status_color='[R1]') 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User 2 | *.stackdump 3 | 4 | app/data/statistic.txt 5 | config/* 6 | database/* 7 | storage/* 8 | 9 | !.gitkeep 10 | 11 | # Byte-compiled / optimized / DLL files 12 | __pycache__/ 13 | *.py[cod] 14 | *$py.class 15 | 16 | # C extensions 17 | *.so 18 | 19 | # Distribution / packaging 20 | .Python 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | wheels/ 33 | *.egg-info/ 34 | .installed.cfg 35 | *.egg 36 | MANIFEST 37 | 38 | # PyInstaller 39 | # Usually these files are written by a python script from a template 40 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 41 | *.manifest 42 | *.spec 43 | 44 | # Installer logs 45 | pip-log.txt 46 | pip-delete-this-directory.txt 47 | 48 | # Unit test / coverage reports 49 | htmlcov/ 50 | .tox/ 51 | .coverage 52 | .coverage.* 53 | .cache 54 | nosetests.xml 55 | coverage.xml 56 | *.cover 57 | .hypothesis/ 58 | .pytest_cache/ 59 | 60 | # Translations 61 | *.mo 62 | *.pot 63 | 64 | # Django stuff: 65 | *.log 66 | local_settings.py 67 | db.sqlite3 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # celery beat schedule file 89 | celerybeat-schedule 90 | 91 | # SageMath parsed files 92 | *.sage.py 93 | 94 | # Environments 95 | .env 96 | .venv 97 | env/ 98 | venv/ 99 | ENV/ 100 | env.bak/ 101 | venv.bak/ 102 | 103 | # Spyder project settings 104 | .spyderproject 105 | .spyproject 106 | 107 | # Rope project settings 108 | .ropeproject 109 | 110 | # mkdocs documentation 111 | /site 112 | 113 | # mypy 114 | .mypy_cache/ 115 | -------------------------------------------------------------------------------- /app-check.py: -------------------------------------------------------------------------------- 1 | import os 2 | import app 3 | import time 4 | import json 5 | import threading 6 | 7 | 8 | def real_path(file_name): 9 | return os.path.dirname(os.path.abspath(__file__)) + file_name 10 | 11 | def main(): 12 | try: 13 | config_file = real_path('/config/config.json') 14 | config = json.loads(open(config_file).read()) 15 | inject_host = str('127.0.0.1') 16 | inject_port = int('9080') 17 | socks5_port = str('2080') 18 | socks5_port_list = [socks5_port] 19 | except KeyError: app.json_error(config_file); return 20 | 21 | app.server((inject_host, inject_port)).start() 22 | 23 | ssh_clients = app.ssh_clients((inject_host, inject_port), http_requests_enable=False, log_connecting=False, dynamic_port_forwarding=False) 24 | ssh_clients.accounts = app.generate_accounts(app.convert_hostnames(real_path('/database/accounts.json'))) 25 | 26 | app.log('Type debug for debugging log') 27 | app.log('Type exit to exit') 28 | 29 | while True: 30 | try: 31 | ssh_clients.debug = False 32 | exit = False 33 | 34 | command = app.str_input('\n:: ', newline=True) 35 | if command == 'exit': 36 | exit = True 37 | break 38 | if command == 'debug': 39 | ssh_clients.debug = True 40 | 41 | app.ssh_statistic('clear') 42 | threading.Thread(target=ssh_clients.ssh_client, args=(ssh_clients.unique, socks5_port, )).start() 43 | ssh_clients._connected.add(socks5_port) 44 | ssh_clients.unique += 1 45 | ssh_clients.all_disconnected_listener() 46 | except KeyboardInterrupt: 47 | pass 48 | finally: 49 | if not exit and ssh_clients.all_disconnected() == False: 50 | ssh_clients.all_disconnected_listener() 51 | 52 | if __name__ == '__main__': 53 | main() 54 | -------------------------------------------------------------------------------- /sni-scanner.py: -------------------------------------------------------------------------------- 1 | import re 2 | import app 3 | import ssl 4 | import socket 5 | 6 | 7 | def log(value, status='INFO', status_color='[G1]'): 8 | app.log(value, status=status, status_color=status_color) 9 | 10 | def main(): 11 | log('Type hostname to scan that hostname (example: goo.gl fb.me etc)') 12 | log('Type exit to exit\n') 13 | 14 | host = str('74.125.24.100') 15 | port = int('443') 16 | 17 | while True: 18 | server_name_indications = app.str_input(':: ', newline=True) 19 | server_name_indications = app.filter_array(re.sub(r'\s+', ' ', server_name_indications).split(' ')) 20 | 21 | if len(server_name_indications) and server_name_indications[0] == 'exit': 22 | break 23 | 24 | for server_name_indication in server_name_indications: 25 | try: 26 | if server_name_indication == 'exit': print(':: exit'); return 27 | socket_tunnel = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 28 | socket_tunnel.settimeout(3) 29 | log('Connecting to {host} port {port}'.format(host=host, port=port)) 30 | socket_tunnel.connect((host, port)) 31 | log('Server name indication: {server_hostname}'.format(server_hostname=server_name_indication)) 32 | socket_tunnel = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2).wrap_socket(socket_tunnel, server_hostname=server_name_indication, do_handshake_on_connect=True) 33 | certificate = ssl.DER_cert_to_PEM_cert(socket_tunnel.getpeercert(True)).splitlines() 34 | certificate = '\n'.join(certificate[:13] + certificate[-13:]) 35 | log('Certificate: \n\n{}\n'.format(certificate)) 36 | log('Connection established') 37 | log('[Y1]Connected', status_color='[Y1]') 38 | except socket.timeout: 39 | log('[R1]Connection timeout', status_color='[R1]') 40 | except socket.error: 41 | log('[R1]Connection closed', status_color='[R1]') 42 | finally: 43 | socket_tunnel.close(); print() 44 | 45 | if __name__ == '__main__': 46 | main() 47 | -------------------------------------------------------------------------------- /app/http_requests.py: -------------------------------------------------------------------------------- 1 | import time 2 | import requests 3 | import threading 4 | from .app import * 5 | 6 | 7 | class http_requests(threading.Thread): 8 | def __init__(self, socks5_ports, enable): 9 | super(http_requests, self).__init__() 10 | 11 | self.socks5_ports = socks5_ports 12 | self.enable = enable 13 | self.daemon = True 14 | self._stop = True 15 | 16 | def log(self, value, status='INFO', status_color='[G1]'): 17 | if not self.enable: return 18 | if not self._stop: log(value, status=status, status_color=status_color) 19 | 20 | def stop(self): 21 | if not self.enable: return 22 | self.log('HTTP Requests stopped') 23 | self._stop = True 24 | 25 | def run(self): 26 | if not self.enable: return 27 | self._stop = False 28 | self.log('HTTP Requests started') 29 | 30 | threads = [] 31 | 32 | for socks5_port in self.socks5_ports: 33 | threads.append(threading.Thread(target=self.task, args=(socks5_port, ))) 34 | 35 | for thread in threads: 36 | thread.daemon = True 37 | thread.start() 38 | 39 | for thread in threads: 40 | thread.join() 41 | 42 | threads = [] 43 | 44 | def task(self, socks5_port): 45 | while not self._stop: 46 | try: 47 | response = requests.head( 48 | url='http://141.0.11.241', 49 | proxies={ 50 | 'http': 'socks5h://127.0.0.1:{}'.format(socks5_port), 51 | 'https': 'socks5h://127.0.0.1:{}'.format(socks5_port) 52 | }, 53 | timeout=10, 54 | allow_redirects=False 55 | ) 56 | self.log('Response: {}'.format(response.status_code), status=socks5_port) 57 | except requests.exceptions.Timeout: 58 | self.log('[R1]Response: ERR', status=socks5_port, status_color='[R1]') 59 | except requests.exceptions.ConnectionError: 60 | self.log('[R2]Response: ERR', status=socks5_port, status_color='[R2]') 61 | except Exception as exception: 62 | pass 63 | finally: time.sleep(30.000) 64 | -------------------------------------------------------------------------------- /ssh.py: -------------------------------------------------------------------------------- 1 | import os 2 | import app 3 | import time 4 | import json 5 | import threading 6 | 7 | 8 | def real_path(file_name): 9 | return os.path.dirname(os.path.abspath(__file__)) + file_name 10 | 11 | def main(): 12 | try: 13 | config_file = real_path('/config/config.json') 14 | config = json.loads(open(config_file).read()) 15 | inject_host = str(config['inject_host_external']) 16 | inject_port = int(config['inject_port_external']) 17 | except KeyError: app.json_error(config_file); return 18 | 19 | app.log('Inject set to {} port {}'.format(inject_host, inject_port)) 20 | 21 | ssh_clients = app.ssh_clients((inject_host, inject_port), external=True) 22 | ssh_clients.accounts = app.generate_accounts(app.convert_hostnames(real_path('/database/accounts.json'))) 23 | 24 | app.log('Type debug for debugging log') 25 | app.log('Type exit to exit') 26 | 27 | while True: 28 | try: 29 | ssh_clients.debug = False 30 | exit = False 31 | 32 | command = app.str_input('\n:: ', newline=True) 33 | if command == 'exit': 34 | exit = True 35 | break 36 | if command == 'debug': 37 | ssh_clients.debug = True 38 | 39 | try: 40 | config_file = real_path('/config/config.json') 41 | config = json.loads(open(config_file).read()) 42 | ssh_clients.socks5_port_list = app.filter_array(config['socks5_port_list_external']) 43 | except KeyError: app.json_error(config_file); continue 44 | 45 | if len(ssh_clients.socks5_port_list) == 0: ssh_clients.socks5_port_list.append('1080') 46 | 47 | app.ssh_statistic('clear') 48 | for socks5_port in ssh_clients.socks5_port_list: 49 | thread = threading.Thread(target=ssh_clients.ssh_client, args=(ssh_clients.unique, socks5_port, )) 50 | thread.daemon = True 51 | thread.start() 52 | time.sleep(60*60*24*30*12) 53 | except KeyboardInterrupt: 54 | pass 55 | finally: 56 | if not exit: 57 | ssh_clients.unique += 1 58 | ssh_clients.all_disconnected_listener() 59 | 60 | if __name__ == '__main__': 61 | main() 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Brainfuck Tunnel 2 | 3 | SSH Tunneling for Dynamic Port Forwarding (Free charge Internet Access). 4 | 5 | 6 | Requirements 7 | ------------ 8 | 9 | ### Packages 10 | 11 | nc (openbsd-version) (be aware if you are installing wrong nc) 12 | git 13 | openssh 14 | sshpass 15 | python3 16 | corkscrew 17 | python3-pip 18 | 19 | 20 | ### Using Termux on Android? 21 | 22 | $ pkg install pip git python openssh sshpass corkscrew 23 | 24 | 25 | ### Using Cygwin on Windows? 26 | 27 | **1. apt-cyg** 28 | 29 | $ wget rawgit.com/transcode-open/apt-cyg/master/apt-cyg -P /bin/; chmod +x /bin/apt-cyg 30 | 31 | 32 | **2. required packages** 33 | 34 | $ apt-cyg install nc tar curl make openssh python3 autoconf gcc-core corkscrew python3-pip 35 | 36 | 37 | **3. sshpass** 38 | 39 | $ curl -LO http://downloads.sourceforge.net/sshpass/sshpass-1.06.tar.gz 40 | $ md5sum sshpass-1.06.tar.gz 41 | $ tar xvf sshpass-1.06.tar.gz 42 | $ cd sshpass-1.06 43 | $ ./configure 44 | $ make 45 | $ make install 46 | 47 | 48 | Python 3 Modules 49 | ---------------- 50 | 51 | $ python3 -m pip install --upgrade pip 52 | $ python3 -m pip install requests beautifulsoup4 53 | $ python3 -m pip install -U requests[socks] 54 | 55 | 56 | Configurations 57 | -------------- 58 | 59 | Run `reset.py` first to export all default settings. 60 | 61 | **1. Tunel Type** 62 | 63 | 0: Direct -> SSH 64 | 1: Direct -> SSH (SSL/TLS) 65 | 2: HTTP Proxy -> SSH 66 | 67 | 68 | **2. SOCKS5 Port** 69 | 70 | "socks5_port_list_external": [ 71 | "#1080", 72 | "#1081", 73 | "#1082", 74 | "#1083", 75 | "#1084", 76 | "#1085" 77 | ] 78 | 79 | "socks5_port_list": [ 80 | "1080", 81 | "1081", 82 | "1082", 83 | "1083", 84 | "1084", 85 | "1085" 86 | ] 87 | 88 | If `socks5_port_list_external` like that (no port or all port commented), You will execute 1 SSH Client (default port: `1080`). 89 | If `socks5_port_list` like that. You will execute 5 SSH Clients. 90 | Add ports to execute more SSH Clients. 91 | 92 | 93 | **3. Proxy Command** 94 | 95 | Please googling for this topic or see `config/config.json` (run `reset.py` first). 96 | 97 | proxy_host: {inject_host} 98 | proxy_port: {inject_port} 99 | 100 | 101 | Usage 102 | ----- 103 | 104 | $ git clone https://github.com/AztecRabbit/Brainfuck-Tunnel brainfuck-tunnel 105 | $ cd brainfuck-tunnel; ls -l 106 | $ python3 (file-name) 107 | 108 | 109 | Update 110 | ------ 111 | 112 | $ cd brainfuck-tunnel 113 | $ git pull 114 | $ python3 reset-database.py 115 | 116 | 117 | Note 118 | ---- 119 | 120 | Ctrl-C to Change Server 121 | Ctrl-Pause to Exit 122 | 123 | 124 | Contact 125 | ------- 126 | 127 | Facebook Group : [Internet Freedom] 128 | 129 | 130 | [Internet Freedom]: https://www.facebook.com/groups/171888786834544/ 131 | -------------------------------------------------------------------------------- /app/default/database/servers.json: -------------------------------------------------------------------------------- 1 | { 2 | "servers": [ 3 | { 4 | "link": "https://www.monthlyssh.com", 5 | "head": "/create-ssl/{serverid}/", 6 | "post": "/create-ssl.php", 7 | "page": "/ssl", 8 | "pattern-class": "bs pricing-n2", 9 | "pattern-serverid": "ssl/{serverid}/ssl", 10 | "pattern-hostname": "
Host IP {hostname}
", 66 | "replace-username": "jetssh.com-", 67 | "replace-password": "" 68 | }, 69 | { 70 | "link": "https://www.flyssh.com", 71 | "head": "/create-ssl/{serverid}/", 72 | "post": "/create-ssl.php", 73 | "page": "/ssl-server", 74 | "pattern-class": "col-lg-3 col-md-3 col-xs-6", 75 | "pattern-serverid": "ssl/{serverid}/server", 76 | "pattern-hostname": "Host : {hostname} ", 77 | "replace-username": "_flyssh.com", 78 | "replace-password": "" 79 | } 80 | ] 81 | } 82 | -------------------------------------------------------------------------------- /app/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import socket 4 | import datetime 5 | import threading 6 | 7 | 8 | lock = threading.RLock() 9 | 10 | class safe_dict(dict): 11 | def __missing__(self, key): 12 | return '{' + key + '}' 13 | 14 | def real_path(file_name): 15 | return os.path.dirname(os.path.abspath(__file__)) + file_name 16 | 17 | def colors(value): 18 | patterns = { 19 | 'CC' : '\033[0m', 'BB' : '\033[1m', 20 | 'D1' : '\033[30;1m', 'D2' : '\033[30;2m', 21 | 'R1' : '\033[31;1m', 'R2' : '\033[31;2m', 22 | 'G1' : '\033[32;1m', 'G2' : '\033[32;2m', 23 | 'Y1' : '\033[33;1m', 'Y2' : '\033[33;2m', 24 | 'B1' : '\033[34;1m', 'B2' : '\033[34;2m', 25 | 'P1' : '\033[35;1m', 'P2' : '\033[35;2m', 26 | 'C1' : '\033[36;1m', 'C2' : '\033[36;2m', 27 | 'W1' : '\033[37;1m', 'W2' : '\033[37;2m' 28 | } 29 | for code in patterns: 30 | value = value.replace('[{}]'.format(code), patterns[code]) 31 | return value 32 | 33 | def filter_array(array): 34 | for i in range(len(array)): 35 | if array[i].startswith('#'): array[i] = '' 36 | 37 | array = [x for x in array if x] 38 | 39 | return array 40 | 41 | def xstrip(value, string=None): 42 | value = value.strip(string).encode() \ 43 | .replace(b'\x1b[A', b'') \ 44 | .replace(b'\x1b[B', b'') \ 45 | .replace(b'\x1b[C', b'') \ 46 | .replace(b'\x1b[D', b'') \ 47 | .decode().strip() 48 | return value 49 | 50 | def app_format(value, align, width, chars = ''): 51 | value = ('{:' + str(chars) + str(align) + str(width) + '}').format(value) 52 | return value 53 | 54 | def json_shuffle(data): 55 | keys = list(data.keys()) 56 | random.shuffle(keys) 57 | data = [(key, data[key]) for key in keys] 58 | return data 59 | 60 | def str_input(value, newline=False): 61 | with lock: 62 | string = xstrip(str(input(value))) 63 | if newline: print() 64 | 65 | return string 66 | 67 | def log_format(value, time=True, status=None, status_color=''): 68 | value_status = '' 69 | value_time = '' 70 | 71 | if status is not None: 72 | value_status = '[P1]:: [G1]{}{:>4} [P1]:: '.format(status_color, status) 73 | 74 | if time == True: 75 | value_time = '{}[{}] '.format(status_color, datetime.datetime.now().strftime('%H:%M:%S')) 76 | 77 | return colors( 78 | '{value_time}{value_status}{status_color}{value}{end}'.format( 79 | value_time=value_time, 80 | value_status=value_status, 81 | status_color=status_color, 82 | value=value, 83 | end='[CC]\r' 84 | ) 85 | ) 86 | 87 | def log(value, time=True, status='INFO', status_color='[G1]'): 88 | with lock: 89 | print(log_format(value, time=time, status=status, status_color=status_color)) 90 | 91 | def log_replace(value, time=False, status=None, status_color='[Y1]'): 92 | with lock: 93 | sys.stdout.write(log_format(value, time=time, status=status, status_color=status_color)) 94 | sys.stdout.flush() 95 | 96 | def log_exception(value, time=True, status='INFO', status_color='[R1]'): 97 | log('Exception: {}'.format(value), time=time, status=status, status_color='[R1]') 98 | -------------------------------------------------------------------------------- /check-accounts.py: -------------------------------------------------------------------------------- 1 | import os 2 | import app 3 | import json 4 | import queue 5 | import random 6 | import threading 7 | import subprocess 8 | 9 | 10 | def real_path(file_name): 11 | return os.path.dirname(os.path.abspath(__file__)) + file_name 12 | 13 | def log(value, status_color='[G1]'): 14 | app.log(value, status=None, status_color=status_color) 15 | 16 | class thread(threading.Thread): 17 | def __init__(self, queue_accounts, proxy_command): 18 | super(thread, self).__init__() 19 | 20 | self.queue_accounts = queue_accounts 21 | self.proxy_command = proxy_command 22 | self.daemon = True 23 | 24 | def log(self, value, status_color='[G1]'): 25 | log(value, status_color=status_color) 26 | 27 | def run(self): 28 | while True: 29 | account = self.queue_accounts.get() 30 | results_1 = self.connect(account, '80') 31 | results_2 = self.connect(account, '443') 32 | self.log('{hostname:.<56} {results_1} {results_2}'.format( 33 | hostname='[Y1]{} '.format(account['hostname'].replace('.', '[G1].')), 34 | results_1=results_1, 35 | results_2=results_2 36 | )) 37 | self.queue_accounts.task_done() 38 | 39 | if self.queue_accounts.qsize() == 0: break 40 | 41 | def connect(self, account, port): 42 | hostname = account['hostname'] 43 | username = account['username'] 44 | password = account['password'] 45 | proxy_command = '' 46 | if port == '443': 47 | proxy_command = '-o ProxyCommand="{}"'.format(self.proxy_command.format( 48 | inject_host='127.0.0.1', 49 | inject_port='3313' 50 | )) 51 | 52 | response = subprocess.Popen( 53 | ('sshpass -p "{password}" ssh -v {hostname} -p {port} -l "{username}" ' + '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null {proxy_command}').format( 54 | hostname=hostname, 55 | port=port, 56 | username=username, 57 | password=password, 58 | proxy_command=proxy_command 59 | ), 60 | shell=True, 61 | stdout=subprocess.PIPE, 62 | stderr=subprocess.STDOUT 63 | ) 64 | 65 | for line in response.stdout: 66 | line = line.decode().lstrip(r'(debug1|Warning):').strip() + '\r' 67 | 68 | # self.log(line, status_color='[G2]') 69 | 70 | if 'pledge: ' in line: 71 | return '[G1]200' 72 | 73 | elif 'Permission denied' in line: 74 | return '[R1]200' 75 | 76 | elif 'Connection timed out' in line: 77 | return '[Y2]ERR' 78 | 79 | elif 'Connection closed' in line or 'Connection refused' in line or 'Connection reset' in line or 'connection abort' in line: 80 | return '[R2]ERR' 81 | 82 | return ' ' 83 | 84 | def main(): 85 | try: 86 | config_file = real_path('/config/config.json') 87 | config = json.loads(open(config_file).read()) 88 | proxy_command = config['proxy_command'] 89 | if str(proxy_command.strip()) == '': raise KeyError 90 | except KeyError: app.json_error(config_file); return False 91 | 92 | data_accounts = json.loads(open(real_path('/database/accounts.json')).read())['accounts'] 93 | data_deleted_accounts = {} 94 | 95 | for name, value in data_accounts.items(): 96 | data_deleted_accounts[name] = [] 97 | for i in range(len(value)): 98 | account = data_accounts[name][i] 99 | if app.check_hostname(account['hostname']) == False: 100 | data_accounts[name][i]['hostname'].replace('#', '') 101 | data_deleted_accounts[name].append(data_accounts[name][i]) 102 | data_accounts[name][i] = '' 103 | 104 | json_authentications = json.loads(open(real_path('/database/authentications.json')).read())['authentications'] 105 | data_authentications = [] 106 | 107 | for i in range(len(json_authentications)): 108 | data_authentications.append([{'username': json_authentications[i]['username'], 'password': json_authentications[i]['username']}]) 109 | 110 | accounts = app.generate_accounts(data_accounts, data_authentications=random.choice(data_authentications)) 111 | queue_accounts = queue.Queue() 112 | threads = 10 113 | 114 | app.server((str('127.0.0.1'), int('3313')), force_tunnel_type='1', quiet='full').start() 115 | 116 | for account in accounts: 117 | queue_accounts.put(account) 118 | 119 | for i in range(threads): 120 | thread(queue_accounts, proxy_command).start() 121 | 122 | queue_accounts.join() 123 | 124 | print() 125 | 126 | deleted_accounts = app.generate_accounts(data_deleted_accounts, data_authentications=[{'username': 'rabbit', 'password': 'rabbit'}]) 127 | queue_deleted_accounts = queue.Queue() 128 | 129 | for deleted_account in deleted_accounts: 130 | queue_deleted_accounts.put(deleted_account) 131 | 132 | for i in range(threads): 133 | thread(queue_deleted_accounts, proxy_command).start() 134 | 135 | queue_deleted_accounts.join() 136 | 137 | 138 | if __name__ == '__main__': 139 | main() -------------------------------------------------------------------------------- /app/default.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import time 4 | import json 5 | import socket 6 | import shutil 7 | from .app import * 8 | 9 | 10 | def check_hostname(hostname): 11 | return True if re.match(r'([a-zA-Z0-9]+(\.[a-zA-Z0-9]+)+)', hostname) else False 12 | 13 | def convert_hostnames(file_path): 14 | with open(file_path, 'r+') as json_file: 15 | data = json.loads(json_file.read()) 16 | data_accounts = data['accounts'] 17 | length, loop, timeout = 0, 0, 0 18 | 19 | for name, value in data_accounts.items(): 20 | for i in range(len(value)): 21 | if check_hostname(data_accounts[name][i]['hostname']) == True: 22 | length += 1 23 | 24 | for name, value in data_accounts.items(): 25 | for i in range(len(value)): 26 | account = data_accounts[name][i] 27 | if check_hostname(account['hostname']) == False: 28 | continue 29 | try: 30 | if timeout == 3: break 31 | log_replace('[{}/{}] Converting hostnames'.format(app_format(loop+1, align='>', width=len(str(length)), chars='0'), length), time=True, status='INFO', status_color='[Y1]') 32 | host = '' 33 | host = socket.gethostbyname(account['hostname']) 34 | if not host: 35 | raise socket.gaierror 36 | elif host != account['host']: 37 | log('{:.<19} [Y1]{:.<23} {}'.format((account['host'] if account['host'] else '(empty)')+' ', host+' [G1]', account['hostname']), status='INFO', status_color='[G1]') 38 | data_accounts[name][i]['host'] = host 39 | timeout = 0 40 | except socket.gaierror: 41 | log('[{}/{}] Converting hostnames error ({})'.format(app_format(timeout+1, align='>', width=len(str(length)), chars='0'), app_format('3', align='>', width=len(str(length)), chars='0'), account['hostname']), status='INFO', status_color='[R1]') 42 | timeout = timeout + 1 43 | finally: 44 | loop = loop + 1 45 | 46 | json_file.seek(0) 47 | json.dump(data, json_file, indent=2) 48 | json_file.truncate() 49 | 50 | return data_accounts 51 | 52 | def generate_accounts(data_accounts, data_authentications=None): 53 | data_authentications = json.loads(open(real_path('/../database/authentications.json')).read())['authentications'] if data_authentications is None else data_authentications 54 | 55 | accounts = [] 56 | 57 | for i in range(len(data_authentications)): 58 | for name in data_accounts: 59 | for x in range(len(data_accounts[name])): 60 | account = data_accounts[name][x] 61 | if not account: continue 62 | account['hostname'] = account['hostname'].lstrip('#') 63 | if check_hostname(account['hostname']) == False: 64 | continue 65 | accounts.append({ 66 | 'name': name, 67 | 'host': account['host'], 68 | 'hostname': account['hostname'], 69 | 'username': account['username'].replace('{username}', data_authentications[i]['username']), 70 | 'password': account['password'].replace('{password}', data_authentications[i]['password']) 71 | }) 72 | 73 | accounts = [dict(tuples) for tuples in {tuple(dictionaries.items()) for dictionaries in accounts}] 74 | 75 | return accounts 76 | 77 | def get_file_names(value = 'all'): 78 | file_names = [] 79 | 80 | if value == 'all': 81 | file_names = [ 82 | 'config/config.json', 83 | 'config/proxies.txt', 84 | 'config/payload.txt', 85 | 'config/server-name-indication.txt', 86 | 'database/accounts.json', 87 | 'database/authentications.json', 88 | 'database/servers.json' 89 | ] 90 | elif value == 'database': 91 | file_names = [ 92 | 'database/accounts.json', 93 | 'database/authentications.json', 94 | 'database/servers.json' 95 | ] 96 | 97 | return file_names 98 | 99 | def reset_to_default_settings(): 100 | for file_name in get_file_names('all'): 101 | try: 102 | os.remove(real_path('/../' + file_name)) 103 | except FileNotFoundError: pass 104 | 105 | default_settings() 106 | 107 | def reset_database(): 108 | for file_name in get_file_names('database'): 109 | try: 110 | os.remove(real_path('/../' + file_name)) 111 | except FileNotFoundError: pass 112 | 113 | default_settings() 114 | 115 | def default_settings(): 116 | for file_name in get_file_names('all'): 117 | try: 118 | open(real_path('/../' + file_name)) 119 | except FileNotFoundError: 120 | shutil.copyfile(real_path('/default/' + file_name), real_path('/../' + file_name)) 121 | 122 | def json_error(file): 123 | value = 'Exception: {} \n\n'.format(' ' * 24) + \ 124 | ' File {} Error! \n'.format(file).replace('/app/../', '/') + \ 125 | ' Run reset.py first or fixing by your-self. \n' + \ 126 | ' Good-luck! \n' 127 | 128 | log(value, status_color='[R1]') 129 | 130 | def autoload(): 131 | default_settings() 132 | 133 | autoload() 134 | -------------------------------------------------------------------------------- /app/ssh_clients.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import time 5 | import socket 6 | import random 7 | import requests 8 | import threading 9 | import subprocess 10 | from .app import * 11 | from .default import * 12 | from .ssh_create import * 13 | from .ssh_statistic import * 14 | from .http_requests import * 15 | 16 | 17 | class ssh_clients(object): 18 | 19 | _connected = set() 20 | 21 | def __init__(self, inject_host_port, socks5_port_list=[], external=False, http_requests_enable=True, log_connecting=True, dynamic_port_forwarding=True): 22 | super(ssh_clients, self).__init__() 23 | 24 | self.inject_host, self.inject_port = self.inject_host_port = inject_host_port 25 | self.dynamic_port_forwarding = dynamic_port_forwarding 26 | self.http_requests_enable = http_requests_enable 27 | self.socks5_port_list = socks5_port_list 28 | self.log_connecting = log_connecting 29 | self.external = external 30 | 31 | self.http_requests = http_requests(self.socks5_port_list, self.http_requests_enable) 32 | self.proxy_command = '' 33 | self.accounts = [] 34 | self.unique = 0 35 | self.daemon = True 36 | self.debug = False 37 | 38 | def log(self, value, status='INFO', status_color='[G1]'): 39 | log(value, status=status, status_color=status_color) 40 | 41 | def log_debug(self, value, status='DBUG', status_color='[G2]'): 42 | if self.debug: log(value, status=status, status_color=status_color) 43 | 44 | def connected(self, socks5_port): 45 | self._connected.add(socks5_port) 46 | if len(self._connected) >= len(self.socks5_port_list): 47 | self.log('Connected', status='all', status_color='[Y1]') 48 | self.http_requests = http_requests(self.socks5_port_list, self.http_requests_enable) 49 | self.http_requests.start() 50 | 51 | def disconnected(self, socks5_port): 52 | with lock: self.http_requests.stop() 53 | if socks5_port in self._connected: 54 | self._connected.remove(socks5_port) 55 | if len(self._connected) == 0: 56 | ssh_statistic('clear') 57 | 58 | def all_disconnected(self): 59 | return True if len(self._connected) == 0 else False 60 | 61 | def all_disconnected_listener(self): 62 | while True: 63 | if self.all_disconnected(): 64 | self.log('Disconnected', status='all', status_color='[R1]') 65 | break 66 | 67 | def get_config(self): 68 | try: 69 | self.config_file = real_path('/../config/config.json') 70 | self.config = json.loads(open(self.config_file).read()) 71 | self.tunnel_type = self.config['tunnel_type'] if not self.external else self.config['tunnel_type_external'] 72 | self.proxy_command = self.config['proxy_command'] 73 | if not self.tunnel_type or int(self.tunnel_type) >= 3: raise KeyError 74 | if str(self.proxy_command.strip()) == '': raise KeyError 75 | except KeyError: json_error(self.config_file); return False 76 | 77 | def start(self): 78 | if self.get_config() is not None: return 79 | 80 | if len(self.socks5_port_list) == 0: 81 | self.socks5_port_list.append('1080') 82 | 83 | while True: 84 | try: 85 | ssh_statistic('clear') 86 | for socks5_port in self.socks5_port_list: 87 | thread = threading.Thread(target=self.ssh_client, args=(self.unique, socks5_port, )) 88 | thread.daemon = True 89 | thread.start() 90 | time.sleep(60*60*24*30*12) 91 | except KeyboardInterrupt: 92 | pass 93 | finally: 94 | self.unique += 1 95 | self.all_disconnected_listener() 96 | 97 | def ssh_client(self, unique, socks5_port): 98 | while self.unique == unique: 99 | 100 | if self.get_config() is not None: 101 | self.disconnected(socks5_port) 102 | break 103 | 104 | dynamic_port_forwarding = '-CND {}'.format(socks5_port) if self.dynamic_port_forwarding else '' 105 | proxy_command = self.proxy_command 106 | 107 | account = random.choice(self.accounts) 108 | host = account['host'] 109 | port = '443' if self.tunnel_type == '1' else '80' 110 | hostname = account['hostname'] 111 | username = account['username'] 112 | password = account['password'] 113 | 114 | if self.log_connecting == True: 115 | self.log('[G1]Connecting to {hostname} port {port}{end}'.format(hostname=hostname, port=port, end=' '*4), status=socks5_port) 116 | 117 | response = subprocess.Popen( 118 | ( 119 | 'sshpass -p "{password}" ssh -v {dynamic_port_forwarding} {host} -p {port} -l "{username}" ' + '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand="{proxy_command}"'.format( 120 | proxy_command=proxy_command 121 | ) 122 | ).format( 123 | host=host, 124 | port=port, 125 | username=username, 126 | password=password, 127 | inject_host=self.inject_host, 128 | inject_port=self.inject_port, 129 | dynamic_port_forwarding=dynamic_port_forwarding 130 | ), 131 | shell=True, 132 | stdout=subprocess.PIPE, 133 | stderr=subprocess.STDOUT 134 | ) 135 | 136 | for line in response.stdout: 137 | line = line.decode().lstrip(r'(debug1|Warning):').strip() + '\r' 138 | 139 | self.log_debug(line) 140 | 141 | if 'pledge: proc' in line: 142 | self.connected(socks5_port) 143 | 144 | elif 'Permission denied' in line: 145 | self.log('Access Denied', status=socks5_port, status_color='[R1]') 146 | 147 | elif 'Connection closed' in line: 148 | self.log_debug('Connection closed', status=socks5_port, status_color='[R1]') 149 | 150 | elif 'Could not request local forwarding' in line: 151 | self.log('Port used by another programs', status=socks5_port, status_color='[R1]') 152 | unique -= 1 153 | break 154 | 155 | self.disconnected(socks5_port) 156 | -------------------------------------------------------------------------------- /app/ssh_create.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import time 4 | import json 5 | import requests 6 | import threading 7 | from .app import * 8 | from bs4 import BeautifulSoup 9 | 10 | try: 11 | from queue import Queue 12 | except ImportError: 13 | from Queue import Queue 14 | 15 | 16 | class ssh_create(object): 17 | def __init__(self, verbose=False): 18 | super(ssh_create, self).__init__() 19 | 20 | self.hostname_empty_serverid = set() 21 | self.hostname_serverid = [] 22 | self.data_create_ssh = json.loads(open(real_path('/../database/servers.json')).read())['servers'] 23 | self.queue_accounts = Queue() 24 | self.queue_threads = 20 25 | self.accounts = [] 26 | self.verbose = verbose 27 | 28 | self._created = 0 29 | self._total = 0 30 | 31 | def log(self, value, status=None, status_color='[G1]'): 32 | log(value, status=status, status_color=status_color) 33 | 34 | def log_replace(self, value, time=False): 35 | log_replace(value, time=time) 36 | 37 | def log_exception(self, value): 38 | log_exception(value) 39 | 40 | def get_cookies(self, browser): 41 | return requests.utils.dict_from_cookiejar(browser.cookies) 42 | 43 | def created(self): 44 | self._created += 1 45 | 46 | def total(self): 47 | return self._total - self._created 48 | 49 | def create(self, data, account): 50 | serverid = account['serverid'] 51 | hostname = account['hostname'] 52 | username = account['username'].replace(data['replace-username'], '') 53 | password = account['password'].replace(data['replace-password'], '') 54 | HEAD = '{link}{head}'.format(link=data['link'], head=data['head'].format(serverid=serverid)) 55 | POST = '{link}{post}'.format(link=data['link'], post=data['post']) 56 | i = 0 57 | x = 2 58 | 59 | while True: 60 | try: 61 | time.sleep(0.200) 62 | loop, i = 0, i + 1 63 | results = '{username_hostname:.<48} '.format(username_hostname='[Y1]'+username+'[G1]@'+hostname+' ') 64 | browser = requests.session() 65 | response = browser.request('HEAD', HEAD, timeout=10) 66 | time.sleep(0.200) 67 | response = browser.request('POST', POST, 68 | data={'serverid': serverid, 'username': username, 'password': password}, 69 | headers={'Referer': POST}, 70 | cookies=self.get_cookies(browser), 71 | timeout=15 72 | ) 73 | if not response.text: 74 | results = results + '[Y2]200' 75 | elif 'Username already exist' in response.text: 76 | results = results + '[Y1]200' 77 | elif 'has been successfully created' in response.text: 78 | results = results + '[G1]200' 79 | elif 'Account maximum' in response.text: 80 | results = results + '[R1]200' 81 | else: 82 | results = results + '[R1]' + str(response.text) 83 | except requests.exceptions.Timeout: 84 | results = results + '[R1]ERR' 85 | if i < x: loop = 1 86 | except requests.exceptions.ConnectionError: 87 | results = results + '[R2]ERR' 88 | if i < x: loop = 1 89 | except Exception as exception: 90 | results = results + '[R1]Exception: ' + str(exception) 91 | finally: 92 | self.log(results[:-7] + '[R2]END' if i == x else results) 93 | if loop: 94 | self.log_replace(self.total()) 95 | continue 96 | self.created() 97 | self.log_replace(self.total()) 98 | break 99 | 100 | def update_serverid_thread(self, data): 101 | data['name'] = re.sub(r'https?://', '', data['link']) 102 | while True: 103 | try: 104 | response = requests.request('GET', '{link}{page}'.format(link=data['link'], page=data['page']), timeout=10) 105 | response = BeautifulSoup(response.text, 'html.parser') 106 | self.log('[G1]{name:.<44} [G1]200'.format(name=data['name']+' [G1]')) 107 | for element in response.find_all(attrs={'class': data['pattern-class']}): 108 | hostname = re.findall(r'{}'.format(data['pattern-hostname'].format(hostname=r'([a-zA-Z0-9]+(\.[a-zA-Z0-9]+)+)')), str(element)) 109 | serverid = re.findall(r'{}'.format(data['pattern-serverid'].format(serverid=r'([0-9]+)')), str(element)) 110 | hostname = hostname[0][0] if len(hostname) and len(hostname[0]) else '' 111 | serverid = serverid[0] if len(serverid) else '' 112 | 113 | if not hostname or not serverid: 114 | hostname_available = False 115 | for account in self.accounts: 116 | if hostname and hostname == account['hostname']: 117 | hostname_available = True 118 | break 119 | if hostname_available or self.verbose: 120 | self.log('[R1]{hostname:.<44} [R1]{serverid} {verbose}'.format( 121 | hostname=(hostname if hostname else '(empty hostname)') + ' [G1]', 122 | serverid=(serverid if serverid else '(empty serverid)'), 123 | verbose=('(verbose)' if self.verbose else '') 124 | ) 125 | ) 126 | continue 127 | 128 | if self.verbose: 129 | self.log('[G1]{hostname:.<40} [Y1]{serverid}'.format( 130 | hostname=hostname + ' ', 131 | serverid=serverid 132 | ) 133 | ) 134 | 135 | self.hostname_serverid.append({ 136 | 'hostname': hostname, 137 | 'serverid': serverid 138 | }) 139 | except requests.exceptions.Timeout: 140 | self.log('[R1]{name:.<44} [R1]ERR'.format(name=data['name']+' [G1]')) 141 | continue 142 | except requests.exceptions.ConnectionError: 143 | self.log('[R1]{name:.<44} [R2]END'.format(name=data['name']+' [G1]')) 144 | except Exception as exception: 145 | self.log_exception(exception) 146 | break 147 | 148 | def update_serverid(self): 149 | self.log('Updating serverid') 150 | 151 | threads = [] 152 | 153 | for data in self.data_create_ssh: 154 | data['name'] = re.sub(r'https?://', '', data['link']) 155 | name_available = False 156 | for account in self.accounts: 157 | if account['name'] == data['name']: 158 | name_available = True 159 | break 160 | if name_available: threads.append(threading.Thread(target=self.update_serverid_thread, args=(data, ))) 161 | 162 | for thread in threads: 163 | time.sleep(0.200) 164 | thread.daemon = True 165 | thread.start() 166 | 167 | for thread in threads: 168 | thread.join() 169 | 170 | for i in range(len(self.accounts)): 171 | for data in self.hostname_serverid: 172 | hostname = data['hostname'] 173 | serverid = data['serverid'] 174 | if hostname == self.accounts[i]['hostname']: 175 | self.accounts[i]['serverid'] = serverid 176 | 177 | self.hostname_serverid = [] 178 | self.log('Updating serverid complete') 179 | 180 | def create_thread(self): 181 | while True: 182 | time.sleep(0.200) 183 | account = self.queue_accounts.get() 184 | for data in self.data_create_ssh: 185 | if data['name'] == account['name']: 186 | self.create(data, account) 187 | break 188 | if self.queue_accounts.task_done() is None and self.queue_accounts.qsize() == 0: 189 | break 190 | 191 | def start(self): 192 | if len(self.accounts) >= 1: 193 | self.update_serverid() 194 | 195 | for account in self.accounts: 196 | if account.get('serverid'): 197 | self.queue_accounts.put(account) 198 | continue 199 | self.hostname_empty_serverid.add(account['hostname']) 200 | 201 | self._total = self.queue_accounts.qsize() 202 | 203 | for hostname in self.hostname_empty_serverid: 204 | self.log('[R1]{:.<44} [R1](empty serverid)'.format(hostname+' [G1]')) 205 | 206 | value = 'Creating {} ssh accounts'.format(self._total) 207 | self.log(value + ' ' * 8) 208 | self.log_replace(self.total()) 209 | 210 | for i in range(self.queue_threads): 211 | thread = threading.Thread(target=self.create_thread) 212 | thread.daemon = True 213 | thread.start() 214 | 215 | self.queue_accounts.join() 216 | self.accounts = [] 217 | 218 | self.log(value + ' complete') 219 | -------------------------------------------------------------------------------- /app/default/database/accounts.json: -------------------------------------------------------------------------------- 1 | { 2 | "accounts": { 3 | "www.monthlyssh.com": [ 4 | { 5 | "host": "", 6 | "hostname": "#us1.ssl.monthlyssh.com", 7 | "username": "monthlyssh.com-{username}", 8 | "password": "{password}" 9 | }, 10 | { 11 | "host": "", 12 | "hostname": "sg1.ssl.monthlyssh.com", 13 | "username": "monthlyssh.com-{username}", 14 | "password": "{password}" 15 | }, 16 | { 17 | "host": "", 18 | "hostname": "sg2.ssl.monthlyssh.com", 19 | "username": "monthlyssh.com-{username}", 20 | "password": "{password}" 21 | }, 22 | { 23 | "host": "", 24 | "hostname": "#id.ssl.monthlyssh.com", 25 | "username": "monthlyssh.com-{username}", 26 | "password": "{password}" 27 | }, 28 | { 29 | "host": "", 30 | "hostname": "de.ssl.monthlyssh.com", 31 | "username": "monthlyssh.com-{username}", 32 | "password": "{password}" 33 | } 34 | ], 35 | "www.sshaccess.com": [ 36 | { 37 | "host": "", 38 | "hostname": "sg.ssl.sshaccess.com", 39 | "username": "sshaccess.com-{username}", 40 | "password": "{password}" 41 | }, 42 | { 43 | "host": "", 44 | "hostname": "#sg1.ssl.sshaccess.com", 45 | "username": "sshaccess.com-{username}", 46 | "password": "{password}" 47 | }, 48 | { 49 | "host": "", 50 | "hostname": "#sg2.ssl.sshaccess.com", 51 | "username": "sshaccess.com-{username}", 52 | "password": "{password}" 53 | }, 54 | { 55 | "host": "", 56 | "hostname": "#sg3.ssl.sshaccess.com", 57 | "username": "sshaccess.com-{username}", 58 | "password": "{password}" 59 | }, 60 | { 61 | "host": "", 62 | "hostname": "us.ssl.sshaccess.com", 63 | "username": "sshaccess.com-{username}", 64 | "password": "{password}" 65 | }, 66 | { 67 | "host": "", 68 | "hostname": "#us1.ssl.sshaccess.com", 69 | "username": "sshaccess.com-{username}", 70 | "password": "{password}" 71 | }, 72 | { 73 | "host": "", 74 | "hostname": "#us2.ssl.sshaccess.com", 75 | "username": "sshaccess.com-{username}", 76 | "password": "{password}" 77 | }, 78 | { 79 | "host": "", 80 | "hostname": "#us3.ssl.sshaccess.com", 81 | "username": "sshaccess.com-{username}", 82 | "password": "{password}" 83 | }, 84 | { 85 | "host": "", 86 | "hostname": "nl.ssl.sshaccess.com", 87 | "username": "sshaccess.com-{username}", 88 | "password": "{password}" 89 | }, 90 | { 91 | "host": "", 92 | "hostname": "#nl1.ssl.sshaccess.com", 93 | "username": "sshaccess.com-{username}", 94 | "password": "{password}" 95 | }, 96 | { 97 | "host": "", 98 | "hostname": "#nl2.ssl.sshaccess.com", 99 | "username": "sshaccess.com-{username}", 100 | "password": "{password}" 101 | }, 102 | { 103 | "host": "", 104 | "hostname": "in.ssl.sshaccess.com", 105 | "username": "sshaccess.com-{username}", 106 | "password": "{password}" 107 | }, 108 | { 109 | "host": "", 110 | "hostname": "ca.ssl.sshaccess.com", 111 | "username": "sshaccess.com-{username}", 112 | "password": "{password}" 113 | }, 114 | { 115 | "host": "", 116 | "hostname": "uk.ssl.sshaccess.com", 117 | "username": "sshaccess.com-{username}", 118 | "password": "{password}" 119 | }, 120 | { 121 | "host": "", 122 | "hostname": "de.ssl.sshaccess.com", 123 | "username": "sshaccess.com-{username}", 124 | "password": "{password}" 125 | } 126 | ], 127 | "www.speedssh.com": [ 128 | { 129 | "host": "", 130 | "hostname": "us1.ssl.speedssh.com", 131 | "username": "speedssh.com-{username}", 132 | "password": "{password}" 133 | }, 134 | { 135 | "host": "", 136 | "hostname": "#sg1.ssl.speedssh.com", 137 | "username": "speedssh.com-{username}", 138 | "password": "{password}" 139 | }, 140 | { 141 | "host": "", 142 | "hostname": "#sg2.ssl.speedssh.com", 143 | "username": "speedssh.com-{username}", 144 | "password": "{password}" 145 | }, 146 | { 147 | "host": "", 148 | "hostname": "#sg3.ssl.speedssh.com", 149 | "username": "speedssh.com-{username}", 150 | "password": "{password}" 151 | }, 152 | { 153 | "host": "", 154 | "hostname": "#sg4.ssl.speedssh.com", 155 | "username": "speedssh.com-{username}", 156 | "password": "{password}" 157 | }, 158 | { 159 | "host": "", 160 | "hostname": "#sg5.ssl.speedssh.com", 161 | "username": "speedssh.com-{username}", 162 | "password": "{password}" 163 | }, 164 | { 165 | "host": "", 166 | "hostname": "#sg6.ssl.speedssh.com", 167 | "username": "speedssh.com-{username}", 168 | "password": "{password}" 169 | } 170 | ], 171 | "www.portssh.com": [ 172 | { 173 | "host": "", 174 | "hostname": "ssl.us1.portssh.com", 175 | "username": "portssh.com-{username}", 176 | "password": "{password}" 177 | }, 178 | { 179 | "host": "", 180 | "hostname": "ssl.sg1.portssh.com", 181 | "username": "portssh.com-{username}", 182 | "password": "{password}" 183 | }, 184 | { 185 | "host": "", 186 | "hostname": "ssl.sg2.portssh.com", 187 | "username": "portssh.com-{username}", 188 | "password": "{password}" 189 | }, 190 | { 191 | "host": "", 192 | "hostname": "ssl.sg3.portssh.com", 193 | "username": "portssh.com-{username}", 194 | "password": "{password}" 195 | } 196 | ], 197 | "www.mytunneling.com": [ 198 | { 199 | "host": "", 200 | "hostname": "sgdo1.ssl.ipmy.us", 201 | "username": "tunvpn.com-{username}", 202 | "password": "{password}" 203 | }, 204 | { 205 | "host": "", 206 | "hostname": "#sgdo2.ssl.ipmy.us", 207 | "username": "tunvpn.com-{username}", 208 | "password": "{password}" 209 | }, 210 | { 211 | "host": "", 212 | "hostname": "#sgdo3.ssl.ipmy.us", 213 | "username": "tunvpn.com-{username}", 214 | "password": "{password}" 215 | }, 216 | { 217 | "host": "", 218 | "hostname": "#us1.ssl.ipmy.us", 219 | "username": "tunvpn.com-{username}", 220 | "password": "{password}" 221 | }, 222 | { 223 | "host": "", 224 | "hostname": "#us2.ssl.ipmy.us", 225 | "username": "tunvpn.com-{username}", 226 | "password": "{password}" 227 | }, 228 | { 229 | "host": "", 230 | "hostname": "#jp1.ssl.ipmy.us", 231 | "username": "tunvpn.com-{username}", 232 | "password": "{password}" 233 | }, 234 | { 235 | "host": "", 236 | "hostname": "#id.ssl.ipmy.us", 237 | "username": "tunvpn.com-{username}", 238 | "password": "{password}" 239 | }, 240 | { 241 | "host": "", 242 | "hostname": "nl.ssl.ipmy.us", 243 | "username": "tunvpn.com-{username}", 244 | "password": "{password}" 245 | }, 246 | { 247 | "host": "", 248 | "hostname": "uk.ssl.ipmy.us", 249 | "username": "tunvpn.com-{username}", 250 | "password": "{password}" 251 | }, 252 | { 253 | "host": "", 254 | "hostname": "de.ssl.ipmy.us", 255 | "username": "tunvpn.com-{username}", 256 | "password": "{password}" 257 | } 258 | ], 259 | "www.jetssh.com": [ 260 | { 261 | "host": "", 262 | "hostname": "#sgjet2.ssl.jetssh.com", 263 | "username": "jetssh.com-{username}", 264 | "password": "{password}" 265 | }, 266 | { 267 | "host": "", 268 | "hostname": "sg1.ssl.jetssh.com", 269 | "username": "jetssh.com-{username}", 270 | "password": "{password}" 271 | }, 272 | { 273 | "host": "", 274 | "hostname": "sg2.ssl.jetssh.com", 275 | "username": "jetssh.com-{username}", 276 | "password": "{password}" 277 | }, 278 | { 279 | "host": "", 280 | "hostname": "sg3.ssl.jetssh.com", 281 | "username": "jetssh.com-{username}", 282 | "password": "{password}" 283 | }, 284 | { 285 | "host": "", 286 | "hostname": "us.ssl.jetssh.com", 287 | "username": "jetssh.com-{username}", 288 | "password": "{password}" 289 | } 290 | ], 291 | "www.flyssh.com": [ 292 | { 293 | "host": "", 294 | "hostname": "us1.ssl.flyssh.com", 295 | "username": "{username}_flyssh.com", 296 | "password": "{password}" 297 | }, 298 | { 299 | "host": "", 300 | "hostname": "sg1.ssl.flyssh.com", 301 | "username": "{username}_flyssh.com", 302 | "password": "{password}" 303 | }, 304 | { 305 | "host": "", 306 | "hostname": "sg2.ssl.flyssh.com", 307 | "username": "{username}_flyssh.com", 308 | "password": "{password}" 309 | }, 310 | { 311 | "host": "", 312 | "hostname": "sg3.ssl.flyssh.com", 313 | "username": "{username}_flyssh.com", 314 | "password": "{password}" 315 | }, 316 | { 317 | "host": "", 318 | "hostname": "#jp.ssl.flyssh.com", 319 | "username": "{username}_flyssh.com", 320 | "password": "{password}" 321 | } 322 | ] 323 | } 324 | } -------------------------------------------------------------------------------- /app/server_tunnel.py: -------------------------------------------------------------------------------- 1 | import re 2 | import ssl 3 | import time 4 | import json 5 | import random 6 | import select 7 | import socket 8 | import threading 9 | from .app import * 10 | from .default import * 11 | from .ssh_statistic import * 12 | 13 | 14 | class server_tunnel(threading.Thread): 15 | def __init__(self, socket_accept, force_tunnel_type=None, external=False, quiet=False): 16 | super(server_tunnel, self).__init__() 17 | 18 | self.socket_client, (self.client_host, self.client_port) = socket_accept 19 | self.force_tunnel_type = force_tunnel_type 20 | self.external = external 21 | self.quiet = quiet 22 | 23 | self.server_name_indication = open(real_path('/../config/server-name-indication.txt')).readlines()[0].strip() 24 | self.tunnel_type = '' 25 | self.proxies = [] 26 | self.payload = '' 27 | self.config = {} 28 | 29 | self.do_handshake_on_connect = True 30 | self.buffer_size = 65535 31 | self.timeout = 3 32 | self.daemon = True 33 | 34 | def force_log(self, value, status='INFO', status_color='[G1]'): 35 | log(value, status=status, status_color=status_color) 36 | 37 | def log(self, value, status='INFO', status_color='[G1]'): 38 | if not self.quiet: log(value, status=status, status_color=status_color) 39 | 40 | def log_error(self, value, status='INFO'): 41 | self.force_log('[R1]{}'.format(value), status=status, status_color='[R1]') 42 | 43 | def log_external(self, value, status='INFO', status_color='[G1]'): 44 | if self.external: self.log(value, status=status, status_color=status_color) 45 | 46 | def extract_client_request(self): 47 | self.client_request = self.socket_client.recv(self.buffer_size).decode('charmap') 48 | result = re.findall(r'(([a-zA-Z0-9]+(\.[a-zA-Z0-9]+)+):([0-9]+))', self.client_request) 49 | result = result[0] if len(result) else '' 50 | if not result: 51 | self.log('[R1]Target host and port not found', status_color='[R1]') 52 | return False 53 | self.host, self.port = result[1], int(result[3]) 54 | return True 55 | 56 | def get_host_port(self, value): 57 | value = re.findall(r'(([a-zA-Z0-9]+(\.[a-zA-Z0-9]+)+)(:(\d+))?)', value) 58 | value = value[0] if len(value) else [] 59 | value_host = value[1] if len(value) >= 2 and value[1] else '' 60 | value_port = value[4] if len(value) >= 5 and value[4] else '80' 61 | 62 | return (value_host, int(value_port)) if value_host and value_port else '' 63 | 64 | def get_proxy(self): 65 | data_proxies = filter_array(open(real_path('/../config/proxies.txt')).readlines()) 66 | data_proxies = filter_array('\n'.join(data_proxies).split('---')[0].splitlines()) 67 | for proxy in data_proxies: 68 | proxy = self.get_host_port(proxy) 69 | if proxy: self.proxies.append(proxy) 70 | 71 | if len(self.proxies): 72 | self.proxy_host, self.proxy_port = random.choice(self.proxies) 73 | 74 | return True if len(self.proxies) else False 75 | 76 | def get_payload(self): 77 | data_payload = filter_array(open(real_path('/../config/payload.txt')).readlines()) 78 | data_payload = filter_array('\n'.join(data_payload).split('---')[0].splitlines()) 79 | for value in data_payload: 80 | value = value.strip() 81 | if value and not value.startswith('#'): 82 | self.payload += value 83 | 84 | return self.payload 85 | 86 | def payload_decode(self, payload): 87 | payload = payload.replace('[real_raw]', '[raw][crlf][crlf]') 88 | payload = payload.replace('[raw]', '[method] [host_port] [protocol]') 89 | payload = payload.replace('[method]', 'CONNECT') 90 | payload = payload.replace('[host_port]', '[host]:[port]') 91 | payload = payload.replace('[host]', str(self.host)) 92 | payload = payload.replace('[port]', str(self.port)) 93 | payload = payload.replace('[protocol]', 'HTTP/1.0') 94 | payload = payload.replace('[user-agent]', 'User-Agent: Chrome/1.1.3') 95 | payload = payload.replace('[keep-alive]', 'Connection: Keep-Alive') 96 | payload = payload.replace('[close]', 'Connection: Close') 97 | payload = payload.replace('[crlf]', '[cr][lf]') 98 | payload = payload.replace('[lfcr]', '[lf][cr]') 99 | payload = payload.replace('[cr]', '\r') 100 | payload = payload.replace('[lf]', '\n') 101 | 102 | return payload.encode() 103 | 104 | def send_payload(self, payload_encode = ''): 105 | payload_encode = payload_encode if payload_encode else '[method] [host_port] [protocol][crlf][crlf]' 106 | self.log('Payload: \n\n{}\n'.format(('| ' + self.payload_decode(payload_encode).decode()) 107 | .replace('\r', '') 108 | .replace('[split]', '$lf\n') 109 | .replace('\n', '\n| ') 110 | .replace('$lf', '\n') 111 | )) 112 | payload_split = payload_encode.split('[split]') 113 | for i in range(len(payload_split)): 114 | if i > 0: time.sleep(0.200) 115 | self.socket_tunnel.sendall(self.payload_decode(payload_split[i])) 116 | 117 | def certificate(self): 118 | self.log('Certificate:\n\n{certificate}'.format(certificate=ssl.DER_cert_to_PEM_cert(self.socket_tunnel.getpeercert(True)))) 119 | 120 | def convert_response(self, response): 121 | response = response.replace('\r', '').rstrip() + '\n\n' 122 | 123 | if response.startswith('HTTP'): 124 | response = '\n\n| {}\n'.format(response.replace('\n', '\n| ')) 125 | else: 126 | response = '[W2]\n\n{}\n'.format(re.sub(r'\s+', ' ', response.replace('\n', '[CC][Y1]\\n[W2]'))) 127 | 128 | return response 129 | 130 | def handler(self): 131 | sockets = [self.socket_tunnel, self.socket_client] 132 | timeout = 0 133 | self.socket_client.sendall(b'HTTP/1.0 200 Connection established\r\n\r\n') 134 | self.log('Connection established') 135 | while True: 136 | timeout += 1 137 | socket_io, _, errors = select.select(sockets, [], sockets, 3) 138 | if errors: break 139 | if socket_io: 140 | for socket in socket_io: 141 | try: 142 | data = socket.recv(self.buffer_size) 143 | if not data: break 144 | if socket is self.socket_tunnel: 145 | self.socket_client.sendall(data) 146 | if self.quiet != 'full': ssh_statistic('download') 147 | elif socket is self.socket_client: 148 | self.socket_tunnel.sendall(data) 149 | if self.quiet != 'full': ssh_statistic('upload') 150 | timeout = 0 151 | except: break 152 | if timeout == 30: break 153 | 154 | def proxy_handler(self): 155 | x = 0 156 | while True: 157 | if x == 1: self.log('Replacing response') 158 | response = self.socket_tunnel.recv(self.buffer_size).decode('charmap') 159 | if not response: break 160 | response_status = response.replace('\r', '').split('\n')[0] 161 | if re.match(r'HTTP/\d(\.\d)? 200 .+', response_status): 162 | self.log('Response: {}'.format(self.convert_response(response))) 163 | self.handler() 164 | break 165 | else: 166 | self.log('Response: {}'.format(self.convert_response(response))) 167 | self.socket_tunnel.sendall(b'HTTP/1.0 200 Connection established\r\nConnection: keep-alive\r\n\r\n') 168 | x += 1 169 | 170 | # Direct -> SSH 171 | def tunnel_type_0(self): 172 | try: 173 | self.log('Connecting to {host} port {port}'.format(host=self.host, port=self.port)) 174 | self.socket_tunnel.connect((self.host, int(self.port))) 175 | self.send_payload(self.get_payload()) 176 | self.handler() 177 | except socket.timeout: 178 | self.log_external('Connection timeout', status_color='[R1]') 179 | except socket.error: 180 | self.log_external('Connection closed', status_color='[R1]') 181 | finally: 182 | self.socket_tunnel.close() 183 | self.socket_client.close() 184 | 185 | # Direct -> SSH (SSL/TLS) 186 | def tunnel_type_1(self): 187 | try: 188 | self.log('Connecting to {host} port {port}'.format(host=self.host, port=self.port)) 189 | self.socket_tunnel.connect((self.host, int(self.port))) 190 | self.log('Server name indication: {server_hostname}'.format(server_hostname=self.server_name_indication)) 191 | self.socket_tunnel = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2).wrap_socket(self.socket_tunnel, server_hostname=self.server_name_indication, do_handshake_on_connect=self.do_handshake_on_connect) 192 | self.certificate() 193 | self.handler() 194 | except socket.timeout: 195 | self.log_external('Connection timeout', status_color='[R1]') 196 | except socket.error: 197 | self.log_external('Connection closed', status_color='[R1]') 198 | finally: 199 | self.socket_tunnel.close() 200 | self.socket_client.close() 201 | 202 | # HTTP Proxy -> SSH 203 | def tunnel_type_2(self): 204 | try: 205 | if self.get_proxy() == False: 206 | self.log_error('Connecting to remote proxy error (remote proxy not found)') 207 | return 208 | self.log('Connecting to remote proxy {proxy_host} port {proxy_port}'.format(proxy_host=self.proxy_host, proxy_port=self.proxy_port)) 209 | self.socket_tunnel.connect((self.proxy_host, self.proxy_port)) 210 | self.log('Connecting to {host} port {port}'.format(host=self.host, port=self.port)) 211 | self.send_payload(self.get_payload()) 212 | self.proxy_handler() 213 | except socket.timeout: 214 | self.log_external('Connection timeout', status_color='[R1]') 215 | except socket.error: 216 | self.log_external('Connection closed', status_color='[R1]') 217 | finally: 218 | self.socket_tunnel.close() 219 | self.socket_client.close() 220 | 221 | def run(self): 222 | self.socket_tunnel = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 223 | self.socket_tunnel.settimeout(self.timeout) 224 | 225 | try: 226 | self.config_file = real_path('/../config/config.json') 227 | self.config = json.loads(open(self.config_file).read()) 228 | self.tunnel_type = self.config['tunnel_type'] if not self.external else self.config['tunnel_type_external'] 229 | if not self.tunnel_type or int(self.tunnel_type) >= 3: raise KeyError 230 | except KeyError: 231 | json_error(self.config_file) 232 | self.socket_tunnel.close() 233 | self.socket_client.close() 234 | return 235 | 236 | if not self.extract_client_request(): 237 | self.log_error('Client request error (target host and port not found)') 238 | self.socket_tunnel.close() 239 | self.socket_client.close() 240 | return 241 | 242 | if self.force_tunnel_type is not None: 243 | self.tunnel_type = self.force_tunnel_type 244 | 245 | if not self.tunnel_type : 246 | pass 247 | elif self.tunnel_type == '0': self.tunnel_type_0() 248 | elif self.tunnel_type == '1': self.tunnel_type_1() 249 | elif self.tunnel_type == '2': self.tunnel_type_2() 250 | --------------------------------------------------------------------------------