CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/proto/drda/packet.rb
Views: 11704
1
# -*- coding: binary -*-
2
3
4
module Rex
5
module Proto
6
module DRDA::Packet
7
8
class Error < StandardError; end
9
class RespError < Error; end
10
11
# See:
12
# http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.drda/db2z_excsat.htm
13
class MGRLVLLS_PARAM < Struct.new(:length, :codepoint, :payload)
14
def initialize(args={})
15
self[:codepoint] = Rex::Proto::DRDA::Constants::MGRLVLLS
16
self[:payload] = "\x14\x03\x00\x0a\x24\x07\x00\x0a" +
17
"\x14\x74\x00\x05\x24\x0f\x00\x08" +
18
"\x14\x40\x00\x09\x1c\x08\x04\xb8"
19
self[:length] = self[:payload].to_s.size+4
20
end
21
def to_s
22
self.to_a.pack("nna*")
23
end
24
end
25
26
# Currently, only takes a MGRLVLLS param. Extend the struct
27
# when more parameters are defined.
28
class EXCSAT_DDM < Struct.new(:length, :magic, :format, :correlid, :length2,
29
:codepoint, :mgrlvlls)
30
31
def initialize(args={})
32
self[:magic] = 0xd0
33
self[:format] = 0x41
34
self[:correlid] = 1
35
self[:codepoint] = Rex::Proto::DRDA::Constants::EXCSAT
36
self[:mgrlvlls] = args[:mgrlvlls] || MGRLVLLS_PARAM.new.to_s
37
self[:length] = (10 + self[:mgrlvlls].to_s.size)
38
self[:length2] = self[:length]-6
39
end
40
41
def to_s
42
packstr = "nCCnnn"
43
packstr += "a*" # Pack smarter as more params are added.
44
self.to_a.pack(packstr)
45
end
46
end
47
48
# See http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.drda/db2z_accsec.htm
49
# for all sorts of info about SECMEC.
50
class SECMEC_PARAM < Struct.new(:length, :codepoint, :payload)
51
def initialize(args={})
52
self[:length] = 6
53
self[:codepoint] = Rex::Proto::DRDA::Constants::SECMEC
54
self[:payload] = 3 # Plaintext username and password.
55
end
56
def to_s
57
self.to_a.pack("nnn")
58
end
59
end
60
61
# Relational Database name parameter.
62
class RDBNAM_PARAM < Struct.new(:length, :codepoint, :payload)
63
def initialize(args={})
64
self[:length] = 22 # Since the database name is padded out.
65
self[:codepoint] = Rex::Proto::DRDA::Constants::RDBNAM
66
self[:payload] = encode(args[:payload].to_s)
67
end
68
69
def encode(str)
70
Rex::Text.to_ebcdic([str].pack("A18"))
71
end
72
73
def payload=(str)
74
self[:payload] = encode(str.to_s)
75
end
76
77
def to_s
78
self.to_a.pack("nna18")
79
end
80
81
end
82
83
# The ACCSEC DDM is responsible for picking the security mechanism (SECMEC)
84
# which, in our case, will always be plain text username and password. It
85
# also sets the relational database name (RDBNAM), if specified. You need
86
# one to login, but not to probe.
87
class ACCSEC_DDM < Struct.new(:length, :magic, :format, :correlid, :length2,
88
:codepoint, :secmec, :rdbnam)
89
def initialize(args={})
90
self[:magic] = 0xd0
91
self[:format] = args[:format] || 0x01
92
self[:correlid] = 2
93
self[:codepoint] = Rex::Proto::DRDA::Constants::ACCSEC
94
self[:secmec] = SECMEC_PARAM.new.to_s
95
if args[:dbname] # Include a database name if we're given one.
96
self[:rdbnam] = RDBNAM_PARAM.new(:payload => args[:dbname]).to_s
97
end
98
self[:length] = 10 + self[:secmec].to_s.size + self[:rdbnam].to_s.size
99
self[:length2] = self[:length]-6
100
end
101
def dbname=(str)
102
self[:rdbnam] = RDBNAM_PARAM.new(:payload => args[:dbname]).to_s
103
end
104
def to_s
105
packstr = "nCCnnna6"
106
packstr += "a22" if self[:rdbnam]
107
self.to_a.pack(packstr)
108
end
109
end
110
111
class DDM_PARAM < Struct.new(:length, :codepoint, :payload)
112
113
def read(str="")
114
raise DRDA::Error, "Input isn't a String." if !str.kind_of? String
115
raise DRDA::RespError, "DDM_PARAM is too short" if str.size < 4
116
(self[:length], self[:codepoint]) =
117
str.unpack("nn")
118
raise DRDA::RespError, "DDM_PARAM Length is too short" if self[:length] < 4
119
rest = str[4,self[:length]-4] # If it's negative or whatever, it'll end up as "".
120
self[:payload] = rest.to_s[0,self[:length]-4]
121
return self
122
end
123
124
def to_s
125
self.to_a.pack("nna*")
126
end
127
128
end
129
130
class BASIC_DDM < Struct.new(:length, :magic, :format, :correlid,
131
:length2, :codepoint, :payload)
132
def initialize
133
self[:payload] = []
134
end
135
136
def read(str="")
137
self[:payload].clear
138
raise DRDA::Error, "Input isn't a String." if !str.kind_of? String
139
raise DRDA::RespError, "Response is too short." if str.size < 10
140
(self[:length],self[:magic],self[:format],
141
self[:correlid],self[:length2],self[:codepoint]) =
142
str.unpack("nCCnnn")
143
sanity_check
144
rest = str[10,self[:length2]-4]
145
i = 0
146
while (i < rest.size)
147
if self[:codepoint] == Rex::Proto::DRDA::Constants::SQLCARD # These aren't DDM's.
148
this_param = rest[i,self[:length]-10]
149
else
150
this_param = DDM_PARAM.new.read(rest[i,rest.size])
151
end
152
self[:payload] << this_param
153
i += this_param.to_s.size
154
end
155
return self
156
end
157
158
# Just a quick test.
159
def sanity_check
160
if self[:length] < 10
161
raise DRDA::RespError, "DDM Length is too short."
162
elsif self[:length2] < 4
163
raise DRDA::RespError, "DDM Length2 is too short."
164
elsif self[:length]-6 != self[:length2]
165
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)})"
166
end
167
end
168
169
def to_s
170
self.to_a.pack("nCCnnn") + self[:payload].map {|x| x.to_s}.join
171
end
172
173
end
174
175
class SERVER_PACKET < Array
176
177
def read(str="")
178
raise DRDA::Error, "Input isn't a String." if !str.kind_of? String
179
self.clear
180
i = 0
181
while(i < str.size)
182
this_ddm = BASIC_DDM.new.read(str[i,str.size])
183
self << this_ddm
184
i += this_ddm.to_s.size
185
end
186
return self
187
end
188
189
def to_s; self.join; end
190
def sz; self.to_s.size; end
191
192
end
193
194
class PASSWORD_PARAM < Struct.new(:length, :codepoint, :payload)
195
def initialize(args={})
196
self[:codepoint] = Rex::Proto::DRDA::Constants::PASSWORD
197
self[:payload] = Rex::Text.to_ebcdic(args[:payload].to_s)
198
self[:length] = self[:payload].size + 4
199
end
200
def encode(str)
201
Rex::Text.to_ebcdic(str)
202
end
203
def to_s
204
self.to_a.pack("nna*")
205
end
206
end
207
208
class USERID_PARAM < Struct.new(:length, :codepoint, :payload)
209
def initialize(args={})
210
self[:codepoint] = Rex::Proto::DRDA::Constants::USERID
211
self[:payload] = Rex::Text.to_ebcdic(args[:payload].to_s)
212
self[:length] = self[:payload].size + 4
213
end
214
def encode(str)
215
Rex::Text.to_ebcdic(str)
216
end
217
def to_s
218
self.to_a.pack("nna*")
219
end
220
end
221
222
class SECCHK_DDM < Struct.new(:length, :magic, :format, :correlid, :length2,
223
:codepoint, :secmec, :rdbnam, :password, :userid)
224
def initialize(args={}) # Takes :dbname, :dbpass, :dbuser
225
self[:magic] = 0xd0
226
self[:format] = 0x01
227
self[:correlid] = 2
228
self[:codepoint] = Rex::Proto::DRDA::Constants::SECCHK
229
self[:secmec] = SECMEC_PARAM.new.to_s
230
if args[:dbname] # Include a database name if we're given one.
231
self[:rdbnam] = RDBNAM_PARAM.new(:payload => args[:dbname]).to_s
232
end
233
self[:password] = PASSWORD_PARAM.new(:payload => args[:dbpass]).to_s
234
self[:userid] = USERID_PARAM.new(:payload => args[:dbuser]).to_s
235
self[:length] = ( 10 + self[:secmec].to_s.size + self[:rdbnam].to_s.size +
236
self[:password].to_s.size + self[:userid].to_s.size )
237
self[:length2] = self[:length]-6
238
end
239
def dbname=(str)
240
self[:rdbnam] = RDBNAM_PARAM.new(:payload => args[:dbname]).to_s
241
end
242
def to_s
243
packstr = "nCCnnna6"
244
packstr += "a22" if self[:rdbnam]
245
packstr += "a*a*" # username and password
246
self.to_a.pack(packstr)
247
end
248
end
249
250
end
251
end
252
end
253
254
255