metasploit-framework/lib/rex/proto/drda/packet.rb

254 lines
7.1 KiB
Ruby

# -*- coding: binary -*-
require 'rex/proto/drda'
module Rex
module Proto
module DRDA
class Error < StandardError; end
class RespError < Error; end
# See:
# http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.drda/db2z_excsat.htm
class MGRLVLLS_PARAM < Struct.new(:length, :codepoint, :payload)
def initialize(args={})
self[:codepoint] = Constants::MGRLVLLS
self[:payload] = "\x14\x03\x00\x0a\x24\x07\x00\x0a" +
"\x14\x74\x00\x05\x24\x0f\x00\x08" +
"\x14\x40\x00\x09\x1c\x08\x04\xb8"
self[:length] = self[:payload].to_s.size+4
end
def to_s
self.to_a.pack("nna*")
end
end
# Currently, only takes a MGRLVLLS param. Extend the struct
# when more parameters are defined.
class EXCSAT_DDM < Struct.new(:length, :magic, :format, :correlid, :length2,
:codepoint, :mgrlvlls)
def initialize(args={})
self[:magic] = 0xd0
self[:format] = 0x41
self[:correlid] = 1
self[:codepoint] = Constants::EXCSAT
self[:mgrlvlls] = args[:mgrlvlls] || MGRLVLLS_PARAM.new.to_s
self[:length] = (10 + self[:mgrlvlls].to_s.size)
self[:length2] = self[:length]-6
end
def to_s
packstr = "nCCnnn"
packstr += "a*" # Pack smarter as more params are added.
self.to_a.pack(packstr)
end
end
# See http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.drda/db2z_accsec.htm
# for all sorts of info about SECMEC.
class SECMEC_PARAM < Struct.new(:length, :codepoint, :payload)
def initialize(args={})
self[:length] = 6
self[:codepoint] = Constants::SECMEC
self[:payload] = 3 # Plaintext username and password.
end
def to_s
self.to_a.pack("nnn")
end
end
# Relational Database name parameter.
class RDBNAM_PARAM < Struct.new(:length, :codepoint, :payload)
def initialize(args={})
self[:length] = 22 # Since the database name is padded out.
self[:codepoint] = Constants::RDBNAM
self[:payload] = encode(args[:payload].to_s)
end
def encode(str)
Rex::Text.to_ebcdic([str].pack("A18"))
end
def payload=(str)
self[:payload] = encode(str.to_s)
end
def to_s
self.to_a.pack("nna18")
end
end
# The ACCSEC DDM is responsible for picking the security mechanism (SECMEC)
# which, in our case, will always be plain text username and password. It
# also sets the relational database name (RDBNAM), if specified. You need
# one to login, but not to probe.
class ACCSEC_DDM < Struct.new(:length, :magic, :format, :correlid, :length2,
:codepoint, :secmec, :rdbnam)
def initialize(args={})
self[:magic] = 0xd0
self[:format] = args[:format] || 0x01
self[:correlid] = 2
self[:codepoint] = Constants::ACCSEC
self[:secmec] = SECMEC_PARAM.new.to_s
if args[:dbname] # Include a database name if we're given one.
self[:rdbnam] = RDBNAM_PARAM.new(:payload => args[:dbname]).to_s
end
self[:length] = 10 + self[:secmec].to_s.size + self[:rdbnam].to_s.size
self[:length2] = self[:length]-6
end
def dbname=(str)
self[:rdbnam] = RDBNAM_PARAM.new(:payload => args[:dbname]).to_s
end
def to_s
packstr = "nCCnnna6"
packstr += "a22" if self[:rdbnam]
self.to_a.pack(packstr)
end
end
class DDM_PARAM < Struct.new(:length, :codepoint, :payload)
def read(str="")
raise DRDA::Error, "Input isn't a String." if !str.kind_of? String
raise DRDA::RespError, "DDM_PARAM is too short" if str.size < 4
(self[:length], self[:codepoint]) =
str.unpack("nn")
raise DRDA::RespError, "DDM_PARAM Length is too short" if self[:length] < 4
rest = str[4,self[:length]-4] # If it's negative or whatever, it'll end up as "".
self[:payload] = rest.to_s[0,self[:length]-4]
return self
end
def to_s
self.to_a.pack("nna*")
end
end
class BASIC_DDM < Struct.new(:length, :magic, :format, :correlid,
:length2, :codepoint, :payload)
def initialize
self[:payload] = []
end
def read(str="")
self[:payload].clear
raise DRDA::Error, "Input isn't a String." if !str.kind_of? String
raise DRDA::RespError, "Response is too short." if str.size < 10
(self[:length],self[:magic],self[:format],
self[:correlid],self[:length2],self[:codepoint]) =
str.unpack("nCCnnn")
sanity_check
rest = str[10,self[:length2]-4]
i = 0
while (i < rest.size)
if self[:codepoint] == Constants::SQLCARD # These aren't DDM's.
this_param = rest[i,self[:length]-10]
else
this_param = DDM_PARAM.new.read(rest[i,rest.size])
end
self[:payload] << this_param
i += this_param.to_s.size
end
return self
end
# Just a quick test.
def sanity_check
if self[:length] < 10
raise DRDA::RespError, "DDM Length is too short."
elsif self[:length2] < 4
raise DRDA::RespError, "DDM Length2 is too short."
elsif self[:length]-6 != self[:length2]
raise DRDA::RespError, "Codepoint: 0x#{self[:codepoint].to_s(16)} DDM Length2 (0x#{self[:length2].to_s(16)}) isn't six less than Length (0x#{self[:length].to_s(16)})"
end
end
def to_s
self.to_a.pack("nCCnnn") + self[:payload].map {|x| x.to_s}.join
end
end
class SERVER_PACKET < Array
def read(str="")
raise DRDA::Error, "Input isn't a String." if !str.kind_of? String
self.clear
i = 0
while(i < str.size)
this_ddm = BASIC_DDM.new.read(str[i,str.size])
self << this_ddm
i += this_ddm.to_s.size
end
return self
end
def to_s; self.join; end
def sz; self.to_s.size; end
end
class PASSWORD_PARAM < Struct.new(:length, :codepoint, :payload)
def initialize(args={})
self[:codepoint] = Constants::PASSWORD
self[:payload] = Rex::Text.to_ebcdic(args[:payload].to_s)
self[:length] = self[:payload].size + 4
end
def encode(str)
Rex::Text.to_ebcdic(str)
end
def to_s
self.to_a.pack("nna*")
end
end
class USERID_PARAM < Struct.new(:length, :codepoint, :payload)
def initialize(args={})
self[:codepoint] = Constants::USERID
self[:payload] = Rex::Text.to_ebcdic(args[:payload].to_s)
self[:length] = self[:payload].size + 4
end
def encode(str)
Rex::Text.to_ebcdic(str)
end
def to_s
self.to_a.pack("nna*")
end
end
class SECCHK_DDM < Struct.new(:length, :magic, :format, :correlid, :length2,
:codepoint, :secmec, :rdbnam, :password, :userid)
def initialize(args={}) # Takes :dbname, :dbpass, :dbuser
self[:magic] = 0xd0
self[:format] = 0x01
self[:correlid] = 2
self[:codepoint] = Constants::SECCHK
self[:secmec] = SECMEC_PARAM.new.to_s
if args[:dbname] # Include a database name if we're given one.
self[:rdbnam] = RDBNAM_PARAM.new(:payload => args[:dbname]).to_s
end
self[:password] = PASSWORD_PARAM.new(:payload => args[:dbpass]).to_s
self[:userid] = USERID_PARAM.new(:payload => args[:dbuser]).to_s
self[:length] = ( 10 + self[:secmec].to_s.size + self[:rdbnam].to_s.size +
self[:password].to_s.size + self[:userid].to_s.size )
self[:length2] = self[:length]-6
end
def dbname=(str)
self[:rdbnam] = RDBNAM_PARAM.new(:payload => args[:dbname]).to_s
end
def to_s
packstr = "nCCnnna6"
packstr += "a22" if self[:rdbnam]
packstr += "a*a*" # username and password
self.to_a.pack(packstr)
end
end
end
end
end