Skip to content

Commit

Permalink
Land rapid7#10102, SOCKS5 updates for BIND, parsing specs, refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
asoto-r7 committed May 30, 2018
2 parents 39679f6 + da26665 commit 829e1c3
Show file tree
Hide file tree
Showing 8 changed files with 658 additions and 671 deletions.
650 changes: 9 additions & 641 deletions lib/rex/proto/proxy/socks5.rb

Large diffs are not rendered by default.

109 changes: 109 additions & 0 deletions lib/rex/proto/proxy/socks5/packet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# -*- coding: binary -*-

require 'bindata'
require 'rex/socket'

module Rex
module Proto
module Proxy

module Socks5
SOCKS_VERSION = 5

#
# Mixin for socks5 packets to include an address field.
#
module Address
ADDRESS_TYPE_IPV4 = 1
ADDRESS_TYPE_DOMAINNAME = 3
ADDRESS_TYPE_IPV6 = 4

def address
addr = address_array.to_ary.pack('C*')
if address_type == ADDRESS_TYPE_IPV4 || address_type == ADDRESS_TYPE_IPV6
addr = Rex::Socket.addr_ntoa(addr)
end
addr
end

def address=(value)
if Rex::Socket.is_ipv4?(value)
address_type.assign(ADDRESS_TYPE_IPV4)
domainname_length.assign(0)
value = Rex::Socket.addr_aton(value)
elsif Rex::Socket.is_ipv6?(value)
address_type.assign(ADDRESS_TYPE_IPV6)
domainname_length.assign(0)
value = Rex::Socket.addr_aton(value)
else
address_type.assign(ADDRESS_TYPE_DOMAINNAME)
domainname_length.assign(value.length)
end
address_array.assign(value.unpack('C*'))
end

def address_length
case address_type
when ADDRESS_TYPE_IPV4
4
when ADDRESS_TYPE_DOMAINNAME
domainname_length
when ADDRESS_TYPE_IPV6
16
else
0
end
end
end

class AuthRequestPacket < BinData::Record
endian :big

uint8 :version, :initial_value => SOCKS_VERSION
uint8 :supported_methods_length
array :supported_methods, :type => :uint8, :initial_length => :supported_methods_length
end

class AuthResponsePacket < BinData::Record
endian :big

uint8 :version, :initial_value => SOCKS_VERSION
uint8 :chosen_method
end

class Packet < BinData::Record
include Address
endian :big
hide :reserved, :domainname_length

uint8 :version, :initial_value => SOCKS_VERSION
uint8 :command
uint8 :reserved
uint8 :address_type
uint8 :domainname_length, :onlyif => lambda { address_type == ADDRESS_TYPE_DOMAINNAME }
array :address_array, :type => :uint8, :initial_length => lambda { address_length }
uint16 :port
end

class RequestPacket < Packet
end

class ResponsePacket < Packet
end

class UdpPacket < BinData::Record
include Address
endian :big
hide :reserved, :domainname_length

uint16 :reserved
uint8 :frag
uint8 :address_type
uint8 :domainname_length, :onlyif => lambda { address_type == ADDRESS_TYPE_DOMAINNAME }
array :address_array, :type => :uint8, :initial_length => lambda { address_length }
uint16 :port
end
end
end
end
end
105 changes: 105 additions & 0 deletions lib/rex/proto/proxy/socks5/server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# -*- coding: binary -*-

require 'thread'
require 'rex/logging'
require 'rex/socket'
require 'rex/proto/proxy/socks5/server_client'

module Rex
module Proto
module Proxy

module Socks5
#
# A SOCKS5 proxy server.
#
class Server
#
# Create a new SOCKS5 server.
#
def initialize(opts={})
@opts = { 'ServerHost' => '0.0.0.0', 'ServerPort' => 1080 }
@opts = @opts.merge(opts)
@server = nil
@clients = ::Array.new
@running = false
@server_thread = nil
end

#
# Check if the server is running.
#
def is_running?
return @running
end

#
# Start the SOCKS5 server.
#
def start
begin
# create the servers main socket (ignore the context here because we don't want a remote bind)
@server = Rex::Socket::TcpServer.create('LocalHost' => @opts['ServerHost'], 'LocalPort' => @opts['ServerPort'])
# signal we are now running
@running = true
# start the servers main thread to pick up new clients
@server_thread = Rex::ThreadFactory.spawn("SOCKS5ProxyServer", false) do
while @running
begin
# accept the client connection
sock = @server.accept
# and fire off a new client instance to handle it
ServerClient.new(self, sock, @opts).start
rescue
wlog("SOCKS5.start - server_thread - #{$!}")
end
end
end
rescue
wlog("SOCKS5.start - #{$!}")
return false
end
return true
end

#
# Block while the server is running.
#
def join
@server_thread.join if @server_thread
end

#
# Stop the SOCKS5 server.
#
def stop
if @running
# signal we are no longer running
@running = false
# stop any clients we have (create a new client array as client.stop will delete from @clients)
clients = @clients.dup
clients.each do | client |
client.stop
end
# close the server socket
@server.close if @server
# if the server thread did not terminate gracefully, kill it.
@server_thread.kill if @server_thread and @server_thread.alive?
end
return !@running
end

def add_client(client)
@clients << client
end

def remove_client(client)
@clients.delete(client)
end

attr_reader :opts
end
end
end
end
end
Loading

0 comments on commit 829e1c3

Please sign in to comment.