mirror of
https://github.com/Inndy/twnhi-smartcard-agent.git
synced 2025-07-17 20:53:22 +00:00
First public release
This commit is contained in:
357
pysoxy.py
Normal file
357
pysoxy.py
Normal file
@ -0,0 +1,357 @@
|
||||
# This file is part of twnhi-smartcard-agent.
|
||||
#
|
||||
# twnhi-smartcard-agent is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# twnhi-smartcard-agent is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with twnhi-smartcard-agent.
|
||||
# If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Small Socks5 Proxy Server in Python
|
||||
from https://github.com/MisterDaneel/
|
||||
"""
|
||||
|
||||
# Network
|
||||
import socket
|
||||
import select
|
||||
from struct import pack, unpack
|
||||
# System
|
||||
import traceback
|
||||
from threading import Thread, activeCount
|
||||
from signal import signal, SIGINT, SIGTERM
|
||||
from time import sleep
|
||||
import sys
|
||||
|
||||
hijacker = None
|
||||
hijacked_host = None
|
||||
|
||||
#
|
||||
# Configuration
|
||||
#
|
||||
MAX_THREADS = 200
|
||||
BUFSIZE = 2048
|
||||
TIMEOUT_SOCKET = 5
|
||||
LOCAL_ADDR = '127.0.0.1'
|
||||
LOCAL_PORT = 17777
|
||||
# Parameter to bind a socket to a device, using SO_BINDTODEVICE
|
||||
# Only root can set this option
|
||||
# If the name is an empty string or None, the interface is chosen when
|
||||
# a routing decision is made
|
||||
# OUTGOING_INTERFACE = "eth0"
|
||||
OUTGOING_INTERFACE = ""
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
'''Version of the protocol'''
|
||||
# PROTOCOL VERSION 5
|
||||
VER = b'\x05'
|
||||
'''Method constants'''
|
||||
# '00' NO AUTHENTICATION REQUIRED
|
||||
M_NOAUTH = b'\x00'
|
||||
# 'FF' NO ACCEPTABLE METHODS
|
||||
M_NOTAVAILABLE = b'\xff'
|
||||
'''Command constants'''
|
||||
# CONNECT '01'
|
||||
CMD_CONNECT = b'\x01'
|
||||
'''Address type constants'''
|
||||
# IP V4 address '01'
|
||||
ATYP_IPV4 = b'\x01'
|
||||
# DOMAINNAME '03'
|
||||
ATYP_DOMAINNAME = b'\x03'
|
||||
|
||||
|
||||
class ExitStatus:
|
||||
""" Manage exit status """
|
||||
def __init__(self):
|
||||
self.exit = False
|
||||
|
||||
def set_status(self, status):
|
||||
""" set exist status """
|
||||
self.exit = status
|
||||
|
||||
def get_status(self):
|
||||
""" get exit status """
|
||||
return self.exit
|
||||
|
||||
|
||||
def error(msg="", err=None):
|
||||
""" Print exception stack trace python """
|
||||
if msg:
|
||||
traceback.print_exc()
|
||||
print("[-] {} - Code: {}, Message: {}".format(msg, str(err[0]), err[1]))
|
||||
else:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def proxy_loop(socket_src, socket_dst):
|
||||
""" Wait for network activity """
|
||||
while not EXIT.get_status():
|
||||
try:
|
||||
reader, _, _ = select.select([socket_src, socket_dst], [], [], 1)
|
||||
except select.error as err:
|
||||
error("Select failed", err)
|
||||
return
|
||||
if not reader:
|
||||
continue
|
||||
try:
|
||||
for sock in reader:
|
||||
data = sock.recv(BUFSIZE)
|
||||
if not data:
|
||||
return
|
||||
if sock is socket_dst:
|
||||
socket_src.send(data)
|
||||
else:
|
||||
socket_dst.send(data)
|
||||
except socket.error as err:
|
||||
error("Loop failed", err)
|
||||
return
|
||||
|
||||
|
||||
def connect_to_dst(dst_addr, dst_port):
|
||||
""" Connect to desired destination """
|
||||
sock = create_socket()
|
||||
if OUTGOING_INTERFACE:
|
||||
try:
|
||||
sock.setsockopt(
|
||||
socket.SOL_SOCKET,
|
||||
socket.SO_BINDTODEVICE,
|
||||
OUTGOING_INTERFACE.encode(),
|
||||
)
|
||||
except PermissionError as err:
|
||||
print("[-] Only root can set OUTGOING_INTERFACE parameter")
|
||||
EXIT.set_status(True)
|
||||
try:
|
||||
sock.connect((dst_addr, dst_port))
|
||||
print('[+] Connect to %s:%d' % (dst_addr, dst_port))
|
||||
return sock
|
||||
except socket.error as err:
|
||||
error("Failed to connect to DST", err)
|
||||
return 0
|
||||
|
||||
|
||||
def request_client(wrapper):
|
||||
""" Client request details """
|
||||
# +----+-----+-------+------+----------+----------+
|
||||
# |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
|
||||
# +----+-----+-------+------+----------+----------+
|
||||
try:
|
||||
s5_request = wrapper.recv(BUFSIZE)
|
||||
except ConnectionResetError:
|
||||
if wrapper != 0:
|
||||
wrapper.close()
|
||||
error()
|
||||
return False
|
||||
# Check VER, CMD and RSV
|
||||
if (
|
||||
s5_request[0:1] != VER or
|
||||
s5_request[1:2] != CMD_CONNECT or
|
||||
s5_request[2:3] != b'\x00'
|
||||
):
|
||||
return False
|
||||
# IPV4
|
||||
if s5_request[3:4] == ATYP_IPV4:
|
||||
dst_addr = socket.inet_ntoa(s5_request[4:-2])
|
||||
dst_port = unpack('>H', s5_request[8:len(s5_request)])[0]
|
||||
# DOMAIN NAME
|
||||
elif s5_request[3:4] == ATYP_DOMAINNAME:
|
||||
sz_domain_name = s5_request[4]
|
||||
dst_addr = s5_request[5: 5 + sz_domain_name - len(s5_request)]
|
||||
port_to_unpack = s5_request[5 + sz_domain_name:len(s5_request)]
|
||||
dst_port = unpack('>H', port_to_unpack)[0]
|
||||
else:
|
||||
return False
|
||||
return (dst_addr, dst_port)
|
||||
|
||||
|
||||
def request(wrapper):
|
||||
"""
|
||||
The SOCKS request information is sent by the client as soon as it has
|
||||
established a connection to the SOCKS server, and completed the
|
||||
authentication negotiations. The server evaluates the request, and
|
||||
returns a reply
|
||||
"""
|
||||
dst = request_client(wrapper)
|
||||
# Server Reply
|
||||
# +----+-----+-------+------+----------+----------+
|
||||
# |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
|
||||
# +----+-----+-------+------+----------+----------+
|
||||
rep = b'\x07'
|
||||
bnd = b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00'
|
||||
hijacked = False
|
||||
if dst:
|
||||
if dst[0] == hijacked_host.encode():
|
||||
print('[*] Hijack %s to local server' % hijacked_host)
|
||||
hijacked = True
|
||||
socket_dst = True
|
||||
else:
|
||||
socket_dst = connect_to_dst(dst[0], dst[1])
|
||||
|
||||
if not dst or socket_dst == 0:
|
||||
rep = b'\x01'
|
||||
else:
|
||||
rep = b'\x00'
|
||||
if hijacked:
|
||||
bnd = b'\x01\x01\x01\x01\x01\x01'
|
||||
else:
|
||||
bnd = socket.inet_aton(socket_dst.getsockname()[0])
|
||||
bnd += pack(">H", socket_dst.getsockname()[1])
|
||||
|
||||
reply = VER + rep + b'\x00' + ATYP_IPV4 + bnd
|
||||
try:
|
||||
wrapper.sendall(reply)
|
||||
except socket.error:
|
||||
if wrapper != 0:
|
||||
wrapper.close()
|
||||
return
|
||||
# start proxy
|
||||
if rep == b'\x00':
|
||||
if hijacked:
|
||||
hijacker(wrapper)
|
||||
else:
|
||||
proxy_loop(wrapper, socket_dst)
|
||||
if wrapper != 0:
|
||||
wrapper.close()
|
||||
if socket_dst != 0 and socket_dst != True:
|
||||
socket_dst.close()
|
||||
|
||||
|
||||
def subnegotiation_client(wrapper):
|
||||
"""
|
||||
The client connects to the server, and sends a version
|
||||
identifier/method selection message
|
||||
"""
|
||||
# Client Version identifier/method selection message
|
||||
# +----+----------+----------+
|
||||
# |VER | NMETHODS | METHODS |
|
||||
# +----+----------+----------+
|
||||
try:
|
||||
identification_packet = wrapper.recv(BUFSIZE)
|
||||
except socket.error:
|
||||
error()
|
||||
return M_NOTAVAILABLE
|
||||
# VER field
|
||||
if VER != identification_packet[0:1]:
|
||||
return M_NOTAVAILABLE
|
||||
# METHODS fields
|
||||
nmethods = identification_packet[1]
|
||||
methods = identification_packet[2:]
|
||||
if len(methods) != nmethods:
|
||||
return M_NOTAVAILABLE
|
||||
for method in methods:
|
||||
if method == ord(M_NOAUTH):
|
||||
return M_NOAUTH
|
||||
return M_NOTAVAILABLE
|
||||
|
||||
|
||||
def subnegotiation(wrapper):
|
||||
"""
|
||||
The client connects to the server, and sends a version
|
||||
identifier/method selection message
|
||||
The server selects from one of the methods given in METHODS, and
|
||||
sends a METHOD selection message
|
||||
"""
|
||||
method = subnegotiation_client(wrapper)
|
||||
# Server Method selection message
|
||||
# +----+--------+
|
||||
# |VER | METHOD |
|
||||
# +----+--------+
|
||||
if method != M_NOAUTH:
|
||||
return False
|
||||
reply = VER + method
|
||||
try:
|
||||
wrapper.sendall(reply)
|
||||
except socket.error:
|
||||
error()
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def connection(wrapper):
|
||||
""" Function run by a thread """
|
||||
if subnegotiation(wrapper):
|
||||
request(wrapper)
|
||||
|
||||
|
||||
def create_socket():
|
||||
""" Create an INET, STREAMing socket """
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(TIMEOUT_SOCKET)
|
||||
except socket.error as err:
|
||||
error("Failed to create socket", err)
|
||||
sys.exit(0)
|
||||
return sock
|
||||
|
||||
|
||||
def bind_port(sock):
|
||||
"""
|
||||
Bind the socket to address and
|
||||
listen for connections made to the socket
|
||||
"""
|
||||
try:
|
||||
print('[+] Socks5 proxy bind on {}:{}'.format(LOCAL_ADDR, str(LOCAL_PORT)))
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.bind((LOCAL_ADDR, LOCAL_PORT))
|
||||
except socket.error as err:
|
||||
error("Bind failed", err)
|
||||
sock.close()
|
||||
sys.exit(0)
|
||||
# Listen
|
||||
try:
|
||||
sock.listen(10)
|
||||
except socket.error as err:
|
||||
error("Listen failed", err)
|
||||
sock.close()
|
||||
sys.exit(0)
|
||||
return sock
|
||||
|
||||
|
||||
def exit_handler(signum, frame):
|
||||
""" Signal handler called with signal, exit script """
|
||||
print('[*] Signal handler called with signal', signum)
|
||||
EXIT.set_status(True)
|
||||
|
||||
|
||||
def main(hijack, host):
|
||||
""" Main function """
|
||||
global hijacker
|
||||
global hijacked_host
|
||||
hijacker = hijack
|
||||
hijacked_host = host
|
||||
new_socket = create_socket()
|
||||
bind_port(new_socket)
|
||||
#signal(SIGINT, exit_handler)
|
||||
#signal(SIGTERM, exit_handler)
|
||||
while not EXIT.get_status():
|
||||
if activeCount() > MAX_THREADS:
|
||||
sleep(3)
|
||||
continue
|
||||
try:
|
||||
wrapper, _ = new_socket.accept()
|
||||
wrapper.setblocking(1)
|
||||
except socket.timeout:
|
||||
continue
|
||||
except socket.error:
|
||||
error()
|
||||
continue
|
||||
except TypeError:
|
||||
error()
|
||||
sys.exit(0)
|
||||
recv_thread = Thread(target=connection, args=(wrapper, ))
|
||||
recv_thread.start()
|
||||
new_socket.close()
|
||||
|
||||
|
||||
EXIT = ExitStatus()
|
||||
if __name__ == '__main__':
|
||||
main()
|
Reference in New Issue
Block a user