2014-12-12 07:20:14 +00:00
|
|
|
# -*- coding: binary -*-
|
|
|
|
|
|
|
|
module Rex
|
|
|
|
module Proto
|
|
|
|
module Kerberos
|
2014-12-14 06:51:44 +00:00
|
|
|
# This class is a representation of kerberos client.
|
2014-12-12 07:20:14 +00:00
|
|
|
class Client
|
2014-12-14 06:51:44 +00:00
|
|
|
# @!attribute hostname
|
|
|
|
# @return [String] The kerberos server hostname
|
2014-12-12 07:20:14 +00:00
|
|
|
attr_accessor :hostname
|
2014-12-14 06:51:44 +00:00
|
|
|
# @!attribute port
|
|
|
|
# @return [Fixnum] The kerberos server port
|
2014-12-12 07:20:14 +00:00
|
|
|
attr_accessor :port
|
2014-12-14 06:51:44 +00:00
|
|
|
# @!attribute protocol
|
|
|
|
# @return [String] The transport protocol used (tcp/udp)
|
2014-12-12 07:20:14 +00:00
|
|
|
attr_accessor :protocol
|
2014-12-14 06:51:44 +00:00
|
|
|
# @!attribute context
|
|
|
|
# @return [Hash]
|
2014-12-12 07:20:14 +00:00
|
|
|
attr_accessor :context
|
2014-12-14 06:51:44 +00:00
|
|
|
# @!attribute connection
|
|
|
|
# @return [IO]
|
2014-12-12 07:20:14 +00:00
|
|
|
attr_accessor :connection
|
|
|
|
|
|
|
|
def initialize(opts = {})
|
|
|
|
self.hostname = opts[:hostname]
|
|
|
|
self.port = opts[:port] || 88
|
|
|
|
self.protocol = opts[:protocol] || 'tcp'
|
|
|
|
self.context = opts[:context] || {}
|
|
|
|
end
|
|
|
|
|
2014-12-14 06:51:44 +00:00
|
|
|
# Creates a connection through a Rex socket
|
|
|
|
#
|
|
|
|
# @return [Rex::Socket::Tcp]
|
|
|
|
# @raise [RuntimeError] if the connection can not be created
|
2014-12-12 07:20:14 +00:00
|
|
|
def connect
|
|
|
|
return connection if connection
|
|
|
|
|
2014-12-14 06:51:44 +00:00
|
|
|
case protocol
|
|
|
|
when 'tcp'
|
|
|
|
self.connection = create_tcp_connection
|
|
|
|
when 'udp'
|
|
|
|
raise ::RuntimeError, 'Kerberos Client: UDP unsupported'
|
|
|
|
else
|
|
|
|
raise ::RuntimeError, 'Kerberos Client: unknown transport protocol'
|
|
|
|
end
|
2014-12-12 07:20:14 +00:00
|
|
|
|
|
|
|
connection
|
|
|
|
end
|
|
|
|
|
2014-12-14 06:51:44 +00:00
|
|
|
# Closes the connection
|
2014-12-12 07:20:14 +00:00
|
|
|
def close
|
|
|
|
if connection
|
|
|
|
connection.shutdown
|
|
|
|
connection.close unless connection.closed?
|
|
|
|
end
|
|
|
|
|
|
|
|
self.connection = nil
|
|
|
|
end
|
|
|
|
|
2014-12-14 06:51:44 +00:00
|
|
|
# Sends a kerberos request through the connection
|
|
|
|
#
|
2014-12-15 01:23:21 +00:00
|
|
|
# @param req [Rex::Proto::Kerberos::Model::KdcRequest] the request to send
|
2014-12-14 06:51:44 +00:00
|
|
|
# @return [Fixnum] the number of bytes sent
|
|
|
|
# @raise [RuntimeError] if the transport protocol is unknown or not supported
|
2014-12-12 07:20:14 +00:00
|
|
|
def send_request(req)
|
|
|
|
connect
|
|
|
|
|
|
|
|
sent = 0
|
|
|
|
case protocol
|
|
|
|
when 'tcp'
|
|
|
|
sent = send_request_tcp(req)
|
|
|
|
when 'udp'
|
|
|
|
sent = send_request_udp(req)
|
|
|
|
else
|
|
|
|
raise ::RuntimeError, 'Kerberos Client: unknown transport protocol'
|
|
|
|
end
|
|
|
|
|
|
|
|
sent
|
|
|
|
end
|
|
|
|
|
2014-12-14 06:51:44 +00:00
|
|
|
# Receives a kerberos response through the connection
|
|
|
|
#
|
2014-12-15 01:23:21 +00:00
|
|
|
# @return [Rex::Proto::Kerberos::Model::KrbError] the kerberos response message
|
2014-12-14 06:51:44 +00:00
|
|
|
# @raise [RuntimeError] if the connection isn't established
|
|
|
|
# @raise [RuntimeError] if the transport protocol is unknown or unsupported
|
|
|
|
# @raise [RuntimeError] if the response can't be parsed
|
2014-12-12 07:20:14 +00:00
|
|
|
def recv_response
|
|
|
|
if connection.nil?
|
|
|
|
raise ::RuntimeError, 'Kerberos Client: connection not established'
|
|
|
|
end
|
|
|
|
|
|
|
|
res = nil
|
|
|
|
case protocol
|
|
|
|
when 'tcp'
|
|
|
|
res = recv_response_tcp
|
|
|
|
when 'udp'
|
|
|
|
res = recv_response_udp
|
|
|
|
else
|
|
|
|
raise ::RuntimeError, 'Kerberos Client: unknown transport protocol'
|
|
|
|
end
|
|
|
|
|
|
|
|
res
|
|
|
|
end
|
|
|
|
|
2014-12-14 06:51:44 +00:00
|
|
|
# Sends a kerberos request, and reads the response through the connection
|
|
|
|
#
|
2014-12-15 01:23:21 +00:00
|
|
|
# @param req [Rex::Proto::Kerberos::Model::KdcRequest] the request to sent
|
|
|
|
# @return [Rex::Proto::Kerberos::Model::KrbError] The kerberos message
|
2014-12-14 06:51:44 +00:00
|
|
|
# @raise [RuntimeError] if the transport protocol is unknown or unsupported
|
|
|
|
# @raise [RuntimeError] if the response can't be parsed
|
2014-12-12 07:20:14 +00:00
|
|
|
def send_recv(req)
|
|
|
|
send_request(req)
|
2014-12-13 08:46:00 +00:00
|
|
|
res = recv_response
|
2014-12-12 07:20:14 +00:00
|
|
|
|
|
|
|
res
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2014-12-14 06:51:44 +00:00
|
|
|
# Creates a TCP connection
|
|
|
|
#
|
|
|
|
# @return [Rex::Socket::Tcp]
|
|
|
|
def create_tcp_connection
|
2014-12-14 06:54:26 +00:00
|
|
|
#timeout = (t.nil? or t == -1) ? 0 : t
|
|
|
|
timeout = 0
|
|
|
|
|
2014-12-14 06:51:44 +00:00
|
|
|
self.connection = Rex::Socket::Tcp.create(
|
|
|
|
'PeerHost' => hostname,
|
|
|
|
'PeerPort' => port.to_i,
|
|
|
|
#'LocalHost' => self.local_host,
|
|
|
|
#'LocalPort' => self.local_port,
|
|
|
|
'Context' => context,
|
|
|
|
#'Proxies' => self.proxies,
|
|
|
|
'Timeout' => timeout
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2014-12-12 07:20:14 +00:00
|
|
|
# Sends a Kerberos Request over a tcp connection
|
|
|
|
#
|
2014-12-15 01:23:21 +00:00
|
|
|
# @param req [Rex::Proto::Kerberos::Model::KdcRequest] the request to send
|
2014-12-12 07:20:14 +00:00
|
|
|
# @return [Fixnum] the number of bytes sent
|
|
|
|
# @raise [RuntimeError] if the request can't be encoded
|
|
|
|
def send_request_tcp(req)
|
|
|
|
data = req.encode
|
2014-12-14 06:51:44 +00:00
|
|
|
length = [data.length].pack('N')
|
|
|
|
connection.put(length + data)
|
2014-12-12 07:20:14 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def send_request_udp(req)
|
|
|
|
raise ::RuntimeError, 'Kerberos Client: UDP unsupported'
|
|
|
|
end
|
|
|
|
|
|
|
|
# Receives a Kerberos Response over a tcp connection
|
|
|
|
#
|
2014-12-15 01:23:21 +00:00
|
|
|
# @return [Rex::Proto::Kerberos::Model::KrbError] the kerberos message response
|
2014-12-14 23:54:17 +00:00
|
|
|
# @raise [RuntimeError] if the response can't be processed
|
2014-12-14 22:32:25 +00:00
|
|
|
# @raise [EOFError] if expected data can't be read
|
2014-12-12 07:20:14 +00:00
|
|
|
def recv_response_tcp
|
2014-12-14 22:32:25 +00:00
|
|
|
length_raw = connection.get_once(4)
|
|
|
|
unless length_raw && length_raw.length == 4
|
|
|
|
raise ::RuntimeError, 'Kerberos Client: failed to read response'
|
|
|
|
end
|
|
|
|
length = length_raw.unpack('N')[0]
|
2014-12-12 07:20:14 +00:00
|
|
|
|
2014-12-14 22:32:25 +00:00
|
|
|
data = connection.get_once(length)
|
|
|
|
unless data && data.length == length
|
|
|
|
raise ::RuntimeError, 'Kerberos Client: failed to read response'
|
|
|
|
end
|
|
|
|
|
2014-12-14 23:54:17 +00:00
|
|
|
res = decode_kerb_response(data)
|
|
|
|
|
|
|
|
res
|
2014-12-12 07:20:14 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def recv_response_udp
|
|
|
|
raise ::RuntimeError, 'Kerberos Client: UDP unsupported'
|
|
|
|
end
|
2014-12-14 23:54:17 +00:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
# Decodes a Kerberos response
|
|
|
|
#
|
|
|
|
# @param input [String] the raw response message
|
2014-12-15 01:23:21 +00:00
|
|
|
# @return [Rex::Proto::Kerberos::Model::KrbError] the kerberos message response
|
2014-12-14 23:54:17 +00:00
|
|
|
# @raise [RuntimeError] if the response can't be processed
|
|
|
|
def decode_kerb_response(data)
|
|
|
|
asn1 = OpenSSL::ASN1.decode(data)
|
|
|
|
msg_type = asn1.value[0].value[1].value[0].value
|
|
|
|
|
|
|
|
case msg_type
|
|
|
|
when Rex::Proto::Kerberos::Model::KRB_ERROR
|
2014-12-15 01:23:21 +00:00
|
|
|
res = Rex::Proto::Kerberos::Model::KrbError.decode(asn1)
|
2014-12-14 23:54:17 +00:00
|
|
|
else
|
|
|
|
raise ::RuntimeError, 'Kerberos Client: Unknown response'
|
|
|
|
end
|
|
|
|
|
|
|
|
res
|
|
|
|
end
|
2014-12-12 07:20:14 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|