Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/proto/proxy/socks5/server_client.rb
Views: 11766
# -*- coding: binary -*-12require 'bindata'3require 'rex/socket'4require 'rex/proto/proxy/socks5/packet'56module Rex7module Proto8module Proxy910#11# A client connected to the proxy server.12#13module Socks514#15# A mixin for a socket to perform a relay to another socket.16#17module TcpRelay18#19# TcpRelay data coming in from relay_sock to this socket.20#21def relay(relay_client, relay_sock)22@relay_client = relay_client23@relay_sock = relay_sock24# start the relay thread (modified from Rex::IO::StreamAbstraction)25@relay_thread = Rex::ThreadFactory.spawn("SOCKS5ProxyServerTcpRelay", false) do26loop do27closed = false28buf = nil2930begin31s = Rex::ThreadSafe.select([@relay_sock], nil, nil, 0.2)32next if s.nil? || s[0].nil?33rescue34closed = true35end3637unless closed38begin39buf = @relay_sock.sysread( 32768 )40closed = buf.nil?41rescue42closed = true43end44end4546unless closed47total_sent = 048total_length = buf.length49while total_sent < total_length50begin51data = buf[total_sent, buf.length]52sent = self.write(data)53total_sent += sent if sent > 054rescue55closed = true56break57end58end59end6061if closed62@relay_client.stop63::Thread.exit64end65end66end67end68end6970#71# A client connected to the SOCKS5 server.72#73class ServerClient74AUTH_NONE = 075AUTH_GSSAPI = 176AUTH_CREDS = 277AUTH_NO_ACCEPTABLE_METHODS = 2557879AUTH_PROTOCOL_VERSION = 180AUTH_RESULT_SUCCESS = 081AUTH_RESULT_FAILURE = 18283COMMAND_CONNECT = 184COMMAND_BIND = 285COMMAND_UDP_ASSOCIATE = 38687REPLY_SUCCEEDED = 088REPLY_GENERAL_FAILURE = 189REPLY_NOT_ALLOWED = 290REPLY_NET_UNREACHABLE = 391REPLY_HOST_UNREACHABLE = 492REPLY_CONNECTION_REFUSED = 593REPLY_TTL_EXPIRED = 694REPLY_CMD_NOT_SUPPORTED = 795REPLY_ADDRESS_TYPE_NOT_SUPPORTED = 89697HOST = 198PORT = 299100#101# Create a new client connected to the server.102#103def initialize(server, sock, opts={})104@server = server105@lsock = sock106@opts = opts107@rsock = nil108@client_thread = nil109@mutex = ::Mutex.new110end111112# Start handling the client connection.113#114def start115# create a thread to handle this client request so as to not block the socks5 server116@client_thread = Rex::ThreadFactory.spawn("SOCKS5ProxyClient", false) do117begin118@server.add_client(self)119# get the initial client request packet120handle_authentication121122# handle the request123handle_command124rescue => exception125# respond with a general failure to the client126response = ResponsePacket.new127response.command = REPLY_GENERAL_FAILURE128@lsock.put(response.to_binary_s)129130wlog("Client.start - #{$!}")131self.stop132end133end134end135136def handle_authentication137request = AuthRequestPacket.read(@lsock.get_once)138if @opts['ServerUsername'].nil? && @opts['ServerPassword'].nil?139handle_authentication_none(request)140else141handle_authentication_creds(request)142end143end144145def handle_authentication_creds(request)146unless request.supported_methods.include? AUTH_CREDS147raise "Invalid SOCKS5 request packet received (no supported authentication methods)."148end149response = AuthResponsePacket.new150response.chosen_method = AUTH_CREDS151@lsock.put(response.to_binary_s)152153version = @lsock.read(1)154raise "Invalid SOCKS5 authentication packet received." unless version.unpack('C').first == 0x01155156username_length = @lsock.read(1).unpack('C').first157username = @lsock.read(username_length)158159password_length = @lsock.read(1).unpack('C').first160password = @lsock.read(password_length)161162# +-----+--------+163# | VER | STATUS |164# +-----+--------+ VERSION: 0x01165# | 1 | 1 | STATUS: 0x00=SUCCESS, otherwise FAILURE166# +-----+--------+167if username == @opts['ServerUsername'] && password == @opts['ServerPassword']168raw = [ AUTH_PROTOCOL_VERSION, AUTH_RESULT_SUCCESS ].pack ('CC')169ilog("SOCKS5: Successfully authenticated")170@lsock.put(raw)171else172raw = [ AUTH_PROTOCOL_VERSION, AUTH_RESULT_FAILURE ].pack ('CC')173@lsock.put(raw)174raise "Invalid SOCKS5 credentials provided"175end176end177178def handle_authentication_none(request)179unless request.supported_methods.include? AUTH_NONE180raise "Invalid SOCKS5 request packet received (no supported authentication methods)."181end182response = AuthResponsePacket.new183response.chosen_method = AUTH_NONE184@lsock.put(response.to_binary_s)185end186187def handle_command188request = RequestPacket.read(@lsock.get_once)189response = nil190case request.command191when COMMAND_BIND192response = handle_command_bind(request)193when COMMAND_CONNECT194response = handle_command_connect(request)195when COMMAND_UDP_ASSOCIATE196response = handle_command_udp_associate(request)197end198@lsock.put(response.to_binary_s) unless response.nil?199end200201def handle_command_bind(request)202# create a server socket for this request203params = {204'LocalHost' => request.address_type == Address::ADDRESS_TYPE_IPV6 ? '::' : '0.0.0.0',205'LocalPort' => 0,206}207params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')208bsock = Rex::Socket::TcpServer.create(params)209210# send back the bind success to the client211response = ResponsePacket.new212response.command = REPLY_SUCCEEDED213response.address = bsock.getlocalname[HOST]214response.port = bsock.getlocalname[PORT]215@lsock.put(response.to_binary_s)216217# accept a client connection (2 minute timeout as per the socks4a spec)218begin219::Timeout.timeout(120) do220@rsock = bsock.accept221end222rescue ::Timeout::Error223raise "Timeout reached on accept request."224end225226# close the listening socket227bsock.close228229setup_tcp_relay230response = ResponsePacket.new231response.command = REPLY_SUCCEEDED232response.address = @rsock.peerhost233response.port = @rsock.peerport234response235end236237def handle_command_connect(request)238# perform the connection request239params = {240'PeerHost' => request.address,241'PeerPort' => request.port,242}243params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')244@rsock = Rex::Socket::Tcp.create(params)245246setup_tcp_relay247response = ResponsePacket.new248response.command = REPLY_SUCCEEDED249response.address = @rsock.getlocalname[HOST].split('-')[-1]250response.port = @rsock.getlocalname[PORT]251response252end253254def handle_command_udp_associate(request)255response = ResponsePacket.new256response.command = REPLY_CMD_NOT_SUPPORTED257response258end259260#261# Setup the TcpRelay between lsock and rsock.262#263def setup_tcp_relay264# setup the two way relay for full duplex io265@lsock.extend(TcpRelay)266@rsock.extend(TcpRelay)267# start the socket relays...268@lsock.relay(self, @rsock)269@rsock.relay(self, @lsock)270end271272#273# Stop handling the client connection.274#275def stop276@mutex.synchronize do277unless @closed278begin279@lsock.close if @lsock280rescue281end282283begin284@rsock.close if @rsock285rescue286end287288@client_thread.kill if @client_thread and @client_thread.alive?289@server.remove_client(self)290@closed = true291end292end293end294end295end296end297end298end299300301